分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 教程案例

.Net Core 扩展使用Refit

发布时间:2023-09-06 02:30责任编辑:沈小雨关键词:暂无标签

.Net Core 扩展使用Refit

标签(空格分隔): 未分类


在.net core 2.1当中,目前可以是用HttpClientFactory进行Http的调用,它的使用方法我不再多说,具体参见(https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/http-requests?view=aspnetcore-2.2#consumption-patterns)。微软的官网写的非常详细了。

在调用过程中使用Refit可以进行强类型的客户端调用,个人比较喜欢这个。目前还有一个组件是老九大佬写的一个类似的客户端(https://github.com/dotnetcore/WebApiClient)

在WebApi当中使用Refit。

 ???????public void ConfigureServices(IServiceCollection services) ???????{ ???????????services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); ???????????services.AddHttpClient("github", c => ???????????{ ???????????????c.BaseAddress = new Uri("https://api.github.com"); ???????????}) ???.AddTypedClient(c => Refit.RestService.For<IGitHubApi>(c)); ???????????services.AddRefitClient<IGitHubApi>(); ???????} ???????????????

接口的定义为:

 ???[Headers("User-Agent: Refit Integration Tests")] ???//[RefitClient("github")] ???public interface IGitHubApi ???{ ???????[Get("/users/{username}")] ???????Task<User> GetUser(string userName); ???}

当我们有很多接口需要注入的时候,就得写很多的类似强类型的AddRefitClient的代码,个人觉得非常的麻烦。是否能够根据标签程序集进行批量注入。
Refit的注入,其实注入的是一个RestService

 ???????public static IHttpClientBuilder AddRefitClient<T>(this IServiceCollection services, RefitSettings settings = null) where T : class ???????{ ???????????services.AddSingleton(provider => RequestBuilder.ForType<T>(settings)); ???????????return services.AddHttpClient(UniqueName.ForType<T>()) ??????????????????????????.AddTypedClient((client, serviceProvider) => RestService.For<T>(client, serviceProvider.GetService<IRequestBuilder<T>>())); ???????}

具体的类型是在代码编译阶段用BuildTask生成了带有RefitHttpAttribute的接口类型的实体类。

具体创建的类型如下:

[ExcludeFromCodeCoverage, DebuggerNonUserCode, Preserve]internal class AutoGeneratedIGitHubApi : IGitHubApi{ ???// Fields ???[CompilerGenerated, DebuggerBrowsable(0)] ???private HttpClient <Client>k__BackingField; ???private readonly IRequestBuilder requestBuilder; ???// Methods ???public AutoGeneratedIGitHubApi(HttpClient client, IRequestBuilder requestBuilder) ???{ ???????this.Client = client; ???????this.requestBuilder = requestBuilder; ???} ???public virtual Task<User> GetUser(string userName) ???{ ???????object[] objArray1 = new object[] { userName }; ???????object[] objArray = objArray1; ???????Type[] typeArray1 = new Type[] { typeof(string) }; ???????return (Task<User>) this.requestBuilder.BuildRestResultFuncForMethod("GetUser", typeArray1, null)(this.Client, objArray); ???} ???// Properties ???public HttpClient Client { get; protected set; }}

目前我们要解决的是如何批量的进行注入的问题:个人在Refit的源码上添加了一些代码以支持批量注入

 ??//配置的实体 ???public class ApiSettings ???{ ???????public List<ApiOption> Apis { get; set; } = new List<ApiOption>(); ???} ???public class ApiOption ???{ ???????????public string Name { get; set; } ???????public string Version { get; set; } ???????//httpClient的baseAddress的配置 ???????public string Url { get; set; } ???} ???//配置的搜索的程序集的名称 ???public class DISettings ???{ ???????public List<string> RefitClientAssemblyNames { get; set; } = new List<string>(); ???} ???/// <summary> ???/// 批量注入时打上这个标签标明是Refit的接口 ???/// </summary> ???public class RefitClientAttribute : Attribute ???{ ???????/// <summary> ???????/// appsetting中的apiname ???????/// </summary> ???????public string ApiName { get; set; } ???????public RefitClientAttribute(string apiName) ???????{ ???????????ApiName = apiName; ???????} ???} ???/// <summary> ???/// 基于HttpClient的扩展方法 ???/// </summary> ???public static class RefitHttpClientExtension ???{ ???????public static IServiceCollection ScanAddHttpClient(this IServiceCollection serviceCollection, ApiSettings settings) ???????{ ???????????#region 批量注入HttpClient ??????????????????????if (settings == null) ???????????{ ???????????????throw new ArgumentNullException("settings"); ???????????} ???????????var apis = settings.Apis; ???????????if (apis == null || apis.Count == 0) ???????????{ ???????????????throw new ArgumentNullException("settings.Apis"); ???????????} ???????????foreach (var ass in apis) ???????????{ ???????????????serviceCollection.AddHttpClient(ass.Name).ConfigureHttpClient(client => client.BaseAddress = new Uri(ass.Url)); ???????????} ???????????#endregion ???????????return serviceCollection; ???????} ???????public static IServiceCollection ScanAddRefitClient(this IServiceCollection serviceCollection, IConfiguration configration, RefitSettings refitSettings = null) ???????{ ???????????DISettings dISettings = configration.GetSection("DISettings").Get<DISettings>(); ???????????if (dISettings == null) ???????????{ ???????????????throw new ArgumentNullException("dISettings", "配置文件中不存在DISettings节点"); ???????????} ???????????if (dISettings == null || dISettings.RefitClientAssemblyNames == null || dISettings.RefitClientAssemblyNames.Count == 0) ???????????{ ???????????????throw new ArgumentNullException("dISettings.RefitClientAssemblyNames", "配置文件DISettings节点不存在RefitClientAssemblyNames节点!!"); ???????????} ???????????ApiSettings apiSettings = configration.GetSection("ApiSettings").Get<ApiSettings>(); ???????????if (apiSettings == null) ???????????{ ???????????????throw new ArgumentNullException("apiSettings", "配置文件中不存在ApiSettings节点"); ???????????} ???????????if (apiSettings.Apis == null || apiSettings.Apis.Count == 0) ???????????{ ???????????????throw new ArgumentNullException("apiSettings.Apis", "配置文件ApiSettings节点不存在Apis节点!!"); ???????????} ???????????return serviceCollection.ScanAddRefitClient(apiSettings, dISettings, refitSettings); ???????} ???????public static IServiceCollection ScanAddRefitClient(this IServiceCollection serviceCollection, ApiSettings apiSettings, DISettings dISettings, RefitSettings refitSettings = null) ???????{ ???????????#region 批量注入HttpClient ???????????serviceCollection.ScanAddHttpClient(apiSettings); ???????????#endregion ???????????var registTypes = GetAttributeType(dISettings); ???????????serviceCollection.ScanAddRefitClient(registTypes, refitSettings); ???????????return serviceCollection; ???????} ???????public static IServiceCollection ScanAddRefitClient(this IServiceCollection serviceCollection, List<Type> registTypes, RefitSettings refitSettings = null) ???????{ ???????????foreach (var type in registTypes) ???????????{ ???????????????var name = type.GetCustomAttribute<RefitClientAttribute>().ApiName; ???????????????serviceCollection.AddRefitClient(type, name, refitSettings); ???????????} ???????????return serviceCollection; ???????} ???????private static List<Type> GetAttributeType(DISettings options) ???????{ ???????????var registTypes = new List<Type>(); ???????????var assemblys = options.RefitClientAssemblyNames; ???????????foreach (var assembly in assemblys) ???????????{ ???????????????registTypes.AddRange(GetAssemblyType(assembly, type => ???????????????{ ???????????????????var refitAttrs = type.GetCustomAttributes<RefitClientAttribute>(); ???????????????????if (refitAttrs == null || refitAttrs.Count() == 0) ???????????????????{ ???????????????????????return false; ???????????????????} ???????????????????else ???????????????????{ ???????????????????????return true; ???????????????????} ???????????????})); ???????????} ???????????return registTypes; ???????} ???????private static List<Type> GetAssemblyType(string assemblyName, Predicate<Type> predicate = null) ???????{ ???????????var registTypes = new List<Type>(); ???????????var ass = Assembly.Load(new AssemblyName(assemblyName)); ???????????var types = ass.DefinedTypes.Where(p => p.IsPublic && p.IsInterface).Select(x => x.AsType())?.ToList(); ???????????foreach (var type in types) ???????????{ ???????????????if (predicate == null) ???????????????{ ???????????????????registTypes.Add(type); ???????????????????continue; ???????????????} ???????????????if (predicate(type)) ???????????????{ ???????????????????registTypes.Add(type); ???????????????} ???????????} ???????????return registTypes; ???????} ???} ???

在HttpClientFactoryExtensions扩展类中新增了两个基于Type的扩展方法而不是泛型的扩展方法

 /// <summary> ???????/// Adds a Refit client to the DI container ???????/// </summary> ???????/// <param name="services">container</param> ???????/// <param name="t">type of refit interface</param> ???????/// <param name="httpclientName">httpclient‘s name</param> ???????/// <param name="settings">Optional. Settings to configure the instance with</param> ???????/// <returns></returns> ???????public static IServiceCollection AddRefitClient(this IServiceCollection services,Type t,string httpclientName, RefitSettings settings = null) ???????{ ???????????var builder = RequestBuilder.ForType(t, settings); ???????????services.AddSingleton(provider => builder); ???????????services.AddSingleton(t, provider => ???????????{ ???????????????var client=provider.GetService<IHttpClientFactory>().CreateClient(httpclientName); ???????????????if (client == null) ???????????????{ ???????????????????throw new Exception($"please inject the httpclient ?named {httpclientName} httpclient!! "); ???????????????} ???????????????return RestService.For(client, builder, t); ???????????}); ???????????return services; ???????} ???????/// <summary> ???????/// Adds a Refit client to the DI container ???????/// </summary> ???????/// <param name="services">container</param> ???????/// <param name="t">type of refit interface</param> ???????/// <param name="client>httpclient</param> ???????/// <param name="settings">Optional. Settings to configure the instance with</param> ???????/// <returns></returns> ???????public static IServiceCollection AddRefitClient(this IServiceCollection services, Type t, HttpClient client,RefitSettings settings = null) ???????{ ???????????var builder = RequestBuilder.ForType(t, settings); ???????????services.AddSingleton(provider => builder); ???????????services.AddSingleton(t, provider => ???????????{ ???????????????return RestService.For(client, builder, t); ???????????}); ???????????return services; ???????}

第三:在是基于Refit的源码RestService改写下能够支持RestService.For(Type interType)的方法

 ???????public static object For(HttpClient client, IRequestBuilder builder,Type t) ???????{ ???????????var generatedType = typeMapping.GetOrAdd(t, GetGeneratedType(t)); ???????????return Activator.CreateInstance(generatedType, client, builder); ???????} ???????//它就是在这里获取生成好的接口的注入类型的名字 ???????static Type GetGeneratedType(Type t) ???????{ ???????????string typeName = UniqueName.ForType(t); ???????????var generatedType = Type.GetType(typeName); ???????????if (generatedType == null) ???????????{ ???????????????var message = t.Name + " doesn‘t look like a Refit interface. Make sure it has at least one " + "method with a Refit HTTP method attribute and Refit is installed in the project."; ???????????????throw new InvalidOperationException(message); ???????????} ???????????return generatedType; ???????} ???????????????//获取名字方法也是泛型的也要加一个基于Type的 ???????public static string ForType(Type t) ???????{ ???????????string typeName; ???????????if (t.IsNested) ???????????{ ???????????????var className = "AutoGenerated" + t.DeclaringType.Name + t.Name; ???????????????typeName = t.AssemblyQualifiedName.Replace(t.DeclaringType.FullName + "+" + t.Name, t.Namespace + "." + className); ???????????} ???????????else ???????????{ ???????????????var className = "AutoGenerated" + t.Name; ???????????????if (t.Namespace == null) ???????????????{ ???????????????????className = $"{className}.{className}"; ???????????????} ???????????????typeName = t.AssemblyQualifiedName.Replace(t.Name, className); ???????????} ???????????return typeName; ???????}

在AppSetting文件当中配置一些配置:

{ ?"ApiSettings": { ???"Apis": [ ?????{ ???????"Name": "gitHubApi", ???????"Version": "", ???????"Url": "https://api.github.com" ?????} ???] ?}, ?"DISettings": { ???"RefitClientAssemblyNames": [ "RefitWebTest" ] ?}}

Refit的接口主要打上标签即可

 ???[Headers("User-Agent: Refit Integration Tests")] ???//参数的名字和配置文件的Apis:Name一致即可 ???[RefitClient("gitHubApi")] ???public interface IGitHubApi ???{ ???????[Get("/users/{username}")] ???????Task<User> GetUser(string userName); ???}

改写以后的使用:

 ???????????//批量注入带有RefitClientAttribute标签的接口 ???????????services.ScanAddRefitClient(Configuration); ???????????

这样就实现了基于Refit的批量注入,注入的HttpClient的名字为配置文件当中的名字。

最后打包成Nuget包才能正确的使用,个人尝试发现必须Nuget包的名字是Refit才能正确的生成接口的代理类,(我知道改包名不是很好=。=)改个nuget的名字都不行,不知道为什么不知道有没有大佬知道,望指教!!
贴一下Refit的BuildTask的targets文件

<?xml version="1.0" encoding="utf-8"?><Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> ?<PropertyGroup> ???<CoreCompileDependsOn> ?????$(CoreCompileDependsOn); ?????GenerateRefitStubs; ???</CoreCompileDependsOn> ?</PropertyGroup> ???<PropertyGroup> ???<IntermediateOutputPath Condition="$(IntermediateOutputPath) == ‘‘ Or $(IntermediateOutputPath) == ‘*Undefined*‘">$(MSBuildProjectDirectory)obj\$(Configuration)\</IntermediateOutputPath> ???<RefitTaskAssemblyFile Condition="‘$(MSBuildRuntimeType)‘ == ‘Core‘">$(MSBuildThisFileDirectory)..\MSBuildCore20\InterfaceStubGenerator.BuildTasks.dll</RefitTaskAssemblyFile> ???<RefitTaskAssemblyFile Condition="‘$(MSBuildRuntimeType)‘ != ‘Core‘">$(MSBuildThisFileDirectory)..\MSBuildFull46\InterfaceStubGenerator.BuildTasks.dll</RefitTaskAssemblyFile> ???<RefitMinCoreVersionRequired>2.0</RefitMinCoreVersionRequired> ???<!-- Our default CLI version for error checking purposes --> ???<RefitNetCoreAppVersion>$(BundledNETCoreAppTargetFrameworkVersion)</RefitNetCoreAppVersion> ???<RefitNetCoreAppVersion Condition="‘$(RefitNetCoreAppVersion)‘ == ‘‘">1.0</RefitNetCoreAppVersion> ???????<!-- ???Refit internal namespace to be added to internally generated Refit code. ????Can be overriden by user in case of namespace clashes. ???--> ???<RefitInternalNamespace Condition=" ‘$(RefitInternalNamespace)‘ == ‘‘ ">$(RootNamespace)</RefitInternalNamespace> ?</PropertyGroup> ???<UsingTask TaskName="Refit.Generator.Tasks.GenerateStubsTask" AssemblyFile="$(RefitTaskAssemblyFile)" /> ???<Target Name="GenerateRefitStubs" BeforeTargets="CoreCompile"> ???<Error Condition="‘$(MSBuildRuntimeType)‘ == ‘Core‘ and ‘$(RefitMinCoreVersionRequired)‘ > ‘$(RefitNetCoreAppVersion)‘ " ??????????Text="Refit requires at least the .NET Core SDK v2.0 to run with ‘dotnet build‘" ??????????ContinueOnError="false" ??????????/> ???????<GenerateStubsTask SourceFiles="@(Compile)" ??????????????????????BaseDirectory="$(MSBuildProjectDirectory)" ??????????????????????OutputFile="$(IntermediateOutputPath)\RefitStubs.g.cs" ??????????????????????RefitInternalNamespace="$(RefitInternalNamespace)" ??????????????????????/> ???<Message Text="Processed Refit Stubs" /> ??????<ItemGroup Condition="Exists(‘$(IntermediateOutputPath)\RefitStubs.g.cs‘)"> ???????<Compile Include="$(IntermediateOutputPath)\RefitStubs.g.cs" /> ???</ItemGroup> ?????</Target></Project>

测试发现,只要改了打包的报名称,这个Task就不执行了,不知道为啥!!
如有知道忘告知!!

.Net Core 扩展使用Refit

原文地址:https://www.cnblogs.com/dasajia2lang/p/10268448.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved