天天躁日日躁狠狠躁AV麻豆-天天躁人人躁人人躁狂躁-天天澡夜夜澡人人澡-天天影视香色欲综合网-国产成人女人在线视频观看-国产成人女人视频在线观看

ASP.NET MVC的全球化方案

  由于項目需要最近在學(xué)習(xí)ASP.NET MVC。在實踐中,網(wǎng)站要支持多語言,需要全球化。在MVC下我實現(xiàn)了一個全球化框架,在這里與各位分享一下,不足之處也請各位看官指教。

  讓URL支持全球化

  經(jīng)常上微軟網(wǎng)站的朋友可能很熟悉類似包含../zh-cn/..、../en-us/..的url形式,這就是本文要使用的全球化方案。當(dāng)然還有使用QueryString傳遞參數(shù)的方案,基本思路我想是類似的。

  由于MVC天生的URL路由原理,使得這個方案很容易被接受。

  基本思路

  這個方案的基本思路是:

  1.當(dāng)用戶訪問的url含有合法的culture參數(shù)時,能夠直接路由到對應(yīng)的controller,在controller初始化時設(shè)置線程的Culture;

  2.當(dāng)用戶訪問的url不包含culture參數(shù)時,同樣被路由到對應(yīng)的controller,但controller在執(zhí)行action前,重定向到包含Culture的url。這里的Culture按照先檢測cookie,再檢測語言瀏覽器設(shè)置,最后使用默認(rèn)值的優(yōu)先級順序實施。

    先看下效果演示,注意url,點擊下載例子

image

image

  Resource.resx

  在接下去之前先回顧一下資源文件。在ASP.NET web應(yīng)用程序(winform同樣)中定義的資源文件.resx實際上是一個xml配置文件,通常我們只關(guān)心其中的key/value配置;我們可以建立一個或多個.resx,這些.resx會對應(yīng)生成一個cs文件,這個cs文件會定義一個類(可能是Resource類,取決于你的資源文件的命名),通過訪問這個類的靜態(tài)屬性即可訪問這些key,而選擇哪個.resx讀取的關(guān)鍵就是CultureInfo,只要我們設(shè)置當(dāng)前線程的CultureInfo,Resource便會自動識別對應(yīng)的.resx配置文件。而在.resx的命名上,需要按照這樣的規(guī)則:

  Resource.zh-cn.resx(對應(yīng)簡體中文資源文件)

  Resource.en-us.resx(對應(yīng)美國英語資源文件)

  中間的Culture名字很重要。

  通常在開發(fā)時,只要一個默認(rèn)的Resource.resx,當(dāng)開發(fā)完成之后,拷貝一個相同的Resource.resx,并改名字成上面的樣子,然后手動或自動將其中的所有value都翻譯成對應(yīng)的語言。

  解決路由問題

  在這個方案中,首先要考慮的是url路由配置。首先,理想情況下,我們所有的url都是domain/culture/controller/action/param1/..這種形式,那么只要一份以culture開頭的路由就可以了。但是事實上并非這么簡單,如果用戶不知道這個規(guī)則,他手動輸入了domain/controller/action/param1..那么這種url將不能被正確路由。這種情況在初次訪問網(wǎng)站的時候最為常見(通常我們都會鍵入www.microsoft.com而不會在后面加上任何的culture參數(shù))。那么難道我們要為了這種場景寫兩份路由嗎?顯然不是,或者說不用手動做這件事。這里要解決的第一個問題出現(xiàn)了。我的方案是:只為domain/controller/action/param1..這種路由手動寫代碼配置,這也比較符合習(xí)慣;然后通過一個方法,遍歷route表中的所有路由,并在每個url規(guī)則前面加上一個參數(shù)ci表示culture,生成一份新的路由加到路由表中即可。這樣做盡管沒有減少路由規(guī)則,但是至少不用手動一個個寫了,要不然沒人會同意這個方案的。下面是代碼和解釋:

protected void Application_Start(){    AreaRegistration.RegisterAllAreas();    RegisterRoutes(RouteTable.Routes);    RegisterGlobalizationRoutes();    ...}
private void RegisterGlobalizationRoutes(){    //RouteTable.Routes即路由表    if (RouteTable.Routes == null)        return;    //創(chuàng)建一個新的路由集合,存放將要添加到路由    RouteCollection rc = new RouteCollection();    //這里需要跳過routes.IgnoreRoute("{resource}.axd/{*pathInfo}");    //由于IgnoreRouteInternal是個私有類,所以這里只能反射    //skip IgnoreRouteInternal    var routes = RouteTable.Routes.SkipWhile(p => (p.GetType().Name == "IgnoreRouteInternal"));    int insertpoint = RouteTable.Routes.Count() - routes.Count();    //遍歷所有需要處理的路由    foreach (var r in routes)    {        Route item = (r as Route);        //下面的代碼創(chuàng)建一個新的路由對象,在url規(guī)則前面加上ci參數(shù),并拷貝其他設(shè)置        Route newitem = new Route(            //string.Format(@"{ci}/{0}",item.Url),            @"{ci}/" + item.Url,            new MvcRouteHandler());        newitem.Defaults = new RouteValueDictionary(item.Defaults);        newitem.Constraints = new RouteValueDictionary(item.Constraints);        //ci參數(shù)需要驗證,因為只有合法的culture才能被接受        newitem.Constraints.Add("ci", new CulturePrefixRule());        newitem.DataTokens = new RouteValueDictionary();        newitem.DataTokens["Namespaces"] = item.DataTokens["Namespaces"];        rc.Add(newitem);    }    //帶ci參數(shù)的路由應(yīng)當(dāng)靠前放,所以這里插入到前面    foreach (var c in rc)    {        RouteTable.Routes.Insert(insertpoint++, c);    }}
 
//實現(xiàn)IRouteConstraint的一個類private class CulturePrefixRule : IRouteConstraint{    IEnumerable<string> cultureConllection = CultureInfo.GetCultures(CultureTypes.SpecificCultures).Select(p => p.Name.ToLower());    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)    {        if (values[parameterName] != null)            return cultureConllection.Contains(values[parameterName].ToString().ToLower());        else            return false;    }}

  這里要注意幾點:

  1.routes.IgnoreRoute("{resource}.axd/{*pathInfo}");會在路由表中添加一條IgnoreRouteInternal類型的路由,只不過這條是需要被跳過的而已。三個類的關(guān)系是:

  RouteBase->Route->IgnoreRouteInternal

  而不巧的是IgnoreRouteInternal是個私有類,因此,只能借助反射了。

  2.為路由設(shè)置Constraints屬性時,實際上是為其指定一個IRouteConstraint。MVC內(nèi)部有一個實現(xiàn)了IRouteConstraint的接受正則表達(dá)式的類,我們在MapRoute方法中用一個string初始化Constraints,實際上就是實例化了這個類。而這里我們的需求顯然要復(fù)雜點:需要判斷ci參數(shù)是否是支持的,所以也就有了CulturePrefixRule實現(xiàn)IRouteConstraint。

  3.帶有ci參數(shù)的路由更“特殊”,所以最好還是放在路由表前面。原因我就不再累述了。

  在Controller的Action執(zhí)行前跳轉(zhuǎn)

  所有的Controller都應(yīng)該具有一個相同的行為:能夠針對沒有ci參數(shù)的url實施跳轉(zhuǎn)。因此自然想到實現(xiàn)一個基類Controller,這里我命名為BaseController,代碼如下:

public class BaseController : Controller{    protected string redirectUrl;    protected override void Initialize(System.Web.Routing.RequestContext requestContext)    {        base.Initialize(requestContext);        object cultureValue;        //檢測ci參數(shù)        if (requestContext.RouteData.Values.TryGetValue("ci", out cultureValue))        {            //設(shè)置當(dāng)前線程的culture            try            {                Thread.CurrentThread.CurrentUICulture = CultureProvider.GetCultureInfo(cultureValue.ToString());                Thread.CurrentThread.CurrentCulture = CultureProvider.GetCultureInfo(cultureValue.ToString());
                Response.Cookies.Add(new HttpCookie(CultureProvider.culturecookiekey,cultureValue.ToString()));            }            catch { throw new Exception("Culture Error!"); }        }        else //如果沒有ci參數(shù)        {            //check cookie            HttpCookie cLang = requestContext.HttpContext.Request.Cookies[CultureProvider.culturecookiekey];            if (cLang != null)            {                cultureValue = cLang.Value;            }            else //check brower setting            {                string[] langs = requestContext.HttpContext.Request.UserLanguages;                if (langs != null && langs.Length > 0)                {                    cultureValue = langs[0].Split(';').First();                }            }            if (cultureValue == null)            {                cultureValue = CultureProvider.culturedefault;            }            //設(shè)置redirectUrl,如果不需要重定向到化redirectUrl 為null            redirectUrl = string.Format(@"/{0}{1}",                cultureValue.ToString(),                requestContext.HttpContext.Request.RawUrl);        }    }    protected override IActionInvoker CreateActionInvoker()    {        return new CustomControllerActionInvoker(redirectUrl);    }}//一個IActionInvoker 的實現(xiàn),MVC默認(rèn)使用ControllerActionInvoker,因為在//redirectUrl != null 的時候需要在action執(zhí)行之前執(zhí)行重定向internal class CustomControllerActionInvoker : ControllerActionInvoker{    string redirectUrl;    public CustomControllerActionInvoker(string url)        : base()    {        redirectUrl = url;    }    protected override ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters)    {        object returnValue;       //ChildAction內(nèi)部不能重定向        if (!string.IsNullOrEmpty(redirectUrl) && !controllerContext.IsChildAction)            returnValue = new RedirectResult(redirectUrl);        else            returnValue = actionDescriptor.Execute(controllerContext, parameters);        ActionResult result = CreateActionResult(controllerContext, actionDescriptor, returnValue);        return result;    }}public static class CultureProvider{    public const string culturecookiekey = "Lang";    public const string culturedefault = "en-US";    public static CultureInfo GetCultureInfo(string ci)    {        try        {            return new CultureInfo(ci);        }        catch        {            return null;        }    }}

  只要所有的Controller繼承這個BaseController即可。

  這里需要重點指出的是CustomControllerActionInvoker類,事實上發(fā)現(xiàn)從這個類入手解決重定向問題花了我不少時間,為此我不得不調(diào)試MVC的源碼。當(dāng)然最初的想法是在每個action執(zhí)行時手動判斷redirectUrl,從而指導(dǎo)重定向,但顯然,沒人愿意將自己已經(jīng)寫好的action都拿出來一個個改,所以也就有了這個小小的探索。

  頁面中的鏈接、跳轉(zhuǎn)

  最后令我感到即高興又擔(dān)心的問題是:當(dāng)我使用這個框架后,頁面中的所有鏈接和跳轉(zhuǎn)因素幾乎都能自動在url前面加上ci參數(shù)!雖然我知道類似Html.ActionLink之類的helper有從路由表中產(chǎn)生url的能力,但是能夠自動添加上ci,還是讓我感到有點始料未及。不過,鏈接的url是否正確,還是要注意,有一些特殊情況。

頁面中使用資源

  在頁面中引用資源可以直接在C#腳本中引用Resource類。這里提供一個helper。這個Html的擴展方法。

    public static class ResourceExtensions    {        public static string Resource(this Controller controller, string expression, params object[] args)        {            ResourceExpressionFields fields = GetResourceFields(expression, "~/");            return GetGlobalResource(fields, args);        }        public static string Resource(this HtmlHelper htmlHelper, string expression, params object[] args)        {            string path = "~/";            ResourceExpressionFields fields = GetResourceFields(string.Format("Resource,{0}", expression), path);            return GetGlobalResource(fields, args);        }        static string GetGlobalResource(ResourceExpressionFields fields, object[] args)        {            return string.Format((string)HttpContext.GetGlobalResourceObject(fields.ClassKey, fields.ResourceKey, CultureInfo.CurrentUICulture), args);        }        static ResourceExpressionFields GetResourceFields(string expression, string virtualPath)        {            var context = new ExpressionBuilderContext(virtualPath);            var builder = new ResourceExpressionBuilder();            return (ResourceExpressionFields)builder.ParseExpression(expression, typeof(string), context);        }    }

  需要注意的是這個方法默認(rèn)認(rèn)為Resource是資源的類名,所以必要的話需要修改

  ResourceExpressionFields fields = GetResourceFields(string.Format("Resource,{0}", expression), path); 中的"Resource,{0}"

  結(jié)語

  初學(xué)MVC,甚至可以說是初學(xué)web開發(fā)。以上是我個人提出的一種方案,不知道有沒有什么不足之處,還請各位看官提出見解,探討一下。

  點擊下載例子

  其他相關(guān)資源:

  http://blog.miniASP.com/post/2010/01/ASPNET-MVC-Developer-Note-Part-15-Globalization-and-Localization.ASPx

NET技術(shù)ASP.NET MVC的全球化方案,轉(zhuǎn)載需保留來源!

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請第一時間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: 强壮的公次次弄得我高潮韩国电影 | 欧美三级在线完整版免费 | 亚洲 欧美 国产 综合久久 | 亚洲精品第五页中文字幕 | 暖暖日本在线手机免费完整版 | 扒开黑女人p大荫蒂老女人 扒开粉嫩的小缝末成年小美女 | 印度最猛性ⅹxxxxx | 小寡妇好紧进去了好大看视频 | yellow免费观看完整版直播 | 菠萝视频高清版在线观看 | 公粗挺进了我的密道在线播放贝壳 | 天堂so导航 | 97视频在线播放 | 国产偷国产偷亚州清高APP | 亚洲欧美偷拍视频一区 | 任你躁国语自产二区在线播放 | 快播欧美大片 | 国产露脸无码A区久久蘑菇 国产露脸无码A区久久 | 偷尝禁果H1V1幸运的山熊 | 日本理伦片午夜理伦片 | 日韩精品一区二区三区AV在线观看 | 国产欧美一本道无码 | 色噜噜噜亚洲男人的天堂 | 国产在线精品亚洲一品区 | 一个人的免费高清影院 | 精品国产乱码久久久人妻 | 91综合久久久久婷婷 | 337p欧洲亚大胆精品 | 777精品久无码人妻蜜桃 | 日本美女毛茸茸 | 妈妈的职业3完整版在线播放 | 男人吃奶摸下弄进去好爽 | 蜜桃人妻无码AV天堂三区 | 十次啦中文网 | 国产剧果冻传媒星空在线观看 | AV无码国产精品午夜A片麻豆 | 色精品极品国产在线视频 | 最新无码国产在线视频2020 | 福利一区福利二区 | 一本大道熟女人妻中文字幕在线 | 果冻传媒色AV国产播放 |