全稱 Hypermedia AS The Engine Of Application State,即超媒體做爲應用程序狀態引擎。它做爲 REST 統一界面約束中的一個子約束,是 REST 架構中最重要、最複雜,也是構建成熟 REST 服務的核心服務器
Richardson 成熟度模型是根據 REST 約束對 API 成熟度進行衡量的一種方法,該成熟模型使用3個因素來決定服務的成熟度,即 URI、HTTP 方法和 HATEOAS。一個 API 應用程序越多地採用這些特性,就越成熟。根據上述3個因素,RESTful API 應用的成熟度分爲3級:數據結構
HATEOAS 使 API 在其響應消息中不只提供資源,還提供 URL。這些 URL 可以告訴客戶端如何使用 API,它們由服務器根據應用程序當前的狀態動態生成,而客戶端在獲得響應後,經過這些 URL 就可以知道服務器提供哪些操做,並使用這些連接與服務器進行交互架構
全稱 Graph Query Language,做爲查詢語言,最主要的特色是可以根據客戶端準確地得到它所須要的數據app
做爲 API 查詢語言,GraphQL 提供了一種以聲明的方式從服務器上獲取數據的方法async
{ authors{ name, email } }
執行後的結果以下:ide
{ "data":{ "authors":{ "name":"Author 1", "email":"author1@xxx.com" }, ... } }
儘管 GraphQL 可以與 REST 實現一樣的目的,但它們各自的實現方式以及特色有較大的差別,主要體如今:ui
GraphQL 僅使用一個端點便可執行並響應全部 Graph 查詢請求,所以它徹底能夠與 Library.API 項目中現有的 REST 端點共存,彌補 RESTful API 的不足this
添加nugetspa
Install-Package GraphQL
GraphQL 中有一個很是重要的概念--Schema,它定義了 GraphQL 服務提供什麼樣的數據結構,執行查詢時,必須指定一個 Schemacode
添加兩個類 AuthorType 和 BookType
namespace Library.API.GraphQLSchema { public class AuthorType : ObjectGraphType<Author> { public AuthorType(IRepositoryWrapper repositoryWrapper) { Field(x => x.Id, type: typeof(IdGraphType)); Field(x => x.Name); Field(x => x.BirthData); Field(x => x.BirthPlace); Field(x => x.Email); Field<ListGraphType<BookType>>("books", resolve: context => repositoryWrapper.Book.GetBooksAsync(context.Source.Id).Result); } } } namespace Library.API.GraphQLSchema { public class BookType : ObjectGraphType<Book> { public BookType() { Field(x => x.Id, type: typeof(IdGraphType)); Field(x => x.Title); Field(x => x.Description); Field(x => x.Pages); } } }
接下來建立查詢類 LibraryQuery
namespace Library.API.GraphQLSchema { public class LibraryQuery : ObjectGraphType { public LibraryQuery(IRepositoryWrapper repositoryWrapper) { // 返回全部做者的信息 Field<ListGraphType<AuthorType>>("authors", resolve: context => repositoryWrapper.Author.GetAllAsync().Result); // 返回指定做者信息 Field<AuthorType>("author", arguments: new QueryArguments(new QueryArgument<IdGraphType>() { Name = "id" }), resolve: context => { Guid id = Guid.Empty; if (context.Arguments.ContainsKey("id")) { id = new Guid(context.Arguments["id"].ToString() ?? string.Empty); } return repositoryWrapper.Author.GetByIdAsync(id).Result; }); } } }
接下來建立 Schema
namespace Library.API.GraphQLSchema { public class LibrarySchema : Schema { public LibrarySchema(LibraryQuery query, IDependencyResolver denDependencyResolver) { Query = query; DependencyResolver = denDependencyResolver; } } }
當 GraphQL 類型、查詢以及 Schema 都建立完成後,應將它們添加到依賴注入容器中
添加一個擴展方法,並在擴展方法中添加全部類型
namespace Library.API.Extentions { public static class GraphQLExtensions { public static void AddGraphQLSchemaAndTypes(this IServiceCollection services) { services.AddSingleton<AuthorType>(); services.AddSingleton<BookType>(); services.AddSingleton<LibraryQuery>(); services.AddSingleton<ISchema, LibrarySchema>(); // 用於執行 Graph 查詢 services.AddSingleton<IDocumentExecuter, DocumentExecuter>(); // 用於獲取指定的依賴 services.AddSingleton<IDependencyResolver>(provider => new FuncDependencyResolver(provider.GetRequiredService)); } } }
爲了方便解析客戶端請求中的 GraphQL 查詢內容,添加一個類
namespace Library.API.GraphQLSchema { public class GraphQLRequest { /// <summary> /// 用於接收客戶端請求正文中的 Graph 查詢 /// </summary> public string Query { get; set; } } }
接下來,在項目中添加一個控制器
namespace Library.API.Controllers { [Route("graphql")] [ApiController] public class GraphQLController : ControllerBase { public IDocumentExecuter DocumentExecuter { get; set; } public ISchema LibrarySchema { get; set; } public GraphQLController(ISchema librarySchema, IDocumentExecuter documentExecuter) { LibrarySchema = librarySchema; DocumentExecuter = documentExecuter; } [HttpPost] public async Task<IActionResult> Post([FromBody] GraphQLRequest query) { var result = await DocumentExecuter.ExecuteAsync(options => { options.Schema = LibrarySchema; options.Query = query.Query; }); if (result.Errors?.Count > 0) { return BadRequest(result); } return Ok(result); } } }
運行程序,以 POST 方式請求 URL:http://localhost:5001/graphql
請求內容以下:
{ "query": "query{ authors{ id, name, birthPlace, birthDate, books{ title, pages } } }" }
能夠獲得與請求的內容徹底一致的請求結果,代表客戶端能夠根據須要在請求的查詢中定義所須要的信息,經過一次查詢,便可返回全部須要的數據
在 LibraryQuery 類中還添加了對指定 author 的查詢,能夠經過如下請求內容查詢
{ "query": "query{ authors(id:"86072f62-5ec8-4266-9356-752a8496d56a"){ id, name, birthPlace, birthDate, books{ title, pages } } }" }
本做品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。
歡迎轉載、使用、從新發布,但務必保留文章署名 鄭子銘 (包含連接: http://www.cnblogs.com/MingsonZheng/ ),不得用於商業目的,基於本文修改後的做品務必以相同的許可發佈。
若有任何疑問,請與我聯繫 (MingsonZheng@outlook.com) 。