MVC 5 How to define Owin LoginPath with localized routes

前端 未结 8 659
有刺的猬
有刺的猬 2020-12-12 19:18

I have a MVC 5 website with localized routes defined as

routes.MapRoute(
            name: \"Default\",
            url: \"{culture}/{controller}/{action}/{         


        
相关标签:
8条回答
  • 2020-12-12 19:36

    I found a lot more easy way:

    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)
       });
    
    [...]
    
    0 讨论(0)
  • 2020-12-12 19:39

    How about this:

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

    and

      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);
        }
    
    0 讨论(0)
  • 2020-12-12 19:43

    I exactly had the same problem and figured out a way to overcome this limitation.

    In the CookieAuthenticationOptions options, there is a "Provider" property which is initialized with the CookieAuthenticationProvider. This implements a method called ApplyRedirect and a delegate OnApplyRedirect. My first idea was to overwrite this ApplyRedirect and implement the required logic to handle localized routes. But unfortunately it can't be overriden. Passing my logic to OnApplyRedirect causes to overwrite the default behavior. You theoretically can grab the source of this behavior, copy it into your project and modify it to your needs, but this is obviously not a good practice. First, I decided to make a wrapper around the CookieAuthenticationProvider with two extension points using delegates and preserving the default behavior except of the url that is used - or easier, work around the wrapper (thx to lafi).

    Then in the auth configuration I added my custom logic to the provider:

    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
        });
    }
    

    See also this code:

    • CookieAuthenticationHandler
    • CookieAuthenticationProvider

    Hope it fits your needs.

    UPDATE: For less confusion, I updated my answer to use @Lafis enhancement, not using a wrapper class to apply the extended behavior. Please also give credit to @Lafis when upvoting.

    0 讨论(0)
  • 2020-12-12 19:43

    I think I am a year late in this answer but the main objective here is to share knowledge...:)

    I just found the same problem in an application that I am developing now. I looked into the amount of code we need (in the previous posts) to fix this and I get worried (most of the code were complex and touch the insides of the beast). So I tried to find an easier solution and what I did was add the following route to my routes collection:

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

    This will allow my login action in the account controller to be invoked and my standard mechanism (an override to the method BeginExecuteCore from Controller) is able to attach the current UI culture to the URL.

    I hope it helps someone.

    Added: My standard mechanism:

    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);
        }
    
    0 讨论(0)
  • 2020-12-12 19:47

    To enhance @martinoss answer, you may reach the same result without implementing a wrapper. Just copy the original handler, assign a new one that implements your redirection logic to modify context.RedirectionUri, and at the end call the original handler.

    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
    });
    
    0 讨论(0)
  • 2020-12-12 19:47

    I improved Sentinel answer, to keep return url:

    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);
            }
    
    0 讨论(0)
提交回复
热议问题