Thoughts after using ASP.Net Web API (beta) in anger for a week
Nitpicker corner: Yes, I know about Open Rasta, NancyFX, FubuMVC and all of the other cool frameworks out there. I am not here today to talk about them. I am not interested in talking about them and why they are so much better in this post.
You might be aware that I am doing a lot of stuff at the HTTP level, owing to the fact that both RavenDB and RavenFS are REST based servers.
As such, I had to become intimately familiar with the HTTP spec, how things work at the low level ASP.Net level, etc. I even had to write my own abstractions to be able to run both inside IIS and as a service. Suffice to say, I feel that I have a lot of experience in building HTTP based system. That said, I am also approaching things from a relatively different angle than most people. I am not aiming to build a business application, I am actually building infrastructure servers.
There was a lot of buzz about the ASP.Net Web API, and I took a brief look at the demo, marked it as “nice, need to check it out some day” in my head and moved on. Then I run into a strange problem in RavenFS. RavenFS is a sibling to RavenDB. Whereas RavenDB is a document database, RavenFS is a distributed & replicated file server for (potentially) very large files. (It is currently in beta testing ,and when we are doing giving it all the bells and whistle of a real product, we will show it to the world , it isn’t really important to this post).
What is important is that I run into a problem with RavenFS, and I felt that there was a strong likelihood that I was doing something wrong in the HTTP layer that was causing this. Despite its outward simplicity, HTTP is pretty complex when you get down to business. So I decided to see what would happen if I would replace the HTTP layer for RavenFS with ASP.Net Web API.
That means that I have been using it in anger for the last week, and here is what I think about it so far.
First, it is a beta. That is something that is important to remember, because it means that it isn’t done yet.
Second, I am talking strictly about the server API. I haven’t even touched the client API as of now.
Third, and most important. I am impressed. It is a really clean API, nice interface, well thought out and quite nice to work with.
More than that, I had to do a bunch of stuff that really isn’t trivial. And there are very little docs for it as of now. I was able to do pretty much everything I wanted by just walking the API and figuring things out on my own.
Things that I particularly liked:
- The API guides you to do the right thing. For example, different headers have different meaning, and you can see that when you look at the different headers collections. You have headers that goes in the response, headers that go in the request, headers that goes for the content, and so on. It really guides you properly to using this as you should.
- A lot of the stuff that is usually hard is now pretty easy to do. Multi part responses, for example. Ranged requests, or proper routing.
- I was able to plug in DI for what I was doing in a couple of minutes without really knowing anything about how things work. And I could do that by providing a single delegate, rather than implement a complex interface.
- I provides support for self hosting, which is crucial for doing things like unit testing the server.
- It is Async to the core.
- I really like the ability to return a value, or a task, or a task of a value, or return an HttpResponseMessage which I can customize to my heart content.
Overall, it just makes sense. I get how it works, and it doesn’t seems like I have to fight anything to get things done.
Please note that this is porting a major project to a completely new platform, and doing some really non trivial things in there while doing this.
Things that I didn’t like with it:
Put simply, errors. To be fair, this isn’t a complaint about the standard error handling, this works just fine. The issue is with infrastructure errors.
For example, if you try to push a 5MB request to the server, by default the request will just die. No error message, and the status code is 503 (message unavailable). This can be pretty frustrating to try to figure out, because there is nothing to tell you what the problem is. And I didn’t look at the request size at first. It just seemed that some request worked, and some didn’t. Even after that I found that it is the size that mattered, it was hard to figure out where we need to fix that (and the answer to that is different depending on where you are running!).
Another example is using PUT or DELETE in your requests. As long as you are running in SelfHost, everything will work just fine. If you switch to IIS, you will get an error (405, Method Not Allowed), again, with no idea how to fix this or why this is happening. This is something that you can fix in the config (sometimes), but it is another error that has horrible usability.
Those are going to be pretty common errors, I am guessing, and any error like that is actually a road block for the users. Having an error code and nothing else thrown at you is really frustrating, and this is something that can really use a good error report. Including details about how to fix the problem.
There are a bunch of other issues that I run into (an NRE when misconfiguring the routing that was really confusing and other stuff like that), but this is beta software, and those are things that will be fixed.
The one thing that I miss as a feature is good support for nested resources (/accounts/1/people/2/notes), which can be a great way to provide additional context for the application. What I actually want to use this for is to be able to do things like: /folders <—FoldersController.Get, Post, Put, Delete and then have: /folders/search <- FolderController.GetSearch., PostSearch, etc.
So I can get the routing by http method even when I am doing controller and action calls.
Final thoughts, RavenFS is now completely working using this model, and I like it. It is really nice API, it works, but most importantly, it makes sense.
Comments
I'll admit I haven't looked at Web Api much other than making a couple of small demos to play with, but I prefer Service Stack. The API is much nicer IMO.
Btw, about PUT and DELETE.This is old IIS problem. Remove WebDAV.
I guess WCF will not be the one library to rule them all after all :)
Wait... RavenFS? Must find out more...
+1 for the (easy?) self hosting.
@Oren: yes about everything, just one thing: it can be because it's still in beta, it may be because there is no docs or whatever, but the fact that we are used to the fact that "every controller implements IController" combined with the fact that ApiController must not implement IController is a... fun thing to discover :-)
Btw yes, i agree: excellent job
Did you find it easy to write test? I remember ScottGu specifically saying that the API was built with testing in mind. Could you circumvent mocking HttpContext, or did you still have to do that in your scenarios?
When you mention self-hosting and its importance to unit testing the server, do you really mean in-memory hosting? Like
var server = new HttpServer(configuration); var client = new HttpClient(server);
405, Method Not Allowed
Did you check that these verbs were allowed by the relevant Handler Mappings (in IIS7)?
WCF error messages aren't always helpful either in figuring out why something isn't working...hopefully they fix this by the time it is released. :) as it seems to have been a significant flaw with WCF too...
@Khalid - I don't know specifically about HttpContext because I haven't played with it, but there is a new abstraction for Request/Response - it's called HttpRequestMessage/HttpResponseMessage. The Gu posted about it recently (http://weblogs.asp.net/scottgu/archive/2012/02/23/asp-net-web-api-part-1.aspx).
Khalid, you don't have to touch HttpContent. In Web API you can work directly with new HTTP message abstractions which are just light data containers.
+1 for "The one thing that I miss as a feature is good support for nested resources"
Wow, Oren. The title of this post is extraordinarily misleading. Would you consider re-writing the title? Maybe an update-note at the top? I have no personal interest in this, btw; I merely feel that the title should be somewhat aligned with the content.
Ward, How is this title misleading?
Its misleading because from the title you sound like you are angry about how much the API sucks, then in the post it sounds like you mostly like it.
Its misleading because from the title you sound like you are angry about how much the API sucks, then in the post it sounds like you mostly like it.
Perhaps Ward means that title makes it seem that you are mad at WebApi
Exactly as Jason and Walt say. I read the title and I think you're going to give a blistering critique of WebApi because "you're angry". The body of the post suggests the opposite.
While you're at it you might clarify your note: "I am not interested in talking about them and why they are so much better in this post".
I am NOT suggesting that you should talk about "them" ... that would be fodder for a different post. It is good to postpone that discussion for another day as you have done.
What is unclear to me is if you hold the opinion that "they are so much better". It would be weird if you thought they were "so much better" give the mostly glowing remarks about WebApi. I mean if they were significantly better, why mess around with WebApi?
Hope this is helpful somehow. I really did appreciate your (pre)review.
I thought exactly the same as Ward. I came here to check why You think WebAPI suck so much :)
In some cultures "doing something in anger" means "doing it extensively/eagerly" in a such context.
@Glenn: just out of curiosity, is there a real reason why the apicontroller doesn't implements icontroller (breaking all the rest of the standard mvc chain) or it was just a case of not enough time in porting the old non-mvc code to the new environment?
I'm been banging me head against a virtual wall for a week or so thinking "should I use this for my new API stuff I'm learning to do? Maybe I should post something in the RavenDb groups asking what Ayende thinks..".
.. then come back from 3 days off the grid ..
and Ayende does it :)
Thanks Ayende! Really appreciate this post. If you're giving it a tick (with some grumbles here and there) I'm going to give it a go, too :)
BTW -> did u end up leaving it in, in your RavenFs stuff? or did u end up rolling back, despite having some positive stuff, with WebAPI?
Yep, the title is misleading. I was having a good time with the web api but when your post popped up in my reader I was going like "ok, now let's go and see why Oren hates this" :-)
Maybe Ayende was angry he could not find any blogworthy conceptual flaws. I am glad to hear that, congratulations Glenn, you do deserve some REST.
I think people aren't understanding "in anger" in the title comes from the English expression "to use in anger". I've always understood this expression to mean something like: use X aggressively to see if it meets your needs/requirements, often pushing X to its limits of features or performance.
E.g. I've would say "I used my new SSD in anger and am super happy with it", meaning I ran plenty of benchmarks, performance tests and found the results to be good.
Guys, "using in anger" usually means "using in a non trivial scenario, not a demo scenario". In related to the fact that if things don't work, you will be angry, not that you are angry.
Ward, "so much better" - in most of these types of posts, I get comments about framework X and why it is so much better than what I am talking about right now
Justin, It is in RavenFS, and we will ship with that, yes.
I agree, the asp.net web api is very good, except for the routing.
I can understand why they decided to use the same routing as the mvc as it makes it almost a complete drop in for the mvc...
It's frustrating for me because I've been testing with preview 4, 5 and 6 and being able to freely specify the url template per method was very convenient.
The other pain point I've found is they have dropped operation handlers in favor for action filters, although I could be easily swayed that action filters are a cleaner way of intercepting an operation...
Ayende/Robert,
What I've done to get around the URL routing issue in Web API is creating an attribute for the action methods that contains a URI template and verb - then reflect over those during app startup to configure the routes. For example:
[Route( Template = "/accounts/{accountId}/people/{personId}/notes", Method = "GET" )] public HttpResponseMessage Get(int accountId, int personId)
// foreach route attribute: config.Routes.MapHttpRoute ( name: endpoint.Name, // unique name routeTemplate: routeTemplate, // template from attribute defaults: new { controller = controllerRouteName, // name of the controller action = endpoint.Name // name of the method }, constraints: new { httpMethod = httpMethodConstraint } );
Robert,
Check out message handlers - we had a few operation handlers that we were able to port as message handlers: http://www.asp.net/web-api/overview/working-with-http/http-message-handlers
Ward - I'm pretty sure Oren was being sarcastic at the onrush of everyone promoting "my favorite framework" and declaring it universally better than WebAPI.
Especially funny is the quote from Phillip Haydon above: "I'll admit I haven't looked at Web Api much other than making a couple of small demos to play with, but I prefer Service Stack. The API is much nicer IMO."
Clearly no real analysis was done. Why not just admit that you don't want to learn something new?
Even better, if you insert [Cool technology of the day] in that statement, nobody challenges you. What if I said this instead - everyone would be all over it!
"By the way, I haven't looked at MVC much. But I prefer WebForms, the API is much nicer."
I could consider using web api if it was a direct complement to MVC, sadly it chooses to COMPETE with MVC.
I want 1 Model, 1 Controller action to return JSON, HTML (razor). Theoretically XML or Atom.
@dotnetchris: i know it seems to make sense, but if you really think about it, it doesn't.
Although technically it could be done (and i'll say, quite easily), it's not that it's simply "html". The html you get back from a common ui view in html is a different beast than an "html representation of a resource".
So, even though technically could be done, it creates too much friction in practice to exchange the 2 flows.
Just my 2 cents.
@Steve, That route attribute idea looks like a life saver. I'll give it a go! Actually, I don't know why I didn't think of it myself.
The reason I was using operaton handlers was I needed to have code executed in the same thread as the action handler itself, mainly to support localization.
The one benefit of using action filters over operation handlers is that the configuration of the action filters are more straight forward (or at least easier to access for thoes who have never worked with WCF before)
"Use in anger" does NOT mean you are angry. It means in earnest, see here for a non-authoritative reference (http://www.phrases.org.uk/bulletin_board/55/messages/494.html) you went in full force and put it to the test. I understand how people are taking it because well Oren sometimes does come off pretty angry in these posts however I don't think his usage of the phrase is wrong.
Aside from that Oren, this is a great post, thanks for doing it!
Saying something is 'used in anger' makes perfect sense if you understand the English language. Great post.
Definitions aside, you've got lots of people who mis-undertand the title, myself included. It doesn't matter why or that technically it isn't wrong or that you can explain it in the comments people see after the come here and read the article and read all the comments. Take the feedback and change the title. Folks are finding your blog because it is on the homepage of asp.net (where they have clipped the title) and the resistance here detracts from all other evidence, this post included, that you're a pretty smart guy!
Chris, "When I use a word,' Humpty Dumpty said in rather a scornful tone, 'it means just what I choose it to mean — neither more nor less."
interesting, have you tried OpenRasta ? if you did I would like to hear you experience with it compare to the Web API from MS. We have been using OR for building our service api for a while now, i have to say it's really pleasant to work with. thx
Ha! Okay - uncle! If the ground rules are you get to choose the meanings of the words you use, then I'll use more grains of salt when I read your articles. Don't forget how Humpty Dumpty turned out in the end, tho! Truthfully I'm glad folks like you publish how you put technology through the ringer as it usually ends up forming a better product in the end.
There have been a number of articles debating how the IDependencyResolver interface is "broken" because it has no Release method. http://mikehadlow.blogspot.co.uk/2011/02/mvc-30-idependencyresolver-interface-is.html This would also apply to the lambda approach.
Do you have an opinion? How do you deal with the potential memory leak?
Comment preview