Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clean way of avoiding "null entry for parameter id of non-nullable type" error for input parameters

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:

  1. There are quite a few cases where I have a single int param as input, where one specific object is requested (a details page for a product for example).
  2. The parameter is a GET parameter, so it is visible in the url, and is easy to change.
  3. If the user changes the parameter in the url the webserver responds with an error page, logs the error, etc.

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.

like image 851
vinczemarton Avatar asked Nov 11 '22 09:11

vinczemarton


1 Answers

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.

like image 83
David Marchelya Avatar answered Nov 14 '22 21:11

David Marchelya