为何ASP.NET配置的通配404路由未被捕获反而触发Application_Error?
问题原因
你的Default路由优先级高于PageNotFound路由,当你访问/asd这类路径时,会先命中Default路由,框架会把asd识别为controller参数值,尝试查找对应的AsdController,找不到就抛出404异常,直接进入Application_Error,根本不会匹配到后面的全量路由。
你之前加命名空间和关闭命名空间回退的修改,解决的是多区域控制器重名的匹配问题,和这个场景无关,所以不生效。
解决方法
给Default路由添加控制器存在性约束,只有匹配到的控制器真实存在时,才命中Default路由,否则就会向下匹配PageNotFound路由。
首先自定义路由约束类:
using System.Web.Mvc; using System.Web.Routing; using System.Reflection; using System.Linq; public class ControllerExistsConstraint : IRouteConstraint { public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) { string controllerName = values["controller"]?.ToString(); if (string.IsNullOrWhiteSpace(controllerName)) { return false; } // 获取当前程序集下所有继承自IController的控制器类型 var allControllers = Assembly.GetExecutingAssembly() .GetTypes() .Where(t => typeof(IController).IsAssignableFrom(t) && t.Name.EndsWith("Controller", System.StringComparison.OrdinalIgnoreCase)); // 判断对应控制器是否存在 return allControllers.Any(t => t.Name.Equals($"{controllerName}Controller", System.StringComparison.OrdinalIgnoreCase)); } }
然后修改路由注册代码,给Default路由加上约束:
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 }, // 新增控制器存在约束 constraints: new { controller = new ControllerExistsConstraint() }, namespaces: new string[] { "MyAppName.Controllers" } ).DataTokens["UseNamespaceFallback"] = false; routes.MapRoute( name: "PageNotFound", url: "{*url}", defaults: new { controller = "Home", action = "Error" } ); }
补充说明
- 如果需要处理「控制器存在但Action不存在」的场景,可以在基础控制器中重写
HandleUnknownAction方法,跳转到自定义错误页,避免触发Application_Error。 - 路由是按注册顺序从上到下匹配的,全量捕获路由必须放在所有路由的最后,否则会覆盖前面的正常路由规则。
- 如果你的控制器放在单独的类库项目中,需要修改
ControllerExistsConstraint中获取程序集的逻辑,改为加载控制器所在的程序集判断即可。
内容的提问来源于stack exchange,提问作者markzzz
相关产品推荐
相关产品推荐

