I'm implementing a REST layer in .NET MVC 3. I'm looking for a clean way to grab the Accept header to determine if I should return Json or Xml.
I would also like to be able to spoof this header with a GET parameter for debugging (I want this to persist to prod too).
Here's how I'm currently detecting this:
if (Request.AcceptTypes.Contains("application/json") || Request.Url.Query.Contains("application/json"))
This is the only place in my controller code that touches the Request object directly. I would like a cleaner, more testable way to read this. My ideal solution would be a parameter on the controller.
I tried several keywords to see if the default model binder would pick up on it, but nothing I tried worked.
So what's the cleanest way to get this information? A custom model binder? Can you provide an example?
An action filter attribute would be a good, clean solution.
There's a good tutorial here : http://www.asp.net/mvc/tutorials/older-versions/controllers-and-routing/understanding-action-filters-cs
I'm not seeing any better alternatives to a custom model binder. I'll post my implementation of the binder here in case anyone else sees this. Using a model binder allows the Accept header to be strongly bound to a direct input on the action, allowing for direct testing of the return types and doesn't force you to artificially have more actions than you need, nor result to the dynamic typed viewdata/bag.
Here's the Model Binder with a supporting enum type:
public enum RequestAcceptType
{
NotSpecified,
Json,
Xml
}
public class RequestAcceptTypeModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException("bindingContext");
}
RequestAcceptType acceptType = RequestAcceptType.NotSpecified;
// Try for Json
if (controllerContext.HttpContext.Request.AcceptTypes.Contains("application/json") || controllerContext.HttpContext.Request.Url.Query.Contains("application/json"))
{
acceptType = RequestAcceptType.Json;
}
// Default to Xml
if (acceptType == RequestAcceptType.NotSpecified)
{
acceptType = RequestAcceptType.Xml;
}
return acceptType;
}
}
Here's the relevant bit in Global.asax in the Application_Start method:
ModelBinders.Binders[typeof(RequestAcceptType)] = new RequestAcceptTypeModelBinder();
Then to use it in your actions, just make an argument (any name) with the enum type:
public ActionResult Index(RequestAcceptType acceptType)
If nobody responds with a better method in a couple days, I'll accept this as the answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With