You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用RedirectToRouteResult重定向时路由表无匹配路由问题排查

排查RedirectToRouteResult路由匹配失败问题

让我来帮你定位这个路由错误的根源,看完你的代码后,我发现几个核心问题导致了"No Route in route table matches the supplied values"错误:

问题根源

  1. 路由约束不匹配:你的CultureConstraint要求culture参数必须是两位小写字母(正则[a-z]{2}),但你在代码里传递的culture值是"GB"(大写地区代码),这直接违反了约束规则,路由系统自然找不到匹配项。
  2. 路由参数概念混淆:你把地区代码(比如GB)赋值给了路由的culture参数,但从路由定义来看,这个参数应该是语言代码(比如en),而非地区代码——这不仅不符合约束,也混淆了语言和地区的概念。
  3. 路由注册顺序不合理:更具体的路由(比如TourWithCulture)被放在了通用路由(DefaultWithCulture)之后,路由匹配是从上到下执行的,通用路由会先拦截请求,导致特定路由永远不会被匹配到。
  4. RouteValueDictionary处理不严谨:你直接复用了当前请求的RouteData.Values,但没有确保参数格式完全符合路由约束,容易出现格式错误。

修复步骤

1. 修正Culture参数格式,匹配路由约束

首先,把传递给路由的culture值统一为小写的语言代码,确保符合[a-z]{2}的约束:

public override void OnActionExecuting(ActionExecutingContext filterContext) {
    bool redirectToLanguagedVersion = filterContext.RouteData.Values["culture"].ToString() == "zz";
    string defaultLanguage = "en";
    string culture = defaultLanguage; // 这里直接用语言代码作为路由的culture参数

    if (redirectToLanguagedVersion) {
        if (filterContext.HttpContext.User.Identity.IsAuthenticated) {
            var user = IdentityManager.GetUser(filterContext.HttpContext.User.Identity.GetUserId());
            // 优先用用户设置的语言代码,转为小写确保符合约束
            culture = user.CultureInfoLanguage?.ToLowerInvariant() ?? defaultLanguage;
        } else {
            culture = filterContext.RequestContext.HttpContext.Cache["UserCultureInfoLanguage"]?.ToString().ToLowerInvariant() ?? defaultLanguage;
        }

        // 构建正确的路由参数,显式指定必要项
        var routeValues = new RouteValueDictionary {
            { "culture", culture },
            { "controller", filterContext.RouteData.Values["controller"] ?? "Home" },
            { "action", filterContext.RouteData.Values["action"] ?? "Index" },
            { "id", filterContext.RouteData.Values["id"] }
        };

        filterContext.Result = new RedirectToRouteResult("DefaultWithCulture", routeValues);
    } else {
        // 这里再根据语言和地区设置线程文化,比如如果用户有地区信息,可以组合成"en-GB"
        string userRegion = filterContext.RouteData.Values["culture"].ToString() == "en" ? "GB" : "US"; // 示例逻辑,可替换为用户实际地区
        string fullCulture = $"{culture}-{userRegion}";
        Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(fullCulture);
        Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(fullCulture);
    }
}

2. 调整路由注册顺序

把更具体的路由放在通用路由前面,确保特定请求能匹配到正确的路由:

public static void RegisterRoutes(RouteCollection routes) {
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    // 先注册属性路由
    routes.MapLocalizedMvcAttributeRoutes(
        urlPrefix: "{culture}/",
        constraints: new { culture = new CultureConstraint(defaultCulture: "zz", pattern: "[a-z]{2}") }
    );

    // 先注册特定路由(带title参数的Tour路由)
    routes.MapRoute(
        name: "TourWithCulture",
        url: "{culture}/{controller}/{action}/{id}/{title}",
        defaults: new { controller = "Tours", action = "Explore", id = UrlParameter.Optional, title = UrlParameter.Optional },
        constraints: new { culture = new CultureConstraint(defaultCulture: "zz", pattern: "[a-z]{2}") }
    );

    routes.MapRoute(
        name: "Tour",
        url: "{controller}/{action}/{id}/{title}",
        defaults: new { culture = "zz", controller = "Tours", action = "Explore", id = UrlParameter.Optional, title = UrlParameter.Optional },
        constraints: new { culture = new CultureConstraint(defaultCulture: "zz", pattern: "[a-z]{2}") }
    );

    // 再注册通用带culture的路由
    routes.MapRoute(
        name: "DefaultWithCulture",
        url: "{culture}/{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
        constraints: new { culture = new CultureConstraint(defaultCulture: "zz", pattern: "[a-z]{2}") }
    );

    // 最后注册默认不带culture的路由
    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { culture = "zz", controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

3. 优化CultureConstraint的匹配逻辑

如果希望约束可以兼容大小写(或者严格匹配),可以调整CultureConstraint的Match方法:

public class CultureConstraint : IRouteConstraint {
    private readonly string _defaultCulture;
    private readonly string _pattern;

    public CultureConstraint(string defaultCulture, string pattern) {
        _defaultCulture = defaultCulture;
        _pattern = pattern;
    }

    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) {
        if (!values.TryGetValue(parameterName, out var value)) return false;
        
        string culture = value.ToString();
        // 允许默认值"zz"用于触发重定向
        if (culture.Equals(_defaultCulture, StringComparison.OrdinalIgnoreCase)) return true;
        
        // 用忽略大小写的方式匹配正则,或者去掉IgnoreCase严格匹配小写
        return Regex.IsMatch(culture, _pattern, RegexOptions.IgnoreCase);
    }
}

4. OutputCache的跨用户语言隔离优化

为了避免新用户看到其他用户的缓存页面,你可以在OutputCacheAttribute中添加VaryByCustom="culture",然后在Global.asax中实现自定义缓存区分逻辑:

public override string GetVaryByCustomString(HttpContext context, string custom) {
    if (custom.Equals("culture", StringComparison.OrdinalIgnoreCase)) {
        return Thread.CurrentThread.CurrentCulture.Name;
    }
    return base.GetVaryByCustomString(context, custom);
}

内容的提问来源于stack exchange,提问作者DavidShukvani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:47:17