2011-02-02 129 views
3

我正在制定一個新項目的概念,我需要支持多語言URL。理想情況下,所有URL都需要使用用戶的本地語言。所以,我們不希望使用domain.com/en/contactdomain.com/es/contact但我們喜歡domain.com/contactdomain.com/contactar(contactar是西班牙語聯繫)。內部兩者應該路由到相同的ContactController類。使用ASP.NET MVC的多語言網址

這可以通過爲每種語言向Global.asax.cs添加多條靜態路由來處理,但我們希望使其變得非常動態,並希望系統的用戶能夠更改URL的翻譯通過內容管理系統。所以我們需要一些從URL到控制器和動作的動態映射。

通過查看MVC3的源代碼我想通了MvcHandlerProcessRequestInit方法是負責確定創建哪個控制器。它僅查找RouteData以獲取控制器的名稱。覆蓋默認MVC路由的一種方法是創建一個簡單的默認路由,使用自定義RouteHandler。 This RouteHandler強制MVC使用我自己的定製子版本MvcHandler,它覆蓋了ProcessRequestInit方法。此重寫方法將我自己的動態找到的控制器和操作插入RouteData,然後再調用回原始ProcessRequestInit

我已經試過這樣:

的Global.asax.cs

routes.Add(
    new Route("{*url}", new MultilingualRouteHandler()) 
    { 
     Defaults = new RouteValueDictionary(new { controller = "Default", action = "Default" }) 
    } 
); 

MultilingualRouteHandler.cs

public class MultilingualRouteHandler : IRouteHandler 
{ 

    public IHttpHandler GetHttpHandler(RequestContext requestContext) 
    { 
     return new MultilingualMVCHandler(requestContext); 
    } 

} 

MultilingualMvcHandler.cs

public class MultilingualMVCHandler : MvcHandler 
{ 

    public MultilingualMVCHandler(RequestContext context) : base(context) 
    { 
    } 

    protected override void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) 
    { 

     if (RequestContext.RouteData.Values.ContainsKey("controller")) 
     { 
      RequestContext.RouteData.Values.Remove("controller"); 
     } 

     if (RequestContext.RouteData.Values.ContainsKey("action")) 
     { 
      RequestContext.RouteData.Values.Remove("action"); 
     } 

     RequestContext.RouteData.Values.Add("controller", "Product"); 
     RequestContext.RouteData.Values.Add("action", "Index"); 

     base.ProcessRequestInit(httpContext, out controller, out factory); 

    } 

} 

在這個處理程序中,我將控制器和操作硬編碼爲測試目的的固定值,但要實現這種動態並不困難。它的工作原理,但唯一的問題是,我不得不修改ASP.NET MVC3的源代碼以使其工作。問題是ProcessRequestInit方法MvcHandler是私有的,因此不能被覆蓋。我修改了源代碼並將其更改爲受保護的虛擬,這使我可以覆蓋它。

這是偉大的,但可能不是最好的解決方案。我總是需要分發我自己版本的System.Web.Mvc.dll,這很麻煩。它會好得多,它會與RTM版本一起工作。

我是否缺少任何其他可能性掛鉤到ASP.NET MVC,這將允許我動態地確定控制器和要啓動的操作,具體取決於URL?我想到的另一種方式是在* Application_Start *上動態創建RouteCollection,但我認爲這會讓它在運行中更改變得更加困難。

我會很感激我還沒有找到的鉤子的任何提示。

回答

4

現在這是相當老,螺母以防萬一別人正在尋找類似的東西...

除非我完全誤解你想要做什麼,這是很簡單實在。

第1步:添加一個新的路線global.ascx.cs包含您的個人路由引擎參考

routes.Add(new MyProject.Routing.ContentRoutingEngine()); 

確保它在正確的地方的路線的列表,以便其他路由如果需要,引擎可以在它之前捕獲東西,或者如果引擎不處理特定路由,則繼續路徑搜索。我把它放在忽略之後,但在MVC默認路由之前。

第2步:創建內容路由引擎,確保它從System.Web.Routing.RouteBase抽象類繼承,並根據需要覆蓋GetRouteData和GetVirtualPath方法,例如,

public class ContentRoutingEngine : RouteBase 
{ 
    public override RouteData GetRouteData(HttpContextBase httpContext) 
    { 
     var routeHandler = new MvcRouteHandler(); 
     var currentRoute = new Route("{controller}/{action}", routeHandler); 
     var routeData = new RouteData(currentRoute, routeHandler); 

     // set your values dynamically here 
     routeData.Values["controller"] = "Home" ; 
     // or 
     routeData.Values.Add("action", "Index"); 

     // return the route, or null to have it passed to the next routing engine in the list 
     return routeData; 
    } 

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) 
    { 
     //implement this to return url's for routes, or null to just pass it on 
     return null; 
    } 
} 

而且應該這樣做。您可以根據需要在引擎中動態更改路由,並且不需要更改MVC源。讓標準的MVC RouteHandler實際調用控制器。

後記:很明顯,上面的代碼不是生產標準 - 它的寫法是儘可能明顯地發生了什麼。

+0

看起來像一個很好的解決方案,可能工作一樣好或比實際的IRouteHandler或Conroller實現更好......我現在要玩它:) – bbqchickenrobot 2011-06-24 21:55:16

0

如果您允許通過您的CMS修改網址,那麼您將不得不保留所有舊版本的網址,以便您可以301重定向到新的網址。

最好的選擇是將url標記(例如「contactar」)與相應的控制器一起放入db中。

查詢,並創建你的路線。

創建將處理301重定向

0

我認爲最完美的解決方案將使用一些行爲過濾器定製ActionInvoker相結合的路線。這樣,您可以調用應用了特定過濾器的操作。像ActionName屬性一樣,只能接受多個值(名稱)。

編輯:看看ActionMethodSelectorAttribute,meybe畢竟你不需要自定義的ActionInvoker。