经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C# » 查看文章
【c#版本Openfeign】Net8 自带OpenFeign实现远程接口调用
来源:cnblogs  作者:四处观察  时间:2023/9/25 16:50:01  对本文有异议

引言

    相信巨硬,我们便一直硬。Net版本到现在已经出了7了,8也已经在预览版了,相信在一个半月就会正式发布,其中也有很多拭目以待的新功能了,不仅仅有Apm和Tap的结合,TaskToAscynResult,以及UnsafeAccessor用来获取私有变量,性能比反射,EMIT更高,还有针对AsyncLocal封装的IAsyncContext,IAsyncState,用来存异步上下文的一些数据,当然了,最让我期待的还是自带了一个OpenFeign,在看新增的东西的时候,其他的都觉得一般般,个人觉得哈,当看到这个AutoClient新增的包的时候,好奇心的驱使下,我点进去看了一下,哇,官网终于出这玩意了,使用简单,根据特性,然后使用Sg来生成我们对应的实现从而我们只需要定义一个接口,打上特性,就可以生成一个对应的代理类,调用远程Api接口,太令人心动,为此特地升级了VS,下载了Net8,体验新功能,接下来,我们就看看他的使用案例。附官网链接:https://learn.microsoft.com/zh-cn/dotnet/api/microsoft.extensions.http.autoclient.autoclientattribute?view=dotnet-plat-ext-8.0

AutoClient

    在使用自带的OpenFeign的时候,我们还需要下载一个扩展包 Microsoft.Extensions.Http.AutoClient,当然还有 Microsoft.Extensions.Http的扩展包了,接下来我们定义一个接口,IBussiness,打上AutoClient特性,第一个参数是我们在注入Httpclient的时候,给的名字,我这里叫TestApi,这里会根据使用了AutoClient特性自定生成一个BussIness的类,在下图可以看到,自动生成了一个AutoClient.g.cs文件,里面的类就是Bussiness,其中包括了我们的TestPost方法以及路由信息,在上面的代码中,我们使用了Post特性,代表我们这个是Post请求,以及方法参数限制必须有一个CancellationToken,这个Post里面的内容,就是我另外一个项目种的接口地址。

  

  1. builder.Services.AddHttpClient("TestApi",s=>s.BaseAddress=new Uri(" http://localhost:5062"));
    [AutoClient("TestApi")]
  2. public interface IBussiness
  3. {
  4. [Post("/Test/TestPost")]
  5. public Task<string> TestPost(CancellationToken cancellationToken);
  6. }

 

  1. // <auto-generated/>
  2. #nullable enable
  3. #pragma warning disable CS1591 // Compensate for https://github.com/dotnet/roslyn/issues/54103
  4.  
  5. namespace WebApplication1.Api
  6. {
  7. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Gen.AutoClient", "8.0.0.0")]
  8. public class Bussiness : IBussiness
  9. {
  10. private static class Statics
  11. {
  12. public static readonly global::System.Net.Http.Headers.MediaTypeHeaderValue ApplicationJsonHeader = new("application/json")
  13. {
  14. CharSet = global::System.Text.Encoding.UTF8.WebName
  15. };
  16. public static readonly global::System.Net.Http.Headers.MediaTypeHeaderValue TextPlainHeader = new("text/plain")
  17. {
  18. CharSet = global::System.Text.Encoding.UTF8.WebName
  19. };
  20. public static readonly global::System.Uri UriTestPost = new("/Test/TestPost", global::System.UriKind.Relative);
  21. public static readonly global::Microsoft.Extensions.Http.Telemetry.RequestMetadata RequestMetadataTestPost = new()
  22. {
  23. DependencyName = "Bussiness",
  24. RequestName = "TestPost",
  25. RequestRoute = "/Test/TestPost"
  26. };
  27. }
  28. private readonly global::System.Net.Http.HttpClient _httpClient;
  29. private readonly global::Microsoft.Extensions.Http.AutoClient.AutoClientOptions _autoClientOptions;
  30. public Bussiness(global::System.Net.Http.HttpClient httpClient, global::Microsoft.Extensions.Http.AutoClient.AutoClientOptions autoClientOptions)
  31. {
  32. _httpClient = httpClient;
  33. _autoClientOptions = autoClientOptions;
  34. }
  35. public async global::System.Threading.Tasks.Task<string> TestPost(global::System.Threading.CancellationToken cancellationToken)
  36. {
  37. var httpRequestMessage = new global::System.Net.Http.HttpRequestMessage()
  38. {
  39. Method = global::System.Net.Http.HttpMethod.Post,
  40. RequestUri = Statics.UriTestPost,
  41. };
  42. try
  43. {
  44. global::Microsoft.Extensions.Telemetry.TelemetryExtensions.SetRequestMetadata(httpRequestMessage, Statics.RequestMetadataTestPost);
  45. return await SendRequest<string>("Bussiness", Statics.RequestMetadataTestPost.RequestRoute, httpRequestMessage, cancellationToken)
  46. .ConfigureAwait(false);
  47. }
  48. finally
  49. {
  50. httpRequestMessage.Dispose();
  51. }
  52. }
  53. private async global::System.Threading.Tasks.Task<TResponse> SendRequest<TResponse>(
  54. string dependencyName,
  55. string path,
  56. global::System.Net.Http.HttpRequestMessage httpRequestMessage,
  57. global::System.Threading.CancellationToken cancellationToken)
  58. where TResponse : class
  59. {
  60. var response = await _httpClient.SendAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false);
  61. if (typeof(TResponse) == typeof(global::System.Net.Http.HttpResponseMessage))
  62. {
  63. return (response as TResponse)!;
  64. }
  65. try
  66. {
  67. if (!response.IsSuccessStatusCode)
  68. {
  69. var error = await global::Microsoft.Extensions.Http.AutoClient.AutoClientHttpError.CreateAsync(response, cancellationToken).ConfigureAwait(false);
  70. throw new global::Microsoft.Extensions.Http.AutoClient.AutoClientException(global::System.FormattableString.Invariant($"The '{dependencyName}' HTTP client failed with '{response.StatusCode}' status code."), path, error);
  71. }
  72. if (typeof(TResponse) == typeof(string))
  73. {
  74. #if NET5_0_OR_GREATER
  75. var rawContent = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
  76. #else
  77. cancellationToken.ThrowIfCancellationRequested();
  78. var rawContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
  79. #endif
  80.  
  81. return (rawContent as TResponse)!;
  82. }
  83. var mediaType = response.Content.Headers.ContentType?.MediaType;
  84. if (mediaType == "application/json")
  85. {
  86. var deserializedResponse = await global::System.Net.Http.Json.HttpContentJsonExtensions.ReadFromJsonAsync<TResponse>(response.Content, _autoClientOptions.JsonSerializerOptions, cancellationToken)
  87. .ConfigureAwait(false);
  88. if (deserializedResponse == null)
  89. {
  90. var error = await global::Microsoft.Extensions.Http.AutoClient.AutoClientHttpError.CreateAsync(response, cancellationToken).ConfigureAwait(false);
  91. throw new global::Microsoft.Extensions.Http.AutoClient.AutoClientException(global::System.FormattableString.Invariant($"The '{dependencyName}' REST API failed to deserialize response."), path, error);
  92. }
  93. return deserializedResponse;
  94. }
  95. var err = await global::Microsoft.Extensions.Http.AutoClient.AutoClientHttpError.CreateAsync(response, cancellationToken).ConfigureAwait(false);
  96. throw new global::Microsoft.Extensions.Http.AutoClient.AutoClientException(global::System.FormattableString.Invariant($"The '{dependencyName}' REST API returned an unsupported content type ('{mediaType}')."), path, err);
  97. }
  98. finally
  99. {
  100. response.Dispose();
  101. }
  102. }
  103. }
  104. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Gen.AutoClient", "8.0.0.0")]
  105. public static class AutoClientsExtensions
  106. {
  107. public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddBussiness(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services)
  108. {
  109. return services.AddBussiness(_ => { });
  110. }
  111. public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddBussiness(
  112. this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services,
  113. global::System.Action<global::Microsoft.Extensions.Http.AutoClient.AutoClientOptions> configureOptions)
  114. {
  115. global::Microsoft.Extensions.DependencyInjection.OptionsServiceCollectionExtensions.AddOptionsWithValidateOnStart<global::Microsoft.Extensions.Http.AutoClient.AutoClientOptions, global::Microsoft.Extensions.Http.AutoClient.AutoClientOptionsValidator>(services, "Bussiness").Configure(configureOptions);
  116. global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAddSingleton<IBussiness>(services, provider =>
  117. {
  118. var httpClient = global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService<global::System.Net.Http.IHttpClientFactory>(provider).CreateClient("TestApi");
  119. var autoClientOptions = global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService<global::Microsoft.Extensions.Options.IOptionsMonitor<global::Microsoft.Extensions.Http.AutoClient.AutoClientOptions>>(provider).Get("Bussiness");
  120. return new Bussiness(httpClient, autoClientOptions);
  121. });
  122. return services;
  123. }
  124. }
  125. }

 

  下面这段代码,是我另一个项目接口的代码,可以看到,路由是Test,方法的路由是TestPost,返回了一个字符串true,因为,在使用AutoClient的时候,返回类型必须是引用类型,接下来,我们调用一下测试看看,在返回的结果中,我们可以看到返回了我们在另一个项目中返回的结果,true,同时,AutoClient还支持Get,Patch,Delete,Get,Put,Body(标记是在Body中),Header,Query等诸多特性,就是一个c#版本的OpenFeign,简直爽的不要不要的。

  1. [Route("Test")]
  2. public class TestController : ControllerBase
  3. {
  4. public TestController()
  5. {
  6. }
  7. [HttpPost("TestPost")]
  8. public Task<string> TestPost()
  9. {
  10. return Task.FromResult("true");
  11. }
  12. }

 

 结语

    今天就要开始十月一假期了,后续节后来了,会持续带来新增api的一些玩法,包括IAsyncContext,还有其他的在等待探索,欢迎大家关注。

原文链接:https://www.cnblogs.com/1996-Chinese-Chen/p/17724792.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号