.Net Core 扩展使用Refit
.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的更多相关文章
- 自动注册服务NET Core扩展IServiceCollection
NET Core扩展IServiceCollection自动注册服务 前言 在ASP.NET Core中使用依赖注入中使用很简单,只需在Startup类的ConfigureServices()方法中, ...
- Orchard Module,Theme,Core扩展加载概述
Orchard 源码探索(Module,Theme,Core扩展加载概述) 参考: http://www.orchardch.com/Blog/20120830071458 1. host.Initi ...
- ServiceStack.Redis 的 ASP.NET Core 扩展库
给大家安利一款 ServiceStack.Redis 的 ASP.NET Core 扩展库,它是基于 ServiceStack.Redis.Core 开发的. 简单易用,开源免费,使用ASP.NET ...
- Log4net 的 ASP.NET Core 扩展库
给大家安利一款 log4net 的 ASP.NET Core 扩展库,它是基于 log4net 开发的. 简单易用,开源免费,使用ASP.NET Core自身提供的DI容器来实现服务的注册和消费.直接 ...
- ASP.NET Core扩展库
亲爱的.Neter们,在我们日复一日的编码过程中是不是会遇到一些让人烦恼的事情: 日志配置太过复杂,各种模板.参数也搞不清楚,每次都要去查看日志库的文档,还需要复制粘贴一些重复代码,好无赖 当需要类型 ...
- ASP.NET Core扩展库之日志
上一篇我们对Xfrogcn.AspNetCore.Extensions扩展库功能进行了简单的介绍,从这一篇文章开始,我将逐步介绍扩展库中的核心功能. 日志作为非业务的通用领域基础功能, ...
- .NET Core扩展IServiceCollection自动注册服务
前言 在ASP.NET Core中使用依赖注入中使用很简单,只需在Startup类的ConfigureServices()方法中,通过IServiceCollection接口进行注入即可,其它的无需关 ...
- Orchard 源码探索(Module,Theme,Core扩展加载概述)
参考: http://www.orchardch.com/Blog/20120830071458 1. host.Initialize(); private static IOrchardHost H ...
- .Net Core扩展 SharpPlugs简单上手
SharpPlugs .Net Core 鋒利扩展,这是本人的开源项目 地址是 GitHub地址 大家喜欢 的话可以加个星哦 当前功能 DI AutoMapper ElasticSearch WebA ...
随机推荐
- 一款实现滑动切换效果的插件---swiper
Swiper是纯javascript打造的滑动特效插件,面向手机.平板电脑等移动终端. Swiper能实现触屏焦点图.触屏Tab切换.触屏多图切换等常用效果. Swiper开源.免费.稳定.使用简单. ...
- Jquery 取值,赋值学习总结
<h2>获取和设置文本框值:</h2> <input type="button" value="赋值文件框" id="v ...
- 【ACM-ICPC 2018 徐州赛区网络预赛】D.Easy Math 杜教筛
代码 #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 20000000; ...
- 一个类的类类型是Class类的实例,即类的字节码
new 是静态加载类,编译时期加载.一遍功能性的类 需要动态加载
- L103
Give everyday the chance to become the most beautiful day of your life.把每天都过成你生命中最美好的一天.competence 能 ...
- python实现无序列表:链表
介绍链表前我们先了解下什么是列表. 在对基本数据结构的讨论中,我们使用 Python 列表来实现所呈现的抽象数据类型.列表是一个强大但简单的收集机制,为程序员提供了各种各样的操作.然而,不是所有的编程 ...
- bzoj1002轮状病毒
高精度练习题 根据什么什么基尔霍夫矩阵 反正就是高精度练习 #include<iostream> #include<cstdio> using namespace std; s ...
- Unity Webplayer installation error- Unity Webplayer update finished, but installed..
https://forum.unity3d.com/threads/unity-webplayer-installation-error-unity-webplayer-update-finished ...
- 【转】 Pro Android学习笔记(三四):Menu(5):动态菜单
目录(?)[-] OptionsMenu的创建方式 如何再次创建OptionsMenu 每次访问都重新填充菜单项 OptionsMenu的创建方式 OptionMenu在第一次访问该菜单时调用,只调用 ...
- makefile 基础知识
$@ 目标文件名 $< 第一个依赖文件名 $^ 规则所有依赖文件列表 如果不想让执行语句被打印出来,就在语句前面加上@符号 模式规则 %.o:%.c 后缀规则 .c.o 生成单进程的Mak ...