在不使用 MVC 更改 URL 的情况下为特定 URL 创建路由

Posted

技术标签:

【中文标题】在不使用 MVC 更改 URL 的情况下为特定 URL 创建路由【英文标题】:Create Route for a specific URL without changing the URL with MVC 【发布时间】:2018-07-10 14:21:19 【问题描述】:

我有一个在 www.domain.com 上运行的 MVC Web 应用程序,我需要为同一个 Web 应用程序的另一个域 www.domain2.com 配置不同的 URL 绑定。

新域www.domain2.com 必须返回一个特定的控制器操作视图,例如/Category/Cars

routes.MapRoute(
    name: "www.domain2.com",
    url: "www.domain2.com",
    defaults: new  controller = "Category", action = "Cars", id = UrlParameter.Optional 
);

如何在不更改 URL 的情况下实现这一点,因此访问者插入 url www.domain2.com 并接收视图 www.domain.com/category/cars 但 url 仍然是 www.domain2.com

编辑:

我已经尝试过这种方法,但它不起作用:

routes.MapRoute(
    "Catchdomain2",
    "www.domain2.com",
    new  controller = "Category", action = "Cars" 
);

【问题讨论】:

见MVC Custom Routing Subdomain,类似。 嗨!谢谢!这并不容易理解。我总是有一个从 www.domain2.com 到 www.domain.com/Category/Cars 的案例。有没有更简单的方法来做到这一点? @NightOwl888 我尝试了一种基于***.com/questions/1618896/… 的不同方法,但它不起作用:( 【参考方案1】:

域通常不是路由的一部分,这就是您的示例不起作用的原因。要使路由仅适用于特定域,您必须自定义路由。

默认情况下,您的路由配置中的所有路由都将在可以访问该网站的所有域上可用。

对此最简单的解决方案是创建一个custom route constraint 并使用它来控制特定 URL 将匹配的域。

域约束

    public class DomainConstraint : IRouteConstraint
    
        private readonly string[] domains;

        public DomainConstraint(params string[] domains)
        
            this.domains = domains ?? throw new ArgumentNullException(nameof(domains));
        

        public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
        
            string domain =
#if DEBUG
                // A domain specified as a query parameter takes precedence 
                // over the hostname (in debug compile only).
                // This allows for testing without configuring IIS with a 
                // static IP or editing the local hosts file.
                httpContext.Request.QueryString["domain"]; 
#else
                null;
#endif
            if (string.IsNullOrEmpty(domain))
                domain = httpContext.Request.Headers["HOST"];

            return domains.Contains(domain);
        
    

用法

public class RouteConfig

    public static void RegisterRoutes(RouteCollection routes)
    
        routes.IgnoreRoute("resource.axd/*pathInfo");

        // This ignores Category/Cars for www.domain.com and www.foo.com
        routes.IgnoreRoute("Category/Cars", new  _ = new DomainConstraint("www.domain.com", "www.foo.com") );

        // Matches www.domain2.com/ and sends it to CategoryController.Cars
        routes.MapRoute(
            name: "HomePageDomain2",
            url: "",
            defaults: new  controller = "Category", action = "Cars" ,
            constraints: new  _ = new DomainConstraint("www.domain2.com") 
        );

        routes.MapRoute(
            name: "Default",
            url: "controller/action/id",
            defaults: new  controller = "Home", action = "Index", id = UrlParameter.Optional ,

            // This constraint allows the route to work either 
            // on "www.domain.com" or "www.domain2.com" (excluding any other domain)
            constraints: new  _ = new DomainConstraint("www.domain.com", "www.domain2.com") 
        );
    

如果您在 Visual Studio 的新项目中启动它,您会注意到它显示错误。这是因为localhost:<port> 不是已配置的域。但是,如果您导航到:

/?domain=www.domain.com

您将看到主页。

这是因为仅对于调试版本,它允许您覆盖“本地”域名以进行测试。您可以configure your local IIS server 使用本地静态IP 地址(添加到您的网卡)并添加本地主机文件条目以在本地测试它没有查询字符串参数

请注意,在进行“发布”构建时,无法使用查询字符串参数进行测试,因为这会带来潜在的安全漏洞。

如果您使用网址:

/?domain=www.domain2.com

它将运行CategoryController.Cars 操作方法(如果存在)。

请注意,由于Default 路由涵盖了范围广泛的 URL,因此大部分站点将可供www.domain.comwww.domain2.com 使用。例如,您可以在以下位置访问“关于”页面:

/Home/About?domain=www.domain.com
/Home/About?domain=www.domain2.com

您可以使用IgnoreRoute extension method 来阻止您不想要的 URL(它接受路由约束,因此该解决方案也可以在那里工作)。

如果您主要希望在域之间共享功能,此解决方案将起作用。如果您希望在一个网站中有 2 个域,但让它们像单独的网站一样,则如果您使用上述路由约束为项目中的每个“网站”使用 Area 会更容易管理区域路线。

public class Domain2AreaRegistration : AreaRegistration

    public override string AreaName
    
        get
        
            return "Domain2";
        
    

    public override void RegisterArea(AreaRegistrationContext context)
    
        context.MapRoute(
            name: "Domain2_default",
            url: "controller/action/id",
            defaults: new  action = "Index", id = UrlParameter.Optional , 
            constraints: new  _ = DomainConstraint("www.domain2.com") 
        );
    

上述配置将使www.domain2.com每个 URL(即 0、1、2 或 3 段长)路由到 Domain2 区域中的控制器。

【讨论】:

您好,谢谢!从性能的角度来看(我已经能够为我的案例运行 @ArthNRick 解决方案),哪种解决方案效果最好? (你的或 ArthNRick 的) 从性能的角度来看,它们大致相同,因为 ArthNRick 的解决方案将调用级联到另一个操作方法而不是执行重定向(这会向浏览器发出 302 响应并告诉它查找服务器上的新位置 - 额外的网络往返)。但是,如果您最终在www.domain2.com 上拥有多个视图,那么该解决方案将无法很好地扩展,让我们面对现实吧——这可能会发生。路由是一个独立的关注点,而不是动作方法中发生的事情,域名显然属于路由的范围。 另外请记住,来自www.domain.com所有 URL 也将在www.domain2.com 上可用(这是默认行为)。如果这是您的意图,您应该使用Canonical Tag 告诉搜索引擎您打算将相同的内容放在 2 个不同的 URL 上。如果这不是您想要的,请使用DomainConstraint 阻止来自您不想看到它们的域的路由。【参考方案2】:

在应用程序的默认操作中确保 url 是第二个域之一,然后返回所需的方法。类似:

      public ActionResult Index()
      

        if (Request.Url.Host.Equals("domain2"))
            return AnotherAction();

      

【讨论】:

您好,谢谢!使用简单的路线不是更好吗?关于分析的观点。 我相信这是不可能的,你需要的是一个动作重定向,因为“domain.com”假定了默认路由(通常是“controller/action/id”),依此类推默认路线,您应该更正位置【参考方案3】:

同意楼上的回答。

如果您想要更漂亮的实现,请尝试操作过滤器。 来自there 的操作过滤器使用示例。

public override void OnActionExecuting(ActionExecutingContext filterContext)

    var controller = (SomeControllerBase) filterContext.Controller;
    filterContext.Result = controller.RedirectToAction("index", "home");

从there 获取操作过滤器内的 URL 示例。

var url = filterContext.HttpContext.Request.Url;

把东西放在一起,玩得开心:)

【讨论】:

更漂亮的实现?这甚至没有提供如何将单个操作路由到两个单独的路由的示例,并且根本没有谈论重定向等问题。这应该是一个评论。 @d_petrishin 请不要忽视 *** 的 be nice 政策。 @GrumpyCrouton dick 是誓言的同义词,你是什么意思? @d_petrishin 作为同义词不会使一个词变得更合适或更不合适。你不应该称呼别人的名字。 @GrumpyCrouton 但你也这样做,为什么我不应该这样做?

以上是关于在不使用 MVC 更改 URL 的情况下为特定 URL 创建路由的主要内容,如果未能解决你的问题,请参考以下文章

如何在不更改 pvalue 的情况下为 kruskal wallis 测试执行循环比较多列上的 3 个组?

如何在不影响 SYSTEM/IE 代理的情况下为 Webbrowser Control 设置代理

如何在不调用该 Activity 的 API 调用的情况下为横向和纵向模式使用不同的设计?

在不更改 url 的情况下访问脚本

如何在不更改页面的情况下更改内容(非散列 URL)?

如何在不更改浏览器历史记录的情况下更改 url