随着信息量的增加,对REST API数据进行灵活报告的需求也在增长。报告API扩展了现有的REST API,使其数据可以在易于使用的工具中进行分析。对于具有广泛REST API的项目,开发了一个解决方案,可以根据一致的模式查询GET端点数据。
API开发者指定哪些GET端点可用于报告。用户可以在UI中浏览、过滤、排序,并将数据转换为各种格式。可以在单个报告中实现具有链接数据的复杂查询。报告API包括一个端点,充当查询和GET端点之间的代理。无需进一步定制即可评估新的GET端点。提供了单一模型模式,以确保所有查询都可以通过相同的端点访问。报告API为此目的使用ReportDataSet,它与ADO.NET DataSet兼容,并且可以序列化以进行传输。
以下图示显示了对GetWeatherForecast端点的请求流程。
在第一步中,API客户端使用GetQueries端点查询可用的GET方法。返回值包括所有方法及其查询参数。
用户定义查询参数,并使用ExecuteQuery报告端点和目标方法的参数执行查询,例如GetWeatherForecast。
使用DI实例化所需的控制器(WeatherForecastController)并执行GetWeatherForecast方法。这是在ExecuteQuery请求的ControllerContext中完成的,以防止不必要的访问。方法的返回值是一个类型为WeatherForecast的列表,作为ReportingDataSet返回。
API客户端应用程序可以将ReportingDataSet转换为ADO.NET DataSet,从而允许进行表格分析和格式转换。
使用反射评估数据比使用直接对象访问需要更长的时间。需要处理大量数据的报告应该通过特殊服务获取数据。
将报告添加到REST API有三个步骤。
使用NuGet包安装库。
dotnet add package RestApiReporting.NET
将ReportingController.cs文件添加到API控制器中。
using RestApiReporting.Service;
public class ReportingController : ReportingControllerBase {
public ReportingController(IApiReportingService reportingService) : base(reportingService) {
}
}
在DI容器中注册报告服务。
using RestApiReporting.Service;
...
builder.Services.AddReporting();
...
如果只想使查询端点可用,则控制器派生自ReportingQueryControllerBase类,服务使用Builder.Services.AddReportingQuery()注册。
现在REST API包括报告控制器服务,可以启动。
默认情况下,所有GET端点都注册为查询。实现IReport的类将注册为报告。可以使用QueryFilter和ReportFilter控制行为。过滤在以下级别进行控制:
ReportingAttribute和ReportingIgnoreAttribute标记属性提供了包含或排除程序集、类型和方法的能力。带有ReportingIgnoreAttribute的程序集、类型和方法总是被忽略。ReportingAttribute可以用来以两种方式控制反射。
在排除模式下,所有不需要的端点都被排除。
builder.Services.AddReporting(
new QueryFilter(
methodFilter: method => !method.IsReporting()));
在包含模式下,只捕获所需的端点。
builder.Services.AddReporting(
new QueryFilter(
typeFilter: type => type.IsReporting()));
一旦Reporting Controller注册到REST API,就可以启动。以下是如何启动包含的WebApi REST API示例。
dotnet run --project WebApi\WebApi.csproj --urls=https://localhost:7082
报告控制器出现在Swagger的URL https://localhost:7082。
报告Web应用程序是一个Blazor服务器应用程序,需要在启动时提供REST API URL参数。以下示例展示了如何使用URL https://localhost:8036启动报告Web应用程序,并使用URL https://localhost:7082通过REST执行查询和报告,使用--api参数。
dotnet run --project %~dp0WebApp\WebApp.csproj
--urls=https://localhost:8036 --api=https://localhost:7082
之后,可以在浏览器中打开Web应用程序 https://localhost:8036,并且会出现两个页面。
此页面允许执行所有查询方法。查询总是有一个单一的结果表。对于返回单个对象的GET端点,也是如此。
报告页面允许构建报告。报告可以包含多个相关表格中的数据。
Web应用程序设置在appsettings.json文件中定义。
设置 | 描述 | 默认 |
---|---|---|
ApiUrl | API URL,例如https://localhost:7161/ | x |
AppTitle | 应用程序标题 | system |
LayoutMode | 页面布局模式: | Large |
NameFormat | 名称格式化样式: | PascalSentence |
DenseMode | 网格密集模式 | false |
DataPageCount | 每页网格行数 | 10 |
FilterMode | 网格过滤模式: | Simple |
在开发模式下,建议将端点配置外包给User Secrets。
报告是一个实现IReport接口的C#类。
public interface IReport {
string Name { get; }
string? Description { get; }
IList? SupportedCultures { get; }
IList? Parameters { get; }
Task BuildAsync(IApiQueryService queryService, ReportRequest request);
}
报告具有名称、描述、文化信息和报告参数。报告是使用BuildAsync方法构建的。报告参数的类型ApiMethodParameter与查询方法的方法参数相同。
以下示例为客户端及其员工生成报告。
public class TenantEmployeeReport : IReport {
public string Name => "TenantEmployee";
public string? Description => "Employees by tenant";
public IList? SupportedCultures => null;
public IList? Parameters => null;
public async Task BuildAsync(
IApiQueryService queryService, ReportRequest request)
{
// tenants
var tenants = await queryService.QueryAsync(
new ReportQuery(
controllerContext: request.ControllerContext,
methodName: "GetTenants",
primaryKey: "Id"));
if (tenants == null) {
return new ReportResponse(request);
}
// employees
var employees = await queryService.QueryAsync(
new ReportQuery(
controllerContext: request.ControllerContext,
methodName: "GetEmployees",
primaryKey: "Id",
parameters: request.Parameters));
if (employees == null) {
return new ReportResponse(request);
}
// data set
var dataSet = new ReportDataSet(nameof(TenantEmployeeReport));
dataSet.Tables.Add(tenants.ToReportDataTable());
dataSet.Tables.Add(employees.ToReportDataTable());
dataSet.Relations.Add(
new() {
Name = Name,
ParentTable = tenants.TableName,
ParentColumn = "Id",
ChildTable = employees.TableName,
ChildColumn = "TenantId"
});
return new ReportResponse(dataSet, request);
}
}
将泛型IEnumerable集合转换为表格的能力允许将查询结果和自定义服务的结果合并到一个数据集中。