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.
When retrieving data
with a GET, your response body will contain data. This may be an array for
multiple records (e.g. GET /products) or a single object (e.g. GET
/products/42). A POST may not return a response body at all and instead use an HTTP
Header to point the user to the newly created resource. If your service needs
to return an error then the in-built error handling will return an object with
a message property exposing the error message. In each of these cases, we are
returning very different data to the client. Depending on your client, this can
make it very difficult to consume your service.
Delegating handlers
are extremely useful for situations like above. They hook into the very early
and very late stages of the request-response pipeline making them ideal for
manipulating the response right before it is sent back to the client.
Implementation:
Step 1: Create a Webapi
project.
Step 2: Create a generic class
for response.
Example:
namespace
Core.Model.Common
{
using System.Net;
using
System.Runtime.Serialization;
/// <summary>
/// APi global response wrapper
template
/// </summary>
[DataContract]
public class ApiResponse
{
/// <summary>
/// Version number
/// </summary>
[DataMember]
public string Version { get { return "1.0.0"; } }
/// <summary>
/// Status code
/// </summary>
[DataMember]
public int StatusCode { get; set; }
/// <summary>
/// Error message
/// </summary>
[DataMember(EmitDefaultValue = false)]
public string ErrorMessage { get; set; }
/// <summary>
/// Result
/// </summary>
[DataMember(EmitDefaultValue = false)]
public object Result { get; set; }
/// <summary>
/// The Response constructor
/// </summary>
/// <param name="statusCode"></param>
/// <param name="result"></param>
/// <param name="errorMessage"></param>
public ApiResponse(HttpStatusCode
statusCode, object result = null, string errorMessage = null)
{
StatusCode = (int)statusCode;
Result =
result;
ErrorMessage = errorMessage;
}
}
}
Step 3: Create a message handler and override SendAsync method.
public
class GlobalResponseWrappingHandler : DelegatingHandler
{
/// <summary>
/// Send async overiride for
message handler
/// </summary>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected override async
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
//Bypass if request is from
swagger ui
if
(request.RequestUri.AbsolutePath.ToLower().Contains("swagger"))
{
return await base.SendAsync(request,
cancellationToken);
}
else
{
var response = await base.SendAsync(request,
cancellationToken);
return BuildApiResponse(request,
response);
}
}
/// <summary>
/// Build api response with
custom response class
/// </summary>
/// <param name="request"></param>
/// <param name="response"></param>
/// <returns></returns>
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;
}
}
Step 4: Register the message handler in webapi config
var
config = GlobalConfiguration.Configuration;
config.MessageHandlers.Add(new
GlobalResponseWrappingHandler());
Step 5: Add the below lines in Application_Start
var
json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
Step 6: Run the project.