这里要注意几点:
1.routes.IgnoreRoute("{resource}.axd/{*pathInfo}");会在路由表中添加一条IgnoreRouteInternal类型的路由,只不过这条是需要被跳过的而已。三个类的关系是:
RouteBase->Route->IgnoreRouteInternal
而不巧的是IgnoreRouteInternal是个私有类,因此,只能借助反射了。
2.为路由设置Constraints属性时,实际上是为其指定一个IRouteConstraint。MVC内部有一个实现了IRouteConstraint的接受正则表达式的类,我们在MapRoute方法中用一个string初始化Constraints,实际上就是实例化了这个类。而这里我们的需求显然要复杂点:需要判断ci参数是否是支持的,所以也就有了CulturePrefixRule实现IRouteConstraint。
3.带有ci参数的路由更“特殊”,所以最好还是放在路由表前面。原因我就不再累述了。
在Controller的Action执行前跳转
所有的Controller都应该具有一个相同的行为:能够针对没有ci参数的url实施跳转。因此自然想到实现一个基类Controller,这里我命名为BaseController,代码如下:
01 |
public class BaseController : Controller |
03 |
protected string redirectUrl; |
05 |
protected override void Initialize(System.Web.Routing.RequestContext requestContext) |
07 |
base .Initialize(requestContext); |
10 |
if (requestContext.RouteData.Values.TryGetValue( "ci" , out cultureValue)) |
15 |
Thread.CurrentThread.CurrentUICulture = CultureProvider.GetCultureInfo(cultureValue.ToString()); |
16 |
Thread.CurrentThread.CurrentCulture = CultureProvider.GetCultureInfo(cultureValue.ToString()); |
01 |
Response.Cookies.Add( new HttpCookie(CultureProvider.culturecookiekey,cultureValue.ToString())); |
03 |
catch { throw new Exception( "Culture Error!" ); } |
08 |
HttpCookie cLang = requestContext.HttpContext.Request.Cookies[CultureProvider.culturecookiekey]; |
11 |
cultureValue = cLang.Value; |
15 |
string [] langs = requestContext.HttpContext.Request.UserLanguages; |
16 |
if (langs != null && langs.Length > 0) |
18 |
cultureValue = langs[0].Split( ';' ).First(); |
22 |
if (cultureValue == null ) |
24 |
cultureValue = CultureProvider.culturedefault; |
27 |
redirectUrl = string .Format( @"/{0}{1}" , |
28 |
cultureValue.ToString(), |
29 |
requestContext.HttpContext.Request.RawUrl); |
34 |
protected override IActionInvoker CreateActionInvoker() |
36 |
return new CustomControllerActionInvoker(redirectUrl); |
42 |
internal class CustomControllerActionInvoker : ControllerActionInvoker |
45 |
public CustomControllerActionInvoker( string url) |
50 |
protected override ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary< string , object > parameters) |
54 |
if (! string .IsNullOrEmpty(redirectUrl) && !controllerContext.IsChildAction) |
55 |
returnValue = new RedirectResult(redirectUrl); |
57 |
returnValue = actionDescriptor.Execute(controllerContext, parameters); |
58 |
ActionResult result = CreateActionResult(controllerContext, actionDescriptor, returnValue); |
63 |
public static class CultureProvider |
65 |
public const string culturecookiekey = "Lang" ; |
66 |
public const string culturedefault = "en-US" ; |
68 |
public static CultureInfo GetCultureInfo( string ci) |
72 |
return new CultureInfo(ci); |
只要所有的Controller继承这个BaseController即可。
这里需要重点指出的是CustomControllerActionInvoker类,事实上发现从这个类入手解决重定向问题花了我不少时间,为此我不得不调试MVC的源码。当然最初的想法是在每个action执行时手动判断redirectUrl,从而指导重定向,但显然,没人愿意将自己已经写好的action都拿出来一个个改,所以也就有了这个小小的探索。
页面中的链接、跳转
最后令我感到即高兴又担心的问题是:当我使用这个框架后,页面中的所有链接和跳转因素几乎都能自动在url前面加上ci参数!虽然我知道类似Html.ActionLink之类的helper有从路由表中产生url的能力,但是能够自动添加上ci,还是让我感到有点始料未及。不过,链接的url是否正确,还是要注意,有一些特殊情况。
页面中使用资源
在页面中引用资源可以直接在C#脚本中引用Resource类。这里提供一个helper。这个Html的扩展方法。
01 |
public static class ResourceExtensions |
03 |
public static string Resource( this Controller controller, string expression, params object [] args) |
05 |
ResourceExpressionFields fields = GetResourceFields(expression, "~/" ); |
06 |
return GetGlobalResource(fields, args); |
09 |
public static string Resource( this HtmlHelper htmlHelper, string expression, params object [] args) |
12 |
ResourceExpressionFields fields = GetResourceFields( string .Format( "Resource,{0}" , expression), path); |
13 |
return GetGlobalResource(fields, args); |
16 |
static string GetGlobalResource(ResourceExpressionFields fields, object [] args) |
18 |
return string .Format(( string )HttpContext.GetGlobalResourceObject(fields.ClassKey, fields.ResourceKey, CultureInfo.CurrentUICulture), args); |
21 |
static ResourceExpressionFields GetResourceFields( string expression, string virtualPath) |
23 |
var context = new ExpressionBuilderContext(virtualPath); |
24 |
var builder = new ResourceExpressionBuilder(); |
25 |
return (ResourceExpressionFields)builder.ParseExpression(expression, typeof ( string ), context); |
需要注意的是这个方法默认认为Resource是资源的类名,所以必要的话需要修改
ResourceExpressionFields fields = GetResourceFields(string.Format("Resource,{0}", expression), path); 中的"Resource,{0}"
结语
初学MVC,甚至可以说是初学web开发。以上是我个人提出的一种方案,不知道有没有什么不足之处,还请各位看官提出见解,探讨一下。
点击下载例子
其他相关资源:
http://blog.miniasp.com/post/2010/01/ASPNET-MVC-Developer-Note-Part-15-Globalization-and-Localization.aspx
(责任编辑:admin) |