Asp.NetCore abp vnext 自动api 和动态api客户端


 ABP Vnext UI 原理 自动API 和C#动态api客户端

自动Api控制器

当你从领域层以及数据持久层中查询出了数据并且 将他们转交给应用层进行显示时 你发现 显示之前经过HttpApi层的中转 还要托管再Web项目中

1.手动api控制器

第一步 为什么要有这个Httpapi层  UI界面为了实现应用层接口的通用 如果将web何应用层直接依赖 第三方就没有可用的直接webapi可以调用 你发现你还需要在写一个webapi 所以我们应该创建一个专门的接口层让web直接去托管他 这样不仅web可以直接快速部署也可以供第三方直接访问接口

第二步 在Httpapi层中创建控制器 依赖公共应用层 只依赖了接口 这时候你发现了  那么如果只依赖接口是肯定不行的 两种办法:插件加载实现类  以及 web托管(我们这次选择这个 并用httpapi进行暴露接口在host托管项目中你可以看见依赖了实现)

我想强调一点的是 这其实是一种延迟的思想

 

 

为什么需要把业务上的控制器独立出来成一个项目 而不是直接和web主机的控制器放在一起   

业务角度: 原因就是为了做成模块化 为了在所有模块中进行复用  强调一点 模块化就是为了复用 如果和主机放在一起 就不能复用 所以我们只能把他做成独立的项目拆分出来 

代码角度:复用控制器 在abp框架中 通过Abp.ASP.NETCOREMVC 模块复用  我们现在已知的httpapi的作用是 UI将商品给到hhtpapi 然后通过他委托给servers逻辑  在这里 我们是不是可以直接通过servers 就能生成api呢?通过他的名称 生成 这样我们就不用自己写控制器了 

第三步:自动api的生成:在应用实现层中 继承 IRemoteService 接口 即可使用abp 自动api控制器  如果你不想让某个服务暴露 你可以给某个服务上 加上 特性  [RemoteService(IsEnabled =false)] 默认为True 即可

第四步 解析 abp是如何 通过applicaitionserver 自动生成控制:在托管主机的模块类中 有代码 

 

 1   private void ConfigureConventionalControllers()
 2         {
 3             Configure<AbpAspNetCoreMvcOptions>(options =>
 4             {
 5                 options.ConventionalControllers.Create(typeof(EBusinessApplicationModule).Assembly, options => {
 6 
 7                     options.RootPath = "My.EBusiness";
 8                 });
 9             });
10         }

 

 加载当前项目自动生成控制器 

options.RootPath = "My.EBusiness"; 此选项可以规定api名称 

 

 具体加载  依赖Application 通过自动api控制器加载方法 把servers 转化为控制器 运行一下  红框中就是自动生成的控制器 以及指定名称

 

 我们看源码 究竟是如何生成的?

我们找到 Volo.Abp.Asp.NetCore.MVC--》Volo->Abp->ASPNETCORE->MVC->Conventions 在该模块中 有生成api的各种约定

 

 

 

当你的servers继承了 IRemoteService 接口时  他会将该类中 方法 以Get post Update delete 生成

 

 Get Post Put Delet 方法    截取具体方法名 做成api的名称  以及获取相应的参数   /api/app/product  其中api不能更改  app可以通过上述 选项进行修改  后面具体方法名称以及方法参数 则是abo自动解析 

思考  :既然这样能够自动生成 为何我们还要写一个Httpapit呢  明明可以自动生成  :

答案:为了能够实现个性化的api   所以 abp在此 既考虑了 复用 又考虑到了 个性化  以及特性对servers 过滤加上  [RemoteService(IsEnabled =false)]

动态C#Api客户端 abp核心模块 之 HttpApi.Client

什么是 动态C#Api客户端:对应用servers接口的动态代理 目的 解决客户端调用服务端复杂性的问题;

上一节中 我们暴露的是webapi接口 如果我们使用webapi调用服务时 每增加一个需求 就会增加一个 类去调用服务 者极大的违背了 开闭原则 

第一步 为什么要有HttpClient  如果我们只想让我们的系统通过webapi 或者grpc/rpc进行远程调用 就需要使用HttpClient了 也就是说 如果我们整个项目只需要做成webapi的形式 那我们只需要选择一个就行了 并通过web托管 细心的同学可能想到  既然Httpapi 和Httpclient 都是可以做远程通信为何要重复做一件事呢? 实际上在Httpapi.Client 是直接可以使用RPC直接进行远程调用的 当然也可进行托管 而Httpapi则是 真正的Webapi项目( 页面上接收参数有复用 一对多的关系)

如何使用C#动态api客户端:创建一个 客户端以调用 我们的服务

 

 

 

 

 实现C#api动态客户端的前提 1:OA项目 是个模块项目 2:让OA项目 依赖Httpapi.Client  并且在OA中引入Httpapi.Client  

  既然普通的Http调用违背了开闭原则 那我们该如何调用呢? 在Abp 的HttpApi.Client的加持下  使用IOC注入application.Contracts 的接口 直接进行具体的方法调用  就能完成调用 

启动测试 发现  接口变成了代理类而不是原来的接口  这个代理类就是 Httpclient创建的

C#动态api客户端 原理分析:

原理可以总结一句话 当你调用接口时httpclient 拦截执并通过http形式执行你接口对应服务   可以通过日志输出可以明显看见 他替我们完成了最为繁琐的一步  

1.OA--依赖--》Httpapi.Client  -----依赖 --》application.Contracts  这样会让OA间接依赖 application.Contracts  才能使用IOC进行获取 但是 依赖的时接口 我们肯定不能直接调用;----------》动态代理

2.此时需要为接口创建代理:此时 abp 的HttpApi.Client基于application.Contracts 的接口 创建了代理(提前透露 所谓的代理 其实就是获取服务名称 然后拼接出真正的Url 使用HttpClient 去调用 获取返回结果)--------》拼接URL 并且通过httpclient进行调用

源码分析

首先动态代理的代码在项目HttpApi.Client 模块类中

  其中

AddHttpClientProxies 就是创建代理的具体方法
 public override void ConfigureServices(ServiceConfigurationContext context)
        {
            context.Services.AddHttpClientProxies(
                typeof(EBusinessApplicationContractsModule).Assembly,
                RemoteServiceName
            );
        }

查看源码 FRAMEWORK-->SRC--->Volo.Abp.Http.clien-->Extensions--->Dependencyinjetion-->

 1  public static IServiceCollection AddStaticHttpClientProxies(
 2             [NotNull] this IServiceCollection services,
 3             [NotNull] Assembly assembly,
 4             [NotNull] string remoteServiceConfigurationName = RemoteServiceConfigurationDictionary.DefaultName)
 5         {
 6             Check.NotNull(services, nameof(assembly));
 7 
 8             var serviceTypes = assembly.GetTypes().Where(IsSuitableForClientProxying).ToArray();
 9 
10             foreach (var serviceType in serviceTypes)
11             {
12                 AddHttpClientFactory(services, remoteServiceConfigurationName);
13 
14                 services.Configure<AbpHttpClientOptions>(options =>
15                 {
16                     options.HttpClientProxies[serviceType] = new HttpClientProxyConfig(serviceType, remoteServiceConfigurationName);
17                 });
18             }
19 
20             return services;
21         }
 1      public static IServiceCollection AddHttpClientProxy(
 2             [NotNull] this IServiceCollection services,
 3             [NotNull] Type type,
 4             [NotNull] string remoteServiceConfigurationName = RemoteServiceConfigurationDictionary.DefaultName,
 5             bool asDefaultService = true)
 6         {
 7             Check.NotNull(services, nameof(services));
 8             Check.NotNull(type, nameof(type));
 9             Check.NotNullOrWhiteSpace(remoteServiceConfigurationName, nameof(remoteServiceConfigurationName));
10 
11             AddHttpClientFactory(services, remoteServiceConfigurationName);
12 
13             services.Configure<AbpHttpClientOptions>(options =>
14             {
15                 options.HttpClientProxies[type] = new HttpClientProxyConfig(type, remoteServiceConfigurationName);
16             });
17 
18             var interceptorType = typeof(DynamicHttpProxyInterceptor<>).MakeGenericType(type);
19             services.AddTransient(interceptorType);
20 
21             var interceptorAdapterType = typeof(AbpAsyncDeterminationInterceptor<>).MakeGenericType(interceptorType);
22 
23             var validationInterceptorAdapterType =
24                 typeof(AbpAsyncDeterminationInterceptor<>).MakeGenericType(typeof(ValidationInterceptor));
25 
26             if (asDefaultService)
27             {
28                 services.AddTransient(
29                     type,
30                     serviceProvider => ProxyGeneratorInstance
31                         .CreateInterfaceProxyWithoutTarget(
32                             type,
33                             (IInterceptor)serviceProvider.GetRequiredService(validationInterceptorAdapterType),
34                             (IInterceptor)serviceProvider.GetRequiredService(interceptorAdapterType)
35                         )
36                 );
37             }
38 
39             services.AddTransient(
40                 typeof(IHttpClientProxy<>).MakeGenericType(type),
41                 serviceProvider =>
42                 {
43                     var service = ProxyGeneratorInstance
44                         .CreateInterfaceProxyWithoutTarget(
45                             type,
46                             (IInterceptor)serviceProvider.GetRequiredService(validationInterceptorAdapterType),
47                             (IInterceptor)serviceProvider.GetRequiredService(interceptorAdapterType)
48                         );
49 
50                     return Activator.CreateInstance(
51                         typeof(HttpClientProxy<>).MakeGenericType(type),
52                         service
53                     );
54                 });
55 
56             return services;
57         }
AddHttpClientProxy

 

你会发现 这个方法 就做了一件事 

context.Services.AddHttpClientProxies(
                typeof(EBusinessApplicationContractsModule).Assembly, RemoteServiceName
EBusinessApplicationContractsModule 此程序集中加载所有类型  通过for循环 调用AddHttpClientProxy方法 创建所有的代理 具体依赖来自于  

Volo.abp.castle.core此模块将在后期文章中详解

 这样我们可以想一想 C#Api动态客户端有什么缺陷? 

  不能跨语言 仅仅局限于ABp框架中 甚至是.NET5中 

这就有了自动api了 

动态客户端对内调用  自动api向外提供访问

 

 

三五八团楚云飞的小Tips 模块为了复用:业务模块组件之 数据库持久层 

如何再数据持久层中切换数据库 首先你可以查看源码  abp封装了MIc 的EntitiyFramework框架 

 

 

 再AbpEntitiyFramework中 我们默认使用的是Mysql 如何切换到Sqlserver  只需要下载组件 然后再数据持久层

 

 

 

 

 修改 Options.UseSqlserver();即可

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM