.Net Core 二级域名绑定到指定的控制器

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010584641/article/details/79550182

    在说二级域名绑定之前,先说一下.net core中的区域,关于区域这一块儿在很久之前的博客中,已经提过,详见《03-dotnet core创建区域[Areas]及后台搭建》,在这篇博客中,创建区域的时候,都是手动创建的,手动创建区域Areas文件夹,手动添加M-V-C等文件目录,由于在最早的VS2017版本中,直接右键添加区域的选项居然没了,然而在后来的VS版本更新中,这一功能又恢复了,也不知微软是出于什么考虑。区域的功能,就不用多说了,至于如何创建如何使用,也不多说,这些都是在MVC4的功能了。今天主要说一下关于二级域名如何绑定到指定的控制器。

    二级域名也无需多说了吧,比如我们进入百度,我们直接输入www.baidu.com或者输入baidu.com(重定向到www.baidu.com),会直接进入百度的首页,而如果我们输入的是,news.baidu.com,则直接进入百度新闻,我们称news.baidu.com为二级域名,那输入不同的二级域名会进入不同的主页面,那这是如何实现的呢。关于二级域名绑定其实使用一些代理服务器如nginx等,都可以实现域名绑定,这些我们都不讨论,这里我们只讨论在.net core 项目中如何配置。

    这里借助于一篇博客,园子中早已经有人对路由作了扩展,使其可以自定义域名绑定控制器,原文地址:《asp.net core mvc中如何把二级域名绑定到特定的控制器上》 。下面附上代码:二级域名逻辑判断,基于RouteBase扩展

public class SubDomainRouter : RouteBase
  {
      private readonly IRouter _target;
      private readonly string _subDomain;
      public SubDomainRouter(
         IRouter target,
         string subDomain,//当前路由规则绑定的二级域名
         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;//获取当前请求域名,然后跟_subDomain比较,如果不想等,直接忽略
          
          if (string.IsNullOrEmpty(domain) || string.Compare(_subDomain, domain) != 0)
          {
              return Task.CompletedTask;
          }
      
     //如果域名匹配,再去验证访问路径是否匹配
 
          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);
      }
  }

如何把域名绑定到指定的控制器上,代码如下:

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?}";//路径规则中不再包含控制器信息,但是上面通过constrains限定了查找时所要求的控制器名称
                    routeBuilder.Routes.Add(new SubDomainRouter(routeBuilder.DefaultHandler, domain, template, defaults, constrains, inlineConstraintResolver));
 
             
            return routeBuilder;
        }
}

使用方法:

routes.MapDomainRoute("xxx.domain.com","areaname","controllername");

以上代码为原博客中的代码,使用起来也很方便,只需要传值域名,区域名,控制器名,就可以实现我们想要的结果。下面我对以上代码稍作了修改,通过配置文件配置域名和控制器的绑定。新建一个关于域名、区域、控制器名的Model:    

    /// 域名信息实体类
    /// <summary>
    /// 域名信息实体类
    /// </summary>
    public class SubDomain
    {
        /// 域名
        /// <summary>
        /// 域名
        /// </summary>
        public string Domain { get; set; }

        /// 区域名
        /// <summary>
        /// 区域名
        /// </summary>
        public string AreaName { get; set; }

        /// 控制器名
        /// <summary>
        /// 控制器名
        /// </summary>
        public string Controller { get; set; }
    }

另外在MapDomainRoute扩展方法中,去掉了对区域的判断, 这样也可以配置非区域的控制器了,代码如下:

public static IRouteBuilder MapDomainRoute(
           this IRouteBuilder routeBuilder, string domain, string area, string controller)
        {
            try
            {
                if (string.IsNullOrEmpty(domain))
                {
                    throw new ArgumentNullException("domain can not be null");
                }
                //string.IsNullOrEmpty(area) || //去掉该判断,不限制仅限区域使用配置
                if (string.IsNullOrEmpty(controller))
                {
                    throw new ArgumentNullException("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?}";//路径规则中不再包含控制器信息,但是上面通过constrains限定了查找时所要求的控制器名称
                routeBuilder.Routes.Add(new SubDomainRouter(routeBuilder.DefaultHandler, domain, template, defaults, constrains, inlineConstraintResolver));
                return routeBuilder;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

同时也添加了一个对该方法的重载:

public static void MapDomainRoute(this IRouteBuilder routeBuilder, List<SubDomain> listDomains)
        {
            try
            {
                if (listDomains == null || listDomains.Count <= 0)
                {
                    return;
                }

                foreach (SubDomain domain in listDomains)
                {
                    MapDomainRoute(routeBuilder, domain.Domain, domain.AreaName, domain.Controller);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

将域名路由信息配置到appsetting.json文件中,例如:


当然在.net core中可以直接从IConfiguration对象中可以直接获取到配置文件下的信息,但我也没找到可以直接序列化我为要的标准的Json格式,就使用了直接读取文件的方式,借助Json.Net,又反序列化成我要的实体信息List<SubDomain>,代码如下:

//获取域名路由配置规则信息
var jsonDomain = JObject.Parse(File.ReadAllText("appsettings.json"))["SubDomains"];
if (jsonDomain != null)
{
      var infos = JsonConvert.DeserializeObject<List<SubDomain>>(jsonDomain.ToString());
      routes.MapDomainRoute(infos);
}

这样也就可以直接通过配置文件去修改配置了,效果展示:

https://www.allenchoi.net


https://blog.allenchoi.net


https://admin.allenchoi.net


以上就实现了我们想要的结果。后面内容应该会涉及微服务内容了,我会结合微软的示例项目eShop,应用到自己的项目中。另外,微软官方出了一本电子书《.NET微服务:容器化.NET应用架构指南》,大家可以免费下载,下载地址:https://aka.ms/microservicesebook

原文中涉及到的博客地址(尊重原创):http://www.cnblogs.com/dxp909/p/6994354.html


扫描二维码关注我的公众号,共同学习,共同进步!

猜你喜欢

转载自blog.csdn.net/u010584641/article/details/79550182
今日推荐