5

我想包装我所有的 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 核心中没有实现这个类。现在,我们必须使用中间件。

这是 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 核心中没有TryGetContentValue,HttpError和其他东西。所以,我想先阅读响应正文:

 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的,我得到流无法读取的错误。如何正确包装响应?

4

1 回答 1

0

我建议使用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);
        }
    });
});
于 2016-10-15T21:23:55.303 回答