全称 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) 。