包装所有响应

Posted

技术标签:

【中文标题】包装所有响应【英文标题】:Wrapping all responses 【发布时间】:2017-02-24 18:18:15 【问题描述】:

我想包装我所有的 http 响应。 例如,我们有一个返回一些 JSON 数据的操作:

public IActionResult Get() 

    var res = new
        
            MessageBody = "Test",
            SomeData = 1
        ;

        return Ok(res);

我希望我的回复看起来像:

    
    "StatusCode":200,
    "Result":
    
        "MessageBody ":"Test",
        "SomeData":1        
    

如果有错误,则响应中必须包含ErrorMessage 字段。

在 mvc 5 中我使用了DelegationHandler,但是在 asp.net core 中这个类没有实现。现在,我们必须使用中间件。

这是 mvc 5 的代码:

public class WrappingHandler : DelegatingHandler

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    
        var response = await base.SendAsync(request, cancellationToken);

        return BuildApiResponse(request, response);
    

    private static HttpResponseMessage BuildApiResponse(HttpRequestMessage request, HttpResponseMessage response)
    
        object content;
        string errorMessage = null;

        if (response.TryGetContentValue(out content) && !response.IsSuccessStatusCode)
        
            HttpError error = content as HttpError;

            if (error != null)
            
                content = null;
                errorMessage = error.Message;

#if DEBUG
                errorMessage = string.Concat(errorMessage, error.ExceptionMessage, error.StackTrace);
#endif
            
        

        var newResponse = request.CreateResponse(response.StatusCode, new ApiResponse(response.StatusCode, content, errorMessage));

        foreach (var header in response.Headers)
        
            newResponse.Headers.Add(header.Key, header.Value);
        

        return newResponse;
    

还有一个用于 asp.net 核心的中间件。 asp.net core中没有TryGetContentValueHttpError等东西。所以,我想先阅读响应正文:

 public class FormatApiResponseMiddleware
 
        private readonly RequestDelegate _next;

        public FormatApiResponseMiddleware(RequestDelegate next)
        
            _next = next;
        

        private bool IsSuccessStatusCode(int statusCode)
        
            return (statusCode >= 200) && (statusCode <= 299);

        

        public async Task Invoke(HttpContext context)
        
            object content = null;
            string errorMessage = null;

            if (!IsSuccessStatusCode(context.Response.StatusCode))
            
                content = null;
                //how to get error 
            

            var body= context.Response.Body;
        

但是,Body 流具有 CanRead 等于 false 并且我收到无法读取流的错误。如何正确包装响应?

【问题讨论】:

您到底想完成什么?是模型验证吗?异常处理?` @Tseng 我更新了问题,添加了一些细节。 【参考方案1】:

我建议使用ExceptionHandlerMiddleware 作为您的中间件应该如何实现的模板/示例。

例如,您应该知道响应已经开始的情况

// We can't do anything if the response has already started, just abort.
if (context.Response.HasStarted)

    _logger.LogWarning("The response has already started, the error handler will not be executed.");
    throw;

或者不要忘记清除当前响应,如果你想替换它:

context.Response.Clear();

此外,也许您会发现重用它并实现自己的错误处理程序而不是完整的中间件很有用。这样您就可以向客户端发送自定义 JSON 错误。为此,定义一个代表您的自定义错误的类:

public class ErrorDto

   public int Code  get; set; 
   public string Message  get; set; 

   // other fields

   public override string ToString()
   
      return JsonConvert.SerializeObject(this);
   

然后在Configure方法中注册一个异常处理中间件。注意中间件的注册顺序,比如注册在MVC之前:

app.UseExceptionHandler(errorApp =>

    errorApp.Run(async context =>
    
        context.Response.StatusCode = 500; // or another Status
        context.Response.ContentType = "application/json";

        var error = context.Features.Get<IExceptionHandlerFeature>();
        if (error != null)
        
            var ex = error.Error;

            await context.Response.WriteAsync(new ErrorDto()
            
                Code = 1, //<your custom code based on Exception Type>,
                Message = ex.Message // or your custom message
                // … other custom data
            .ToString(), Encoding.UTF8);
        
    );
);

【讨论】:

以上是关于包装所有响应的主要内容,如果未能解决你的问题,请参考以下文章

asp.net core 中优雅的进行响应包装

SignalR ObjC 包装响应?

防止 ServiceContractGenerator 生成消息契约(请求/响应包装器)

函数包装器静态检查 HTTP 响应的参数

资源响应没有用“数据”包装[重复]

删除 [ ] 包装 json 响应