我应该在哪里编写代码来覆盖 MVC 请求的路由数据值?

Where should I write the code to override Route data values of a MVC Request?

我想根据 User.Identity 中的用户 claim 覆盖 RouteData 中名为 lang 的项目的值。

例如,如果路由数据中的 lang 值为 en 示例。com/en/login),我想读取 lang claim 并用它覆盖 roue 数据中的 lang 。因此登录页面中的其他链接应遵循 new lang 值(而不是 en)。

我在哪里设置语言:

在我的项目中,我在Application_AuthenticateRequest中设置了UiThread的语言。所以我在那里更改了 lang 路由值,但似乎没有预期的结果(为什么?):

requestContext.RouteData.Values["lang"] = langName;

我应该在哪里编写代码来覆盖请求的路由值? 你的建议是什么

创建一个实现 IActionFilter 的 class,覆盖其中的 "lang" 值,并配置 MVC 以使用该 IActionFilter

using System.Web.Mvc;

namespace WebApplication1.Filters
{
    public class LangOverrideFilter : IActionFilter
    {
        public void OnActionExecuted(ActionExecutedContext filterContext)
        {
        }

        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            filterContext.RouteData.Values["lang"] = "en";
            //Access userclaims through filterContext.HttpContext.User.
        }
    }
}

在App_Start/FilterConfig.cs:

using System.Web;
using System.Web.Mvc;
using WebApplication1.Filters;

namespace WebApplication1
{
    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new LangOverrideFilter());
            filters.Add(new HandleErrorAttribute());
        }
    }
}

我的 App_Start/RouteConfig.cs 用于测试:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

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

            routes.MapRoute(
                name: "Default",
                url: "{lang}/{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}

Application_AuthenticateRequestMVC life cycle 中为时已晚,无法对路由值产生任何影响。

MVC 在动作过滤器 运行 之前的请求中很早就使用 解析 ModelBinder 中的值,因此动作过滤器不是解决此问题的方法。

你基本上有几个选择。

  1. 创建您自己的 Route。路由负责将请求转化为路由值,因此您可以在此处放置条件逻辑来构建您的请求。
  2. 使用全局注册IAuthorizationFilter。授权过滤器 运行 在 ModelBinder 之前,因此会影响传递到模型和操作方法中的值。

However, as a dissatisfied user of other web sites that take control over the language selection process instead of just letting the user decide based on the URL/selection, I have to protest. You think you are helping the user by doing this, but in fact you are making your web site more frustrating to use.

  1. Most users will enter your site through a search engine or one of your site's marketing channels. In each of these cases, they will automatically enter your site in their own language because they are searching in their own language. At least that is what will happen if you and don't use any cookies or session state to block search engines from indexing your localized pages.
  2. Users who are returning through their browser history or bookmarks will also always come back in their own language.
  3. A (usually) very small fraction of users may enter your site by typing www.somesite.com, in which case they won't come in under their own language. They will have no problem recognizing that they are seeing the wrong language and will immediately look for some sort of language dropdown or flag icon to switch to their language. Unless you are using some sort of remember me feature, this step will always happen before they log in.

So, there is little point in "overriding" the culture when the user logs in. They will have already selected the right culture in 99.999% of all cases that reach this stage.

如果您仍然坚持通过这样做来“帮助”您的用户,请不要覆盖 URL 的文化。在登录操作的 post 部分放置 RedirectToAction 以将它们重定向到具有新文化的 URL 仅在登录时 .这将使您的(现在感到沮丧的)用户能够覆盖您的覆盖并改回他们打算使用的语言来查看网站。

如果您没有让他们选择以 URL 或某种语言 select 以他们想要的语言查看网站,或者,当他们看到不要 return.

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }

    // This doesn't count login failures towards account lockout
    // To enable password failures to trigger account lockout, change to shouldLockout: true
    var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
    switch (result)
    {
        case SignInStatus.Success:
            // Change to the culture of the signed in user by replacing the
            // first segment of the URL.
            returnUrl = ChangeCultureInUrl(returnUrl);
            return RedirectToLocal(returnUrl);
        case SignInStatus.LockedOut:
            return View("Lockout");
        case SignInStatus.RequiresVerification:
            return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
        case SignInStatus.Failure:
        default:
            ModelState.AddModelError("", "Invalid login attempt.");
            return View(model);
    }
}

同样,我的建议是 让用户 select 他们的文化 ,但如果出于某种原因您仍然认为这是必要的,上述解决方案要好得多而不是劫持文化并从用户手中夺取控制权。