The order in which routes are declared makes a difference. There is the possibility of a URL hit with two routes, but the router will compare with the routes in the order they were declared. The first hit wins.
The right thing to do is to specific routes at the beginning, because if they are not suitable, then the route will be analyzed Default. We could start like this:
routes.MapRoute(
name: "RouteEvent",
url: "{ProdutoNome}",
defaults: new
{
controller = "Produto",
action = "Detalhe",
ProdutoNome = UrlParameter.Optional
}
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
First let’s understand that EVERYTHING that comes after the domain and port (ex: after localhost:43760/), ie all the path to the URL comes in parameter form to the RouteValueDictionary
, including the name of the Controller, Action (and Area if you had any), as well as the parameters of the action itself.
Even "would work" but not as we would like. The problem is that all Urls with only one parameter (or none) would fall in the first route:
localhost:43760 (no parameters)
localhost:43760/Nomedomeuproduto (with a parameter Nomedomeuproduto)
localhost:43760/Home (with a parameter Home)
localhost:43760/Account (with a parameter Account)
...because the first route consists of a single parameter ProdutoNome
, which is also optional.
These Urls would not fall into it, however:
localhost:43760/Home/Index (two parameters, Home and Index)
localhost:43760/Account/Login (two parameters, Account and Login)
For the route Routeevent expects only one parameter (ProdutoNome
which may even be omitted), but not two parameters (e.g.: Home + Index or Account + Login).
By removing the ProdutoNome = UrlParameter.Optional
, at least the localhost:43760
no longer falls on this route (because the ProdutoNome
is mandatory). Still, all other Urls with a single parameter (e.g.: Controller only, omitting Action) would fall on that route.
Solution
To solve this, we have to create a Constraint on the specific route, to check the Urls with a single parameter, and find out if this parameter is actually a Product or a Controller. Example:
public class ProdutoConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
string nomeProduto = values[parameterName].ToString();
using (var ctx = new MeuDbContext())
return ctx.Produtos.Any(p => p.Nome == nomeProduto);
}
}
...or any other way for you to ensure that the value in question is a product.
You could also do the reverse if you wanted to avoid a SELECT
in the database. I could guarantee that the parameter received does not match the name of any Controller (maybe it’s even better this way):
public class ProdutoConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
return !Assembly.GetAssembly(typeof(MvcApplication))
.GetTypes().Where(type => typeof(Controller).IsAssignableFrom(type))
.Any(c => c.Name.Replace("Controller", "") == values[parameterName].ToString());
}
}
So your route would be that way:
routes.MapRoute(
name: "RouteEvent",
url: "{ProdutoNome}",
defaults: new
{
controller = "Produto",
action = "Detalhe"
},
constraints: new { ProdutoNome = new ProdutoConstraint() }
);
Note: Maybe you have a Controller whose name is also the name of a product, obviously your Controller will be ignored, because it will understand that you want to see the product, once it exists. But it would be the opposite if your routine was trying to ensure that the parameter was not an existing Controller, in this case the Controller would be displayed instead of the product.
Recalling that the RouteEvent
must be declared in RouteConfig.cs
above the route Default
.
It is because you have already registered a route that gives
match
in this URL, the default MVC route will capture this and try to find a controller whose name isNomeDoMeuProduto
– Jéf Bueno
I don’t think so. Because in my route file only this route is being recorded.
– Bruno Heringer
There is not even the standard record?
– Jéf Bueno
In this case I retreated to test. But yes, there will be.
– Bruno Heringer
Post the route archive?
– novic