欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

4. abp中的asp.net core模块剖析

程序员文章站 2023-11-10 22:41:58
相关模块 1. AbpAspNetCoreModule 2. AbpAspNetCoreMvcModule 3. AbpAspNetCoreMvcContractsModule abp通过这三个模块加载并配置了 asp.net core。,最主要的就是AbpAspNetCoreMvcModule模块 ......
相关模块
  1. abpaspnetcoremodule
  2. abpaspnetcoremvcmodule
  3. abpaspnetcoremvccontractsmodule

abp通过这三个模块加载并配置了 asp.net core。,最主要的就是abpaspnetcoremvcmodule模块类,abp如何基于aspnet core构建自己的控制器和appservices,就是在这个类中。

  • abpaspnetcoremvcmodule
    将abpaspnetcoremvcconventionalregister类添加到conventionalregistrarlist列表中,该类主要是用来注入依赖及获取服务生命周期的。

      public override void preconfigureservices(serviceconfigurationcontext context)
      {
          context.services.addconventionalregistrar(new abpaspnetcoremvcconventionalregistrar());
      }

    接下来就是重点,在configureservices方法中配置视图和控制器,当然是基于 asp.net core mvc。首先配置razor:

      context.services.insert(0,
          servicedescriptor.singleton<iconfigureoptions<razorviewengineoptions>>(
              new configureoptions<razorviewengineoptions>(options =>
                  {
                      options.fileproviders.add(
                          new razorviewenginevirtualfileprovider(
                              context.services.getsingletoninstance<iobjectaccessor<iserviceprovider>>()
                          )
                      );
                  }
              )
          )
      );
    

    配置api描述符:

      configure<apidescriptionmodeloptions>(options =>
      {
          options.ignoredinterfaces.addifnotcontains(typeof(iasyncactionfilter));
          options.ignoredinterfaces.addifnotcontains(typeof(ifiltermetadata));
          options.ignoredinterfaces.addifnotcontains(typeof(iactionfilter));
      });

    可以看到 aspnetcore mvc中的过滤器接口,我们将其添加到了api描述模型选项类中。下面就是注入mvc:

      var mvccorebuilder = context.services.addmvccore();
      var mvcbuilder = context.services.addmvc()
          .adddataannotationslocalization(options =>
          {
              options.dataannotationlocalizerprovider = (type, factory) =>
              {
                  var resourcetype = abpmvcdataannotationslocalizationoptions.assemblyresources.getordefault(type.assembly);
                  return factory.create(resourcetype ?? type);
              };
          })
          .addviewlocalization();

    使用di创建控制器,使用的是aspnet core默认的控制器激活器servicebasedcontrolleractivator类:

      //use di to create controllers
      context.services.replace(servicedescriptor.transient<icontrolleractivator, servicebasedcontrolleractivator>());

    abp提供了一个基于约定的控制器特性提供器类,这个类是基于 aspnetcore mvc的controllerfeatureprovider构建自己的控制器,并检索判断控制器。

      //add feature providers
      var partmanager = context.services.getsingletoninstance<applicationpartmanager>();
      var application = context.services.getsingletoninstance<iabpapplication>();
    
      partmanager.featureproviders.add(new abpconventionalcontrollerfeatureprovider(application));

    该类源码:

      public class abpconventionalcontrollerfeatureprovider : controllerfeatureprovider
      {
          private readonly iabpapplication _application;
    
          public abpconventionalcontrollerfeatureprovider(iabpapplication application)
          {
              _application = application;
          }
    
          protected override bool iscontroller(typeinfo typeinfo)
          {
              //todo: move this to a lazy loaded field for efficiency.
              if (_application.serviceprovider == null)
              {
                  return false;
              }
    
              var configuration = _application.serviceprovider
                  .getrequiredservice<ioptions<abpaspnetcoremvcoptions>>().value
                  .conventionalcontrollers
                  .conventionalcontrollersettings
                  .getsettingornull(typeinfo.astype());
    
              return configuration != null;
          }
      }

    由上,abp会基于aspnetcore mvc配置abp的mvc模块,特别是controller的创建。从代码里面可以看出获取到abpaspnetcoremvcoptions的服务再去检索规约的控制器。由此返回是否是控制器。abpaspnetcoremvcoptions类是abp对aspnet core mvc的一个封装,源码如下:

      public class abpaspnetcoremvcoptions
      {
          public conventionalcontrolleroptions conventionalcontrollers { get; }
    
          public abpaspnetcoremvcoptions()
          {
              conventionalcontrollers = new conventionalcontrolleroptions();
          }
      }
    
      //规约控制器集合
      public class conventionalcontrolleroptions
      {
          public conventionalcontrollersettinglist conventionalcontrollersettings { get; }
    
          public list<type> formbodybindingignoredtypes { get; }
    
          public conventionalcontrolleroptions()
          {
              conventionalcontrollersettings = new conventionalcontrollersettinglist();
    
              formbodybindingignoredtypes = new list<type>
              {
                  typeof(iformfile)
              };
          }
    
          public conventionalcontrolleroptions create(assembly assembly, [canbenull] action<conventionalcontrollersetting> optionsaction = null)
          {
              var setting = new conventionalcontrollersetting(assembly, moduleapidescriptionmodel.defaultrootpath); // defaultrootpath = app,abp路由就是以这个app开头的。
              optionsaction?.invoke(setting);
              setting.initialize();
              conventionalcontrollersettings.add(setting);
              return this;
          }
      }

    abpaspnetcoremvcoptions实际上是通过conventionalcontrolleroptions来完成规约的配置,来实现自定义的路由以及动态api。

  • aspnetcoredescriptionmodelprovider

    abp是如何aspnet core创建自己的api的呢?有这么一个类aspnetcoredescriptionmodelprovider,这个类就是提供了aspnet core的描述模型,源码如下:

      public class aspnetcoreapidescriptionmodelprovider : iapidescriptionmodelprovider, itransientdependency
      {
          public ilogger<aspnetcoreapidescriptionmodelprovider> logger { get; set; }
    
          private readonly iapidescriptiongroupcollectionprovider _descriptionprovider;
          private readonly abpaspnetcoremvcoptions _options;
          private readonly apidescriptionmodeloptions _modeloptions;
    
          public aspnetcoreapidescriptionmodelprovider(
              iapidescriptiongroupcollectionprovider descriptionprovider,
              ioptions<abpaspnetcoremvcoptions> options,
              ioptions<apidescriptionmodeloptions> modeloptions)
          {
              _descriptionprovider = descriptionprovider;
              _options = options.value;
              _modeloptions = modeloptions.value;
    
              logger = nulllogger<aspnetcoreapidescriptionmodelprovider>.instance;
          }
    
          public applicationapidescriptionmodel createapimodel()
          {
              //todo: can cache the model?
    
              var model = applicationapidescriptionmodel.create();
    
              foreach (var descriptiongroupitem in _descriptionprovider.apidescriptiongroups.items)
              {
                  foreach (var apidescription in descriptiongroupitem.items)
                  {
                      if (!apidescription.actiondescriptor.iscontrolleraction())
                      {
                          continue;
                      }
    
                      addapidescriptiontomodel(apidescription, model);
                  }
              }
    
              return model;
          }
    
          private void addapidescriptiontomodel(apidescription apidescription, applicationapidescriptionmodel model)
          {
              var controllertype = apidescription.actiondescriptor.ascontrolleractiondescriptor().controllertypeinfo.astype();
              var setting = findsetting(controllertype);
    
              var modulemodel = model.getoraddmodule(getrootpath(controllertype, setting));
    
              var controllermodel = modulemodel.getoraddcontroller(controllertype.fullname, calculatecontrollername(controllertype, setting), controllertype, _modeloptions.ignoredinterfaces);
    
              var method = apidescription.actiondescriptor.getmethodinfo();
    
              var uniquemethodname = getuniqueactionname(method);
              if (controllermodel.actions.containskey(uniquemethodname))
              {
                  logger.logwarning($"controller '{controllermodel.controllername}' contains more than one action with name '{uniquemethodname}' for module '{modulemodel.rootpath}'. ignored: " + method);
                  return;
              }
    
              logger.logdebug($"actionapidescriptionmodel.create: {controllermodel.controllername}.{uniquemethodname}");
              var actionmodel = controllermodel.addaction(uniquemethodname, actionapidescriptionmodel.create(
                  uniquemethodname,
                  method,
                  apidescription.relativepath,
                  apidescription.httpmethod,
                  getsupportedversions(controllertype, method, setting)
              ));
    
              addparameterdescriptionstomodel(actionmodel, method, apidescription);
          }
    
          private static string calculatecontrollername(type controllertype, conventionalcontrollersetting setting)
          {
              var controllername = controllertype.name.removepostfix("controller").removepostfix(applicationservice.commonpostfixes);
    
              if (setting?.urlcontrollernamenormalizer != null)
              {
                  controllername = setting.urlcontrollernamenormalizer(new urlcontrollernamenormalizercontext(setting.rootpath, controllername));
              }
    
              return controllername;
          }
      }

    这个类为我们提供了从action到controller的描述,构建了abp自己的api。它的调用有两个地方,一个是abpapidefinitioncontroller,一个是proxyscriptmanager,前者是定义abp的api控制器定义的地方,后者则是生成代理脚本的地方,abp的示例项目bookstore中会调用接口abp/serviceproxyscript生成一个js文件,这个js文件里面就是api的url地址,前端通过访问这个api地址来访问appservice等后端方法。源码如下:

      [area("abp")]
      [route("abp/serviceproxyscript")]
      [disableauditing]
      public class abpserviceproxyscriptcontroller : abpcontroller
      {
          private readonly iproxyscriptmanager _proxyscriptmanager;
    
          public abpserviceproxyscriptcontroller(iproxyscriptmanager proxyscriptmanager)
          {
              _proxyscriptmanager = proxyscriptmanager;
          }
    
          [httpget]
          [produces("text/javascript", "text/plain")]
          public string getall(serviceproxygenerationmodel model)
          {
              model.normalize();
              return _proxyscriptmanager.getscript(model.createoptions());
          }
      }
  • abphttpmodule模块
    abp创建jquery代理脚本生成器,主要用来生产api提供给前端访问

      public override void configureservices(serviceconfigurationcontext context)
      {
          configure<abpapiproxyscriptingoptions>(options =>
          {
              options.generators[jqueryproxyscriptgenerator.name] = typeof(jqueryproxyscriptgenerator);
          });
      }