MVC 5如何使用本地化路由定义Owin LoginPath

时间:2014-01-16 17:39:15

标签: c# asp.net-mvc-5 owin

我有一个MVC 5网站,其本地化路线定义为

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

默认文化导致"en-US"


启动时出现问题我必须使用LoginPath属性定义登录URL,该属性设置一次,它将始终使用提供的值,例如如果“/ en-Us / Account / Login”是指定值,则为默认文化。然后我尝试使用UrlHelper类,希望体验一些魔力,但结果显然是一样的:

var httpContext = HttpContext.Current;
        if (httpContext == null) {
          var request = new HttpRequest("/", "http://example.com", "");
          var response = new HttpResponse(new StringWriter());
          httpContext = new HttpContext(request, response);
        }

        var httpContextBase = new HttpContextWrapper(httpContext);
        var routeData = new RouteData();
        var requestContext = new RequestContext(httpContextBase, routeData);
        UrlHelper helper = new UrlHelper(requestContext);

        var loginPath = helper.Action("Login", "Account");

        // Enable the application to use a cookie to store information for the signed in user
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,                
            LoginPath = new PathString(loginPath)
        });

我的问题是:有没有办法破解这种机制来动态检索当前文化,或者我被迫将当前文化设置为cookie,当我被重定向到登录页面时,使用cookie值来在渲染页面之前设置当前文化?

由于

8 个答案:

答案 0 :(得分:39)

我确实遇到了同样的问题,并找到了克服这种限制的方法。

CookieAuthenticationOptions选项中,有一个“Provider”属性,该属性使用CookieAuthenticationProvider初始化。这实现了一个名为ApplyRedirect的方法和一个委托OnApplyRedirect。 我的第一个想法是覆盖这个ApplyRedirect并实现处理本地化路由所需的逻辑。但不幸的是它无法覆盖。将我的逻辑传递给OnApplyRedirect会导致覆盖默认行为。理论上你可以抓住source of this behavior,将它复制到你的项目中并根据你的需要进行修改,但这显然不是一个好习惯。 首先,我决定使用委托围绕CookieAuthenticationProvider创建包含两个扩展点的包装器,并保留除使用的url之外的默认行为 - 或者更容易,解决包装器(thx到lafi)。

然后在auth配置中,我将自定义逻辑添加到提供者:

public void ConfigureAuth(IAppBuilder app)
{
    UrlHelper url = new UrlHelper(HttpContext.Current.Request.RequestContext);

    CookieAuthenticationProvider provider = new CookieAuthenticationProvider();

    var originalHandler = provider.OnApplyRedirect;

    //Our logic to dynamically modify the path (maybe needs some fine tuning)
    provider.OnApplyRedirect = context =>
    {
        var mvcContext = new HttpContextWrapper(HttpContext.Current);
        var routeData = RouteTable.Routes.GetRouteData(mvcContext);

        //Get the current language  
        RouteValueDictionary routeValues = new RouteValueDictionary();
        routeValues.Add("lang", routeData.Values["lang"]);

        //Reuse the RetrunUrl
        Uri uri = new Uri(context.RedirectUri);
        string returnUrl = HttpUtility.ParseQueryString(uri.Query)[context.Options.ReturnUrlParameter];
        routeValues.Add(context.Options.ReturnUrlParameter, returnUrl);

        //Overwrite the redirection uri
        context.RedirectUri = url.Action("login", "account", routeValues);
        originalHandler.Invoke(context);
    };

    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        LoginPath = new PathString(url.Action("login", "account")),
        //Set the Provider
        Provider = provider
    });
}

另见此代码:

希望它符合您的需求。

<强>更新 为了减少混淆,我更新了我的答案以使用@Lafis增强,而不是使用包装类来应用扩展行为。在upvoting时也请归功于@Lafis。

答案 1 :(得分:31)

要增强@martinoss答案,您可以在不实现包装器的情况下获得相同的结果。只需复制原始处理程序,分配一个实现重定向逻辑的新处理程序以修改context.RedirectionUri,最后调用原始处理程序。

CookieAuthenticationProvider provider = new CookieAuthenticationProvider();

var originalHandler = provider.OnApplyRedirect;
provider.OnApplyRedirect = context =>
{
    //insert your logic here to generate the redirection URI
    string NewURI = "....";
    //Overwrite the redirection uri
    context.RedirectUri = NewURI;
    originalHandler.Invoke(context);
};

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
   AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
   LoginPath = new PathString(url.Action("Login", "Account")),
   Provider = provider
});

答案 2 :(得分:13)

这个怎么样:

var cao = new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login"),
            Provider = new CookieAuthenticationProvider { OnApplyRedirect = ApplyRedirect }
        };
app.UseCookieAuthentication(cao);

  private static void ApplyRedirect(CookieApplyRedirectContext context)
    {

        UrlHelper _url = new UrlHelper(HttpContext.Current.Request.RequestContext);
        String actionUri = _url.Action("Login", "Account", new { });
        context.Response.Redirect(actionUri);
    }

答案 3 :(得分:4)

如果不对网址格式等承担太多责任,您可以执行以下操作

public static void Configuration(IAppBuilder app)
{
    UrlHelper url = new UrlHelper(HttpContext.Current.Request.RequestContext);
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        LoginPath = new PathString(url.Action("LogOn", "Account", new { area = "Account" })),
        Provider = new CookieAuthenticationProvider
        {
            OnApplyRedirect = context => context.Response.Redirect(context.RedirectUri.Replace(CultureHelper.GetDefaultCulture(), Thread.CurrentUiCulture.Name))
        }
    });
}

答案 4 :(得分:2)

我改进了Sentinel的答案,保留了返回网址:

private static void ApplyRedirect(CookieApplyRedirectContext context)
        {
            //use this way to keep return url
            var loginUrl = context.RedirectUri.Insert(
                context.RedirectUri.IndexOf("/Account/Login"),
                "/" + CultureHelper.GetCurrentCulture());

            context.Response.Redirect(loginUrl);
        }

答案 5 :(得分:1)

我认为我在这个答案中迟了一年,但这里的主要目标是分享知识......:)

我刚刚在我正在开发的应用程序中发现了同样的问题。我查看了我们需要的代码量(在之前的帖子中)以解决这个问题,我很担心(大多数代码都很复杂并触及了野兽的内部)。所以我试图找到一个更简单的解决方案,我所做的是将以下路线添加到我的路线集合中:

routes.MapRoute(
            name: "loginRoute",
            url: "account/login",
            defaults:new { culture = "", controller = "account", action = "login", id = UrlParameter.Optional });

这将允许我在帐户控制器中执行我的登录操作,并且我的标准机制(从Controller的方法BeginExecuteCore覆盖)能够将当前UI文化附加到URL。

我希望它有所帮助。

补充:我的标准机制:

protected override IAsyncResult BeginExecuteCore(AsyncCallback callback, object state)
    {
        var cultureName = RouteData.Values["culture"] as string;

        var cultureCookie = Request.Cookies["_culture"];
        if (cultureCookie != null && string.IsNullOrEmpty(cultureName))
        {
            cultureName = cultureCookie.Value;
        }

        if (cultureName == null)
            cultureName = Request.UserLanguages != null && Request.UserLanguages.Length > 0 ? Request.UserLanguages[0] : null; 

        cultureName = CultureHelper.GetImplementedCulture(cultureName); 

        if (RouteData.Values["culture"] as string != cultureName)
        {
            RouteData.Values["culture"] = cultureName.ToLowerInvariant(); // lower case too

            var cookie = Request.Cookies["_culture"];
            if (cookie != null)
                cookie.Value = cultureName;   // update cookie value
            else
            {
                cookie = new HttpCookie("_culture") { Value = cultureName, Expires = DateTime.Now.AddYears(1) };
            }
            Response.Cookies.Add(cookie);

            // Redirect user
            Response.RedirectToRoute(RouteData.Values);
        }

        Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureName);
        Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;

        return base.BeginExecuteCore(callback, state);
    }

答案 6 :(得分:0)

如果将culture放在loginpath上,则解决方案甚至更简单:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    //Your other properties here
    LoginPath = new PathString("/{culture}/User/Login")
});

答案 7 :(得分:-3)

我发现了更简单的方法:

UrlHelper _url = new UrlHelper(HttpContext.Current.Request.RequestContext);

public void ConfigureAuth(IAppBuilder app)
{
   String actionUri = _url.Action("Login", "Account", new { });
   String unescapeActionUri = System.Uri.UnescapeDataString(actionUri);

   app.UseCookieAuthentication(new CookieAuthenticationOptions
   {
      AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
      LoginPath = new PathString(unescapeActionUri)
   });

[...]