C#客户端与C#服务端通信的方式有很多种。有些很强大,有些则不那么强大。有些速度非常快,有些则不是。了解不同的选项很重要,这样您才能决定哪一个最适合您。本文将介绍当今最流行的技术以及它们为何被如此广泛地使用。我们将讨论REST、gRPC以及介于两者之间的所有内容。最佳场景让我们考虑一下我们希望客户端和服务器之间的通信在最佳环境中看起来如何。我在想这样的事情://onclientsidepublicvoidFoo(){varserver=newMyServer(newUri("https://www.myserver.com/");)intsum=server.Calculator.SumNumbers(12,13)??;}//onserversideclassCalculatorController:Controller{publicintSumNumbers(inta,intb){returna+b;}}我当然想要完整的Intellisense。当我点击服务器时。希望VisualStudio显示所有控制器。我想在单击CalculatorController和时查看所有操作。我还想要一流的性能、低网络负载和双向通信。而且我想要一个能够完美处理版本控制的强大系统,以便我可以毫不费力地部署新的客户端版本和新的服务器版本。要问的问题太多了?请注意,我在这里谈论的是无状态API。这相当于一个C#项目,其中只有两种类型的类:?静态类,只有静态方法。?POCO类[1]只有原始类型或其他POCO类的字段和属性。在API中使用状态会带来复杂性,这是万恶之源。因此,为了本文的缘故,让我们保持良好和无状态。传统的RESTRESTAPI出现于2000年代初,风靡整个互联网。它是迄今为止最流行的创建Web服务的方法。REST为客户端到服务器的请求定义了一组固定的操作GET、POST、PUT和DELETE。每个请求都将得到一个包含有效负载(通常是JSON)的响应。请求参数包含在查询本身中,如果是POST请求,则作为有效负载(通常是JSON)。有一个称为RESTfulAPI的标准,它定义了以下规则(您实际上不必使用它):GET用于检索资源PUT用于更改资源的状态POST用于创建资源DELETE是用于删除资源如果到目前为止您已经了解了如果您是REST的新手,上面的解释可能不会削减它,所以这里有一个例子。在.NET中,内置了对REST的支持。事实上,ASP.NETWebAPI默认构建为RESTWeb服务。这是典型的客户端和ASP.NET服务器的样子:在服务器中:[Route("People")]publicclassPeopleController:Controller{[HttpGet]publicPersonGetPersonById(intid){Personperson=_db.GetPerson(id);returnperson;//AutomaticallyserializedtoJSON}}在客户端:varclient=newHttpClient();stringresultJson=awaitclient.GetStringAsync("https://www.myserver.com/People/GetPersonById?id=123");Personperson=JsonConvert.DeserializeObject(resultJson);REST非常方便,但不是最优的。所以让我们看看我们是否可以做得更好。ReFitReFit不能替代REST。相反,它建立在REST之上,允许我们像调用简单方法一样调用服务器端点。这是通过在客户端和服务器之间共享接口来实现的。在服务器端,您的控制器将实现一个接口:publicinterfaceIMyEmployeeApi{[Get("/employee/{id}")]TaskGetEmployee(stringid);}然后,在客户端,您需要包含相同的界面并使用此代码:varapi=RestService.For("https://www.myserver.com");varemployee=awaitapi.GetEmployee("abc");就这么简单。除了几个NuGet包之外,无需运行困难的自动化或使用任何第3方工具。这更接近最优解。现在我们有了IntelliSense,客户端和服务器之间的契约很牢固。但还有另一种选择,在某些方面甚至更好。Swagger与ReFit一样,Swagger也是建立在REST之上的。OpenAPI[2]或Swagger是RESTAPI的规范。它使用简单的JSON文件描述RESTWeb服务。这些文件是Web服务的API模式。他们包括:?API中的所有路径(URL)。?每个路径的预期操作(GET、POST等)。每个路径可以处理不同的操作。例如,单个路径https://mystore.com/Product可能接受POST操作以添加产品和GET操作以返回产品。每个路径和操作的预期参数。每条路径的预期响应。每个参数和响应对象的类型。这个JSON文件本质上是客户端和服务器之间的契约。这是一个swagger文件的示例,它描述了一个名为SwaggerPetstore[3]的Web服务(为了清楚起见,我删除了一些部分):{"swagger":"2.0","info":{"version":"1.0.0","title":"SwaggerPetstore","description":"AsampleAPIthatusesapetstoreasanexampletodemonstratefeaturesintheswagger-2.0specification",},"host":"petstore.swagger.io","basePath":"/api","schemes":["http"],"consumes":["application/json"],"produces":["application/json"],"paths":{"/pets":{"get":{"description":"从用户有权访问的系统返回所有宠物""operationId":"findPets","produces":["application/json","application/xml",],"parameters":[{"name":"tags","in":"query",“描述”:“tagstofilterby","re??quired":false,"type":"array","items":{"type":"string"},"collectionFormat":"csv"},{"name":"limit","在“:”查询“,”描述“:”maximumnumberofresultstoreturn“,”必需“:假,”类型“:”整数“,”格式“:”int32“}],”响应“:{“200”:{”description":"petresponse","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",newOpenApiInfo{Title="MyWebAPI",Version="v1"}));在Startup.cs中添加Configure方法:app.UseSwagger();最后,控制器内的动作应该用[HttpXXX]和[FromXXX]属性修饰:){//...}就像服务器端一样简单。运行项目时,swagger.json会生成一个文件,可以用来生成客户端。使用AutoRest从Swagger生成客户端要开始使用AutoRest[8],请安装NPM[9]:npminstall-gautorest。安装后,您将需要使用AutoRest的命令行界面从这个swagger.json文件生成一个C#客户端。这是一个例子:autorest--input-file="./swagger.json"--output-folder="GeneratedClient"--namespace="MyClient"--override-client-name="MyClient"--csharpthis将生成一个包含生成的C#文件的GeneratedClient文件夹。请注意,名称空间和客户端名称已被覆盖。从此处,将此文件夹添加到VisualStudio中的客户端项目。您需要安装Microsoft.Rest.ClientRuntimeNuGet包,因为生成的代码依赖于它。安装后,您可以像使用常规C#类一样使用API:varclient=newMyClient();Employeeemployee=client.Employee(id:"abc");您可以在AutoRest的文档[10]中阅读一些细微之处。您需要使该过程自动化,因此我建议阅读PatrikSvensson的教程[11]以获得一些好的建议,以及PeterJausovec的这篇文章[12]。Swagger的问题在于JSON文件是在运行时创建的,因此这使得在CI/CD过程中实现自动化有点困难。在传统REST与Swagger与ReFit之间进行选择时,请注意以下几点。如果您有一个非常简单的私有RESTAPI,也许不必为客户端生成和共享接口而烦恼。小任务并不能证明额外的努力是值得的。Swagger支持多种语言,而ReFit只支持.NET。Swagger也是许多工具、测试、自动化和UI工具的基础。如果您要创建大型公共API,它可能是最佳选择。Swagger比ReFit复杂得多。使用ReFit,只需为您的服务器和客户端项目添加一个接口即可。另一方面,使用ReFit,您必须为每个控制器创建新接口,而Swagger会自动处理。但在你决定任何事情之前,请检查与REST无关的第四个选项。gRPCgRPC[13](gRPCRemoteProcedureCall)是Google开发的开源远程过程调用系统。它有点像REST,因为它提供了一种从客户端向服务器发送请求的方法。但这在很多方面都有所不同,以下是相同点和不同点:与REST一样,gRPC与语言无关。有适用于所有流行语言的工具,包括C#。gRPC是合约的基础,使用.proto文件来定义合约。这有点类似于Swaggerswagger.json和ReFit的共享接口。可以从这些文件生成任何编程语言的客户端。gRPC使用ProtocolBuffers(Protobuf)[14]二进制序列化。这与REST(通常序列化为JSON或XML)不同。二进制序列化更小,因此更快。gRPC用于使用HTTP/2协议创建持久连接。该协议更简单、更紧凑。REST使用HTTP1.x协议(通常是HTTP1.1)。HTTP1.1要求对每个请求进行TCP握手,而HTTP/2保持连接打开。HTTP/2连接使用多路复用流。这意味着单个TCP连接可以支持多个流。这些流可以并行执行,而不必像在HTTP1.1中那样相互等待。gRPC允许双向流式传输。有两种使用gRPC的方法。对于.NETCore3.0,有一个完全托管的库,称为gRPCfor.NET[15]。对于其中任何一个,您都可以使用gRPCC#[16],它是用本机代码构建的。这并不意味着gRPCfor.NET可以替代gRPCC#。让我们看一个为.NET更新gRPC的示例。.NET的gRPC的服务器端这不是教程,而是更多关于预期内容的一般概念。这就是示例控制器在gRPC中的样子:Message="Hello"+request.Name});}}您需要在Startup.cs中添加以下配置:app.UseEndpoints(endpoints=>{endpoints.MapGrpcService();});API在.proto文件中该文件是项目的一部分,如syntax="proto3";serviceGreeter{rpcSayHello(HelloRequest)returns(HelloReply);}messageHelloRequest{stringname=1;}messageHelloReply{stringmessage=1;}将此.proto文件添加到.csproj:.NET的gRPC客户端是从.proto文件生成的。代码本身非常简单:varchannel=GrpcChannel.ForAddress("https://localhost:5001");varclient=newGreeter.GreeterClient(channel);varresponse=awaitclient.SayHello(newHelloRequest{Name="World"});Console.WriteLine(响应.Message);gRPC与RESTgRPC听起来不错。它在引擎盖下更快更简单。那么,我们都应该从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负载的青睐。参考资料[1]POCO类:https://www.c-sharpcorner.com/UploadFile/5d065a/poco-classes-in-entity-framework/[2]OpenAPI:https://swagger.io/specification/[3]SwaggerPetstore: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]gRfor.NE吨PC:https://github.com/grpc/grpc-dotnet[16]gRPCC#: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-我们/dotnet/api/system.net.sockets.tcplistener?view=netframework-4.8[22]UdpClient:https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.udpclient?查看=netframework-4.8[23]WCF:https://docs.microsoft.com/en-us/dotnet/framework/wcf/whats-wcf