.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.For()返回的一个类型。

具体见源码:

        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的更多相关文章

  1. 自动注册服务NET Core扩展IServiceCollection

    NET Core扩展IServiceCollection自动注册服务 前言 在ASP.NET Core中使用依赖注入中使用很简单,只需在Startup类的ConfigureServices()方法中, ...

  2. Orchard Module,Theme,Core扩展加载概述

    Orchard 源码探索(Module,Theme,Core扩展加载概述) 参考: http://www.orchardch.com/Blog/20120830071458 1. host.Initi ...

  3. ServiceStack.Redis 的 ASP.NET Core 扩展库

    给大家安利一款 ServiceStack.Redis 的 ASP.NET Core 扩展库,它是基于 ServiceStack.Redis.Core 开发的. 简单易用,开源免费,使用ASP.NET ...

  4. Log4net 的 ASP.NET Core 扩展库

    给大家安利一款 log4net 的 ASP.NET Core 扩展库,它是基于 log4net 开发的. 简单易用,开源免费,使用ASP.NET Core自身提供的DI容器来实现服务的注册和消费.直接 ...

  5. ASP.NET Core扩展库

    亲爱的.Neter们,在我们日复一日的编码过程中是不是会遇到一些让人烦恼的事情: 日志配置太过复杂,各种模板.参数也搞不清楚,每次都要去查看日志库的文档,还需要复制粘贴一些重复代码,好无赖 当需要类型 ...

  6. ASP.NET Core扩展库之日志

        上一篇我们对Xfrogcn.AspNetCore.Extensions扩展库功能进行了简单的介绍,从这一篇文章开始,我将逐步介绍扩展库中的核心功能.     日志作为非业务的通用领域基础功能, ...

  7. .NET Core扩展IServiceCollection自动注册服务

    前言 在ASP.NET Core中使用依赖注入中使用很简单,只需在Startup类的ConfigureServices()方法中,通过IServiceCollection接口进行注入即可,其它的无需关 ...

  8. Orchard 源码探索(Module,Theme,Core扩展加载概述)

    参考: http://www.orchardch.com/Blog/20120830071458 1. host.Initialize(); private static IOrchardHost H ...

  9. .Net Core扩展 SharpPlugs简单上手

    SharpPlugs .Net Core 鋒利扩展,这是本人的开源项目 地址是 GitHub地址 大家喜欢 的话可以加个星哦 当前功能 DI AutoMapper ElasticSearch WebA ...

随机推荐

  1. XShell 连接虚拟机中的服务器 失败 、连接中断(Connection closed by foreign host.)

    在使用XShell连接虚拟机中的服务器时,报以下错误并断开连接,之前连接还是挺稳定的,忽然就这样了 Last login: Thu Aug :: from 192.168.1.102 [root@no ...

  2. unit Base64Unit;

    unit Base64Unit; unit Base64Unit; //Download by http://www.codefans.net interface uses Classes, SysU ...

  3. 【DP专辑】ACM动态规划总结(转)

    http://blog.csdn.net/cc_again/article/details/25866971 动态规划一直是ACM竞赛中的重点,同时又是难点,因为该算法时间效率高,代码量少,多元性强, ...

  4. Java 对象引用以及对象赋值

    一.Vehicle veh1 = new Vehicle(); 通常这条语句执行的动作被称为创建一个对象,其实他包含了四个动作. 1.new Vehicle  :表示在堆空间内创建了一个Vehicle ...

  5. NOI2018网络同步赛游记

    Day1 t1是一道NOI选手眼中的送分题,对于我来说还是有难度的,用了个把小时想了出来可持久化并查集的做法,最后一个点被卡常.赛后才发现Kruskal重构树是这样的简单.t2.t3由于我真的是太弱了 ...

  6. Aravis 库编译方法

    Aravis 库编译方法 March 21, 2015 9:40 PM 首先下载 aravis 库的源代码:aravis 库下载地址 这里我们使用的是 aravis_0_2_0,比较老的一个版本. 首 ...

  7. 洛谷 P4546 & bzoj 5020 在美妙的数学王国中畅游 —— LCT+泰勒展开

    题目:https://www.luogu.org/problemnew/show/P4546 先写了个55分的部分分,直接用LCT维护即可,在洛谷上拿了60分: 注意各处 pushup,而且 spla ...

  8. bzoj 4006 管道连接 —— 斯坦纳树+状压DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4006 用斯坦纳树求出所有关键点的各种连通情况的代价,把这个作为状压(压的是集合选择情况)的初 ...

  9. NorthSJ项目零碎知识点

    1 div显示纵向滚动条: <div style="overflow-y:auto;overflow-x:hidden;"></div> 2 FineUI的 ...

  10. js---复选框(全选,不选,反选)demo1--

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/stri ...