Bug
https://github.com/davidliang2008/DL.Issues.AspNetCore.Mvc.CustomRouting
When you want a more user/SEO friendly URLs on your site, you would set up custom routings to use slugs instead of ids.
```c#
public void Configure(IApplicationBuilder app, IHostingEnvironmentt env)
{
...
app.UseMvc(routes =>
{
routes.MapRoute(
name: "productListByTypeRoute",
template: "products/{type}",
defaults: new { area = "", controller = "products", action = "listByType" }
);
routes.MapRoute(
name: "productListByCategoryRoute",
template: "products/{type}/{category}",
defaults: new { area = "", controller = "products", action = "listByCategory" }
);
routes.MapRoute(
name: "productDetailsRoute",
template: "products/{type}/{category}/{product}",
defaults: new { area = "", controller = "products", action = "details" }
);
routes.MapRoute(
name: "areaRoute",
template: "{area:exists}/{controller=dashboard}/{action=index}/{id?}"
);
routes.MapRoute(
name: "default",
template: "{controller=home}/{action=index}/{id?}");
);
});
...
}
And then I have the controller and action setup to handle those requests.
```c#
public class ProductsController : Controller
{
...
public async Task<IActionResult> ListByType(string type)
{
...
return View(...);
}
public async Task<IActionResult> ListByCategory(string type, string category)
{
...
return View(...);
}
public async Task<IActionResult> Details(string type, string category, string product)
{
...
return View(...);
}
...
}
So the following links are expected to call their corresponding actions:
| Link | Action | Parameters |
| ---- | ------ | ---------- |
| /products/countertop | ListByType | type=countertop |
| /products/countertop/2cm-granite | ListByCategory | type=countertop, category=2cm-granite |
| /products/countertop/2cm-granite/imperial-splendor | Details | type=countertop, category=2cm-granite, product=imperial-splendor |
Everything works fine until you add an area! All custom URLs would return 404!
```c#
[Area("admin")]
public abstract class AdminControllerBase : Controller {}
```c#
public class DashboardController : AdminControllerBase
{
public IActionResult Index()
{
return View();
}
}
Notes:
[Area("admin")] is commented out, all custom links would work with the setting new { area = "", ... }.[Area("admin")] on, the only way to get those custom links to work is to remove area = "" from custom routing setup.Microsoft.AspNetCore.Mvc 2.0.7!Microsoft.AspNetCore.Mvc or Microsoft.AspNetCore.App or Microsoft.AspNetCore.All:V2.1.0
@davidliang2008 From the repro app its not clear where you are trying to generate the links from. Are you doing it from the DashboardController to Products? if so, the ambient value of 'admin' is probably being used.
No, all product links are public. They're not under Admin area. I have the ProductController defined just as a regular Controller and 3 more actions within it: ListByType, ListByCategory and Details. For testing purpose, All 3 actions return Index view with different string so that we know which action is getting called.
The issue is:
[Area()] is not used.area="" in those 3 custom route mapping defaults in Startup.cs, which I don't understand.Hope I explained it clearly.
Ah yes, I am able to reproduce the issue. Investigating the cause...
So seems like the root cause of the issue is change in behavior of the GetHashCode api on StringComparer.
In netcoreapp2.0, the following prints 0:
```c#
Console.WriteLine(StringComparer.OrdinalIgnoreCase.GetHashCode("")); // here "" can be thought as area=""
Since this used to give `0`, we have logic of a string being null also as `0`
In netcoreapp2.1, the above code prints a non-zero value which causes a look up miss for action descriptors where they are built with key like area=null and the look value is area=""
cc @rynowak
@davidliang2008 as a workaround, you can change your route configuration to be like
```c#
defaults: new { area = (string)null, controller = "products", action = "listByType" }
FYI @stephentoub in case the behavior change was unintentional.
You guys are awesome!!
FYI @stephentoub in case the behavior change was unintentional.
It's intentional. Randomized string hashing was enabled for IgnoreCase in 2.1.