4. abp中的asp.net core模块剖析
相关模块
- abpaspnetcoremodule
- abpaspnetcoremvcmodule
- 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); }); }