【问题标题】:Duplicate routes need to fallback to next matching route when no Action is found找不到 Action 时,重复的路由需要回退到下一个匹配的路由
【发布时间】:2012-12-02 07:10:02
【问题描述】:

我有一种情况,我需要在 ASP.NET WebForms 应用程序中覆盖旧 HttpHandler API 中的方法。我使用 WebAPI 作为新 API 的基础,它运行良好,除了在新 API 中找不到操作时我需要路由回退。目前这不是真的,相反,只要 WebAPI 控制器没有匹配的操作,我就会得到“404:未找到”。

我对新 API 的路由如下:

var apiroute = routes.MapHttpRoute(
    "API Default",
    "api/{language}-{country}/{controller}/{action}/{id}",
    new { id = RouteParameter.Optional }
);

旧的 Httphandler API 注册路由如下(顺序在 WebAPI 路由之后):

var route = new Route("api/{language}-{country}/Person/{command}", this);
RouteTable.Routes.Add(route);

如果有一个带有 List 操作的 PersonController,我希望 /api/en-US/Person/List 匹配第一个路由,但如果没有,则回退到旧 api。

问题:是否可以在第一个路由中添加一个过滤器,以便仅在控制器上确实有 Action 时才匹配?我还能如何做到这一点?

【问题讨论】:

    标签: asp.net routing webforms asp.net-web-api asp.net-routing


    【解决方案1】:

    更新

    您可能还想调查attribute based routing 这可能是一个更简洁的解决方案,只有您使用路由属性注释的操作才会与路由匹配,这可能会帮助您实现您想要的。

    或者

    虽然可能有比这更好的方法,但如果您实现自定义 IRouteConstraint 是可能的,例如this article.

    您可以改进的高度粗略和现成的方法如下:

    public class IfActionExistsConstraint : IRouteConstraint
    {
        public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
        {
            var actionName = values["Action"] as string;
    
            if (actionName == null)
            {
                return false;
            }
    
            var controllerName = values["Controller"] as string;
            var controllerTypeResolver = GlobalConfiguration.Configuration.Services.GetHttpControllerTypeResolver();
            var controllerTypes = controllerTypeResolver.GetControllerTypes(GlobalConfiguration.Configuration.Services.GetAssembliesResolver());
            var controllerType = controllerTypes.SingleOrDefault(ct => ct.Name == string.Format("{0}Controller", controllerName));
    
            if(controllerType == null)
            {
                return false;
            }
    
            return controllerType.GetMethod(actionName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance) != null;
        }
    }
    

    注册:

    var apiroute = routes.MapHttpRoute(
        name: "API Default",
        routeTemplate: "api/{language}-{country}/{controller}/{action}/{id}",
        defaults: new { id = RouteParameter.Optional },
        constraints: new { IfActionExistsConstraint = new IfActionExistsConstraint() }
    );
    

    【讨论】:

    • 我希望有一种更优化的方式来实现同样的目标,但这可能是最好的方式。添加一些类型和名称的内存缓存,它可能不会对性能产生如此巨大的影响。我还考虑过设置每条要具体覆盖的路由,但我还没有决定哪种方法是易用性和性能之间的最佳权衡。
    • @magix 同意这是一个很大的开销,尽管可以通过内存缓存(这是 ControllerSelectors 框架所做的)来优化它。我已经更新了我的答案以包括基于属性的路由,这可以为定义路由提供更精细的粒度,也可能会有所帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-02-22
    • 1970-01-01
    • 1970-01-01
    • 2021-02-13
    • 2020-11-11
    • 2013-03-21
    • 1970-01-01
    相关资源
    最近更新 更多