trevorc
trevorc

Reputation: 3031

How do I handle null parameters dictionary error for MVC3 routes?

Most of the routes I use are based on the default route defined in a standard MVC3 application:

 routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional });

However, if I have route that is "/Book/Update/1" and the '1' is left off because a user typed in the address manually without the 1, the null parameters error is thrown. Obviously my route requires this 1 and is not optional, but I don't won't to define a custom route for every one of my actions. If I turn on custom errors I am not sure what type of error this is to redirect to the proper html error page.

Question: How do I handle this error like it's a 404? I don't see any problem with this approach because the reality is that leaving off an Id number in a route is the same as a 404 not found.

Or am I missing something in designing my routes? Seems pretty straight forward to me.

Thanks.

Update: I appreciate the answers given below. They are both reasonable approaches. For more clarity on my question let me elaborate a bit more. A route of "/Book/Update/1" should in my opinion have only 1 single purpose. Update book 1. Any other deviation from that should fail and redirect to a 404. I don't see this as being any different than a static html page that doesn't exist. I may arguing about a feature that just isn't included in .net, I just thought there could be simpler way.

Update2: Should have dug a little deeper. There is a great example here. I found the second answer the most helpful.

Upvotes: 4

Views: 2032

Answers (3)

Evolution Rich
Evolution Rich

Reputation: 56

Late to the party I know, but hopefully this might help someone else. I took Jamie's answer above and extended it to make it simpler to use so you don't need to list all of the parameters individually, allow different redirects from different actions and to automatically take into account nullable type parameters.

public class RequiredActionParametersAttribute : ActionFilterAttribute
{
    private readonly string _redirectUrlConfigKey;

    public RequiredActionParametersAttribute(string redirectUrlConfigKey)
    {
        _redirectUrlConfigKey = redirectUrlConfigKey;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (string.IsNullOrWhiteSpace(_redirectUrlConfigKey))
            return;

        var redirectUrl = ConfigurationManager.AppSettings[_redirectUrlConfigKey];
        if (string.IsNullOrWhiteSpace(redirectUrl))
            return;

        var actionDescriptor = filterContext.ActionDescriptor;
        var parameterDescriptors = actionDescriptor.GetParameters();

        foreach (var parameter in filterContext.ActionParameters)
        {
            if (parameter.Value == null)
            {
                var parameterDescriptor = parameterDescriptors.First(p => p.ParameterName == parameter.Key);
                if (!IsNullableType(parameterDescriptor.ParameterType))
                {
                    filterContext.Result = new RedirectResult(redirectUrl);
                    break;
                }
            }
        }
    }

    private static bool IsNullableType(Type type)
    {
        return Nullable.GetUnderlyingType(type) != null;
    }
}

Then on the action

    [RequiredActionParameters("DetailsPageExceptionRedirect")]
    public ActionResult Details(int param1, int param2, bool param3, int? param4)

You can add different redirects for different actions to the config file and pass the config key in to the action filter. It will then check for each parameter's existence and if it's null, check whether it's a nullable type, then if not will redirect to the location you've specified.

Upvotes: 0

Jamie
Jamie

Reputation: 3051

I had this problem as well, what i did is create a re-usable action filter where i specify the required parameters in.

The action filter:

  public class RequiredActionParameters : ActionFilterAttribute
{
    private readonly IEnumerable<string> _parameters;

    public RequiredActionParameters(params string[] parameters)
    {
        _parameters = parameters;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (!filterContext.IsChildAction && !filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
        {
            foreach (var parameter in _parameters)
            {
                if (filterContext.ActionParameters[parameter] == null)
                {
                    filterContext.Result = new RedirectResult("/Error/NotFound");
                    break;
                }
            }
        }
    }
}

On the action in the controller:

 [RequiredActionParameters("restaurantId", "testId")]
 public ActionResult Details(long restaurantId, long testId)
 {
        View();
 }

I tested it with error log tool elmah and it didn't report any errors.

Upvotes: 0

danludwig
danludwig

Reputation: 47375

public ActionResult Update(int? id)
{
    if (!id.HasValue) return HttpNotFound();

    //Debug.Assert(id.Value != null);
    ...
}

Upvotes: 2

Related Questions