How To Make Your Web API Responses Consistent and Useful

You might also like

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 7

How to make your Web API

responses consistent and useful


A custom wrapper for your Web API responses can provide meaningful
information and ensure consistency regardless of success or failure.

 


 


 


 


 


Thinkstock
When working with ASP.NET Web API, it is important to return a consistent response for all the
requests that are processed by your API regardless of success or failure. This makes it a lot easier
to consume the API, without requiring complex code on the client. By using a custom wrapper
for Web API responses, you can ensure that all of your responses have a consistent structure, and
you can also include custom metadata. This article presents a discussion on how we can decorate
Web API responses with useful information.

Let's understand the problem we are trying to solve. In essence, your standard Web API response
will vary when executing different action methods, and we need a way to get around the
inconsistency of these responses. When you are trying to retrieve data using HTTP GET, the
response may contain data for one or more records. When you are executing a HTTP POST, the
response may not even include a body, but only a header. And when there is an error when
executing the request, the API will return an object with the appropriate error message.

The differences among all of these responses make it difficult to consume the API, because the
consumer needs to know the type and structure of the data that is being returned in each case.
Both the client code and the service code become difficult to manage.

[ Also on InfoWorld: The best new features in .NET 6 ]


Now that we know what the problem is all about, let’s examine the two ways to overcome the
problem in Web API. One of the options is to use a message handler. The other option is to
intercept the call using a filter and override the OnActionExecuted method. We would
prefer to use a message handler because it is called earlier in the request processing pipeline.
(Incidentally, you can take advantage of message handlers to process a request even before it
reaches the HttpControllerDispatcher.) So, let’s implement a custom message handler
to wrap the response by extending the DelegatingHandler class. This will provide a way to
send out responses from the Web API in a consistent manner.

Implementing a custom response handler in Web API


Create a new Web API project in Visual Studio and save it with the name of your choice. Now,
select the Web API project you have created in the Solution Explorer Window and create a
Solution Folder. Create a file named CustomResponseHandler.cs inside this folder. This file will
contain our custom message handler. Next, copy the following code and past it inside this file.
This is the skeleton of our custom message handler.

public class CustomResponseHandler :DelegatingHandler


    {
        protected override async Task<HttpResponseMessage>
SendAsync(HttpRequestMessage request, CancellationToken
cancellationToken)
        {
            throw new NotImplementedException("Not yet
implemented.");
        }
    }

Here's a class named ResponseMetadata. I will contain all of the information we want to


return from our Web API as part of the response.

public class ResponseMetadata


    {
        public string Version { get; set; }
        public HttpStatusCode StatusCode { get; set; }
        public string ErrorMessage { get; set; }
        public object Result { get; set; }
        public DateTime Timestamp { get; set; }
        public long? Size { get; set; }
    }

We will now write a method that checks to see whether or not a response is valid. The following
method, IsResponseValid, returns true if the response is valid and false otherwise. We
will use this method in our custom delegating handler.
Nominations are open for the 2024 Best Places to Work in IT

private bool IsResponseValid(HttpResponseMessage response)


    {
        if ((response != null) && (response.StatusCode ==
HttpStatusCode.OK))
            return true;
        return false;
    }

You can retrieve the response content using the following code snippet and check if there is any
error.

object responseContent;
if(response.TryGetContentValue(out responseContent))
{
    HttpError httpError = responseContent as HttpError;
    if(httpError !=null)
    {
        errorMessage = httpError.Message;
        statusCode = HttpStatusCode.InternalServerError;
        responseContent = null;
    }
}

The following code snippet shows how you can create an instance
of ResponseMetadata and initialize its properties with relevant information.

ResponseMetadata responseMetadata = new ResponseMetadata();


responseMetadata.Version = "1.0";
responseMetadata.StatusCode = statusCode;
responseMetadata.Content = responseContent;
DateTime dt = new DateTime(DateTime.Now.Year, DateTime.Now.Month,
DateTime.Now.Day, DateTime.Now.Hour, DateTime.Now.Minute,
DateTime.Now.Second, DateTime.Now.Millisecond);
responseMetadata.Timestamp = dt;
responseMetadata.ErrorMessage = errorMessage;
responseMetadata.Size = responseContent.ToString().Length;

The next thing we need to do is create a new response object using


the CreateResponse method on the request object as shown below.
var result = request.CreateResponse(response.StatusCode,
responseMetadata);

Note how the response metadata we just created has been passed in the second argument of
the CreateResponse method.

Our complete custom response handler for Web API


Here is the complete code listing of the CustomResponseHandler class for your reference.

public class CustomResponseHandler : DelegatingHandler


    {
        protected override async Task<HttpResponseMessage>
SendAsync(HttpRequestMessage request, CancellationToken
cancellationToken)
        {
            var response = await base.SendAsync(request,
cancellationToken);
            try
            {
                return GenerateResponse(request, response);
            }
            catch (Exception ex)
            {
                return
request.CreateResponse(HttpStatusCode.InternalServerError,
ex.Message);
            }
        }
        private HttpResponseMessage
GenerateResponse(HttpRequestMessage request, HttpResponseMessage
response)
        {          
            string errorMessage = null;
            HttpStatusCode statusCode = response.StatusCode;
            if (!IsResponseValid(response))
            {
                return
request.CreateResponse(HttpStatusCode.BadRequest, "Invalid
response..");
            }
            object responseContent;
            if(response.TryGetContentValue(out responseContent))
            {
                HttpError httpError = responseContent as
HttpError;
                if(httpError !=null)
                {
                    errorMessage = httpError.Message;
                    statusCode =
HttpStatusCode.InternalServerError;
                    responseContent = null;
                }
            }
            ResponseMetadata responseMetadata = new
ResponseMetadata();
            responseMetadata.Version = "1.0";
            responseMetadata.StatusCode = statusCode;
            responseMetadata.Content = responseContent;
            DateTime dt = new DateTime(DateTime.Now.Year,
DateTime.Now.Month, DateTime.Now.Day, DateTime.Now.Hour,
DateTime.Now.Minute, DateTime.Now.Second,
DateTime.Now.Millisecond);
            responseMetadata.Timestamp = dt;
            responseMetadata.ErrorMessage = errorMessage;
            responseMetadata.Size =
responseContent.ToString().Length;
            var result =
request.CreateResponse(response.StatusCode, responseMetadata);  

            return result;
        }
        private bool IsResponseValid(HttpResponseMessage
response)
        {
            if ((response != null) && (response.StatusCode ==
HttpStatusCode.OK))
                return true;
            return false;          
        }
    }

Finally, you should add the following statement in the Register method of


the WebApiConfig class to register the custom handler.

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

And that’s it! You can now execute your Web API methods and see how the responses are
wrapped using the custom message handler we implemented.
In this article we implemented a custom message handler that was used to wrap Web API
responses to include additional metadata. Note that this was just a simple implementation. I will
leave it to you to add more features such as response headers, cookie information, etc. Happy
coding!

Source : https://www.infoworld.com/article/3674115/how-to-make-your-web-api-responses-
consistent-and-useful.html

You might also like