How to bind a secondary domain name to a specific controller in Asp. net Core MVC

  • 2021-09-24 21:59:09
  • OfStack

Application Scenario: Enterprise portal website will set up different sections according to different contents, such as Sina sports and entertainment channels, and so on. In some cases, it is necessary to set different level 2 domain names for different sections, such as Sina Sports sports.sina.com.cn.

In asp. net core mvc, If you want to achieve the effect of plates, It is possible to build different controllers for different sections (of course, there are other technologies, and the implementation methods are not discussed here). In this case, how to bind the controller with a unique level 2 domain name, for example, the controller corresponding to the sports channel is called SportController. When accessing the system through sports. XXX. com domain name, it directly enters SportController, and other controllers cannot be accessed through this level 2 domain name.

Having finished the scene above, let's look at how to achieve it.

There is routing rule configuration in asp. net core mvc, and the configuration place is in Startup. Configure method. The specific code is as follows:


app.UseMvc(routes =>
{
   routes.MapRoute(
      name: "default",
      template: "{controller=Home}/{action=Index}/{id?}",
      defaults: new { area="admin"});
});

Unfortunately, I do not support the domain name support (I know that if there is any problem, welcome to correct you). Through routes. MapRouter registration routing rules, and add to RouteCollection, when a request comes, RouterCollection cycle all registered IRouter object, find the first matching IRouter. Although the framework does not support domain name configuration rules, we can implement an IRouter by ourselves, and realize the logic of 2-level domain name judgment in it. I am temporarily named SubDomainRouter here, and the specific implementation code is as follows:


public class SubDomainRouter : RouteBase
 {
   private readonly IRouter _target;
   private readonly string _subDomain;
   public SubDomainRouter(
     IRouter target,
     string subDomain,// Object bound to the current routing rule 2 Class domain name 
     string routeTemplate,
     RouteValueDictionary defaults,
     RouteValueDictionary constrains,
     IInlineConstraintResolver inlineConstraintResolver)
     : base(routeTemplate,
        subDomain,
        inlineConstraintResolver,
        defaults,
        constrains,
        new RouteValueDictionary(null))
   {
     if (target == null)
     {
       throw new ArgumentNullException(nameof(target));
     }
     if (subDomain == null)
     {
       throw new ArgumentNullException(nameof(subDomain));
     }
     _subDomain = subDomain;
     _target = target;
   }
   public override Task RouteAsync(RouteContext context)
   {
     string domain = context.HttpContext.Request.Host.Host;// Gets the current requested domain name, and then follows the _subDomain Compare, if you don't want to wait, ignore it directly 
     if (string.IsNullOrEmpty(domain) || string.Compare(_subDomain, domain) != 0)
     {
       return Task.CompletedTask;
     }
          
       // If the domain name matches, then verify whether the access path matches 
     return base.RouteAsync(context);
   }
   protected override Task OnRouteMatched(RouteContext context)
   {
     context.RouteData.Routers.Add(_target);
     return _target.RouteAsync(context);
   }
   protected override VirtualPathData OnVirtualPathGenerated(VirtualPathContext context)
   {
     return _target.GetVirtualPath(context);
   }
 }

From the above code, we only see the domain name detection, but how to direct the domain name to a specific controller requires us to do some articles when registering this IRouter, and directly go to the code:


public static class RouteBuilderExtensions
  {    public static IRouteBuilder MapDomainRoute(
      this IRouteBuilder routeBuilder,string domain,string area,string controller)
    {
      if(string.IsNullOrEmpty(area)||string.IsNullOrEmpty(controller))
      {
        throw new ArgumentNullException("area or controller can not be null");
      }
      var inlineConstraintResolver = routeBuilder
        .ServiceProvider
        .GetRequiredService<IInlineConstraintResolver>();
        string template = "";
          RouteValueDictionary defaults = new RouteValueDictionary();
          RouteValueDictionary constrains = new RouteValueDictionary();
          constrains.Add("area", area);
          defaults.Add("area", area);
          constrains.Add("controller", controller);
          defaults.Add("controller", string.IsNullOrEmpty(controller) ? "home" : controller);
          defaults.Add("action", "index");
          template += "{action}/{id?}";// Controller information is no longer included in the path rule, but the above pass constrains Qualifies the controller name required for lookup 
          routeBuilder.Routes.Add(new SubDomainRouter(routeBuilder.DefaultHandler, domain, template, defaults, constrains, inlineConstraintResolver));
      return routeBuilder;
    }
}

Finally, we can register the corresponding rules in Startup, as follows:


public static class RouteBuilderExtensions
  {    public static IRouteBuilder MapDomainRoute(
      this IRouteBuilder routeBuilder,string domain,string area,string controller)
    {
      if(string.IsNullOrEmpty(area)||string.IsNullOrEmpty(controller))
      {
        throw new ArgumentNullException("area or controller can not be null");
      }
      var inlineConstraintResolver = routeBuilder
        .ServiceProvider
        .GetRequiredService<IInlineConstraintResolver>();
        string template = "";
          RouteValueDictionary defaults = new RouteValueDictionary();
          RouteValueDictionary constrains = new RouteValueDictionary();
          constrains.Add("area", area);
          defaults.Add("area", area);
          constrains.Add("controller", controller);
          defaults.Add("controller", string.IsNullOrEmpty(controller) ? "home" : controller);
          defaults.Add("action", "index");
          template += "{action}/{id?}";// Controller information is no longer included in the path rule, but the above pass constrains Qualifies the controller name required for lookup 
          routeBuilder.Routes.Add(new SubDomainRouter(routeBuilder.DefaultHandler, domain, template, defaults, constrains, inlineConstraintResolver));
      return routeBuilder;
    }
}

Related articles: