Suppose you built an ASP.NET Core application that displays reports in ActiveReports JavaScript Viewer, and you need to collect the reporting usage analytics. Possible usage parameters include how often a report is requested, which parameters are passed to a report, and how long it takes to render a report.
Collecting analytics requires 2 sub-tasks:
In our example, ASP.NET Core middleware intercepts requests from JS Viewer to the ASP.NET Core back-end. This article shows how to implement these sub-tasks:
In Visual Studio 2019 create a new "ActiveReports 14 JS Viewer Core MVC" Application. This is the project template for an ASP.NET Core 3.1 application that displays reports with JS Viewer.
Open the automatically added Reports\RdlReport1.rdlx report
IMAGE 1
By default, the application logs ASP.NET Core Diagnostics about requests being sent to the back-end. Before a report is rendered, the POST request "api/reporting/reports/RdlReport1.rdlx/render" is sent to the back-end.
IMAGE 2 In order to access the report name and its parameters, the application intercepts the "render" request, extracts the required information, and saves it somewhere like the database.
To intercept the request, the application injects the branch into the middleware pipeline (use MapWhen branch).
private static Regex RenderRequestRegex = new Regex("/api/reporting/reports/(.+)/render");
class RenderRequest
{
public Dictionary<string, object[]> Parameters { get; set; }
}
This middleware branch is called when the viewer sends the "render" request described above. The report name and report parameters are extracted from the request's path and body respectively.
Note: for simplicity, this information is sent to the logger output with the level "warning". In the actual application, it is saved to the database.
app.UseWhen(context => RenderRequestRegex.IsMatch(context.Request.Path.Value), (app) =>
{
app.Use(async (context, next) =>
{
// extract report name from the request
var reportName = RenderRequestRegex.Match(context.Request.Path.Value).Groups[1].Value;
logger.LogWarning($"Report {reportName} has been requested at {DateTime.Now:dd-MM-yyyy HH:mm:ss}");
context.Request.EnableBuffering();
using (var reader = new StreamReader(
context.Request.Body,
encoding: Encoding.UTF8,
detectEncodingFromByteOrderMarks: false,
bufferSize: 1024,
leaveOpen: true))
{
var body = await reader.ReadToEndAsync();
// extract parameters values from the body request
var renderRequest = Newtonsoft.Json.JsonConvert.DeserializeObject<RenderRequest>(body);
foreach(var kv in renderRequest.Parameters)
{
logger.LogWarning($"Parameter {kv.Key} has values {string.Join(",", kv.Value)}");
}
context.Request.Body.Position = 0;
}
await next.Invoke();
});
});
Using the same technique, it is possible to add authorization to the report rendering processes, for example, granting certain user groups access to specific reports.
Note that if the line await next.Invoke(); is not invoked as it's shown in the code at step 8 then the request processing is terminated. So, await next.Invoke() can be called conditionally.
The full sample code is available in the attached project.