關於MVC Area Routing的怪異問題
問題
同事反應了一個關於MVC Area的怪異問題。客戶自行輸入一個不存在的網址,本應該出現404找不到,但卻發生Exception。如下圖,找到JiahuaController的Index,但找不到View。
原因
Route往子層的Area比對到了,專案的層級如下:
- Root/Home/Index
- Kim(Area/Jiahua/Index
一般來說,瀏覽/Jiahua會比對到上層預設的Route,而透過namespace的限制,照理來說不會往Area比對才對。如下圖,結果,它是在此namespace比對不到後,又往area的contoller尋找...昏。
解決方式
以下兩個擇一即可解決:
1.透過自訂Route,只比對上層的Controller。ps:此方法不是最佳解,這是在找到最佳解前實作出來的。
/// <summary>
/// 此類別的建講式只會被執行一次
/// http://johnatten.com/2013/08/21/customizing-routes-in-asp-net-mvc/
/// https://stackoverflow.com/questions/21583278/getting-all-controllers-and-actions-names-in-c-sharp/21583402
/// </summary>
public class IsRootController : IRouteConstraint
{
private Dictionary<string, string> _rootControllers = new Dictionary<string, string>();
public IsRootController()
{
Assembly asm = Assembly.GetExecutingAssembly();
var _rootControllers = asm.GetTypes()
.Where(type => typeof(Controller).IsAssignableFrom(type) && type.FullName.IndexOf(".Areas.") == -1) //filter controllers
.Select(type => type.Name.Replace("Controller", ""))
.ToDictionary(c => c, c => c);//https://stackoverflow.com/questions/627867/linq-query-to-return-a-dictionarystring-string
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
string controller = values["controller"].ToString();
return _rootControllers.ContainsKey(controller);
}
}
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
, namespaces: new[] { "WebApplication1.Controllers" }
, constraints: new { controller = new IsRootController() }
);
}
}
2.在DataToken加入UseNamespaceFallback=false,告設Route只比對目前namespace的controller就好。
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
, namespaces: new[] { "WebApplication1.Controllers" }
).DataTokens["UseNamespaceFallback"] = false;
參考文章