ASP.NET Core 系列之 GraphQL - 完全教程(译)
原文地址:Getting Started with GraphQL in ASP.NET Core – Complete Guide
在这篇文章中,我们主要讨论一下 GraphQL 的趋势,这是一项由 Facebook 开发并进行开源的酷技术。我们将与 REST API 进行对比,并理解 GraphQL 的使用场景。同时,我们也将建立一个小的示例程序来演示如果将 GraphQL 集成到 ASP.NET Core 中,您可以在文章模块找到源代码。下面步入正题!
什么是 GraphQL?
GraphQL(或者叫图形化查询语言)是由 Facebook 发明和开源的 API 标准。它基本上是对 REST API 的一种替代。GraphQL 不像传统的 REST API,它将控制权交由给客户端,以便客户端应用/用户能够请求他自己想要的特定数据。简单来讲,它是一种可以取代 REST API 以构建高性能 API 的技术。话虽如此,但这并不意味着 REST API 将很快被取消。在大多数情况下, GraphQL 要优于 REST API。
GraphQL 是 API 的查询语言,它为 API 中的数据提供了定义(和 Swagger 类似,但可以做更多)。它本质上是一种技术,运行客户访问他们需要的数据,不会多,也不会少。这为客户端和开发人员提供了很大的权力和灵活性。
设计 GraphQL 背后的主要目标是使查询更智能。到目前为止,使用传统的 REST API 方法,我们可能经历了数据 过度提取(API返回 10 个字段,但我们只需要 2 个字段)或 抓取不足(API 仅返回 ID,但我们需要更多的相关数据,这迫使我们进行第二次 API 资源调用)。这意味着请求和响应始总是修改,并在应用程序的整个生命周期内保持固定,是这样的吗?GraphQL 使您的系统更智能,只提供所需的内容,从而减少到服务器的路由请求数量。
让我们说得更透彻一些。
以下是 REST API 的工作方式。
api/player/ – 从数据库中获取所有玩家
api/player/10 – 返回玩家 10 的所有详细信息。在恢复响应时,我们必须手动映射/分配所需的属性。服务器不会为您执行此工作。
几个例子,我们需要获取 id 为 10 的用户数据,只需要他的名字和年龄。
REST API 的请求和响应看起来是这样的:
请求:api/user/10
响应:
{
"userName" : "Mukesh",
"Name" : "Mukesh Murugan",
"Destination" :"Trivandrum",
"age" : 25,
"email" : "mukesh@codewithmukesh.com",
"something-else": "more data"
}
现在,我们得到该响应,接着必须要处理它,以筛选出当前所需字段的内容。这不是一项巨大的任务和资源浪费,但想象一下,将这个影响扩到到整个应用程序,如 Fackbook,这将产生昂贵的服务器开销。
让我们看一下 GraphQL 查询是什么样的。这是一个请求,客户可以问他到底需要什么。(我们将在文章的稍后介绍该如何查询和编写。这里只是一个例子。)
{
user(id: 10)
{
name,
age
}
}
并且,响应不多也不少
{
"data":
{
"user":
{
"name" : "Mukesh Murugan",
"age" : 25
}
}
}
这可能是我们从未考虑的地方。我们忙于使代码更智能,但从未真正想过 API 终结点的静态性。同样,这并不意味着您必须放弃 REST API 并转到 GraphQL,但 GraphQL 可以节省服务器资源/时间的消耗,如 Facebook 进行这样的扩展意义是巨大的。
您将需要了解 GraphQL 要解决的问题,以及它是如何与 REST API 进行比较,我们将在下一节中介绍这些问题。
GraphQL 解决的问题
我们从 GraphQL 如何出现开始讲起。我们将看到一个真实的场景。早在 2012 年左右, Facebook 发布了它自己的 Android / ios 应用程序。随着 Facebook 等面向客户服务的复杂性,人们很快注意到,这些应用消耗了太多的带宽、内存、RAM 使用量,并且时刻会进行十几次 API 调用。如果您在那个特定时间在 Facebook 工作, 您可能也会亲身经历过。这最终导致差评和 UX。
为什么它会进行多次 API 调用?这是显而易见的,对吧?例如,请考虑 Facebook 在移动设备上的新闻源。它有与您的个人资料相关的数据,来自您朋友的通知,来自你的朋友/页面/群的更新/帖子,以及大约一百万个😛其他内容,Facebook有一个API端点。现在,在加载新闻源时,将调用所有这些终结点,获取数据(包含大约 40-50% 未使用的字段),并在应用上显示。这产生了几个往返请求。一定会有更好的解决办法,对吧?
因此,GraphQL 引入了将所有这些百万个 API 终结点都凝结到单个终结点。现在,客户可以询问他/她/它到底想要什么,并得到所需的响应。
Facebook 关于其实施 GraphQL 的相关链接:https://developers.facebook.com/docs/graph-api
GraphQL VS REST API
GraphQL 和 REST API 之间有很多区别。让我们看看主要的区别:
- 使用 GraphQL,您只需要使用一个终结点,而使用 REST API 的 API 终结点只有 100 个。GraphQL 技术的工作原理是很酷的。这使得企业级 API 中 GraphQL 性能比 REST API 好得多。此外,由于应用中只有一个终结点,因此在应用程序扩展时维护 GraphQL 会更加容易。
- GraphQL 不需要 HTTP 来运行,而 REST API 需要 HTTP。
- GraphQL 仅支持后发自外服务请求,与 REST 模式不同,后跟 RESTPI。
- GraphQL 和 REST API 都有 JSON 响应。
- GraphQL 允许客户端/使用者以所需的格式对数据进行塑型。它给客户端提供完全控制,以定义请求和响应。如您所了解,使用 REST API 时,响应和请求始终在代码中的服务器端定义。基本上,GraphQL 允许您在数据方面拥有您想要的。
- 基于上一点,使用 GraphQL 基本上不会接收不会使用的数据。例如,假设您对 api/product/2 发出请求,并且响应具有产品实体的所有字段。在 10 个案例中的 9 种情况下,您不会使用产品的所有字段,对吗?现在使用 GraphQL,您可以选择设计请求,以便只收到请求的响应。这使得整个系统效率高,并大大减少了带宽消耗。
- 有了所有这些令人敬畏的功能,你真的不必担心 API 版本控制。每个客户都保留自己的版本!使用 REST API 是会有相应限制。
在 ASP.NET Core 中使用 GraphQL
创建新的 ASP.NET Core WebAPI 解决方案。
对于本示例,我们将有一个简单的实体。创建一个新类并命名它为 Models/Customer。类似这样的定义:
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Contact { get; set; }
public string Email { get; set; }
public DateTime DateOfBirth { get; set; }
}
由于此示例更侧重于 ASP.NET Core 中 GraphQL 的实现,所以我将跳过使用 Entity Framework Core 连接到数据库并获取客户记录的步骤。请记住,我们不会在这里创建任何 API 控制器。我提前将一堆示例客户数据输入我的数据库以进行测试。
如果您是初次使用 Entity Framework Core,我强烈建议您阅读以下文章来掌握 EF Core Entity Framework Core in ASP.NET Core – Getting Started
安装依赖包
打开包管理器对应的控制台,安装如下依赖包
Install-Package GraphQL
Install-Package GraphQL.Server.Transports.AspNetCore
Install-Package GraphQL.Server.Ui.Playground
Install-Package Newtonsoft.Json
这里解释一下为什么需要安装上述包。
- GraphQL:在 .NET 中启用 GraphQL 相关服务的核心包
- GraphQL.Server.Transports.AspNetCore:GraphQL 的 HTTP 中间件
- GraphQL.Server.Ui.Playground:现在我们需要一种 UI 来使用 GraphQL 并执行一些测试。这更像是 Swagger,您可以访问 API 的 POST 方法。相反, Rather Playground 也支持智能感知,智能建议和更多其它内容
我们将必须在 ASP.NET Core 的容器中配hi服务,但由于我们创建了需要用到的 GraphQL 类,所以这一步放到最后再操作。
GraphQL 的类型
GraphQL 不了解 Models/Entities 或任何 C# 的 POCO。相反,它需要类型。如前所述,我们已经在 "Models" 文件夹中有一个创建了客户定义的类。要从 GraphQL 终结点获取数据,我们必须使用 ObjectGraphType
让我们创建一个新类,并取名为 GraphQL/Types/CustomerGraphType.cs
class CustomerGraphType : ObjectGraphType<Customer>
{
public CustomerGraphType()
{
Name = "Customer";
Field(x => x.Id, type: typeof(IdGraphType)).Description("Customer Id");
Field(x => x.FirstName).Description("Customer's First Name");
Field(x => x.LastName).Description("Customer's Last Name");
Field(x => x.Contact).Description("Customer's Contact");
Field(x => x.Email).Description("Customer's Email");
}
}
您可以看到,我们将类定义为客户对象图类型。在这里,我们将映射所有应该公开给客户端的字段。这也增加了一个很好的抽象层。API 的安全性。我们还可以向属性添加说明,以帮助客户端更好地了解 GraphQL。
接下来,我们需要定义受支持的查询。这些查询必不可少地返回客户列表或单个客户实体。
创建一个新类并取名为 GraphQL/Queries/CustomerQuery.cs
public class CustomerQuery : ObjectGraphType
{
private readonly ApplicationDbContext _appContext;
public CustomerQuery(ApplicationDbContext appContext)
{
this._appContext = appContext;
Name = "Query";
Field<ListGraphType<CustomerGraphType>>("customers", "Returns a list of Customer", resolve: context => _appContext.Customers.ToList());
Field<CustomerGraphType>("customer", "Returns a Single Customer",
new QueryArguments(new QueryArgument<NonNullGraphType<IntGraphType>> { Name = "id", Description = "Customer Id" }),
context => _appContext.Customers.Single(x => x.Id == context.Arguments["id"].GetPropertyValue<int>()));
}
}
- Line #3 – 6 – 注入应用数据库上下文用于对象访问
- Line #7 – 在这里,我们设置查询的名称属性
在上面的查询类中,我们基本上定义了两个查询。
- Line #8 – 查询以返回所有客户作为列表。我们使用附加上下文的方式负责获取数据的 dbContext 变量。
- Line #9 – 查询以基于 ID (int) 返回特定客户。
GraphQL Schema
由于我们已经定义了查询,因此在 GraphQL 实例 Schema 中包括此查询非常重要。GraphQL Schema 包含以下属性:
- Query – 只能获取数据的查询类。
- Mutations – 更改数据的方法(编辑/删除/添加)。
- Subscriptions – 通知客户端更改。这更像是一个基于事件的订阅。
对于本示例,我们将只关注 Schema 的查询部分。让我们添加新类并将其命名 GraphQL/DemoSchema.cs
public class DemoSchema : Schema
{
public DemoSchema(IDependencyResolver resolver) : base(resolver)
{
Query = resolver.Resolve<CustomerQuery>();
}
}
有了这个查询 Schema 的定义,,我们可以在 ASP.NET Core 的服务容器中注入我们的服务了。打开 Startup,cs 并添加以下内容到 ConfigureServices 方法中。
services.AddTransient<IDependencyResolver>(x =>new FuncDependencyResolver(x.GetRequiredService));
services.AddTransient<DemoSchema>();
services.AddGraphQL(o => o.ExposeExceptions = true)
.AddGraphTypes(ServiceLifetime.Transient);
services.Configure<KestrelServerOptions>(options => options.AllowSynchronousIO = true);
services.Configure<IISServerOptions>(options => options.AllowSynchronousIO = true);
接下来,将以下内容添加到 "Configure" 方法。在这里,我们将注册我们的 Schema,并配置 GraphQLPlayground。在执行应用程序时,我们将了解更多关于 GraphQL Playground 的信息。
app.UseGraphQL<DemoSchema>();
app.UseGraphQLPlayground(options: new GraphQLPlaygroundOptions());
这就是我们需要在 ASP.NET Core 中做的事情,准备启动我们的应用程序,并导航至 localhost:5001/ui/playground
关于 GraphQL Playground
GraphQL Playground 更多地像是将你的 GraphQL API 进行可视化的界面。它提供各种强大的功能,如智能感知,智能推荐,实时错误语法通知,请求格式化等。
点击右侧的 "Schema" 的 Tab 标签页,还记得我们之前为 GraphQL 创建的一个 Schema 查询么?你可以在这找到相关定义。在查询 Tab 标签页,你可以找到受支持的方法,即 GetALLCustomer 和 GetALLCustomer。
这里面提到的 Customer 类型,看起来和 Swagger UI 非常相似。
接着看一下每个查询的定义和字段,点击 Docs 你可以看到每个查询的更多详细信息,比如取消名称,描述信息,类型。客户端可以使用这些文档作为了解 GraphQL 功能的参考文案。
测试 GraphQL
让我们开始测试我们创建的两个查询
获取所有 Customers
假设我们需要获得所有客户,但只有某些字段,如名字,姓氏,电子邮件。GraphQL 正是为了解决这个问题,记得吗?
你的查询请求类似这样的:
{
customers{
firstName,
lastName,
email
}
}
添加请求后,只需点击 Play 按钮即可。这将针对数据库执行您的请求。您可以看到返回的结果正是您想要的,😀您也可以通过向请求对象添加新字段来进行测试。
通过 ID 获取某个 Customer
接下来,让我们测试一下基于我们传递的 ID 作为参数来获取单一 Customer 对象。我们将 20 作为要传递的 ID 参数来进行请求
{
customer(id: 20) {
id,
firstName,
lastName,
email
}
}
很好!正如我们所期望的那样。这是一种相当酷的技术, 是吗?现在让我们把文章总结一下。我们将在另一篇文章讨论 GraphQL 在 ASP.NET Core 中的一些更多的先进概念。