c# - 如何创建自定义结果过滤器以将分页 header 添加到来自 webapi Controller 的响应

标签 c# asp.net-core .net-core asp.net-core-webapi

我有以下情况:
我想创建一个自定义结果过滤器,允许我使用 的子类将分页标题添加到响应中ResultFilterAttribute 类(class)。
为了创建标题中使用的分页元数据,我创建了一个分页帮助器服务,该服务被注入(inject)到 Controller 类中,并且需要一个 PagingModel 对象来生成元数据。
要将分页元数据添加到过滤器中的响应中,我需要访问 pagingMetadata 和我想从 Controller 返回的实际值(实体、dto 或其他)。
为了能够将两个对象都传递给结果过滤器,我使用了一个元组。
问题是我想让这个过滤器有点通用,我试图将来自 Controller 的实际值(实体,dto...)转换为 对象 这给了我一个异常(exception),说 Actor 是不可能的。
我怎么能执行类型转换?或者也许我应该使用不同的方法?
我尝试直接在结果过滤器中使用 paginationHelperService 而不是在 Controller 中使用它,但是由于属性类不允许,因此无法从 Controller 传递服务的实例。
我也尝试使用泛型,我认为这将是实现类型转换的最简单方法。如果我可以使属性通用,我可以将对象转换为它的实际类型,但不幸的是这是不可能的,因为它是一个属性类。


// Controller
[HttpGet]
[AddPaginationHeader]
public async Task<IActionResult> Get([FromQuery]PagingModel pagingModel, 
    [FromHeader(Name = "Accept")]string mediaType) {
    var pagedCollection = repository.GetPage(pagingModel);
    PaginationMetadata paginationMetadata = paginationHelperService.GetPagingMetadata(pagingModel);
    if (mediaType == "mycustommediatype") {
        var shapedCollection = ShapeCollectionOfData(pagedCollection);
        return Ok((shapedCollection, pagingModel));
    } else {
        return Ok((pagedCollection, pagingModel));
    }
}

// Custom Result Filter
public override void OnResultExecuting(ResultExecutingContext context) {
    var result = context.Result as ObjectResult;
    if (result?.Value != null && result?.StatusCode >= 200 &&
        result?.StatusCode < 300) {
        (object value, PaginationMetadata paginationMetadata) = ((object, PaginationMetadata))result.Value; // Casting
        string paginationMetadataString = (context.HttpContext.Request.Headers["Accept"] == "mycustommediatype")
            ? JsonConvert.SerializeObject(paginationMetadata.FullMetadata)
            : JsonConvert.SerializeObject(pagingMetadata.GenericMetadata);
        context.HttpContext.Response.Headers.Add("X-Pagination", paging);
        context.Result.Value = value;
    }
}

最佳答案

I tried to use the paginationHelperService directly in the result filter instead of using it in the controller, but it's not possible to pass the instance of the service from the controller since the attribute classes don't allow it.



虽然您不能将服务注入(inject) Attribute ,您可以使用 的属性ServiceFilter(typeof(Your_Filter_Type)) 启用任何过滤器,以便您可以根据需要注入(inject)服务。

例如,创建一个 AddPaginationHeader结果过滤器(不是 Attribute ):
public class AddPaginationHeader : IResultFilter
{
    private readonly IRepository repository;

    // inject services
    public AddPaginationHeader(IRepository repository, ... other services)
    {
        this.repository = repository;
    }

    public void IResultFilter.OnResultExecuting(ResultExecutingContext context)
    {
        ...
    }

    public void IResultFilter.OnResultExecuted(ResultExecutedContext context) { ... }

}

并且不要忘记在 Startup.cs 中注册此服务:
services.AddScoped<AddPaginationHeader>();

最后,您可以通过 [ServiceFilterAttribute] 启用此过滤器:
[HttpGet]
[ServiceFilter(typeof(AddPaginationHeader))]
public async Task Get([FromQuery]PagingModel pagingModel, [FromHeader(Name = "Accept")]string mediaType) 
{
    ....
}

I also tried to use generics, I think it'd be the easiest way to achieve the casting. If I just could make the attribute generic I could cast the object to its actual type but unfortunately that's not possible since it's an attribute class.

The same trick can be performed for generic types too. For example, Change the above AddPaginationHeader filter to class AddPaginationHeader<TResult> : IResultFilter , and you could enable this filter by :

[ServiceFilter(typeof(AddPaginationHeader<(object,PaginationMetadata)>))]

您可以扩展通用 TResult如你所愿。唯一的技巧是通过 ServiceFilter 添加过滤器.

关于c# - 如何创建自定义结果过滤器以将分页 header 添加到来自 webapi Controller 的响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56453772/

相关文章:

c# - 如何为整个项目启用 C# 8.0 的可空引用类型功能

c# - 在 C# 中计算 Switch 语句中的表达式

caching - ASP.NET Core 2.0 Web API响应缓存

c# - AddSingleton 与异步调用?

c# - 将 Serilog.Sinks.MsSqlServer 与 Entity Framework Core 结合使用

c# - azure-sdk-for-net 在 Mac 上引发异常

c# - 为什么需要对我的数据库进行异步调用?

c# - 如何发送带有 PDF 文件的邮件

asp.net-core - 将 IdentityServer4 升级到 Core 3.1 - token 突然没有正确签名?

c# - 从同一个表单同时保存多行 - dotnet core