kbd
kbd

Reputation: 4439

MVC Routing issue: null entry

I have this Controller:

public class TestController : Controller
{
    // GET: Test
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult Edit(int accessLevel)
    {
        return View();
    }
}

Set up in RouteConfig.cs as:

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

routes.MapRoute(
    name: "Test Edit",
    url: "Test/Edit/{accessLevel}",
    defaults: new { controller = "Test", action = "Edit", accessLevel = UrlParameter.Optional }
);

If I go to this URL:

http://localhost:35689/Test/Edit/2

I get this error:

The parameters dictionary contains a null entry for parameter 'accessLevel' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Edit(Int32)' in 'MyProject.Mvc.Client.Controllers.TestController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter. Parameter name: parameters

Any idea why that is? I would think that I'm providing the right datatype with /2.

Upvotes: 1

Views: 1551

Answers (3)

Shyju
Shyju

Reputation: 218722

The specific route definition should be defined before the generic default one.The order of route definitions really matters.

routes.MapRoute(
    name: "Test Edit",
    url: "Test/Edit/{accessLevel}",
    defaults: new { controller = "Test", action = "Edit", 
                                                    accessLevel = UrlParameter.Optional }
);

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", 
                                                             id = UrlParameter.Optional }
);

If you keep the other order like what you have (Generic-default first ,specific one later), When a request comes for Test/Edit/2 It will be matched to the generic route definition because Test is a valid controller and Edit is a valid action method name and 2 could be a valid param value for Id param. Since the request got a valid route definition to match to it's url pattern, It will never be evaluated against other route definitions defined below the first one.

Keep all specific route definitions first and have the generic-default one as the very last one.

Or You may use attribute routing to define this route pattern in the Test controller.To enable attribute routing, you can call the MapMvcAttributeRoutes method in the RegisterRoutes method of RouteConfig.cs. You will still keep the default route definition there.

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapMvcAttributeRoutes();

    routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }

    );
}

and in your TestController

[Route("Test/Edit/{id?}")]
public ActionResult Edit(int? id)
{
   //check id value  and return something
}

Also, there is no point in defining a custom route if it matches with the generic default route definition. In your case, Even if you do not define the custom route ,Test/Edit/2 will go to the Edit action method of TestController as the request matches the default route definition.

People usually use these custom route definition to create nice url patterns like

[Route("Product/{id}/{name}")]
public ActionResult View(int id,string name)
{
   //check id value  and return something
}

This route definition will match the request Product/34/seo-friendly-name. Take a look at the URL of this question and you will understand what i am explaining here.

Upvotes: 8

Dilip Oganiya
Dilip Oganiya

Reputation: 1554

Please interchange your Route in RouteConfig becuase Order of routes are really matters a lot.

Upvotes: 1

mlusiak
mlusiak

Reputation: 1054

Switch the routes in RoutesConfig.cs. They should go from the most specific to general.

Your Default route is catching this one.

Upvotes: 2

Related Questions