使用ASP.NET MVC的多语言URL

Mar*_*urg 4 asp.net-mvc asp.net-mvc-routing asp.net-mvc-3

我正在研究一个新项目的概念,我需要支持多语言URL.理想情况下,所有URL都需要使用用户的本地语言.因此,我们不想使用domain.com/en/contactdomain.com/es/contact,但我们喜欢domain.com/contactdomain.com/contactar(contactar是西班牙语以便联系).在内部,两者都应该路由到同一个ContactController类.

这可以通过为每种语言添加到Global.asax.cs的多个静态路由来处理,但我们希望使其非常动态,并希望系统用户能够通过内容管理更改URL的转换系统.所以我们需要从URL到控制器和动作的某种动态映射.

通过查看MVC3的源代码,我发现MvcHandlerProcessRequestInit方法负责确定要创建哪个控制器.它只是在RouteData中查找控制器的名称.覆盖默认MVC路由的一种方法是创建使用自定义RouteHandler的简单默认路由.此RouteHandler强制MVC使用我自己的自定义子类MvcHandler,它会覆盖ProcessRequestInit方法.在重新调用原始ProcessRequestInit之前,此重写方法将我自己动态找到的控制器和操作插入到RouteData中.

我试过这个:

的Global.asax.cs

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

MultilingualRouteHandler.cs

public class MultilingualRouteHandler : IRouteHandler
{

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

}
Run Code Online (Sandbox Code Playgroud)

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

    }

}
Run Code Online (Sandbox Code Playgroud)

在这个处理程序中,我将控制器和操作硬编码为某些固定值,但是要使这种动态变化并不困难.它的工作原理但唯一的问题是我必须修改ASP.NET MVC3的源代码才能使它工作.问题是MvcHandlerProcessRequestInit方法是私有的,因此无法覆盖.我修改了源代码并将其更改为protected virtual,这允许我覆盖它.

这一切都很棒但可能不是最好的解决方案.我总是需要分发我自己的System.Web.Mvc.dll版本,这很麻烦.它可以与RTM版本一起使用会好得多.

我是否遗漏了任何其他挂钩ASP.NET MVC的可能性,这将允许我动态确定控制器和要启动的操作,具体取决于URL?我想到的另一种方法是在*Application_Start*上动态构建RouteCollection,但我认为这将使得在运行中更改它变得更加困难.

我会很感激我还没有找到任何挂钩的提示.

Nic*_*end 5

现在这已经很老了,以防万一其他人正在寻找类似的东西......

除非我完全误解你想做什么,否则它真的很简单.

步骤1:向global.ascx.cs添加一个新路由,其中​​包含对您的个人路由引擎的引用

routes.Add(new MyProject.Routing.ContentRoutingEngine());
Run Code Online (Sandbox Code Playgroud)

确保它位于路由列表中的正确位置,以便其他路由引擎可以在需要时捕获其中的内容,或者如果引擎未处理特定路由则继续路径搜索.我把它放在忽略之后,但在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;
    }
}
Run Code Online (Sandbox Code Playgroud)

那应该这样做.您可以根据需要在引擎中动态更改路径,并且不需要更改MVC源.让标准MVC RouteHandler实际调用控制器.

后记:显然上面的代码不是生产标准 - 它的编写是为了尽可能明显地发生了什么.