code blog foo - tag line bar

A Usability Issue with ASP.NET Web Api with A Solution

Blog Battle!

One of my buddies Eric Sowell, did a blog post on Web API. The blog post is excellent and will help a lot of developers struggling with this framework. I've mentioned *many* times that I have my reservations about ASP.NET Web Api, and promised Eric that I'd put a blog post up that talks about alternatives for building REST architectures. Well Eric (and company), here it is! :-)

TL;DR: My Solution to the Usability Issues in ASP.NET Web Api

So here is my solution to the issues Eric brought up in his blog post: considering using something other than ASP.NET Web Api. Apparently, ASP.NET Web Api was built so you could author Http Api's, NOT necessarily Rest Api's. I am a complete newb when it comes to ASP.NET Web API, I do however consider myself an expert in Rest and am well versed in ASP.NET MVC, Nancy and ServiceStack. When I see statements such as "ASP.NET Web Api wasn't developed as a Rest framework", that pretty much translates to I have to build the plumbing for a Rest Api implementation. I think that you'll be much happier if you just use ASP.NET MVC's existing constructs or use ServiceStack (highly recommended). The rest of the blog post will compare ServiceStack and ASP.NET MVC to the helpful code Eric provided for creating ASP.NET Web Api plumbing (I would definitely recommend you read his blog entry, a lot of valuable information for those stuck using Web Api).

Some Basics

Here is how you return an error status in:

//ASP.NET Web Api
public HttpResponseMessage Get()
{
    return new HttpResponseMessage(HttpStatusCode.BadGateway);
}

//ASP.NET MVC
public ActionResult Get()
{
    return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}

//ServiceStack
public override object OnGet(Thingy thingy)
{
    return new HttpResult { StatusCode = HttpStatusCode.BadRequest };
}

Comments

Pretttyyy much the same in each one of the implementations. Each one has its flavor of how to return a status code. In the ServiceStack implementation, your class will inherit from RestServiceBase<T> (which will give you the OnGet override).

Here is how you return a status code with a message attached to it:

//WCF Web API
var error = new HttpError("You have failed.");
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, error);

//ASP.NET MVC
return new HttpStatusCodeResult(HttpStatusCode.BadRequest, "You failed.");

//ServiceStack
return new HttpResult(HttpStatusCode.BadRequest, "You failed.");

Start Building Plumbing...

Here is where my concerns begin. Web Api, in the simplest of cases, makes things harder to test by introducing the Request object, now I have to do ninja-like mock backflips to assert the behavior of the method. I'm afraid the pain points continue. Let's take a look at HttpResponseMessage and Payloads.

Edit:

Henrik F. Nielsen (@frystyk) has referred me to a blog post that shows how to run an in memory instance of Web Api so you can run tests against it. Very cool blog post. They certainly don't make it obvious, but the fact that you can do this kind of testing is a huge step in the right direction.

HttpResponseMessage and Payloads

As Eric has demonstrated in his blog post, here is how you would conditionally return one payload over another inside of ASP.NET Web Api:

public HttpResponseMessage Get(string stuff)
{
    if (String.IsNullOrEmpty(stuff))
    {
        var error = new HttpError("You have failed.");
        return Request.CreateErrorResponse(HttpStatusCode.BadRequest, error);
    }
 
    var thingy = new Thingy
    {
        Name = "mabob"
    };
 
    return Request.CreateResponse(HttpStatusCode.Accepted, thingy);
}

And here is the same thing in ASP.NET MVC:

public ActionResult Get(string stuff)
{
    if (String.IsNullOrEmpty(stuff))
    {
        return new HttpStatusCodeResult(502, "You failed.");
    }

    return Json(new Thingy { Name = "Mabob" });
}

Last, but definitely not least, here is ServiceStack:

public override object OnGet(Thingy thingy)
{
    if(string.IsNullOrEmpty(thingy.Content))
    {
        return new HttpResult(HttpStatusCode.BadRequest, "You failed.");
    }

    return new Thingy { Content = "hi" };
}

Comments

With Web Api, a whole lot of the same issues. I agree with Eric, not intuitive at all and harder to bring under test.

ASP.NET MVC is clean and simple. I know what you are going to say: "But you're returning Json!! What about Xml?!" Accept it. XML is dead. As much as I dislike referring to other "authorities" about this kind of stuff, Twitter has discontinued their XML api (read Json Support only section).

ServiceStack is looking prettyyy good, it's kind of weird with having to pass in a specific object as opposed to a simple string. On the plus side, it supports XML (even though I don't consider that too useful). It's worth mentioning that full blown Rest API's will benefit from the same object being passed in.

Edit:

Steven (see comments), mentioned the need to support content types such as application/json+hal (ie hypermedia control). Here is how you can do hypermedia control in ASP.NET MVC (yes, I've had to create the plumbing and am giving it to you): [install-package restful-mvc]. BTW, hypermedia control implementation is NOT available out of the box in ASP.NET Web API (or ServiceStack for that matter), so you'll have to build your own using custom media types...have fun :-(.

Adding More Plumbing

To be fair, WCF Web API has a similar approach to ASP.NET MVC. With this approach, you lose content negotiation (see Eric's blog post for a full explanation).

public HttpResponseMessage Get(string stuff)
{
    if (String.IsNullOrEmpty(stuff))
    {
        var error = new HttpError("You have failed.");
        return Request.CreateErrorResponse(HttpStatusCode.BadRequest, error);
    }
 
    var thingy = new Thingy
    {
        Name = "mabob"
    };
 
    return new HttpResponseMessage(HttpStatusCode.Accepted)
    {
        Content = new ObjectContent<Thingy>(thingy, new System.Net.Http.Formatting.JsonMediaTypeFormatter())
    };
}

Still way too much code to do something so simple. The ASP.NET MVC version and the ServiceStack version do a better job. The last thing Eric talked about in his blog post is custom error responses. Let's do a compare and contrast of that.

Another Thing Eric (and I) Want to Do: Custom Error Response

Taking from Eric's blog, it would be so nice if you could support custom error payload in WCF Web API by doing this:

//Don’t do this. It won’t work.
public HttpResponseMessage Get(string stuff)
{
    if (String.IsNullOrEmpty(stuff))
    {
        return new HttpResponseMessage(HttpStatusCode.BadRequest,
            new Error { Code = 76543, Description = "Please supply stuff" });
    }
    //Other stuff left out
}

Surprise, surprise. As the comments state, it doesn't work. At this point I would be throwing my computer out of the window. But unlike me, Eric is calm and cool. He figured out a good solution. Here is the solution to this problem (I'm placing it here to compare and contrast it with ASP.NET MVC and ServiceStack). He had to code THIS:

//for explanation of the code below, go to Eric's blog. 
//I'm just putting the code here for comparison
public class ExtendedHttpResponseMessage: HttpResponseMessage
{
    public ExtendedHttpResponseMessage(HttpStatusCode statusCode)
        : base(statusCode) { }
 
    public object Payload { get; set; }
}
 
public class ExtendedHttpResponseMessage<T> : ExtendedHttpResponseMessage
{
    public ExtendedHttpResponseMessage(T payload)
        : this(payload, HttpStatusCode.Accepted) { }
 
    public ExtendedHttpResponseMessage(T payload, HttpStatusCode statusCode)
        : base(statusCode)
    {
        Payload = payload;
    }
}

//and this code
public class ExtendedHttpResponseMessageHandler : DelegatingHandler
{
    protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        var result = await base.SendAsync(request, cancellationToken);
 
        var customMessage = result as ExtendedHttpResponseMessage;
        if (customMessage == null)
            return result;
 
        var config = request.GetConfiguration();
        var contentNegotiator = config.Services.GetContentNegotiator();
        var connegResult = contentNegotiator.Negotiate(
            customMessage.Payload.GetType(), request, config.Formatters
        );
 
        var objectContent = new ObjectContent(
            customMessage.Payload.GetType(), customMessage.Payload, connegResult.Formatter);
 
        result.Content = objectContent;
 
        return result;
    }
}

//which allows you to do this:
public HttpResponseMessage Get(string stuff)
{
    if (String.IsNullOrEmpty(stuff))
    {
        return new ExtendedHttpResponseMessage<Error>(new Error 
            { Code = 76543, Description = "Please supply stuff" }, 
            HttpStatusCode.BadRequest);
    }

    //other stuff
}

Gosh be daRnit ASP.NET Web Api...can't you make this stuff easy for us? Here is how you would do custom error payloads in ASP.NET MVC:

//only JSON supported, remember what I said about XML being dead?
public class CustomErrorResult : ActionResult
{
    public object Model { get; set; }
    public int StatusCode { get; set; }
    public override void ExecuteResult(ControllerContext context)
    {
        context.HttpContext.Response.ContentType = "application/json";
        context.HttpContext.Response.Output
            .Write(JsonConvert.SerializeObject(Model)); //JSON.Net FTW
        context.HttpContext.Response.StatusCode = StatusCode;
        context.HttpContext.Response.Output.Flush();
    }
}

public ActionResult Get(string stuff)
{
    if(string.IsNullOrEmpty(stuff))
    {
        return new CustomErrorResult
        {
            StatusCode = HttpStatusCode.BadRequest,
            Model = new Error
            {
                Code = 76543,
                Description = "Please supply stuff."
            }
        };

        //other stuff
    }
}

I personally think this is pretty straight forward. However, the ServiceStack implementation crushes both of these. Check it yo:

public override object OnGet(Thingy request)
{
    return new HttpResult(
        new Error 
        { 
            Code = 76543, 
            Description = "Please supply stuff" 
        })
        {
            StatusCode = HttpStatusCode.BadRequest
        };
}

That's it. Imagine that. Useful stuff available right out of the ServiceStack box. This here is a testament to a mature framework that is actually used for real world problems. ASP.NET Web Api makes the simplest things painful to do, what does that say about the framework? As Eric put it and I quote: "I can haz easy api??? So it’s not that hard to accomplish this???"

And the Winner is....

Not ASP.NET Web API. Consider using something else. If you are using ASP.NET MVC currently, you'll be very happy to know that it can do everything you need it to. Infact, here is a lean Rest Api layer that you can bolt on to ASP.NET MVC (just install the nuget package "restful-mvc" and follow the instructions). Honestly though, if I could do it all over again, I would just use ServiceStack. Thanks for reading this rant, hope you found it helpful :-).


Written: 9/10/2012