2014-01-16 45 views
41

定義Owin LOGINPATH我有一個MVC 5網站提供本地化航線"en-US"定義爲MVC 5如何與本地化路線

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

其中默認的培養結果。


問題出現在啓動時,我有使用LOGINPATH屬性來定義登錄URL,它被設置一次,它總是會使用提供的值,例如如果「/ zh-cn/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值設置當前文化?

謝謝

回答

37

我完全有同樣的問題,並想出了一種方法來克服這種限制。

CookieAuthenticationOptions選項中,有一個「Provider」屬性,它使用CookieAuthenticationProvider進行初始化。這實現了一種稱爲ApplyRedirect的方法和一個代表OnApplyRedirect。 我的第一個想法是覆蓋此ApplyRedirect並實現處理本地化路由所需的邏輯。但不幸的是它不能被覆蓋。將我的邏輯傳遞給OnApplyRedirect會導致覆蓋默認行爲。理論上你可以抓住source of this behavior,將它複製到你的項目中並根據需要進行修改,但這顯然不是一個好習慣。 首先,我決定圍繞CookieAuthenticationProvider使用委託並使用兩個擴展點創建一個包裝,並保留默認行爲,除了使用的url或更容易的解決方法(thx to lafi)。

然後在身份驗證的配置我將我的自定義邏輯供應商:

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

參見下面的代碼:

希望它適合您的需求。

UPDATE: 對於減少混亂,我更新了我的答案使用@Lafis增強,不使用包裝類應用擴展行爲。 upvoting時也請稱讚@Lafis。

+0

嗨,謝謝你的回答!我會在接下來的日子裏嘗試一下,我會讓你知道! – s0nica

+0

完美。什麼最好的片段見過。爲我節省了很多時間。和... @拉菲的回答是提高你的答案(如他所說):) – abzarak

30

要增強@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 
}); 
+0

我用@martinoss和你的解決方案合併,我得到它的工作,所以我決定接受馬丁諾斯的答覆作爲接受的答案並且贊同你的觀點。感謝你們倆:) – s0nica

+0

你是對的,包裝是沒有必要的。我相應地更新瞭解決方案。 – martinoss

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

[...] 
+2

這不回答這個問題。你正在設定路徑,就是這樣。沒有辦法根據每個請求動態地確定問題所要求的路徑。 – Lafi

+0

@Lafi:你的評論是不正確的。這是動態的,並確切地問題是什麼要求......它動態地把自定義路由路徑到Url(例如/ en-en/Controller/Action) –

+0

@Steffen:這不起作用,unescapeActionUri被設置一次(在應用程序啓動時)。我不明白,這是如何動態的。 – HCL

10

如何:

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

更簡單,更清潔,當然有用。這個答案只是導致了一個解決方案,拯救了我的一天! +1 –

+0

不錯的一個TBA,你的一兩個帖子也保存了我的過去! – Sentinel

+0

我添加了新的{ReturnUrl = context.Request.Uri.PathAndQuery}它也給我返回Url。謝謝 – oneNiceFriend

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

不佔用過多的URL格式等的責任,你可以這樣做以下

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

我提高哨兵回答,保持返回網址:

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

不幸的是不工作,錯誤是:在mscorlib中發生類型'System.ArgumentOutOfRangeException'的異常。dll,但未在用戶代碼中處理 附加信息:指定的參數超出了有效值的範圍。 – oneNiceFriend

+0

調試代碼以檢查它是否中斷。 –