Owin Middleware vs ExceptionHandler против HttpMessageHandler (DelegatingHandler)

Пожалуйста, дайте мне понять, как работают три модуля в asp.net Web API 2.1

  • Среднее ПО Owin
  • HttpMessageHandler (или DelegatingHandler)
  • ExceptionHandler

То, что я пытаюсь сделать, - это разработать и веб-api, который будет предоставлять данные json с постоянным форматом, означает, что фактические данные

{"Id":1,"UserName":"abc","Email":"[email protected]"}

Затем мне нравится доставлять json как

{__d:{"Id":1,"UserName":"abc","Email":"[email protected]"}, code:200, somekey: "somevalue"}

Для этого я попытался использовать пользовательский ActionFilterAttribute, но я чувствую (все еще не подтвержден), что это не могло доставить аналогично сформированные данные в случае, если код встретил исключение

Пожалуйста, предложите мне лучшее направление.

Вот мой краткий фрагмент кода пользовательского атрибута. Также предположим, что пользовательский атрибут подходит для этой цели

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public class ResponseNormalizationAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
            base.OnActionExecuted(actionExecutedContext);
            var response = actionExecutedContext.Response;
            object contentValue;
            if (response.TryGetContentValue(out contentValue))
            {
                var nval = new { data=contentValue, status = 200 };


                var newResponse = new HttpResponseMessage { Content = new ObjectContent(nval.GetType(), nval, new JsonMediaTypeFormatter()) };
                newResponse.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
                actionContext.Response = newResponse;
            }
     }
}

Ответ 1

Если вы не используете промежуточное ПО Owin, вы можете обернуть все свои ответы глобально, чтобы он возвращал ваши данные json с постоянным форматом, используя обработчик делегирования.

Напишите пользовательский обработчик, наследующий от DelegatingHandler:

public class ApiResponseHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var response = await base.SendAsync(request, cancellationToken);

        return BuildResponse(request, response);
    }

    private static HttpResponseMessage BuildResponse(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;
            }
        }

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

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

        return newResponse;
    }
}

//ApiResponse is your constant json response
public class ApiResponse
{

    public ApiResponse(int statusCode, object content, string errorMsg)
    {
        Code = statusCode;
        Content = content;
        Error = errorMsg;
        Id = Guid.NewGuid().ToString();
    }

    public string Error { get; set; }

    //your actual data is mapped to the Content property
    public object Content { get; set; }
    public int Code { get; private set; }
    public string Id { get; set; }
}

Зарегистрируйте обработчик в WebApiConfig.cs:

    public static void Register(HttpConfiguration config)
    {
        // Web API routes
        config.MapHttpAttributeRoutes();
        ...

        config.MessageHandlers.Add(new ApiResponseHandler());

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        ...
    }

Я опубликовал аналогичный ответ здесь, в SO, но это в .NET Core и реализовано как промежуточное ПО OWIN (поскольку DelegatingHandler отсутствует в .NET Core). Как я могу обернуть ответы веб-API (в ядре .net) для согласованности?