In my previous posts on this subject I have gradually developed a web service that handles multiple representations and returns appropriate HTTP Response Codes. I’m now moving on to what Roy Fielding described as the Uniform Interface.
A web service exposes a uniform interface if all its resources can be accessed by the same small set of well known methods (or a subset of those methods). In HTTP, the uniform interface contains methods (or verbs) such as GET, PUT, POST, DELETE, HEAD etc. In RESTful Web Services, Richardson and Ruby explain HTTP’s uniform interface as:
All interaction between clients and resources is mediated through a few basic HTTP methods. Any resource will expose some or all of these methods, and a method does the same thing on every resource that supports it.
The benefit of this approach is that clients can be developed to run against many different applications without the developers having to learn a new interface. Imagine if the world wide web was made up of web sites that had each developed their own interface… web browsers would have to be coded to deal with each site individually! If that had been the case, the web would not be the huge success it is now. However, that is the approach being taken by SOAP based web services.
If you have tried the test harness developed in an earlier post, you will have noticed that the GUID service happily returns the same result for all the HTTP Verbs. In this first post about HTTP verbs, I will address filtering out unsupported verbs.
As of Preview 5 of the MVC the AcceptVerbs attribute has allowed us to indicate which HTTP verbs an action method in a controller can respond to. I added this attribute to GuidController’s Get method specifying it would only accept the GET verb. This meant that any requests using other verbs would be rejected.
Unfortunately, if any other verb is used against this simple web service, the controller’s HandleUnknownAction method is triggered. Within HandleUnknownAction it is not simple to decide if the error is due to a wrongly named action or an unsupported verb. The current implementation of HandleUnknownAction in our web service returns 404 – Not Found. Ideally for an unsupported verb we should return 405 – Method Not Allowed.
What I needed to do was to trap requests that used any verb other than GET. Initially I:
- Changed the routing to use a new action, “HttpVerb.”
- Decorated the Get method in GuidController with [ActionName("HttpVerb")] so that it would respond to the new route
- Created a new AllOtherVerbs method in GuidController decorated with [ActionName("HttpVerb")] thereby “overloading” the action name
- Added an AcceptVerbs attribute (initialised with a list of all the common verbs) to the AllOtherVerbs method
- Have AllOtherVerbs return a 405 – Method Not Allowed code
This solution worked, but was far from satisfactory. For one, it would only trap well known verbs that were listed in the ActionName attribute. In Preview 5 there was no way to provide a simple but satisfactory catch all. Two things I looked into and found did not work:
- I thought there might be a wildcard that could be used to initialise the AcceptVerbs attribute. There wasn’t.
- If the AcceptVerbs attribute was dropped from the AllOtherVerbs method, I thought this would catch all other requests. Instead, the web service threw an exception during initialisation as the routing was ambiguous (i.e. in the case of a GET request, should the framework choose the action decorated with [AcceptVerb("GET")] or the action that accepted all verbs).
Luckily, as I was finding this out, ASP.Net MVC Beta was released. A new feature of the Beta was that in the above situation it would choose the action with the AcceptVerb attribute over the action with no AcceptVerb attribute.
Now I could have two methods in GuidController:
public class GuidController : BaseController
public ActionResult Get(int? count)
public ActionResult AllOtherVerbs()
The Get method would only handle GET requests and the AllOtherVerbs method would catch the rest. So that the AllOtherVerbs method would not have to be copied into each controller, I moved it to BaseController. Also, to aid client developers, I decided to always populate the Allow response header to list the verbs that a controller supports. I created an abstract AllowedVerbs property in BaseController to force all derived classes to provided a list of verbs they support.
In my next post I’ll look at using verbs more fully. In the meantime, here is the code so far: RESTfulMVCWebService04.zip (33.74 kb)