In ASP.NET MVC I very much like the feature that it automatically tries to fill the parameters of an Action from the request values.
It makes a lot of stuff look very clean and my hierarchic model for forms can travel there and back through the wire with ease.
There is an annoying problem with it though. The automatic exception for missing non-nullable parameters. It's not that the framework is wrong. I understand the reasoning behind this, but:
This kind of error should be handled well within the application, I don't need to pollute my logs with the error, also a 404 error for these cases would be more appropriate IMHO, since without the identifier parameter "the resource could not be found".
I have a few ideas on how to avoid this, but I want something transparent that handles this in one place.
The thing I have done so far is to make the parameter int?
.
Not really transparent, I need to check ID.HasValue
everywhere and throw a 404 explicitly. This pollutes the Action logic.
What is the best place to tackle this at?
EDIT:
I would also like to point out that although throwing a HttpException(404,"")
is still an exception but since browsers searching for favicons, etc. and crawlers make a ton of requests to bad urls anyway, all my logging, alerts etc. is configured accordingly.
I think the appropriate hook here is the ModelBinder.
You will have to come up with strategy for when you want to return a 404. Based on your details above, and for the sake of simplicity, I am going to assume that any time a parameter named "id" is null or is not passed when expected, that this generates a 404. Here is the example ModelBinder code.
using System.Net;
using System.Web;
using System.Web.Mvc;
public class MyModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var result = base.BindModel(controllerContext, bindingContext);
if (result == null && bindingContext.ModelName.ToLower() == "id")
{
throw new HttpException((int)HttpStatusCode.NotFound, null);
}
return result;
}
}
You wire this up in Global.asax
:
protected void Application_Start()
{
ModelBinders.Binders.DefaultBinder = new MyModelBinder();
}
To get more granular, you could add an attribute only to parameters that you want this behavior for, and use reflection to inspect for the attribute and only throw the 404 Exception in this case, there will be a performance penalty for that. So you could wire up specific ModelBinders only for models that have this behavior as needed. Or you could maintain a dictionary of Controllers/Actions/Parameters to apply the behavior to as well.
Edit: You also note that you are specifically concerned with GET requests. This value is available in the model binder: controllerContext.RequestContext.HttpContext.Request.HttpMethod.
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