【问题标题】:Multilingual URLs with ASP.NET MVC使用 ASP.NET MVC 的多语言 URL
【发布时间】:2011-06-19 22:21:59
【问题描述】:

我正在为一个需要支持多语言 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 方法。这个被覆盖的方法将我自己动态找到的控制器和操作插入到 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 的源代码才能使其正常工作。问题是 MvcHandlerProcessRequestInit 方法是私有的,因此不能被覆盖。我已经修改了源代码并将其更改为受保护的虚拟,这允许我覆盖它。

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

我是否错过了任何其他挂钩到 ASP.NET MVC 的可能性,它允许我根据 URL 动态确定要启动的控制器和操作?我想到的另一种方法是在 *Application_Start* 上动态构建 RouteCollection,但我认为这会使动态更改它变得更加困难。

如果我还没有找到任何关于钩子的提示,我将不胜感激。

【问题讨论】:

    标签: asp.net-mvc asp.net-mvc-3 asp.net-mvc-routing


    【解决方案1】:

    这已经相当老了,只是以防万一其他人正在寻找类似的东西......

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

    第 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 实际调用控制器。

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

    【讨论】:

    • 看起来是一个很好的解决方案,并且可能与实际的 IRouteHandler 或 Conroller 实现一样好或更好......我现在将使用它:)
    • 它在我正在做的一些事情上运行良好,希望对我有所帮助。
    • 老问题,但我从来没有接受过答案。这正是我最终使用的解决方案。
    【解决方案2】:

    我认为最优雅的解决方案是使用一些动作过滤器与自定义 ActionInvoker 相结合。这样,您可以调用应用了特定过滤器的操作。类似于 ActionName 属性,只能接受多个值(名称)。

    编辑:看看ActionMethodSelectorAttribute,也许你根本不需要自定义 ActionInvoker。

    【讨论】:

      【解决方案3】:

      如果您允许通过 CMS 修改 url,那么您必须保留所有旧版本的 url,以便您可以 301 重定向到新版本。

      最好的办法是将 url 标记(例如“contactar”)与其相应的控制器一起放入数据库中。

      查询它,并从中创建您的路线。

      创建将处理 301 的路由

      【讨论】:

        猜你喜欢
        • 2012-03-21
        • 2010-09-22
        • 2015-06-20
        • 2012-03-12
        • 1970-01-01
        • 1970-01-01
        • 2011-07-13
        • 2011-01-09
        • 2017-05-23
        相关资源
        最近更新 更多