浅议C#客户端和服务端通信的几种方法:Rest和GRPC和其他

本文来自:https://michaelscodingspot.com/rest-vs-grpc-for-asp-net/

浅议C#客户端和服务端通信的几种方法:Rest和GRPC

在C#客户端和C#服务器之间进行通信的方法有很多。一些功能强大,而其他功能则不是很多。有些非常快,有些则不是。知道不同的选择很重要,这样您才能决定最适合自己的选择。

本文将介绍当今最流行的技术,以及为何如此广泛地使用它们。我们将讨论REST,gRPC及其两者之间的所有内容。

最佳方案

让我们考虑一下我们希望如何在最佳环境中使客户端与服务器之间的通信看起来像。我在想像这样的东西:

// on client side
public void Foo()
{var server = new MyServer(new Uri("https://www.myserver.com/");)int sum = server.Calculator.SumNumbers(12,13); 
}
// on server side
class CalculatorController : Controller{public int SumNumbers(int a, int b){return a + b;}
}

我当然想要完整的Intellisense。当我单击server. 希望Visual Studio显示所有控制器时。当我单击CalculatorController和时.,我想查看所有操作。我还想要一流的性能,很少的网络负载和双向通信。而且我想要一个能够完美处理版本控制的强大系统,这样我就可以毫不费力地部署新的客户端版本和新的服务器版本。

要求太多吗?

请注意,我在这里谈论的是无状态API。这等效于C#项目,其中只有两种类型的类:

•静态类,只有静态方法。•POCO类[1]仅具有类型为基本类型或其他POCO类的字段和属性。

在API中使用状态会带来复杂性,而这正是万恶之源。因此,为了本文的方便,让我们保持美好和无状态。

传统REST

REST API出现在2000年代初期,席卷了整个互联网。到目前为止,它是创建Web服务的最流行的方法。

REST为客户端到服务器的请求定义了一组固定的操作GET,POST,PUTDELETE。每个请求都将通过包含有效负载(通常为JSON)的响应来回答。请求包含在查询本身中的参数,或者在它是POST请求时包含为有效负载(通常为JSON)的参数。

有一个称为RESTful API的标准,它定义了以下规则(您实际上不必使用它):

•GET用于检索资源•PUT用于更改资源状态•POST用于创建资源•DELETE用于删除资源

如果您到目前为止还不熟悉REST,则上面的解释可能不会减少它,因此这里有一个示例。在.NET中,内置了对REST的支持。实际上,默认情况下,ASP.NET Web API被构建为REST Web服务。这是典型的客户端和ASP.NET服务器的外观:

在服务器中:

[Route("People")]
public class PeopleController : Controller
{[HttpGet]public Person GetPersonById(int id){Person person = _db.GetPerson(id);return person;//Automatically serialized to JSON}
}   

在客户中:

var client = new HttpClient();
string resultJson = await client.GetStringAsync("https://www.myserver.com/People/GetPersonById?id=123");
Person person = JsonConvert.DeserializeObject<Person>(resultJson);

REST非常方便,但是并没有达到最佳方案。因此,让我们看看是否可以做得更好。

ReFit

ReFit不能替代REST。相反,它建立在REST之上,并允许我们像调用简单方法一样调用服务器端点。这是通过在客户端和服务器之间共享接口来实现的。在服务器端,您的控制器将实现一个接口:

public interface IMyEmployeeApi
{[Get("/employee/{id}")]Task<Employee> GetEmployee(string id);
}

然后,在客户端,您需要包括相同的接口并使用以下代码:

var api = RestService.For<IMyEmployeeApi>("https://www.myserver.com");
var employee = await api.GetEmployee("abc");

就这么简单。除了几个NuGet软件包外,无需运行困难的自动化程序或使用任何第三方工具。

这更接近最佳方案。现在,我们有了IntelliSense,并且客户端和服务器之间的合同很牢固。但是还有另一种选择,在某些方面甚至更好。

昂首阔步

像ReFit一样,Swagger也建立在REST之上。OpenAPI[2]Swagger是REST API的规范。它描述了具有简单JSON文件的REST Web服务。这些文件是Web服务的API架构。它们包括:

•API中的所有路径(URL)。•每个路径的预期操作(GET,POST等)。每个路径可以处理不同的操作。例如,单个路径https://mystore.com/Product可能接受添加产品的POST操作和返回产品的GET操作。•每个路径和操作的预期参数。•每个路径的预期响应。•每个参数和响应对象的类型。

该JSON文件实质上是客户端和服务器之间的合同。这是一个描述一个称为Swagger Petstore[3]的Web服务的swagger文件的示例(为清楚起见,我删除了一些部分):

{ "swagger":"2.0","info":{ "version":"1.0.0","title":"Swagger Petstore","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification",},"host":"petstore.swagger.io","basePath":"/api","schemes":[ "http"],"consumes":[ "application/json"],"produces":[ "application/json"],"paths":{ "/pets":{ "get":{ "description":"Returns all pets from the system that the user has access to","operationId":"findPets","produces":[ "application/json","application/xml",],"parameters":[ { "name":"tags","in":"query","description":"tags to filter by","required":false,"type":"array","items":{ "type":"string"},"collectionFormat":"csv"},{ "name":"limit","in":"query","description":"maximum number of results to return","required":false,"type":"integer","format":"int32"}],"responses":{ "200":{ "description":"pet response","schema":{ "type":"array","items":{ "$ref":"#/definitions/Pet"}}},
...

让我们考虑一下这个结果。使用上面的JSON文件,您可以潜在地创建具有完整IntelliSense的C#客户端。毕竟,您知道所有路径,操作,它们期望的参数,什么参数类型,什么是响应等等。

有几种工具可以做到这一点。对于服务器端,可以使用Swashbuckle.AspNetCore[4]将Swagger添加到ASP.NET中并生成所述JSON文件。对于客户端,您可以使用swagger-codegen[5]和AutoRest[6]来使用这些JSON文件并生成客户端。让我们看一个如何做到这一点的例子:

将Swagger添加到ASP.NET服务器

首先添加NuGet包Swashbuckle.AspNetCore[7]。在中ConfigureServices,注册Swagger生成器:

services.AddSwaggerGen(options => options.SwaggerDoc("v1", new OpenApiInfo {Title = "My Web API", Version = "v1"}));

在添加Configure方法中Startup.cs

app.UseSwagger();

最后,控制器内部的动作应使用[HttpXXX][FromXXX]属性修饰:

[HttpPost]
public async Task AddEmployee([FromBody]Employee employee)
{//...
}[HttpGet]
public async Task<Employee> Employee([FromQuery]string id)
{//...
}

就像服务器端一样简单。运行项目时,swagger.json将生成一个文件,可用于生成客户端。

使用AutoRest从Swagger生成客户端

要开始使用AutoRest[8],与安装NPM[9]npm install -g autorest。安装后,您将需要使用AutoRest的命令行界面从该swagger.json文件生成C#客户端。这是一个例子:

autorest --input-file="./swagger.json" --output-folder="GeneratedClient" --namespace="MyClient" --override-client-name="MyClient" --csharp

这将产生一个GeneratedClient包含生成的C#文件的文件夹。请注意,名称空间和客户端名称被覆盖。从这里,将此文件夹添加到Visual Studio中的客户端项目。

您需要安装Microsoft.Rest.ClientRuntimeNuGet软件包,因为生成的代码取决于该软件包。安装后,您可以像使用常规C#类一样使用API:

var client = new MyClient();
Employee employee = client.Employee(id: "abc");

您可以在AutoRest的文档中[10]阅读一些细微之处。而且您需要使该过程自动化,因此我建议阅读Patrik Svensson的教程,[11]以获得一些好的建议以及Peter Jausovec的这篇文章[12]

Swagger的问题是JSON文件是在运行时创建的,因此这使得在CI / CD流程中实现自动化有点困难。

传统REST vs Swagger vs ReFit

进行选择时,请注意以下几点。

•如果您有一个非常简单的私有REST API,则也许不必理会客户端生成和共享接口。小任务并不能证明付出额外的努力是合理的。•Swagger支持多种语言,而ReFit仅支持.NET。Swagger还是许多工具,测试,自动化和UI工具的基础。如果您要创建一个大型的公共API,它将可能是最佳选择。•Swagger比ReFit复杂得多。使用ReFit,只需在服务器和客户端项目中添加一个接口即可。另一方面,使用ReFit,您必须为每个控制器创建新的接口,而Swagger会自动进行处理。

但是在决定任何事情之前,请检查与REST无关的第四个选项。

gRPC

gRPC[13]gRPC远程过程调用)是Google开发的开源远程过程调用系统。它有点像REST,它提供了一种将请求从客户端发送到服务器的方式。但这在许多方面都不同,这是相同点和不同点:

•像REST一样,gRPC与语言无关。有适用于所有流行语言的工具,包括C#。•gRPC是契约的基础,并使用.proto文件来定义契约。这有点类似于Swaggerswagger.json和ReFit的共享界面。可以从那些文件中生成任何编程语言的客户端。•gRPC使用协议缓冲区(Protobuf)[14]二进制序列化。这与REST(通常序列化为JSON或XML)不同。二进制序列化较小,因此更快。•gRPC用于使用HTTP / 2协议创建持久连接。该协议更简单,更紧凑。REST使用HTTP 1.x协议(通常为HTTP 1.1)。•HTTP 1.1要求每个请求都进行TCP握手,而HTTP / 2则保持连接打开。•HTTP / 2连接使用多路复用流。这意味着单个TCP连接可以支持许多流。这些流可以并行执行,而不必像HTTP 1.1中那样互相等待。•gRPC允许双向流。

有两种使用gRPC的方法。对于.NET Core 3.0,有一个完全托管的库,称为.NET的gRPC[15]。对于其中的任何内容,您都可以使用gRPC C#[16],它是使用本机代码构建的。这并不意味着适用于.NET的gRPC可以替代gRPC C#。让我们来看一个用于.NET的更新gRPC的示例。

.NET的gRPC的服务器端

这不是教程,而是更多有关预期内容的一般性想法。这是示例控制器在gRPC中的外观:

public class GreeterService : Greeter.GreeterBase
{public override Task<HelloReply> SayHello(HelloRequest request,ServerCallContext context){_logger.LogInformation("Saying hello to {Name}", request.Name);return Task.FromResult(new HelloReply {Message = "Hello " + request.Name});}
}

您需要添加以下的ConfigureStartup.cs

app.UseEndpoints(endpoints =>
{endpoints.MapGrpcService<GreeterService>();
});

API在.proto文件中描述,该文件是项目的一部分:

syntax = "proto3";service Greeter {rpc SayHello (HelloRequest) returns (HelloReply);
}message HelloRequest {string name = 1;
}message HelloReply {string message = 1;
}

.proto文件添加到.csproj

<ItemGroup><Protobuf Include="Protos\greet.proto" />
</ItemGroup>

.NET的gRPC客户端

客户端是从.proto文件生成的。代码本身非常简单:

var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greeter.GreeterClient(channel);var response = await client.SayHello(new HelloRequest { Name = "World" });Console.WriteLine(response.Message);

gRPC与REST

gRPC听起来不错。它在框架下更快,更简单。那么,我们都应该从REST变为gRPC吗?答案是,这取决于你的应用场景。

以下是一些注意事项:

从我的印象来看,使用gRPC和ASP.NET仍然不是很好。借助对REST的成熟支持,您会变得更好。就基于契约的通信而言,这很不错,除了在REST中有我们已经讨论过的类似替代方案:Swagger和ReFit。

最大的优势是性能。根据这些基准[17],在大多数情况下,gRPC更快。特别是对于大型有效载荷,Protobuf序列化确实有所作为。这意味着对于高负载服务器而言,这是一个巨大的优势。

在大型ASP.NET应用程序中从REST过渡到gRPC将非常困难。但是,如果您具有基于微服务的体系结构,那么逐步完成此过渡就变得容易得多。

其他沟通方式

还有其他一些我完全没有提及的通信方式,但是值得一提的是:

•GraphQL[18]是Facebook开发的API的查询语言。它允许客户端从服务器确切地要求它需要的数据。这样,您可以在服务器上仅创建一个端点,该端点将非常灵活,并且仅返回客户端所需的数据。近年来,GraphQL变得非常流行。•SignalR[19]是一项允许服务器与客户端之间进行实时双向通信的技术。SignalR不仅允许客户端始终向服务器发送请求,还允许服务器向客户端发送推送通知。这样可以查看Web应用程序中的实时更新。SignalR在ASP.NET中非常流行。•TcpClient[20]和TcpListener[21](在中System.Net.Sockets)提供基于TCP的低级连接。基本上,您将建立连接并传输字节数组。对于大型应用程序而言,它不是理想的选择,在大型应用程序中,您可以使用ASP.NET的控制器和操作在大型API中进行订购。•UdpClient[22]提供了一种通过UDP协议进行通信的方法。TCP建立连接,然后发送数据,而UDP仅发送数据。TCP确保数据中没有错误,而UDP没有。UDP可以更有效地快速传输数据,您不必担心它是否可靠且没有错误。一些示例是:视频流,实时广播和IP语音(VoIP)。•WCF[23]是一种较旧的技术,主要在进程之间使用基于SOAP的通信。这是一个庞大的框架,我要说的是它已不再受REST和JSON负载的欢迎。

References

[1] POCO类: https://www.c-sharpcorner.com/UploadFile/5d065a/poco-classes-in-entity-framework/
[2] OpenAPI: https://swagger.io/specification/
[3] Swagger Petstore: https://bfanger.nl/swagger-explained/#operationObject
[4] Swashbuckle.AspNetCore: https://github.com/domaindrivendev/Swashbuckle.AspNetCore
[5] swagger-codegen: https://github.com/swagger-api/swagger-codegen
[6] AutoRest: https://azure.github.io/autorest/
[7] Swashbuckle.AspNetCore: https://www.nuget.org/packages/Swashbuckle.AspNetCore
[8] AutoRest: https://github.com/Azure/autorest
[9] NPM: https://www.w3schools.com/nodejs/nodejs_npm.asp
[10] 文档中: https://azure.github.io/autorest/client/ops.html
[11] 教程,: https://www.patriksvensson.se/2018/10/generating-api-clients-using-autorest
[12] 文章: https://medium.com/@pjausovec/creating-c-client-library-for-web-api-projects-be132c831f9c
[13] gRPC: https://grpc.io/
[14] 协议缓冲区(Protobuf): https://en.wikipedia.org/wiki/Protocol_Buffers
[15] .NET的gRPC: https://github.com/grpc/grpc-dotnet
[16] gRPC C#: https://github.com/grpc/grpc/tree/master/src/csharp
[17] 根据这些基准: https://www.yonego.com/nl/why-milliseconds-matter/#gref
[18] GraphQL: https://graphql.org/
[19] SignalR: https://github.com/SignalR/SignalR
[20] TcpClient: https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.tcpclient?view=netframework-4.8
[21] TcpListener: https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.tcplistener?view=netframework-4.8
[22] UdpClient: https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.udpclient?view=netframework-4.8
[23] WCF: https://docs.microsoft.com/en-us/dotnet/framework/wcf/whats-wcf

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/307357.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode106. 从中序与后序遍历序列构造二叉树(java详解版)

一:题目 二:上码 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* …

COSCon’20开源教育论坛介绍

点击上方“开源社”关注我们| 作者&#xff1a;周添一| 编辑&#xff1a;沈于蓝| 设计&#xff1a;冯艺怡| 责编&#xff1a;王皓月论坛简介在当今数据驱动的智能社会&#xff0c;软件作为数字智能社会的数据基础设施&#xff0c;承载了社会的高效运转。在这个软件定义世界的潮…

leetcode105. 从前序与中序遍历序列构造二叉树

一:题目 二:上码 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* …

想进BAT等大厂,到底要怎么做?

职场&认知洞察 丨 作者 / findyi这是findyi公众号的第82篇原创文章最近我的公众号多了很多学生读者&#xff0c;很多人问我&#xff1a;如果要进BAT等大厂&#xff0c;我是应该直接工作还是考研&#xff1f;对于未来要从事程序员行业的朋友来说&#xff0c;要不要考研&…

.NET 5.0 RC 2 发布,正式版将在 11 月 .NET Conf 大会上发布

原文&#xff1a;http://dwz.win/ThX作者&#xff1a;Richard翻译&#xff1a;精致码农-王亮说明&#xff1a;1. 本译文舍弃了少许我实在不知道如何翻译但又不是很重要的语句。2. 本文有不少超链接&#xff0c;由于微信公众号和头条平台外链会被剔除 URL 地址&#xff0c;所以原…

leetcode216. 组合总和 III

一:题目 二:上码 class Solution {/**递归深度根据k值确定,宽度的话那就[1,9]因为递归不允许重复&#xff0c;那么的话我们需要每次在横向遍历的起始位置1*/private List<List<Integer>> ans new ArrayList<>();private List<Integer> path new Arr…

BeetleX之Websocket协议分析详解

Websocket应用协议已经普及多年了&#xff0c;它是HTTP1.1的内部升级协议&#xff0c;主要作用是补充HTTP1.1无法灵活地主动推送消息给客户端的缺陷问题。在这里主要介绍一下使用组件如何扩展一个完整的Websocket协议。协议介绍Websocket并不复杂&#xff0c;但协议文档内容还是…

甲骨文是否可以要求 Java API 享有版权?这场10年官司怎么结

美国最高法院10月7日就 Oracle 甲骨文诉 Google 谷歌一案进行口头辩论。案件一直以来争议的核心是&#xff0c;甲骨文是否可以要求 Java API 享有版权&#xff0c;如果可以&#xff0c;那么谷歌是否侵犯了这些版权&#xff1f;Java API 是否可以享有版权&#xff1f;Sun 公司在…

C#知多少 | 每个版本都更新了什么?

总所周知&#xff0c;.NET5.0马上就要来了&#xff0c;最后一个预览版RC2也已经发布了&#xff0c;在11月的时候&#xff0c;我们就正式的发布了&#xff0c;然后我们就可以迁移使用了&#xff0c;当然今天说的重点不是.NET&#xff0c;今天说的是伴随着.NET5一起到来的C#9.0&a…

leetcode40. 组合总和 II(树层去重)

一:题目 二:上码 class Solution {/**注意这里的去重:1.我们分为树层去重比如[1,1 2,5] target 8,那么如果我们用树枝去重的话,那么就会出现[1,2,5],[1,2,5]那么的话我们就需要树层去重 就是横向遇见相同的元素跳过。2.树枝去重的 树枝去重就是 我们数组前面使用过的元素不…

WPF开源控件扩展库 - MaterialDesignExtensions

WPF开源控件扩展库 - MaterialDesignExtensionsMaterialDesignExtensions仓库截图logoMaterial Design Extensions 在WPF开源控件库 ????Material Design in XAML Toolkit(本站介绍&#xff1a;????链接)的基础上进行了控件扩展和特性新增。本开源项目中的控件或许不在…

leetcode131. 分割回文串

一:题目 二:上码 class Solution {/**思路:1.纵向递归的话 我们递归深度是 我们的 切割线切到了最后 2.横向for 我们的宽度就是字符串宽度3.在哪截取的话 那就是在哪个坐标的后面*/List<List<String>> ans new ArrayList<>();List<String> path n…

WordPress 已过时?创始人与新架构拥护者开战

喜欢就关注我们吧&#xff01;近日&#xff0c;WordPress 创始人 Matt Mullenweg 与 Netlify 首席执行官 Matt Biilmann 展开了一场口水战&#xff0c;双方就 WordPress 与新兴 WEB 应用架构 JAMStack 孰优孰劣进行了激烈的辩论。何为 JAMStack?JAMStack 是一种将 Web 应用部署…

leetcode93. 复原 IP 地址(思路+详解)

一:题目 二:上码 class Solution {/**思路: 1.跟分割字符串差不多,但是我们需要记录添加的.的个数 当其个数等于3的时候 我们就已经切割成4段了。2.判断字符是否合法1>:以0开头的数字不合法2>:遇见非数字字符不合法3>:大于255不合法*/private List<String> an…

今天的你,又被带节奏了吗?

这是头哥侃码的第220篇原创十一期间&#xff0c;我在某微信群看到几个哥们在讨论 与「带节奏」有关的话题。很显然&#xff0c;像这种略带吐槽的的话题是很容易引起共鸣的。于是你一句他一句的&#xff0c;用自带表情包的方式开始说起了「带节奏」的各种操作。先是A君&#xff…

谷歌最终还是把Knative交了出来

谷歌方面正计划将其开源 Knative 项目的直接控制权让渡给一个指导委员会。Knative 是谷歌开源的一套 Serverless 架构方案&#xff0c;它扩展了 Kubernetes&#xff0c;专注于解决容器为核心的 Serverless 应用的构建、部署和运行的问题&#xff0c;它构建在 Kubernetes 和 Ist…

你那么普通 所以一定要自信啊

你这么普通&#xff0c;必须自信啊 ​ 昨天讲到&#xff0c;不管你准没准备好&#xff0c;该来的都来了。只要你进入了社会&#xff0c;这一切都没得选&#xff0c;好的坏的甭管啥事&#xff0c;都一股脑摆在你面前&#xff0c;你必须要处理。你说我不行啊&#xff0c;我做不到…

leetcode293周赛6064. 不含特殊楼层的最大连续楼层数

一:题目 二:上码 // class Solution { // public:// bool find(vector<int>& v,int i) { // for (auto nums:v) { // if (nums i) return true;//包含某个数 就返回true // } // return false; // }// int maxCon…

leetcode293周赛5234. 移除字母异位词后的结果数组

一:题目 二:上码 class Solution { public:/**1.所谓字母异位词就是相同的字母组成的字符串&#xff08;这个字符串可以相同&#xff09;2.首先判断两个字符串中的字母是否相同用map进行计数&#xff0c;然后在另一个字符串中查找某个字符进行--;3.如果最终的map中所有的value…

七国要求科技巨头预留后门 应对马甲芯片高度警惕

日前&#xff0c;美国、英国、日本、澳大利亚、加拿大、新西兰、印度呼吁科技公司设计产品时确保政府可以访问经过加密的消息和内容。其实&#xff0c;西方科技公司配合西方政府在产品中预留后门早已不是新鲜事&#xff0c;斯诺登就曾经爆料多个美国科技巨头配合美国情报部门收…