我有一个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属性定义登录网址,该属性设置一次,并且将始终使用提供的值,例如如果“ / 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值来设置呈现页面之前的当前文化?
谢谢
我确实遇到了同样的问题,并找到了克服此限制的方法。
在CookieAuthenticationOptions
选项中,有一个“ Provider”属性,该属性使用CookieAuthenticationProvider进行了初始化。这实现了称为ApplyRedirect
和委托OnApplyRedirect
的方法。我的第一个想法是重写ApplyRedirect
并实现处理本地化路由所需的逻辑。但不幸的是,它不能被覆盖。将我的逻辑传递给OnApplyRedirect
会导致覆盖默认行为。从理论上讲,您可以抓住source of this behavior,将其复制到项目中并根据需要进行修改,但这显然不是一个好习惯。首先,我决定使用委托使用两个扩展点在CookieAuthenticationProvider
周围进行包装,并保留默认行为(所用的URL除外),或者更容易地解决包装问题(从thf到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
});
}
另请参见此代码:
希望它满足您的需求。
UPDATE:为了减少混乱,我更新了答案以使用@Lafis增强功能,而不是使用包装器类来应用扩展行为。投票时,也请注明@Lafis。
要增强@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
});
怎么样:
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);
}
无需对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))
}
});
}
我改进了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);
}
我想我这个答案迟到了一年,但这里的主要目标是分享知识...:)
我刚刚在正在开发的应用程序中发现了相同的问题。我调查了解决此问题所需的代码量(在之前的文章中),我很担心(大多数代码很复杂,并且触及了野兽的内部)。因此,我试图找到一个更简单的解决方案,然后将以下路由添加到路由集合中:
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);
}
如果将culture
放在loginpath
上,则解决方案甚至更简单:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
//Your other properties here
LoginPath = new PathString("/{culture}/User/Login")
});
我发现了很多简单的方法:
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)
});
[...]