In my last post we introduced overloading of POST as a way to allow clients that can’t make PUT or DELETE requests. We handled the overloading within the controller… the ItemPost method looked for a query string parameter “_method” and if it found it, handed off processing to one of the other actions.
At the end of that post I admitted I wasn’t happy with the approach (as much the same code would be needed time and again throughout the web service) and I suggested some alternatives. As promised, I’ve thought about it and in this post I’ll present the solution I chose.
Of the three alternative approaches, only the third, “Implement a variant of AcceptVerbsAttribute” seemed viable. To my mind it is also the “correct” solution. In my seventh post of this series I described how the MVC framework uses a class called the ActionMethodSelector to choose which method on a controller should handle a particular request. This class looks for ActionMethodSelectorAttributes on the relevant methods. It uses these attributes to filter out unsuitable methods.
We have been using the AcceptVerbsAttribute to differentiate between relevant methods based on the current request’s HTTP Verb. It derives from ActionMethodSelectorAttribute. All we need is to create a version that also considers overloaded POST methods. As ever, there is a choice of approaches:
- Derive from AcceptVerbsAttribute and override the IsValidForRequest method;
- Copy the code for AcceptVerbsAttribute, change the class name and alter the IsValidForRequest method;
- Create a class to wrap AcceptVerbsAttribute and where possible delegate to the wrapped object.
The first option is a none starter as AcceptVerbsAttribute is sealed. Option two is viable but would not benefit from any bug fixes / improvements made to to AcceptVerbsAttribute. Option three is not as good as inheriting but does allow us to reuse the functionality already available in AcceptVerbsAttribute. Following option 3, I created the AcceptVerbsAndOverloadsAttribute (I know, not a very nice name!) derived from ActionMethodSelectorAttribute. Below is its IsValidForRequest method:
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
if (controllerContext == null)
throw new ArgumentNullException("controllerContext");
string incomingVerb = controllerContext.HttpContext.Request.HttpMethod;
// If the request is a POST, look for an overloaded method
string methodValue = "";
if (string.Compare(incomingVerb, "POST", true) == 0)
methodValue = controllerContext.HttpContext.Request.Params[_parameterKey];
// If the POST is overloaded use the method value, otherwise perform
// the normal functionality
result = Verbs.Contains(methodValue, StringComparer.OrdinalIgnoreCase);
result = _acceptVerbsAttribute.IsValidForRequest(controllerContext, methodInfo);
If the current HTTP Verb is POST, the function checks for the presence of the _parameterKey in the request’s list of parameters (this key defaults to “_method”). If the parameter is found, the list of verbs supported is checked for the parameter’s value. For all other requests, processing is delegated to the wrapped AcceptVerbsAttribute.
With this new attribute we can support overloaded POST without having to copy code into every action that handles POST requests. We just need to use the new attribute, instead of AcceptVerbsAttribute, on all related actions that need to take overloading into consideration.
The code (written in C#) in this download uses the new attribute: RESTfulMVCWebService07.zip (45.90 kb). In my next post I’d like to move on to creating a resource.