先簡單對比以下GraphQL和WebAPI:
GraphQL和Web API(如RESTful API)是用於構建和提供Web服務的不同技術。
- 資料獲取方式:
- Web API:通常使用RESTful API,客戶端透過傳送HTTP請求(如GET、POST、PUT、DELETE)來獲取特定的資料。每個請求通常返回一個固定的資料結構,包含在響應的主體中。
- GraphQL:客戶端可以使用GraphQL查詢語言來精確指定需要的資料。客戶端傳送一個GraphQL查詢請求,伺服器根據查詢的結構和欄位來返回相應的資料。
- 資料獲取效率:
- Web API:每個請求返回的資料通常是預定義的,無論客戶端需要的資料量大小,伺服器都會返回相同的資料結構。這可能導致客戶端獲取到不必要的資料,或者需要發起多個請求來獲取所需資料。
- GraphQL:客戶端可以精確指定需要的資料,避免了不必要的資料傳輸。客戶端可以在一個請求中獲取多個資源,並且可以根據需要進行欄位選擇、過濾、排序等操作,從而提高資料獲取效率。
- 版本管理:
- Web API:通常使用URL版本控制或者自定義的HTTP頭來管理API的版本。當API發生變化時,可能需要建立新的URL或者HTTP頭來支援新的版本。
- GraphQL:GraphQL中沒有顯式的版本控制機制,而是透過向現有的型別和欄位新增新的欄位來擴充套件現有的API。這樣可以避免建立多個不同版本的API。
- 客戶端開發體驗:
- Web API:客戶端需要根據API的文件來構造請求和解析響應。客戶端需要手動處理不同的API端點和資料結構。
- GraphQL:客戶端可以使用GraphQL的強型別系統和自動生成的程式碼工具來進行開發。客戶端可以根據GraphQL的模式自動生成型別定義和查詢程式碼,提供了更好的開發體驗和型別安全性。
在前面我們基礎框架是基於WebAPI(REST FUL API)的模式去開發介面的,所有的響應資料都需要定義一個DTO結構,但是有些場景可能只需要某些欄位,而後端又懶得定義新資料介面對接,這就會導致客戶端獲取到不必要的資料。在這種情況下,使用GraphQL就可以有較好的體驗。
那麼,在我們現有寫好的Service中,如何快速整合GraphQL又無需複雜編碼工作呢。這就是我們接下來要實現的了。
HotChocolate.AspNetCore
HotChocolate.AspNetCore是.NET一個老牌的GraphQL實現庫,它可以讓我們很快速的實現一個GraphQL Server。
安裝HotChocolate.AspNetCore的nuget,在Program中新增程式碼
builder.Services.AddGraphQLServer()
app.MapGraphQL();
這樣就完成一個GraphQLServer的整合。
啟動程式,訪問https://localhost:7080/graphql/ 可以看到整合的介面。可以使用這個介面操作測試我們的graphql查詢。
實現QueryType
接下來實現一個基礎的QueryType,用於擴充套件查詢。
using HotChocolate.Authorization;
namespace Wheel.Graphql
{
[Authorize]
public class Query : IQuery
{
}
[InterfaceType]
public interface IQuery
{
}
}
在AddGraphQLServer()後面新增程式碼
builder.Services.AddGraphQLServer()
.AddQueryType<Query>()
;
使用ExtendObjectType擴充套件Query類,方便介面拆分。
public interface IQueryExtendObjectType
{
}
[ExtendObjectType(typeof(IQuery))]
public class SampleQuery : IQueryExtendObjectType
{
public List<string> Sample()
{
return new List<string> { "sample1", "sample2" };
}
}
[ExtendObjectType(typeof(IQuery))]
public class Sample2Query : IQueryExtendObjectType
{
public string Sample2(string id)
{
return id;
}
}
這裡建立一個IQueryExtendObjectType空介面,用於獲取所有需要擴充套件的QueryAPI
約定所有擴充套件的Query需要繼承IQueryExtendObjectType介面,並加上ExtendObjectType特性標籤。
封裝AddGraphQLServer方法:
using HotChocolate.Execution.Configuration;
using System.Reflection;
namespace Wheel.Graphql
{
public static class GraphQLExtensions
{
public static IRequestExecutorBuilder AddWheelGraphQL(this IServiceCollection services)
{
var result = services.AddGraphQLServer()
.AddQueryType<Query>()
;
var abs = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll")
.Where(x => !x.Contains("Microsoft.") && !x.Contains("System."))
.Select(x => Assembly.Load(AssemblyName.GetAssemblyName(x))).ToArray();
var types = abs.SelectMany(ab => ab.GetTypes()
.Where(t => typeof(IQueryExtendObjectType).IsAssignableFrom(t) && typeof(IQueryExtendObjectType) != t));
if (types.Any())
{
result = result.AddTypes(types.ToArray());
}
return result;
}
}
}
遍歷所有IQueryExtendObjectType並加入GraphQLServer。
啟動專案訪問https://localhost:7080/graphql/
可以看到SchemaDefinition自動生成了我們的兩個查詢。
新增授權
安裝HotChocolate.AspNetCore.Authorization的Nuget包。
在services.AddGraphQLServer()後面新增程式碼.AddAuthorization()
using HotChocolate.Execution.Configuration;
using System.Reflection;
namespace Wheel.Graphql
{
public static class GraphQLExtensions
{
public static IRequestExecutorBuilder AddWheelGraphQL(this IServiceCollection services)
{
var result = services.AddGraphQLServer()
.AddAuthorization()
.AddQueryType<Query>()
;
var abs = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll")
.Where(x => !x.Contains("Microsoft.") && !x.Contains("System."))
.Select(x => Assembly.Load(AssemblyName.GetAssemblyName(x))).ToArray();
var types = abs.SelectMany(ab => ab.GetTypes()
.Where(t => typeof(IQueryExtendObjectType).IsAssignableFrom(t) && typeof(IQueryExtendObjectType) != t));
if (types.Any())
{
result = result.AddTypes(types.ToArray());
}
return result;
}
}
}
未登入前執行查詢,可以看到響應Error。
獲取一個token之後配置一下:
再次請求,可以看到正常查詢。
整合現有Service
改造一下SampleQuery
[ExtendObjectType(typeof(IQuery))]
public class SampleQuery : IQueryExtendObjectType
{
public async Task<List<GetAllPermissionDto>> Sample([Service] IPermissionManageAppService permissionManageAppService)
{
var result = await permissionManageAppService.GetPermission();
return result.Data;
}
}
開啟https://localhost:7080/graphql/ 執行查詢,可以看到正常返回。
當我們需要過濾不查詢某些欄位時,只需要修改Query查詢格式。
分頁查詢,新增一下User的分頁查詢程式碼。
public class SampleQuery : IQueryExtendObjectType
{
public async Task<List<GetAllPermissionDto>> Sample([Service] IPermissionManageAppService permissionManageAppService)
{
var result = await permissionManageAppService.GetPermission();
return result.Data;
}
public async Task<Page<UserDto>> SampleUser(UserPageRequest pageRequest, [Service] IUserManageAppService userManageAppService)
{
var result = await userManageAppService.GetUserPageList(pageRequest);
return result;
}
}
測試:
可以看到,很簡單就可以把現有的API轉換成GraphQL。只不過一些排序分頁邏輯我們沒有采用GraphQL的方式,而是使用我們自己的WebApi分頁查詢的模式。
輪子倉庫地址https://github.com/Wheel-Framework/Wheel
歡迎進群催更。