Question

In ASP.Net Web API, the action returned object will be converted to XML or JSON automatically - is there a way to add additional process to the returned object before it gets converted?

What I want to achieve is to wrap returned object into a generic APIResponse (another class) type which has an object Property called Data that will be assigned with whatever the original returned object is.

for example:

public Books[] FindBooks(string keyword)
{
..
    return new Books[] {new Book(){Name="ASP.NET"}};
}

This will return JSON of book array by default, however I want to wrap the result into an object called APIResponse, so the returned object becomes:

new APIResponse(){
    Data = //the Book[] array return from the action method
}

By doing this, I will be able to keep the freedom of returning normal business objects in Web API however always return the same object type in JSON format when the front-end Ajax requests.

I believe it can be done in a way however I'm not familiar with the Web API life cycle, can any way give some guide on this?

Thank you.

Was it helpful?

Solution

I fixed it by creating a custom MediaTypeFormatter however simply inheriting from JSON formatter which have already got all what I need, here is the very simple code I added, which resolved all issues I have!!!

    public class APIResponseMediaFomatter : JsonMediaTypeFormatter
    {
        public override Task WriteToStreamAsync(Type type, object value, System.IO.Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext)
        {
            ResponseMessage wrappedValue = null;
            if (type != typeof(ResponseMessage) || (value != null && value.GetType() != typeof(ResponseMessage)))
                wrappedValue = new ResponseMessage(value);
            return base.WriteToStreamAsync(typeof(ResponseMessage), wrappedValue, writeStream, content, transportContext);
        }
    }

Cheers!

OTHER TIPS

Interestingly, Web API already works exactly how you describe. It already has generic request and response classes that can hold your object payload. Just do the following,

public HttpResponseMessage FindBooks(string keyword)
{
    ...
    var books = new Books[] {new Book(){Name="ASP.NET"}};
    var content = new ObjectContent<Book[]>(books,new JsonMediaTypeFormatter());
    return new HttpResponseMessage { Content = content);
}

There is no need to re-invent your own generic response object that can hold metadata and data, HTTP has already done that for you.

Why dont you return your wrapper object APIResponse from WebAPI

public APIResponse FindBooks(string keyword)
{
    var books = new Books[] {new Book(){Name="ASP.NET"}};
    return new APIResponse {Data= books };
}

Just use an action filter, and modify the response content inside it:

public class ApiResponseWrapperActionFilter : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        // Check that the response is an object content
        var objectContent = actionExecutedContext.Response.Content as ObjectContent;
        if (objectContent != null)
        {
            // Wrap the returned value in the ApiResponse
            objectContent.Value = new ApiResponse() { Data = objectContent.Value };
        }
    }

Apply the filter to the whole API (in global config) or a whole controller (attribute applied to the controller class) or to the desired methods (attribute in each method).

If you're returning something that it's not an object (a custom response) it will skip the wrapping.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top