I have an internet-facing website that's built using MVC4 and I occasionally get error reports from bots or curious users who send requests for incomplete URLs.
For example:
public class ProductController : Controller
{
[HttpGet]
public void View(int id)
{
// ...
/product/view/1
is valid./product/view
is invalid as the argument is not specified.Such invalid requests raise exceptions resembling:
System.ArgumentException: The parameters dictionary contains a null entry
for parameter 'id' of non-nullable type 'System.Int32' for method
'System.Web.Mvc.ActionResult View(Int32)' in 'Foo.ProductController'. An
optional parameter must be a reference type, a nullable type, or be declared
as an optional parameter.
Parameter name: parameters
at System.Web.Mvc.ActionDescriptor.ExtractParameterFromDictionary(ParameterInfo parameterInfo, IDictionary`2 parameters, MethodInfo methodInfo)
at System.Web.Mvc.ReflectedActionDescriptor.<>c__DisplayClass1.<Execute>b__0(ParameterInfo parameterInfo)
...
As the exception message states, I could make the id
argument nullable and check within the action method, but I have many controllers with many actions.
I'd like to return a BadRequest
/NotFound
response to any request that fails to bind arguments to action parameters, and specify this in a single place in code to apply across all controllers.
How can this be done?
One approach that seems to work is to override OnActionExecuted
in the controller (I use a base controller, so would put it there.)
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Exception == null)
return;
// Avoid 'action parameter missing' exceptions by simply returning an error response
if (filterContext.Exception.TargetSite.DeclaringType == typeof(ActionDescriptor) &&
filterContext.Exception.TargetSite.Name == "ExtractParameterFromDictionary")
{
filterContext.ExceptionHandled = true;
filterContext.Result = new HttpStatusCodeResult((int)HttpStatusCode.BadRequest);
}
}
It feels a little uncomfortable to do this as it may break in future versions of the framework. However if it does break, then the site will revert to returning 500's instead of 400's.
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