19

Please anyone give me some idea how following three modules work together in asp.net Web API 2.1

  • Owin Middleware
  • HttpMessageHandler (or DelegatingHandler)
  • ExceptionHandler

What I am trying to do is to develop and a web api which will deliver a constant format json data, means if the actual data is

{"Id":1,"UserName":"abc","Email":"abc@xyz.com"}

Then I like to deliver json as

{__d:{"Id":1,"UserName":"abc","Email":"abc@xyz.com"}, code:200, somekey: "somevalue"}

For this I tried using custom ActionFilterAttribute but I feel(still not confirm) this could not deliver similarly formated data in case of code encounter an exception

Please suggest me best direction.

Here is my brief code snippet of custom attribute. Also suggest me is custom attribute is good for the purpose

[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;
            }
     }
}
Premchandra Singh
  • 14,156
  • 4
  • 31
  • 37
  • Why do you wanna return the status code as a part of the response body, when it is already a part of the response itself? I also feel that this is very bad design. Think about when you return to your code in a year. Is it easy to read, and easy to find out what is going on? Why not just create a model that corresponds to you desired output and return that in you controller. – Michael Oct 31 '16 at 21:41
  • `HttpMessageHandler` and `ExceptionHandler` is a part of WebAPI. `OwinMiddleware` is a part of a pipeline that is available only if you are using OWIN. With `ActionFilters` you are close to your controller. You can even access the actual instance. OWIN middleware is before you even hit the Web API framework. Which means you don't have access to it's services and etc. – Michael Oct 31 '16 at 21:44

1 Answers1

0

If you are not using Owin middleware, you can wrap all your responses globally so it will return your constant format json data using a delegating handler.

Write a custom handler that inherits from the 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; }
}

Register the handler in 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 }
        );

        ...
    }

I posted a similar answer here in SO but this is in .NET Core and implemented as an OWIN middleware (since DelegatingHandler is gone in .NET Core). How can I wrap Web API responses(in .net core) for consistency?

Community
  • 1
  • 1
alltej
  • 6,787
  • 10
  • 46
  • 87