在 Blazor WebAssembly 中使用 gRPC-Web
對于單頁面應(yīng)用程序,gRPC-Web 是 JSON-over-HTTP 的一種方便、高性能的替代方案。
如果你已經(jīng)了解關(guān)于 gRPC 和 gRPC-Web 的一切,你可以跳到 添加 gRPC 服務(wù)到一個Blazor WebAssembly 應(yīng)用程序 一節(jié)。如果你只是想要一些簡單的 Blazor WebAssembly + gRPC-Web 應(yīng)用程序,請看這個倉庫 https://github.com/SteveSandersonMS/BlazorGrpcSamples。
現(xiàn)狀
與其他所有基于瀏覽器的單頁應(yīng)用程序(SPA)技術(shù)一樣,在 Blazor WebAssembly 中,數(shù)據(jù)交互和觸發(fā)服務(wù)器端操作的最常見方式是 JSON-over-HTTP。它很簡單:客戶端使用預(yù)先約定的 HTTP 方法向某個預(yù)先約定的 URL 發(fā)出 HTTP 請求,然后服務(wù)器執(zhí)行操作并使用預(yù)先約定的 HTTP 狀態(tài)碼和預(yù)先約定格式的 JSON 數(shù)據(jù)進(jìn)行應(yīng)答。
這種方法通常很有效,這樣做的人往往能過上充實的生活。然而,它也有兩個顯而易見的弱點:
JSON 是一種非常冗長的數(shù)據(jù)格式,它沒有優(yōu)化帶寬。
沒有任何機(jī)制可以保證所有這些預(yù)先約定的 url、HTTP 方法、狀態(tài)碼 等等的細(xì)節(jié)在服務(wù)器和客戶端之間實際上是一致的。
什么是 gRPC?
gRPC 是一種遠(yuǎn)程過程調(diào)用(RPC)機(jī)制,最初由谷歌開發(fā)。對于 SPA,你可以將其視為 JSON-over-HTTP 的替代方案。它直接修復(fù)了上面列出的兩個弱點:
它被優(yōu)化為最小的網(wǎng)絡(luò)流量,發(fā)送有效的二進(jìn)制序列化消息
你可以在編譯時保證服務(wù)器和客戶端對端點的存在、數(shù)據(jù)的發(fā)送和接收形式達(dá)成一致,而不需要指定任何URL、狀態(tài)碼 等等。
這是怎么做到的呢?
要編寫gRPC服務(wù),你需要編寫一個.proto文件,它是一組 RPC 服務(wù)及其數(shù)據(jù)形狀的獨立于語言的描述。從這里,你可以用任何語言生成強(qiáng)類型的服務(wù)器和客戶端類,從而保證在編譯時符合你的協(xié)議。然后在運行時,gRPC 處理(反)序列化數(shù)據(jù)并以有效的格式(默認(rèn)為 protobuf )發(fā)送/接收消息。
另一個很大的好處是它不是 REST,所以你不必與同事經(jīng)常爭論哪些 HTTP 方法和狀態(tài)代碼在你的場景中是最幸福和幸運的。它只是簡單的 RPC,在我們還是小孩子的時候就真情實感想要的。
為什么不是每個人都在他們的 SPA 中使用 gRPC ?
傳統(tǒng)上,從基于瀏覽器的應(yīng)用程序中使用 gRPC 是不可能的,因為 gRPC 需要 HTTP/2,而且瀏覽器不公開任何 api,讓 JS、WASM 代碼直接控制 HTTP/2 請求。
但是現(xiàn)在有一個解決方案!gRPC-Web 是 gRPC的擴(kuò)展,它使 gRPC 與基于瀏覽器的代碼兼容(從技術(shù)上講,它是通過 HTTP/1.1 請求執(zhí)行 gRPC 的一種方式)。gRPC-Web 還沒有流行起來,因為到目前為止還沒有多少服務(wù)器或客戶端框架提供對它的支持。
ASP.NET Core 從 3.0 版本開始就提供了強(qiáng)大的 gRPC 支持。現(xiàn)在,在此基礎(chǔ)上,我們將在服務(wù)器和客戶端提供對 gRPC-Web的預(yù)覽支持。如果你想深入了解細(xì)節(jié),可以在來自 James Newton-King 的優(yōu)秀的 pull request 查看全部實現(xiàn)(https://github.com/grpc/grpc-dotnet/pull/695)。
添加 gRPC 服務(wù)到一個 Blazor WebAssembly 應(yīng)用程序
目前還沒有這方面的項目模板,所以將 gRPC 支持添加到 Blazor WebAssembly 應(yīng)用程序需要很多步驟,本文是詳細(xì)的介紹。但好消息是你只需要做一次這樣的設(shè)置。當(dāng)你完成起步與運行起來后,添加更多的 gRPC 端點并調(diào)用它們是非常簡單的。首先,由于 gRPC-Web 包還沒有發(fā)布到 NuGet.org,現(xiàn)在你需要添加兩個臨時的包管理源來獲得 nightly 預(yù)覽。你可以在你的解決方案的根目錄下添加NuGet.config文件。希望一兩個月后就不需要了。
<?xml version="1.0" encoding="utf-8"?> <configuration><packageSources><!--To inherit the global NuGet package sources remove the <clear/> line below --><clear /><add key="nuget" value="https://api.nuget.org/v3/index.json" /><add key="gRPC-nightly" value="https://grpc.jfrog.io/grpc/api/nuget/v3/grpc-nuget-dev" /><add key="blazor-nightly" value="https://dotnetfeed.blob.core.windows.net/aspnet-blazor/index.json" /></packageSources> </configuration>添加 gRPC 服務(wù)到一個托管部署的 Blazor WebAssembly 應(yīng)用程序
如果你已經(jīng)在 ASP.NET Core 服務(wù)端上托管了一個Blazor WebAssembly 應(yīng)用程序,默認(rèn)情況下,你有三個項目:客戶端、服務(wù)端和共享項目。我發(fā)現(xiàn)定義 gRPC 服務(wù)最方便的地方是在共享項目中,因為這樣生成的類對服務(wù)器和客戶機(jī)都可用。首先,編輯你的共享項目的.csproj添加必要的 gRPC 包引用:
<ItemGroup><PackageReference Include="Google.Protobuf" Version="3.11.2" /><PackageReference Include="Grpc.Net.Client" Version="2.27.0-dev202001100801" /><PackageReference Include="Grpc.Tools" Version="2.27.0-dev202001081219"><PrivateAssets>all</PrivateAssets><IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets></PackageReference></ItemGroup>如果這些包不能正確恢復(fù),請確保添加了 nightly 源。
現(xiàn)在可以創(chuàng)建.proto文件來定義服務(wù)了。例如,在共享項目中添加一個名為greet.proto的文件。包含如下內(nèi)容:
syntax = "proto3"; option csharp_namespace = "GrpcGreeter"; package greet;service Greeter {rpc SayHello (HelloRequest) returns (HelloReply); }message HelloRequest {string name = 1; }message HelloReply {string message = 1; }要使gRPC工具從這里生成服務(wù)器和客戶端類,請進(jìn)入共享項目的.csproj并添加以下內(nèi)容:
<ItemGroup><Protobuf Include="greet.proto" /></ItemGroup>此時,解決方案應(yīng)該可以沒有錯誤地編譯通過。
從服務(wù)端公開gRPC服務(wù)
在你的服務(wù)器項目中,創(chuàng)建一個名為GreeterService的新類,包含以下內(nèi)容:
using Grpc.Core; using GrpcGreeter;public class GreeterService : Greeter.GreeterBase {public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context){var reply = new HelloReply { Message = $"Hello from gRPC, {request.Name}!" };return Task.FromResult(reply);} }這個類繼承自 Greeter.GreeterBase,它是從 .proto 文件自動生成的。因此,當(dāng)你將新斷點添加到 .proto 中時,你將有新方法可以在這里進(jìn)行重寫來提供具體實現(xiàn)。
服務(wù)端的最后一部分是使用新的 api 將其公開為一個 gRPC-Web 服務(wù)。你需要為此引用一個包,因此在你的服務(wù)端項目的.csproj中,添加以下包引用:
<PackageReference Include="Grpc.AspNetCore" Version="2.27.0-dev202001100801" /> <PackageReference Include="Grpc.AspNetCore.Web" Version="2.27.0-dev202001100801" />現(xiàn)在,在你的服務(wù)器的Startup.cs文件中,修改ConfigureServices以添加以下行:
services.AddGrpc();注意:如果你只打算公開gRPC服務(wù),你可能不再需要MVC控制器,在這種情況下,你可以從下面刪除services.AddMvc()和endpoints.MapDefaultControllerRoute()。
只是在 app.AddRouting(); 的下面添加以下內(nèi)容,它會處理將傳入的gRPC-web請求映射到服務(wù)端,使其看起來像 gRPC請求:
app.UseGrpcWeb();最后,在app.UseEndpoints語句塊中注冊你的 gRPC-Web 服務(wù)類,并在該語句塊的頂部使用以下代碼行:
endpoints.MapGrpcService<GreeterService>().EnableGrpcWeb();就這樣,你的gRPC-Web服務(wù)端已經(jīng)準(zhǔn)備好了!
在客戶端中使用gRPC服務(wù)
在你的客戶端項目的.csproj中,你需要添加以下兩個 nightly 包的引用:
<PackageReference Include="Grpc.Net.Client.Web" Version="2.27.0-dev202001100801" /> <PackageReference Include="Microsoft.AspNetCore.Blazor.Mono" Version="3.2.0-preview1.20052.1" />后者是為了修復(fù) Blazor WebAssembly 中的一個問題的變通方案,這個問題將在幾周后的下一次預(yù)覽中得到修復(fù)。如果這些包不能正常恢復(fù),請確保添加了 nightly 源。
現(xiàn)在設(shè)置你的客戶端應(yīng)用程序的依賴項注入系統(tǒng),以便能夠提供GreeterClient的實例。這將允許你在客戶端應(yīng)用程序的任何位置調(diào)用 gRPC 服務(wù)。在客戶端項目的Startup.cs中的ConfigureServices方法中添加以下內(nèi)容:
services.AddSingleton(services => {// Create a gRPC-Web channel pointing to the backend servervar httpClient = new HttpClient(new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler()));var baseUri = services.GetRequiredService<NavigationManager>().BaseUri;var channel = GrpcChannel.ForAddress(baseUri, new GrpcChannelOptions { HttpClient = httpClient });// Now we can instantiate gRPC clients for this channelreturn new Greeter.GreeterClient(channel); });需要注意的是Greeter.GreeterClient是從.proto file文件中為你生成的代碼。你不必手動實現(xiàn)它!但你還需要添加以下使用語句,使上述代碼編譯通過:
using GrpcGreeter; using Grpc.Net.Client; using Grpc.Net.Client.Web; using Microsoft.AspNetCore.Components; using System.Net.Http;我們快完成了!現(xiàn)在有一個可工作的服務(wù)端,并希望還有一個可工作的客戶端。我們只需要從 UI 調(diào)用一些 gRPC 服務(wù)。例如,在你的Index.razor文件中,替換成如下內(nèi)容:
@page "/" @using GrpcGreeter @inject Greeter.GreeterClient GreeterClient<h1>Invoke gRPC service</h1><p><input @bind="yourName" placeholder="Type your name" /><button @onclick="GetGreeting" class="btn btn-primary">Call gRPC service</button> </p>Server response: <strong>@serverResponse</strong>@code {string yourName = "Bert";string serverResponse;async Task GetGreeting(){var request = new HelloRequest { Name = yourName };var reply = await GreeterClient.SayHelloAsync(request);serverResponse = reply.Message;} }現(xiàn)在在瀏覽器中嘗試一下。用戶界面看起來是這樣的:
如果你查看瀏覽器開發(fā)者工具的 network 選項卡中的請求,你會看到它在發(fā)送和接收二進(jìn)制protobuf消息:
總結(jié)和示例
現(xiàn)在你已經(jīng)有了基礎(chǔ),如果你愿意,還可以進(jìn)一步使用 gRPC 來進(jìn)行服務(wù)器和客戶機(jī)之間的所有數(shù)據(jù)交換。gRPC 工具將為你生成所有的數(shù)據(jù)傳輸類,提高網(wǎng)絡(luò)流量的效率,并消除 url、HTTP 方法、狀態(tài)代碼和序列化等 HTTP-over-JSON 的問題。
還有一個更詳細(xì)的示例(https://github.com/SteveSandersonMS/BlazorGrpcSamples/tree/master/Hosted),它是一個完整的 Blazor WebAssembly 托管應(yīng)用程序,使用 gRPC 獲取“天氣預(yù)報”數(shù)據(jù)。如果你對從默認(rèn)的基于json的解決方案升級到基于gRPC-Web的解決方案所需的具體步驟感興趣,請參閱這個準(zhǔn)確地顯示了我所做的更改的差異對比(https://github.com/SteveSandersonMS/BlazorGrpcSamples/commit/72544c54085a35cd89aae20030d7f91d75317a2f)。
添加 gRPC 服務(wù)到一個獨立部署的 Blazor WebAssembly 應(yīng)用程序
如果你正在構(gòu)建一個純獨立的 Blazor WebAssembly 應(yīng)用程序,而不是托管在 ASP.NET Core,那么我們就不能對你將擁有什么樣的服務(wù)器做任何假設(shè)(意思是你只開發(fā)客戶端,而服務(wù)端是由別人開發(fā)的場景)。我們只能假設(shè)你要調(diào)用一些與 gRPC-Web 兼容的服務(wù)端點,它們可能是在其他主機(jī)上的 ASP.NET Core 服務(wù)上暴露,或者是一個在另一個 gRPC 服務(wù)周圍的 Envoy gRPC-Web 包裝器(https://blog.envoyproxy.io/envoy-and-grpc-web-a-fresh-new-alternative-to-rest-6504ce7eb880)。在這里我們唯一關(guān)心的是配置你的 Blazor WebAssembly 應(yīng)用程序來使用它。設(shè)置客戶端應(yīng)用程序的大多數(shù)步驟與上面的“托管部署”情況相同。然而,在某些方面,它有點棘手,因為我們不能依賴于我們在“托管”情況下所做的一些假設(shè)。區(qū)別如下:
獲取和使用.proto文件
假定你的外部 gRPC 服務(wù)維護(hù)者可以為你提供定義該服務(wù)的.proto文件。你可以將其復(fù)制到你的客戶端項目中,并在.csproj中添加一個引用它的<Proto>項。為了讓工具工作,你還需要添加類似這樣的包引用:
<!-- Needed temporarily until the next Blazor WebAssembly preview release --><PackageReference Include="Microsoft.AspNetCore.Blazor.Mono" Version="3.2.0-preview1.20052.1" /><!-- gRPC-Web packages --><PackageReference Include="Google.Protobuf" Version="3.11.2" /><PackageReference Include="Grpc.Net.Client" Version="2.27.0-dev202001100801" /><PackageReference Include="Grpc.Net.Client.Web" Version="2.27.0-dev202001100801" /><PackageReference Include="Grpc.Tools" Version="2.27.0-dev202001081219"><PrivateAssets>all</PrivateAssets><IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets></PackageReference>配置客戶端DI服務(wù)
與“托管托管”情況類似,你將向Startup.cs中添加代碼以添加gRPC-Web客戶端服務(wù)。這里的主要區(qū)別是,你必須知道用于訪問外部服務(wù)的 Base URL 是什么,因為我們不能再假設(shè)它的 Base URL 跟托管你的客戶端應(yīng)用程序的服務(wù)端的 Base URL 一樣。因此,你的DI服務(wù)注冊可能看起來更像以下內(nèi)容:
services.AddSingleton(services => { #if DEBUGvar backendUrl = "https://localhost:5001"; // Local debug URL #elsevar backendUrl = "https://some.external.url:12345"; // Production URL #endif// Now we can instantiate gRPC clients for this channelvar httpClient = new HttpClient(new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler()));var channel = GrpcChannel.ForAddress(backendUrl, new GrpcChannelOptions { HttpClient = httpClient });return new Greeter.GreeterClient(channel); });這將根據(jù)你是否在調(diào)試模式下構(gòu)建而改變URL。
總結(jié)和示例
就是這樣!現(xiàn)在,你的獨立部署的 Blazor WebAssembly 應(yīng)用程序可以使用外部的 gRPC-Web 服務(wù)。對于完整的可運行示例,下面是一個示例獨立應(yīng)用程序(https://github.com/SteveSandersonMS/BlazorGrpcSamples/tree/master/Standalone),它在外部 URL 上調(diào)用一個 gRPC-Web 服務(wù)。這個示例附帶了一個實際的用于測試的 gRPC-Web 服務(wù)器,但是你可以分開考慮它。如果你想確切地看到我更改了什么,這里是與默認(rèn)項目模板輸出的差異(https://github.com/SteveSandersonMS/BlazorGrpcSamples/commit/d6ec609f2b7e6591958d38e4a207c9b4f52f0feb)。
你的反饋請求
如果你想進(jìn)一步了解 gRPC,請查閱 ASP.NET Core gRPC 文檔。請向我們提出你對 gRPC-Web 的意見和經(jīng)驗反饋,因為這將幫助我們選擇如何以及是否在未來的 ASP.NET Core 版本中使 gRPC-Web 成 一個標(biāo)準(zhǔn)特性。你可以在這里發(fā)布評論,或者在 GitHub 上發(fā)布標(biāo)題中帶有“反饋”的 issue。
翻譯自原文:https://blog.stevensanderson.com/2020/01/15/2020-01-15-grpc-web-in-blazor-webassembly
相關(guān)文章:
ASP.NET Core 現(xiàn)已支持gRPC-Web
.NET Core ? gRPC
在 .NET Core 上 Code-first 方式實現(xiàn) gRPC?
近期,觀測分析平臺 SkyWalking 的 .NET 自動探針 (SkyAPM-dotnet) 也已經(jīng)支持了 grpc-dotnet 遠(yuǎn)程調(diào)用的鏈路跟蹤采集,歡迎大家使用!如果喜歡,也請大家給點個星星!
項目地址:https://github.com/SkyAPM/SkyAPM-dotnet
總結(jié)
以上是生活随笔為你收集整理的在 Blazor WebAssembly 中使用 gRPC-Web的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【实战 Ids4】║ 在Swagger中
- 下一篇: 【新书推荐】《ASP.NET Core微