问题
I am sure I am doing something wrong, but after spending a few hours on this with no luck, I would be really happy if someone can help me out with this.
Seems like the routing system honors the parameters only in the first routing registration.
This is how my global.asax.cs looks like
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace MvcApplication4
{
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"t", // Route name
"{controller}/{action}/{i}/{v}",
new { i = UrlParameter.Optional, v = UrlParameter.Optional }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}",
new { id = UrlParameter.Optional }
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}
}
This is how the home controller looks like
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MvcApplication4.Controllers
{
public class HomeController : Controller
{
public ActionResult Index(int? id)
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About(int? a)
{
return View();
}
}
}
The problem is when I try this url
http://localhost:52230/Home/Index/2
The id is always null. But if I try
http://localhost:52230/Home/Index?id=2
....it works.
It also works if I register home/index before any other route. In which case the routes registered later have the same problem in accepting parameters.
What am I doing wrong here?
UPDATE:
I want both these controllers to work, how would my route registration look like?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MvcApplication4.Controllers
{
public class HomeController : Controller
{
public ActionResult Index(int? id)
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
}
}
2nd controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MvcApplication4.Controllers
{
public class VisitorController : Controller
{
//
// GET: /VisitorSecurityPoints/
public ActionResult Test(string i,string v)
{
return View();
}
}
}
回答1:
The routes are ordered from specific to general. That means MVC will always try to match the most specific route when looking at the URL which has been requested. The routes will become more "greedy" the further down the route list they are.
If we take a closer look at the two routes in question, we can see that there is no way for MVC to differentiate between the two, unless the v
parameter is supplied. They both have the controller
and action
parameters, and they both also have an optional third parameter. If v
isn't supplied, MVC would have no way of knowing which route to use unless a priority system like this was in place, and that's why the order of the routes in the routing table is important.
There are ways to allow MVC to differentiate between two routes like this, but it depends on the data type of the parameter in question. For example, if i
was a DateTime
, rather than an int
, you could add a Route Constraint which matched dates only. See this article for more information on constraints.
I also second Richard's advice for using RouteDebugger. It's invaluable for helping to understand situations such as this and can lead to cleaner routing table design.
Edit
I should also mention that the reason the URL "works" when you tag ?id=2
on the end is because MVC tries to model bind directly to matching action parameters in the query string. However, note that a parameter matching a route will take precedence over a value supplied in the query string. For example, using the following route:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Navigating to http://localhost:50754/home/index/2
will result in id
being 2.
Navigating to http://localhost:50754/home/index/?id=4
will result in id
being 4.
Navigating to http://localhost:50754/home/index/2?id=4
will result in id
being 2.
Updated after comments
The simplest way to solve your particular problem is to change the route table to include the following route:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"visitors", // Route name
"visitor/{action}/{i}/{v}", // URL with parameters
new
{
controller = "Visitor", action = "Test",
i = UrlParameter.Optional, v = UrlParameter.Optional
}
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Notice the {action}
parameter is still present in this new route. This makes things a little easier to expand on it to cater for other actions in the controller. If you need to constrain the route to the actions that are present on the controller, you'd need to add a route constraint as mentioned earlier.
One caveat to this approach is that the route table can grow quite quickly if you're constantly specifiying new routes to match a few URLs. That can make adding new features to a site more difficult because of the existing routing and can also make maintenance more difficult. Whenever possible, you should try to design a route to match the default {controller}/{action}/{id}
route because it makes maintenance and debugging so much easier in the long run.
回答2:
I believe its because your other route ("t") matches first.
回答3:
You can check which routes are being matched by using RouteDebugger. Just install it using nuget:
PM> install-package RouteDebugger
When you view the page it'll list all of your routes and indicate which one(s) were matched. The first matched route will be used.
来源:https://stackoverflow.com/questions/11819989/parameters-in-routing-do-not-work-mvc-3