《ASP.NET Core 與 RESTful API 開發(fā)實(shí)戰(zhàn)》-- (第7章)-- 讀書筆記(下)

第 7 章 高級(jí)主題
7.4 HATEOAS
全稱 Hypermedia AS The Engine Of Application State,即超媒體作為應(yīng)用程序狀態(tài)引擎。它作為 REST 統(tǒng)一界面約束中的一個(gè)子約束,是 REST 架構(gòu)中最重要、最復(fù)雜,也是構(gòu)建成熟 REST 服務(wù)的核心
Richardson 成熟度模型是根據(jù) REST 約束對(duì) API 成熟度進(jìn)行衡量的一種方法,該成熟模型使用3個(gè)因素來(lái)決定服務(wù)的成熟度,即 URI、HTTP 方法和 HATEOAS。一個(gè) API 應(yīng)用程序越多地采用這些特性,就越成熟。根據(jù)上述3個(gè)因素,RESTful API 應(yīng)用的成熟度分為3級(jí):
第 1 級(jí):資源
第 2 級(jí):HTTP 動(dòng)詞
第 3 級(jí):超文本驅(qū)動(dòng),即 HATEOAS
HATEOAS 使 API 在其響應(yīng)消息中不僅提供資源,還提供 URL。這些 URL 能夠告訴客戶端如何使用 API,它們由服務(wù)器根據(jù)應(yīng)用程序當(dāng)前的狀態(tài)動(dòng)態(tài)生成,而客戶端在得到響應(yīng)后,通過(guò)這些 URL 就能夠知道服務(wù)器提供哪些操作,并使用這些鏈接與服務(wù)器進(jìn)行交互
7.5 GraphQL
全稱 Graph Query Language,作為查詢語(yǔ)言,最主要的特點(diǎn)是能夠根據(jù)客戶端準(zhǔn)確地獲得它所需要的數(shù)據(jù)
作為 API 查詢語(yǔ)言,GraphQL 提供了一種以聲明的方式從服務(wù)器上獲取數(shù)據(jù)的方法
{
authors{
name,
email
}
}
執(zhí)行后的結(jié)果如下:
{
"data":{
"authors":{
"name":"Author 1",
"email":"[email protected]"
},
...
}
}
盡管 GraphQL 能夠與 REST 實(shí)現(xiàn)同樣的目的,但它們各自的實(shí)現(xiàn)方式以及特點(diǎn)有較大的差異,主要體現(xiàn)在:
(1)端點(diǎn):對(duì) REST 而言,每一個(gè) URL 相當(dāng)于一個(gè)資源,而 GraphQL 通過(guò)一個(gè)端點(diǎn)可以返回用戶所需要的任何數(shù)據(jù)
(2)請(qǐng)求方式:REST 充分使用 HTTP 動(dòng)詞來(lái)訪問(wèn)不同的端點(diǎn),而 GraphQL 所有請(qǐng)求都是向服務(wù)器相同端點(diǎn)發(fā)送類似 JSON 格式的信息
(3)資源表現(xiàn)形式:REST 得到的資源是事先定義好的固定的數(shù)據(jù)結(jié)構(gòu),而 GraphQL 能夠根據(jù)客戶端的請(qǐng)求靈活地返回所需要的形式
(4)版本:GraphQL 是在客戶端來(lái)定義資源的表現(xiàn)形式,因此服務(wù)端數(shù)據(jù)結(jié)構(gòu)變化不影響客戶端的使用,即使服務(wù)器發(fā)生更改,也是向后兼容
GraphQL 僅使用一個(gè)端點(diǎn)即可執(zhí)行并響應(yīng)所有 Graph 查詢請(qǐng)求,因此它完全可以與 Library.API 項(xiàng)目中現(xiàn)有的 REST 端點(diǎn)共存,彌補(bǔ) RESTful API 的不足
添加nuget
Install-Package GraphQL
GraphQL 中有一個(gè)非常重要的概念--Schema,它定義了 GraphQL 服務(wù)提供什么樣的數(shù)據(jù)結(jié)構(gòu),執(zhí)行查詢時(shí),必須指定一個(gè) Schema
添加兩個(gè)類 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>("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);
}
}
}
接下來(lái)創(chuàng)建查詢類 LibraryQuery
namespace Library.API.GraphQLSchema
{
public class LibraryQuery : ObjectGraphType
{
public LibraryQuery(IRepositoryWrapper repositoryWrapper)
{
// 返回所有作者的信息
Field>("authors",
resolve: context => repositoryWrapper.Author.GetAllAsync().Result);
// 返回指定作者信息
Field("author", arguments: new QueryArguments(new QueryArgument()
{
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;
});
}
}
}
接下來(lái)創(chuàng)建 Schema
namespace Library.API.GraphQLSchema
{
public class LibrarySchema : Schema
{
public LibrarySchema(LibraryQuery query, IDependencyResolver denDependencyResolver)
{
Query = query;
DependencyResolver = denDependencyResolver;
}
}
}
當(dāng) GraphQL 類型、查詢以及 Schema 都創(chuàng)建完成后,應(yīng)將它們添加到依賴注入容器中
添加一個(gè)擴(kuò)展方法,并在擴(kuò)展方法中添加所有類型
namespace Library.API.Extentions
{
public static class GraphQLExtensions
{
public static void AddGraphQLSchemaAndTypes(this IServiceCollection services)
{
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
// 用于執(zhí)行 Graph 查詢
services.AddSingleton();
// 用于獲取指定的依賴
services.AddSingleton(provider =>
new FuncDependencyResolver(provider.GetRequiredService));
}
}
}
為了方便解析客戶端請(qǐng)求中的 GraphQL 查詢內(nèi)容,添加一個(gè)類
namespace Library.API.GraphQLSchema
{
public class GraphQLRequest
{
///
/// 用于接收客戶端請(qǐng)求正文中的 Graph 查詢
///
public string Query { get; set; }
}
}
接下來(lái),在項(xiàng)目中添加一個(gè)控制器
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 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);
}
}
}
運(yùn)行程序,以 POST 方式請(qǐng)求 URL:http://localhost:5001/graphql
請(qǐng)求內(nèi)容如下:
{
"query":
"query{
authors{
id,
name,
birthPlace,
birthDate,
books{
title,
pages
}
}
}"
}
可以得到與請(qǐng)求的內(nèi)容完全一致的請(qǐng)求結(jié)果,表明客戶端可以根據(jù)需要在請(qǐng)求的查詢中定義所需要的信息,通過(guò)一次查詢,即可返回所有需要的數(shù)據(jù)
在 LibraryQuery 類中還添加了對(duì)指定 author 的查詢,可以通過(guò)以下請(qǐng)求內(nèi)容查詢
{
"query":
"query{
authors(id:"86072f62-5ec8-4266-9356-752a8496d56a"){
id,
name,
birthPlace,
birthDate,
books{
title,
pages
}
}
}"
}相關(guān)文章
《ASP.NET Core 與 RESTful API 開發(fā)實(shí)戰(zhàn)》-- (第7章)-- 讀書筆記(中)
《ASP.NET Core 與 RESTful API 開發(fā)實(shí)戰(zhàn)》-- (第7章)-- 讀書筆記(上)
《ASP.NET Core 與 RESTful API 開發(fā)實(shí)戰(zhàn)》-- (第6章)-- 讀書筆記(下)
《ASP.ENT Core 與 RESTful API 開發(fā)實(shí)戰(zhàn)》-- (第6章)-- 讀書筆記(上)
《ASP.ENT Core 與 RESTful API 開發(fā)實(shí)戰(zhàn)》-- (第5章)-- 讀書筆記(下)
《ASP.ENT Core 與 RESTful API 開發(fā)實(shí)戰(zhàn)》-- (第5章)-- 讀書筆記(中)
《ASP.ENT Core 與 RESTful API 開發(fā)實(shí)戰(zhàn)》-- (第5章)-- 讀書筆記(上)
《ASP.ENT Core 與 RESTful API 開發(fā)實(shí)戰(zhàn)》-- (第4章)-- 讀書筆記(下)
《ASP.ENT Core 與 RESTful API 開發(fā)實(shí)戰(zhàn)》-- (第4章)-- 讀書筆記(上)
《ASP.ENT Core 與 RESTful API 開發(fā)實(shí)戰(zhàn)》(第3章)-- 讀書筆記(下)
《ASP.ENT Core 與 RESTful API 開發(fā)實(shí)戰(zhàn)》(第3章)-- 讀書筆記(中)
《ASP.ENT Core 與 RESTful API 開發(fā)實(shí)戰(zhàn)》(第3章)-- 讀書筆記(上)
《ASP.ENT Core 與 RESTful API 開發(fā)實(shí)戰(zhàn)》-- 讀書筆記(第2章)
《ASP.ENT Core 與 RESTful API 開發(fā)實(shí)戰(zhàn)》-- 讀書筆記(第1章)
