.NET使用Graphql的演示——新一代的API互動

WeskyNet發表於2024-10-17
Graphql是什麼?先來一段AI給的回答:
GraphQL是一種為API設計的查詢語言,與REST相比,它提供了更高效、強大和靈活的方法來與資料互動。GraphQL由Facebook於2012年開發,並於2015年開源。其主要的優勢在於能夠允許客戶端精確地指定他們需要的資料,從而避免了過度獲取或資料不足的問題。
主要特性
  1. 精確獲取需要的資料:
  1. 單一端點:
  1. 型別系統:
  1. 查詢與修改:
  1. 實時資料(Subscription):
優勢和侷限
優勢:
  • 減少資料傳輸:只返回客戶端請求的資料。
  • 減少請求數:多個資料需求可以在單一查詢中解決。
  • 靈活性高:客戶端可以自由構造查詢,無需伺服器頻繁更新API。
侷限:
  • 複雜查詢效能問題:如果不加限制地進行深度查詢或大規模的資料巢狀,可能會對伺服器效能造成影響。
  • 快取策略:相比於REST的URL級別快取,GraphQL需要更復雜的快取策略來最佳化效能。
  • 學習曲線:對於開發者來說,需要學習新的查詢語法及其底層實現。
其他內容就不過多介紹了,大家感興趣可以自行去搜尋有關理論或說明。接下來我直接提供實戰入門演示。
以下開始正式演示正文:
先建立一個webapi專案作為服務端和一個控制檯專案作為客戶端,用來測試使用。以及對應的引用包,如下圖所示:
0
新建Quries資料夾,用來存放查詢使用的類和方法。以及新增一個測試用的類和string型別返回值的方法 Hello()
0
在啟動項或Program裡面,新增Graphql服務,並新增Query的型別註冊:
0
最後還要記得對映端點:
0
然後執行程式,例如我預設執行起來埠是5264,則開啟url(根據自己情況更改url地址):http://localhost:5264/graphql/
然後輸入查詢語句:query:{hello}就可以查出對應的返回內容。
0
客戶端裡面,建立graphql的客戶端請求,並輸入查詢的方法為hello的query語句,以及輸出的結果,如下圖所示。結果和上面的一樣,只是我只輸出data裡面的資料,data裡面的資料就是我們需要的結果。
0
接著做個擴充演示,建立一個巢狀實體類,用來模擬多種情況:
0
建立一個測試使用的服務,模擬具體查詢業務使用。
0
註冊服務和介面以後,執行程式,並在graphql裡面進行執行測試。當前測試的是輸出所有欄位。
0
現在,例如我把子集合去掉不要,那查詢出來也就不會帶有子集合的任何內容:
0
或者只需要指定的其他欄位,刪掉了描述、子集合的城市欄位:
0
同樣的,把查詢語句丟到客戶端程式裡面進行查詢,也可以查出指定欄位的內容:
0
上面演示的是查詢效果,也可以做增刪改等其他操作。
在測試服務類新增一個業務操作,模擬接收到引數以後進行了業務操作,最終返回一個代表成功的資料。例如:
0
新建一個Mutations資料夾,用來存放增刪改操作的類等。例如此處的測試使用的TestMutation.然後建立一個模擬傳入引數進行操作的方法,該方法返回上面服務類裡面的測試方法。
0
需要新增對修改有關操作的註冊:
0
然後啟動,做個測試。使用mutation語句進行操作,操作指定方法,方法裡面指定引數和欄位資料。可以看到服務端進入了前面預設的業務方法內,並且返回的true被客戶端成功接收。
0
在控制檯客戶端,也執行一下mutation操作,也能夠成功呼叫:
0
以上是查詢和修改操作的例子,graphql還可以做資料推送和訂閱,用於實現websocket的效果。
新建一個subscriptions資料夾,用來存放所有的訊息推送和訂閱有關的定義類。例如TestSub,裡面定義了一個推送方法OnTestPublish
0
在前面的測試服務裡面,新增ITopicEventSender事件介面的注入,以及新增一個方法,用來觸發推送功能。並且推送的主題,使用剛才定義的OnTestPublish
0
然後需要提供對推送服務的註冊,以及持久化選擇。
0
使用預設的持久化,該持久化選擇不建議上生產。具體原因,我去AI一下:
  1. 可擴充套件性問題:AddInMemorySubscriptions 儲存訂閱資訊是在記憶體中進行的。這意味著訂閱資料僅存在於單個程序中。如果你的應用程式需要在多個伺服器例項之間進行擴充套件,每個例項的記憶體中都會有獨立的訂閱狀態,從而導致狀態不一致。因此,在大型應用或高負載環境中,這種方法不能很好地擴充套件。
  2. 永續性缺失:使用記憶體儲存的另一個主要問題是資料的永續性。伺服器重啟或發生故障時,所有在記憶體中的訂閱資料將丟失。這對於生產環境來說是不可接受的,因為需要保證服務的穩定性和資料的永續性。
  3. 資源使用效率:隨著訂閱數量的增加,記憶體的使用量也會隨之上升。在記憶體資源有限的環境中,這可能會影響應用程式的整體效能和響應速度。
  4. 故障恢復:在記憶體中的訂閱管理缺乏有效的故障恢復機制。如果系統崩潰或需要進行維護,恢復訂閱狀態將非常困難,可能需要從客戶端重新建立訂閱。
為了解決這些問題,生產環境中通常建議使用持久化和可擴充套件的訂閱儲存方案,比如基於 Redis 的 AddRedisSubscriptions 方法。大佬們感興趣可以自己去擴充下。
現在缺少一個觸發條件,由於咱們建立的是webapi專案,自帶控制器,那我把控制器做個改造,透過swagger來呼叫進行觸發資料推送,直接在請求裡面,呼叫推送方法:
0
最後,由於推送使用了websocket,所以也需要新增對websocket的註冊:
0
然後啟動程式,使用subscription進行訂閱onTestPublish主題訊息。執行以後,會一直監聽,除非我們取消監聽。
0
開啟swagger,直接呼叫並測試,可以看到皮膚接收到了測試推送的資料。
0
客戶端要實現訂閱,需要做一些改動。訂閱的事件是字串型別,所以需要建立一個字串型別的屬性,用來接收資料:
0
然後客戶端建立時候,需要使用websocket端點。然後再建立訂閱語句
0
接下來是訂閱的具體實現演示:
0
允許,並透過swagger呼叫兩次測試,都可以被監聽到。
0
同時,之前開啟的graphql演示皮膚,也可以看到能夠收到後續訊息,說明支援多客戶端接收,符合websocket的推送效果。
0
有關實現的核心程式碼。服務端註冊有關:
 // 新增GraphQL服務
 builder.Services
     .AddGraphQLServer()
     .AddQueryType<TestQuery>()
     .AddMutationType<TestMutation>()
     .AddSubscriptionType<TestSub>()
     .AddInMemorySubscriptions(); // 預設訊息持久化(生產情況建議更換)

 var app = builder.Build();

 app.UseWebSockets();

 // 對映GraphQL端點
 app.MapGraphQL();

客戶端實現:
 var option = new GraphQLHttpClientOptions
 {
     EndPoint = new Uri("http://localhost:5264/graphql"),
     // 設定 WebSocket 端點以支援訂閱
     WebSocketEndPoint = new Uri("ws://localhost:5264/graphql")
 };

 using var client = new GraphQLHttpClient(/*"http://localhost:5264/graphql"*/ option , new NewtonsoftJsonSerializer());
 //            var request = new GraphQLRequest
 //            {
 //                Query = @"mutation{
 //  otherOperation(info:{address:""龍崗區寶龍街道"",city:""大大大深圳"",phone:""10100011""})
 //}"
 //            };

 //            var response = await client.SendQueryAsync<object>(request);
 //            Console.WriteLine(response.Data);

 // 定義訂閱請求
 var subscriptionRequest = new GraphQLRequest
 {
     Query = @"
     subscription {
         onTestPublish
     }"
 };

 // 建立訂閱流
 var subscriptionStream = client.CreateSubscriptionStream<OnTestPublishResponse>(subscriptionRequest);

 // 訂閱訊息流
 var subscription = subscriptionStream.Subscribe(
     response =>
     {
         if (response.Errors != null)
         {
             Console.WriteLine("Error occurred: " + response.Errors);
         }
         else
         {
             Console.WriteLine($"Received message: {response.Data.OnTestPublish}");
         }
     },
     error => Console.WriteLine($"Subscription error: {error.Message}"),
     () => Console.WriteLine("Subscription completed."));

 // 模擬其他邏輯(例如,在某個時刻取消訂閱,這兒透過輸入任意按鍵觸發取消和釋放)
 Console.WriteLine("Press any key to exit...");
 Console.ReadKey();

 // 取消訂閱並關閉 WebSocket 連線
 subscription.Dispose();
 client.Dispose();

如果需要我本地演示的程式碼專案,可以在本人公眾號【Dotnet Dancer】回覆: 程式碼演示 即可獲取開源專案地址。如果需要其他諮詢或合作,可V:WeskyNet001

如果以上內容對你有幫助,歡迎大佬們點贊、關注公眾號或轉發。謝謝大家!

相關文章