简体   繁体   English

使用 ResultFilterAttribute 将复杂的 object 包装为响应

[英]Wrapping a complex object as Response with ResultFilterAttribute

In my controller, I've inherited from a ControllerBase which there is a Result method that is used to wrap the response into a ResponseBase object like this:在我的 controller 中,我从 ControllerBase 继承了一个Result方法,用于将响应包装到ResponseBase object 中,如下所示:

[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
public abstract class BaseApiController : ControllerBase
{ 
 protected async Task Result<T>(T content, Dictionary<string, string> headers = null,
    HttpStatusCode statusCode = HttpStatusCode.OK, string contentType = 
    "application/json")
{
    Response.StatusCode = (int)statusCode;
    Response.ContentType = contentType;
    if (headers != null)
    {
        foreach (var header in headers)
        {
            Response.Headers.Add(header.Key, header.Value);
        }
    }
    var data = Encoding.UTF8.GetBytes
       (MySerializer.Serialize(new ResponseBase<T> { Data = content }));

    await Response.Body.WriteAsync(data.AsMemory(0, data.Length));
 } 
}

And the ResponseBase is like: ResponseBase就像:

public class ResponseBase
{
  [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
  public List<ErrorBase> Errors { get; set; }
}

public class ResponseBase<T> : ResponseBase
{ 
  [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
  public T Data { get; set; }
}


public class ErrorBase
{
  [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
  public string FieldName { get; set; }

  [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
  public string ErrorMessage { get; set; }
}

And finally my controller:最后是我的 controller:

 [ApiVersion("1")]
 public class ConfigurationController : BaseApiController
 {
   private readonly IConfigurationService _configurationService;

   public ConfigurationController(IConfigurationService configurationService)
   {
    _configurationService = configurationService;
   }
 

   [HttpGet("getData")]  
   public async Task GetData()
   {
    await Result(await _configurationService.GetRelatedData());
   }
 }

Now, the question here is, how can I wrap my response into a ResponseBase with a help of ResultFilterAttribute without explicitly calling the Result method in the ControllerBase现在,这里的问题是,如何在ResultFilterAttribute的帮助下将我的响应包装到ResponseBase中,而不显式调用ControllerBase中的Result方法

I've tried to use a ResultFilter to wrap my response but I couldn't find any sample to do this.我尝试使用 ResultFilter 来包装我的响应,但我找不到任何示例来执行此操作。 I've also read this solution but didn't help.我也读过这个解决方案,但没有帮助。

I appreciate any help.我很感激任何帮助。

  1. Implement ResultFilter .实施ResultFilter

In short,简而言之,

1.1. 1.1。 Get the values of context.Result such as StatusCode , ContentType , Value .获取context.Result的值,例如StatusCodeContentTypeValue

1.2. 1.2. Bind the Value to the root class ( ResponseBase ).Value绑定到根 class ( ResponseBase )。

1.3. 1.3. Lastly, produce a new Response.最后,产生一个新的响应。

public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
    Dictionary<string, string> headers = null;
    int statusCode = (int)HttpStatusCode.OK;
    string contentType = "application/json";

    var responseBaseType = typeof(ResponseBase<>).MakeGenericType(typeof(object));
    dynamic? responseBase = Activator.CreateInstance(responseBaseType);

    var result = context.Result as ObjectResult;
    if (result?.Value == null)
    {
        await next();
        return;
    }

    if (result.StatusCode != null)
        statusCode = (int)result.StatusCode;

    if (result.ContentTypes != null
        && result.ContentTypes.Any())
        contentType = result.ContentTypes[0];

    if (statusCode == (int)HttpStatusCode.OK)
        responseBase.Data = result.Value;

    byte[] data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(responseBase));

    context.HttpContext.Response.StatusCode = statusCode;
    context.HttpContext.Response.ContentType = contentType;

    await context.HttpContext.Response.Body.WriteAsync(data.AsMemory(0, data.Length));
}
  1. Modify the API method to return the value with IActionResult type.修改API方法,返回IActionResult类型的值。
[HttpGet("getData")]
public async Task<IActionResult> GetData()
{
    return new ObjectResult(await _configurationService.GetRelatedData());
}
  1. Register ResultFilter as global filter to controller in Program.cs .Program.cs中将 ResultFilter 注册为ResultFilter的全局过滤器。
builder.Services.AddControllers(options =>
{
    options.Filters.Add<ResultFilter>();
});

Note: The ResultFilter isn't a complete solution, while implementing you should consider different scenarios of IActionResult such as BadRequestObjectResult etc.注意: ResultFilter不是一个完整的解决方案,在实现时您应该考虑IActionResult的不同场景,例如BadRequestObjectResult等。


Reference参考

Result Filter in ASP.NET CORE MVC ASP.NET CORE MVC 中的结果过滤器

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM