如何创建自定义结果过滤器以将分页标头添加到Webapi控制器的响应中

时间:2019-06-05 03:04:18

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

我有以下情况:

我想创建一个自定义结果过滤器,该过滤器允许我使用 ResultFilterAttribute 类的子类将分页标题添加到响应中。 为了创建标题中使用的分页元数据,我创建了一个分页助手服务,该服务被注入到控制器类中,并且需要一个PagingModel对象来生成元数据。

要将分页元数据添加到过滤器的响应中,我需要访问pageingMetadata和我想从控制器返回的实际值(实体,dto或whataver)。 为了能够将两个对象都传递到结果过滤器,我使用了一个元组。

问题是我想让这个过滤器有点通用,我正在尝试将来自控制器的实际值(实体,dto ...)强制转换为对象,向我抛出 exception ,说无法进行强制转换。

我该如何进行投射?还是我应该使用其他方法?

我试图直接在结果过滤器中使用paginationHelperService而不是在控制器中使用它,但是由于属性类不允许它,因此无法从控制器传递服务实例。

我也尝试使用泛型,我认为这是实现强制转换的最简单方法。如果我可以使属性通用,则可以将对象转换为实际类型,但是不幸的是,由于它是属性类,因此不可能。


// 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;
    }
}

1 个答案:

答案 0 :(得分:2)

  

我试图直接在结果过滤器中使用paginationHelperService而不是在控制器中使用它,但是由于属性类不允许它,因此无法从控制器传递服务实例。 / em>

尽管您不能将服务注入Attribute,但可以使用 ServiceFilter(typeof(Your_Filter_Type)) 属性来启用任何过滤器,以便您可以随意注入服务。

例如,创建一个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) 
{
    ....
}
  

我也尝试使用泛型,我认为这是实现转换的最简单方法。如果我可以使属性通用,则可以将对象转换为实际类型,但是不幸的是,由于它是属性类,因此不可能。

对于泛型类型也可以执行相同的技巧。例如,将上面的AddPaginationHeader过滤器更改为class AddPaginationHeader<TResult> : IResultFilter,您可以通过以下方式启用此过滤器:

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

您可以根据需要扩展通用TResult。唯一的技巧是通过ServiceFilter添加过滤器。