了解如何在 .NET Core 和 C# 中使用 GraphQL
GraphQL 是 Facebook 开发的一项技术,它使前端能够与后端协商所需的数据。如果您愿意,它还允许 API 构建者将来自不同 API 的数据拼接在一起。这是一项非常酷的技术,它不仅适用于 JavaScript,您绝对可以在下一个 .Net 项目中使用它。
在本文中,我们将介绍:
- 为什么,为什么我们要用这项技术来构建 API
- 它由哪些部分组成
- 演示,让我们构建一个 API 并了解如何让它与我们的数据源协同工作
资源
-
GraphQL.org
这是 GraphQL 组织的网站。它包含大量关于 GraphQL 的构建原因、类型等信息。 -
NuGet 包 graphql-dotnet
这是目前最受欢迎的 NuGet 包。它包含一个 README 页面,可帮助您入门,同时还提供不同类型的应用程序(例如 MVC)的演示。 -
在 VS Code 中开始使用 .Net Core
这展示了如何在 VS Code 中开始使用 .Net Core。 -
我撰写的关于 .Net Core 和 VS Code 的入门文章
我为对 .Net Core 感兴趣的你撰写了一系列文章。它展示了如何构建不同类型的项目、运行测试等等。它甚至涵盖了如何构建无服务器 API。 -
如何在 JavaScript 中构建和使用 GraphQL API
如果您想了解在 JavaScript 中如何完成任务,请查看我写的这些文章。
为什么?我们为什么需要它?REST API 有什么问题?
GraphQL 允许内容协商,这意味着你只需要一个端点。它还允许你精确查询所需的字段。在后端,你可以从不同的数据源获取数据,因此对于前端开发人员和后端开发人员来说,这绝对是一个值得兴奋的理由。
GraphQL 由哪些部分组成
GraphQL 由一个 schema 定义组成。您可以使用 GQL(GraphQL 查询语言)来定义该 schema,也可以通过修饰类来响应某些资源请求(更多内容请参见后续文章)。
除了架构之外,还有一个核心概念叫做解析器。解析器只是一个知道如何处理传入查询的函数。解析器会映射到特定的资源。
然后,您将拥有一个名为 GraphiQL 的可视化环境,可用于运行查询。大多数 GraphQL 实现都附带某种形式的可视化环境。
如何查询
GraphQL 查询起来有点特殊,但其实很简单。要查询资源,你可以这样输入:
{
resource
}
但这还不够。您还需要指定所需的列,如下所示:
{
resource {
column,
column2
}
}
要使用参数进行查询,您可以像这样输入:
{
users(id:1) {
name,
created
}
}
最终结果始终是与查询匹配的 JSON 响应。因此,如果我们执行上述查询,我们将得到如下所示的返回结果:
{
"data": {
"users": [{
"name" : "chris",
"created": "2018-01-01"
}]
}
}
演示 - 让我们构建一个 API。
我们需要将一些 GraphQL 理论与代码结合起来。因此,我们将介绍一个概念,然后展示相应的代码。
设置
我们需要做以下事情
- 创建解决方案
- 创建控制台应用程序
- 安装GraphQL NuGet 包
创建解决方案
让我们首先创建一个解决方案目录,如下所示:
mkdir demo
cd demo
接下来,让我们创建实际的解决方案文件:
dotnet new sln
创建控制台应用
创建控制台项目非常简单,只需输入以下内容:
dotnet new console -o App
然后导航到项目目录:
cd App
添加我们的 GraphQL NuGet 库
接下来,让我们添加 GraphQL NuGet 包
dotnet add package GraphQL
你好 GraphQL
现在我们准备添加代码
// Program.cs
using System;
using GraphQL;
using GraphQL.Types;
namespace App
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var schema = Schema.For(@"
type Query {
hello: String
}
");
var root = new { Hello = "Hello World!" };
var json = schema.Execute(_ =>
{
_.Query = "{ hello }";
_.Root = root;
});
Console.WriteLine(json);
}
}
}
上面的代码有三个有趣的部分:
- 声明我们的模式
- 定义我们的解析器
- 执行我们的查询API
声明我们的模式
我们输入的字符串Schema.For
包含称为 GQL 或 GraphQL 查询语言的内容。它通常定义三件事:
- 查询,这就是我们可以查询的
- 类型,我们目前还没有任何类型,但我们稍后会在文章中介绍。
- 变异,这就是我们所说的语义上的改变
让我们特别关注设置模式的部分:
var schema = Schema.For(@"
type Query {
hello: String
}
");
目前,我们只有,type Query
并且它说我们可以查询,hello
并且响应是字符串类型hello: string
。
解决我们的疑问
Schema 只是整个难题的一部分。另一部分是解析器代码。这部分代码实际上负责处理传入的请求。在我们的例子中,解析器代码如下所示:
var root = new { Hello = "Hello World!" };
正如我们上面看到的,它映射了查询内部的一些内容hello
,并表示 - 如果用户要求hello
,那么我们就会回答Hello World
。
执行查询
在下面的代码中,我们通过将查询分配给我们提供的 lambda 表达式中的{ hello }
属性来执行查询。_.Query
schema.Execute()
var json = schema.Execute(_ =>
{
_.Query = "{ hello }";
_.Root = root;
});
然后将查询的结果分配给我们的变量json
,我们可以轻松地将其打印到终端:
Console.WriteLine(json);
架构优先
这是一种不同的方法,我们将使用类并装饰它们,其想法是,类将包含在我们进行查询时充当解析器的方法。
为了做到这一点,我们需要执行以下操作:
-
更新我们的模式,我们需要一个新的自定义类型,我们需要在
Query
-
创建一些我们需要的类来支持我们的类型、查询以及内存数据库
架构更新
到目前为止,我们的 schema 还没有自定义类型。让我们来改变这种情况,添加自定义类型Jedi
,并将其暴露为可查询的内容,如下所示:
Schema.For(@"
type Jedi {
name: String,
side: String
}
type Query {
hello: String,
jedis: [Jedi]
}
", _ =>
{
_.Types.Include<Query>();
});
上面我们通过编写 来添加Jedi
类型type Jedi
,并在 中定义其属性{}
。然后我们将jedis
其添加到Query
并声明其为 类型[Jedi]
,这意味着它是一个 数组Jedi
。最后,我们给出 的Schema.For()
第二个参数:
_.Types.Include<Query>();
这意味着我们指定了一个Query
处理所有传入请求的类。我们还没有这个类,所以让我们创建它。
创建支持类
首先,我们需要一个类Query
。它现在负责处理Query
我们模式中 内部的所有内容,这意味着它需要处理hello
和jedis
。让我们看一下它Query
是什么样子的:
public class Query
{
[GraphQLMetadata("jedis")]
public IEnumerable<Jedi> GetJedis()
{
return StarWarsDB.GetJedis();
}
[GraphQLMetadata("hello")]
public string GetHello()
{
return "Hello Query class";
}
}
这里我们定义了两个方法GetJedis()
和。注意我们如何在每个方法上GetHello()
使用装饰器来映射它们将处理的 中的哪个属性。我们还看到我们调用了一个类,因此接下来我们需要创建它:GraphQLMetadata
Query
StarWarsDB
public static IEnumerable<Jedi> GetJedis()
{
return new List<Jedi>() {
new Jedi(){ Name ="Luke", Side="Light"},
new Jedi(){ Name ="Yoda", Side="Light"},
new Jedi(){ Name ="Darth Vader", Side="Dark"}
};
}
定义查询的内容
让我们定义用于查询 GraphQL API 的查询:
{ jedis { name, side } }
上面我们说的是,我们想要查询jedis
,并且我们想要列name
和side
。将其与 SQL 表达式进行比较,在 SQL 表达式中,我们可以输入:
SELECT name, side
FROM jedis;
让我们用查询来更新代码,如下所示:
var json = schema.Execute(_ =>
{
_.Query = "{ jedis { name, side } }";
});
Console.WriteLine(json);
结果是:
看起来效果不错,好吧 :)
使用参数
现在我们已经完成了许多工作,但我们需要能够处理参数。有时我们只需要返回一条记录,因此需要根据键过滤更大的集合。为此,我们需要:
-
更新我们的架构以支持带参数的查询
-
在我们的查询类中创建一个能够解析参数的方法
-
验证一切正常
更新我们的架构
首先,我们需要更新我们的模式,以便我们支持带参数的查询,如下所示:
Schema.For(@"
type Jedi {
name: String,
side: String,
id: ID
}
type Query {
hello: String,
jedis: [Jedi],
jedi(id: ID): Jedi
}
", _ =>
{
_.Types.Include<Query>();
});
我们实际上在上面做了两件事。我们将字段添加id
到Jedi
类型中。我们还将此行添加到Query
:
jedi(id: ID): Jedi
更新我们的代码
我们的 Jedi 类没有字段,Id
所以我们需要添加它,如下所示:
public class Jedi
{
public int Id { get; set; }
public string Name { get; set; }
public string Side { get; set; }
}
接下来,我们需要向查询类添加一个方法,如下所示:
[GraphQLMetadata("jedi")]
public Jedi GetJedi(int id)
{
return StarWarsDB.GetJedis().SingleOrDefault(j => j.Id == id);
}
请注意,上面我们只需要将 matchid
作为输入参数即可。代码本身只是一个SingleOrDefault
LINQ 查询,返回一个条目。
更新查询
让我们尝试像这样查询这个新资源:
{ jedi(id: 1) { name } }
结果如下:
有效!!!:)
概括
我们已经讲解了很多内容。我们介绍了 GQL,即 GraphQL 查询语言。此外,我们还学习了如何在 schema 中定义资源、自定义类型以及如何解析这些资源。关于 GraphQL,还有很多内容需要学习,但本文已经够长了。希望这篇文章能让您初次体验 GraphQL,并乐在其中。在下一篇中,我们将介绍如何构建无服务器 API,并利用 GraphQL 将其部署到云端。
文章来源:https://dev.to/dotnet/learn-how-you-can-use-graphql-in-net-core-and-c-4h96