Splitting a Monolith Reporting Web App with Blazor
Over the last several years, there have been many discussions regarding "microservices" architecture. This simply means designing software as a bunch of independently deployable services. This article gives an example of such a design for a web application that is built with Blazor framework and ActiveReports 14.
"Monolith architecture" is the opposite approach to using microservices. An example of the monolith is a reporting web application that is built with ASP.NET and AR JS Viewer and that fulfills multiple responsibilities:
- Hosts the client-facing application, for example, an HTML page that includes the JS report viewer
- Contains reports templates, for example, *.rdlx files
- Handles requests from the JS Report Viewer to render or export reports
In order to build the client-faced application, developers can choose from a wide range of front-end frameworks such as React, Angular, or Vue. The ActiveReports JS Viewer is supported for any front-end framework and there are multiple related samples that can be found in the ActiveReports Github repository.
Recently, Microsoft came up with Blazor framework that allows building client-facing applications with C# instead of JavaScript.
Blazor supports two hosting models:
- Blazor Server - the client-side events (such as click-on a button) are handled on the server-side. The interaction between the client and the server is performed with SignalR framework. The server-side in this scenario is an ASP.NET Core application.
- Blazor Web Assembly - the entire application, its dependencies, and the .NET runtime are downloaded to the browser. The role of the server-side in this scenario is reduced to hosting the web assembly file. An ASP.NET Core web server isn't required to perform that and deployment scenarios allow a serverless approach, for example, distributing the application from a CDN.
Both approaches have pros and cons that are described in detail in the documentation.
For a reporting web application that is built with the Blazor Server hosting model, it's surely possible to utilize the monolith approach described above. But what if you want to take advantage of a Web Assembly hosting model and distribute the application via a CDN?
A Reporting Service that runs and exports reports can't be invoked from within the web assembly because it's not supported. CDN implies no code execution, but distributing the static content.
In that case, Reporting Service could be decoupled into the separate application.
This approach works for both of the Blazor Hosting Models:
In addition, there are a couple of advantages of keeping the Reporting Services in the separate application:
-
Independent development and deployment - you don't have to re-build the client-facing app after updating an existing report or adding a new one.
-
Re-usability - the same reporting service can serve as the report library for multiple web-applications, for example, you could offer web-based report designer.
Let's build the reporting application with Blazor and microservices architecture.
Building Reporting Services Application
In Visual Studio 2019, create ASP.NET Core Web Application, call it "BlazorReporting," select ASP.NET Core 3.1 target and "API" template and uncheck "Configure for HTTPS" checkbox:
Add GrapeCity.ActiveReports.Aspnetcore.Viewer Nuget package in the newly created project
Add a new Folder called "Reports" and add any existing report template to this folder. For example the attached project is using "Medical Reports - BloodTestReport.rdlx" report from https://github.com/activereports/ReportSamples
Set Build Action for the report template file to "Embedded Resource."
Open THE Startup.cs file.
Add the following code at the beginning of the file:
using GrapeCity.ActiveReports.Aspnetcore.Viewer;
Add the following code n the ConfigureServices method.
services.AddReporting();
Add the following code in the Configure method
app.UseReporting(settings =>
{
settings.UseEmbeddedTemplates("BlazorReporting.Reports", typeof(Startup).Assembly);
settings.UseCompression = true;
});
The application should be able to handle cross-domain requests, so it has to be configured accordingly.
Add the following code in ConfigureServices method:
services.AddCors(options =>
{
options.AddPolicy("AllowAll", builder =>
{
builder.SetIsOriginAllowed(origin => new Uri(origin).Host == "localhost")
.AllowCredentials()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
And the following line in the Configure method:
app.UseCors("AllowAll");
Note: CORS configuration depends on the application requirements, here we are using the simplest one that allows requests from any "localhost" origins
Build the Application
Open command line, navigate to the application's bin\Debug\netcoreapp3.1 folder and run BlazorReporting.exe
The reporting service is now available via the URL that specified in the Properties/launchSettings.json file, profiles/BlazorReporting section, for example: http://localhost:5000
Building Client-Facing Application
In Visual Studio 2019, add the new Blazor App project in the solution, call it "BlazorReportingApp", choose Blazor Server App template and uncheck "configure HTTPS" checkbox
Install AR Js Viewer npm package and copy:
- dist\jsViewer.min.css file to the "wwwroot/css" folder of the Blazor Application
- dist\jsViewer.min.js file to the "wwwroot/js" folder(it needs to be created)
Add the following code at the end of <head>
section in Pages_Host.cshtml file of the Blazor Application:
<link href="~/css/jsViewer.min.css" rel="stylesheet" />
<script type="text/javascript" src="~/js/jsViewer.min.js"></script>
<script>
window.initJsViewer = (containerSelector) => {
GrapeCity.ActiveReports.JSViewer.create({
element: containerSelector,
reportService: {
url: "http://localhost:5000/api/reporting"
},
reportID: "Medical Reports - BloodTestReport.rdlx"
});
}
</script>
Replace localhost:5000 with the URL that the Reporting Service Application listens to and AnnualReport.rdlx with the file name of the report template that you have added in the Reporting Service application.
Add new Razor Component in the Page folder of the application, call it "ARViewer" and replace the default code with the following one:
@page "/arviewer"
@inject IJSRuntime JSRuntime;
<div class="theme-default" id="viewerContainer"></div>
@code{
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("initJsViewer", "#viewerContainer");
}
}
}
Add the link to the newly created page into the Shared\NavMenu.razor component:
<li class="nav-item px-3">
<NavLink class="nav-link" href="arviewer">
<span class="oi oi-list-rich" aria-hidden="true"></span> ArViewer
</NavLink>
</li>
Set the Blazor App as the Startup Project, run it and in click on ARViewer item in the menu, here is the expected result:
Download Attachments:
The architecture diagram (BlazorDiagram) | Project sources (BlazorReporting.zip)
If you have any questions, please leave them in the comment thread below.
Happy coding!