zoukankan      html  css  js  c++  java
  • 《ASP.NET Core 与 RESTful API 开发实战》-- (第7章)-- 读书笔记(下)

    第 7 章 高级主题

    7.4 HATEOAS

    全称 Hypermedia AS The Engine Of Application State,即超媒体作为应用程序状态引擎。它作为 REST 统一界面约束中的一个子约束,是 REST 架构中最重要、最复杂,也是构建成熟 REST 服务的核心

    Richardson 成熟度模型是根据 REST 约束对 API 成熟度进行衡量的一种方法,该成熟模型使用3个因素来决定服务的成熟度,即 URI、HTTP 方法和 HATEOAS。一个 API 应用程序越多地采用这些特性,就越成熟。根据上述3个因素,RESTful API 应用的成熟度分为3级:

    • 第 1 级:资源
    • 第 2 级:HTTP 动词
    • 第 3 级:超文本驱动,即 HATEOAS

    HATEOAS 使 API 在其响应消息中不仅提供资源,还提供 URL。这些 URL 能够告诉客户端如何使用 API,它们由服务器根据应用程序当前的状态动态生成,而客户端在得到响应后,通过这些 URL 就能够知道服务器提供哪些操作,并使用这些链接与服务器进行交互

    7.5 GraphQL

    全称 Graph Query Language,作为查询语言,最主要的特点是能够根据客户端准确地获得它所需要的数据

    作为 API 查询语言,GraphQL 提供了一种以声明的方式从服务器上获取数据的方法

    {
        authors{
            name,
            email
        }
    }
    

    执行后的结果如下:

    {
        "data":{
            "authors":{
                "name":"Author 1",
                "email":"author1@xxx.com"
            },
            ...
        }
    }
    

    尽管 GraphQL 能够与 REST 实现同样的目的,但它们各自的实现方式以及特点有较大的差异,主要体现在:

    • (1)端点:对 REST 而言,每一个 URL 相当于一个资源,而 GraphQL 通过一个端点可以返回用户所需要的任何数据
    • (2)请求方式:REST 充分使用 HTTP 动词来访问不同的端点,而 GraphQL 所有请求都是向服务器相同端点发送类似 JSON 格式的信息
    • (3)资源表现形式:REST 得到的资源是事先定义好的固定的数据结构,而 GraphQL 能够根据客户端的请求灵活地返回所需要的形式
    • (4)版本:GraphQL 是在客户端来定义资源的表现形式,因此服务端数据结构变化不影响客户端的使用,即使服务器发生更改,也是向后兼容

    GraphQL 仅使用一个端点即可执行并响应所有 Graph 查询请求,因此它完全可以与 Library.API 项目中现有的 REST 端点共存,弥补 RESTful API 的不足

    添加nuget

    Install-Package GraphQL
    

    GraphQL 中有一个非常重要的概念--Schema,它定义了 GraphQL 服务提供什么样的数据结构,执行查询时,必须指定一个 Schema

    添加两个类 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) 。

  • 相关阅读:
    wait waitpid
    达梦备份还原
    sigprocmask阻塞信号
    sigaction信号处理
    dd命令
    linux系统启动过程
    cpio建立、还原备份档
    configure详解
    Git入门基础教程
    一篇文章了解Github和Git教程
  • 原文地址:https://www.cnblogs.com/MingsonZheng/p/13380982.html
Copyright © 2011-2022 走看看