New Blazor/.NET 6 powered website
One of the best ways to learn new technologies is to come up with an idea and just build something. In my case, I wanted to learn the new Blazor technology from Microsoft, as well as build a .NET 6 API from scratch. I had previously built a system I call my ContentHub, which is a centralized content management system that can feed content to any number of websites. This was built using .NET 4.8, WebAPI, and the UI was done with MVC and KnockoutJS. I'll eventually rewrite the management interface with .NET 6 and Angular, but that's a task for another day.
The database for the ContentHub site is hosted in SQL Server and is the same design I'm going to continue using, so the next step was to look at the existing API and determine whether it was still the right design. In the past, I had multiple API endpoints, but I decided the simplest way to look for content was by path. A path would bring back either a folder, an individual article, or potentially a download or image. I wrapped all these return values into a single wrapper response class. The caller of the API will be intelligent enough to look at the response and figure out what to do with it.
The new .NET 6 API is done from scratch, whereas the .NET 6 API I work on at my full-time job was converted from a .NET 3.1 version. This means that the Program.cs is significantly simpler in .NET 6. I took the time to add on Swagger and AutoMapper, which was a new component I wanted to learn. While this API is primarily for read-only access, there were a few places it came in handy.
In the previous version, I had built a couple of private NuGet packages to make it easier to access the CMS. However, when I did these packages, I didn't lay out the automatic build pipelines correctly and any time I updated the CMS UI, the NuGet packages were built and didn't automatically increment their version numbers. This time, the two projects each have their own build pipelines and automatically release to my private NuGet server. The NCS.CoreFramework project contains some common custom exception types that I use frequently. There will likely be other components added to this library in the future as I build more and more .NET 6 projects. The NCS ContentHub client contains the request and response payload classes as well as a client class that takes care of making all the calls to the ContentHub API. While this could be done manually (as it might in an Angular project), I have several .NET projects that can use this client instead of having to duplicate work.
Finally, I started on the website itself. I decided to try Blazor as an alternative to Angular or VueJS, both of which I've used on projects in the past. I used the helpful videos done by Carl Franklin at https://blazortrain.com/ to get a basic understanding of the technology. Within a little bit of time, I had converted my existing AdminLTE template to Blazor format.
The next step was to integrate the website with the ContentHub. Following the instructions, I created a service class in the Blazor application that made calls into the ContentHub client library, pulled from my private NuGet repository. I learned a lot as I went through the process, such as how to show HTML retrieved directly on the page using the MarkupString attribute. The home page worked pretty easily, since the content is pulled from a single article, and the latest articles are pulled from a second API call.
The more difficult part of the application was to provide a dynamic path option so that as I add articles, folders, etc. the website would update automatically. I had done this under MVC but it took a little bit of searching for Blazor resources, since there aren't nearly as many samples out there.
The trick to this was the @page attribute at the top of each Razor page. In my case, I had my home page with this directive:
For my dynamic content page, which would either show an individual article or a list of articles in a folder, the directive looks like this:
The asterisk on the front of the pageURL variable tells the path resolution to ignore the slashes in the URL. Without it, this would resolve /articles and /casestudies, but wouldn't go further down than that.
The next problem I ran into was more of a conceptual one in terms of how the page is built. In languages like Active Server Pages, you could mix your code with your HTML, and at first glance, Blazor appears to do the same thing. However, I had to reorient my thinking to construct data values in the code section first, after which they would appear in the HTML portion. For instance, I wanted to create a linked list of related tags. I started by doing a foreach loop in the HTML portion, but I needed a little bit of logic to show the separator character. Because you can't really embed logic within the loop, I ended up crafting the HTML in the code portion of the page and then just injected it to the HTML portion using an @ variable. Once I got over this hump, the page started working great.
I have a few more things to work out, such as how to cleanly inject images into the HTML and how to get files out of the CMS to download without having really strange and foreign URLs. However, the vast majority of the site is working well and with a little bit of Azure DevOps, it's automatically deploying to my web server and the content is feeding from the CMS.
I look forward to doing more with Blazor, especially with some smaller applications I maintain, now that I've figured out how to wire it to my content management system.