【问题标题】:Domain-based routing in ASP.NET Core 2.0ASP.NET Core 2.0 中基于域的路由
【发布时间】:2018-08-28 22:17:05
【问题描述】:

我有一个托管在 Azure 应用服务上的 ASP.NET Core 2.0 应用。

此应用程序绑定到domainA.com。我的应用中有一条路线,例如domainA.com/route

现在,我想介绍另一个域,但让它只响应不同的路由——例如,domainB.com

最好的方法是什么?

【问题讨论】:

    标签: c# asp.net-core asp.net-core-2.0 asp.net-core-routing


    【解决方案1】:

    内置方法

    从 ASP.NET Core 3 开始,以及 ASP.NET Core 5 和 6 的持续支持,您可以使用 RequireHost() 扩展方法将单个路由定义限制为特定主机名,如前所述在Allow routing to areas by hostname。 (与问题标题相反,这并不特定于区域。)

    示例

    因此,为了适应@nightowl888's example in the accepted answer,您现在可以实现相同的结果,而无需定义自定义IRouteConstraint

    app.UseMvc(routes =>
    {
    
      routes.MapRoute(
        name: "DomainA",
        template: "route",
        defaults: new { controller = "DomainA", action = "Route" }
      ).RequireHost("domaina.com");
    
      routes.MapRoute(
        name: "DomainB",
        template: "route",
        defaults: new { controller = "DomainB", action = "Route" }
      ).RequireHost("domainb.com");
    
      routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}"
      );
    
    });
    

    属性路由

    或者,如果您更喜欢属性路由as used in @yanga's approach,您现在可以使用新的(但记录不充分)HostAttribute (source code):

    [Host("domainb.com")]
    public DomainController: Controller
    {
      … 
    }
    

    显然,这并没有解决最初的问题,即针对 ASP.NET Core 2 的问题。但是,由于这是一个未(der)记录的功能,我想将其留在这里,以供尝试解决此问题的人们使用ASP.NET Core 3+。

    【讨论】:

      【解决方案2】:

      对于 .net Core MVC,你可以创建一个新的 IRouteConstraint 和一个 RouteAttribute

      => IRouteConstraint.cs

      using System;
      using Microsoft.AspNetCore.Http;
      using Microsoft.AspNetCore.Mvc;
      
      namespace Microsoft.AspNetCore.Routing
      {
          public class ConstraintHost : IRouteConstraint
          {
      
              public string _value { get; private set; }
              public ConstraintHost(string value)
              {
                  _value = value;
              }
      
              public bool Match(HttpContext httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
              {
                  string hostURL = httpContext.Request.Host.ToString();
                  if (hostURL == _value)
                  {
                      return true;
                  }
                  //}
                  return false;
                  //return hostURL.IndexOf(_value, StringComparison.OrdinalIgnoreCase) >= 0;
              }
      
              public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
              {
                  throw new NotImplementedException();
              }
          }
          public class ConstraintHostRouteAttribute : RouteAttribute
          {
              public ConstraintHostRouteAttribute(string template, string sitePermitted)
                  : base(template)
              {
                  SitePermitted = sitePermitted;
              }
      
              public RouteValueDictionary Constraints
              {
                  get
                  {
                      var constraints = new RouteValueDictionary();
                      constraints.Add("host", new ConstraintHost(SitePermitted));
                      return constraints;
                  }
              }
      
              public string SitePermitted
              {
                  get;
                  private set;
              }
          }
      } 
      

      并且在你的 Controller 中可以这样使用它:

          [ConstraintHostRoute("myroute1/xxx", "domaina.com")]
          [ConstraintHostRoute("myroute2/yyy", "domainb.com")]
          public async Task<ActionResult> MyController()
          {
            return View();
          }
      

      【讨论】:

      • 我更喜欢这种方法,因为我们在整个应用程序中都使用属性路由。我试过了,即使域不匹配,它仍然提供路由。例如,127.0.0.1 与 local.customdomain.com 一样提供页面。知道为什么吗?
      【解决方案3】:

      实现此目的的一种方法是创建一个custom route constraint 来指定每个域名的路由功能。

      域约束

          public class DomainConstraint : IRouteConstraint
          {
              private readonly string[] domains;
      
              public DomainConstraint(params string[] domains)
              {
                  this.domains = domains ?? throw new ArgumentNullException(nameof(domains));
              }
      
              public bool Match(HttpContext httpContext, IRouter route, string routeKey, 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.Query["domain"];
      #else
                      null;
      #endif
                  if (string.IsNullOrEmpty(domain))
                      domain = httpContext.Request.Host.Host;
      
                  return domains.Contains(domain);
              }
          }
      

      用法

      app.UseMvc(routes =>
      {
          routes.MapRoute(
              name: "DomainA",
              template: "route",
              defaults: new { controller = "DomainA", action = "Route" },
              constraints: new { _ = new DomainConstraint("domaina.com") });
      
          routes.MapRoute(
              name: "DomainB",
              template: "route",
              defaults: new { controller = "DomainB", action = "Route" },
              constraints: new { _ = new DomainConstraint("domainb.com") });
      
          routes.MapRoute(
              name: "default",
              template: "{controller=Home}/{action=Index}/{id?}");
      });
      

      请注意,如果您在 Visual Studio 中启动它,它将无法使用标准配置。为了在不更改配置的情况下轻松调试,您可以将带有域的 URL 指定为查询字符串参数:

      /route?domain=domaina.com
      

      这只是为了让您不必重新配置 IIS 和您的本地主机文件来进行调试(尽管如果您愿意,您仍然可以这样做)。在 Release 构建期间,此功能已被删除,因此它仅适用于生产中的实际域名。

      由于路由默认响应所有域名,因此只有在域之间共享大量功能时才有意义。如果没有,最好为每个域设置单独的区域:

      routes.MapRoute(
          name: "DomainA",
          template: "{controller=Home}/{action=Index}/{id?}",
          defaults: new { area = "DomainA" },
          constraints: new { _ = new DomainConstraint("domaina.com") }
      );
      
      routes.MapRoute(
          name: "DomainA",
          template: "{controller=Home}/{action=Index}/{id?}",
          defaults: new { area = "DomainB" },
          constraints: new { _ = new DomainConstraint("domainb.com") }
      );
      

      【讨论】:

      • 注意,这不再适用于启用端点路由功能的 ASP.NET Core 2.2(如果您的兼容级别设置为 2.2,这是默认设置),因为端点路由不支持 IRouter可扩展性。详情请见this
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-20
      • 2021-12-29
      • 2021-05-04
      • 2022-10-24
      • 1970-01-01
      相关资源
      最近更新 更多