概述
让我们完成我们的突变!
在本课中,我们将
- 了解有关突变的常见约定参数以及
input
类型 - 了解如何使用突变响应来处理成功和失败的操作
突变参数和输入
我们之前在GraphQL的参数中使用过参数,我们传入了一个名为id
的参数。
type Query {playlist(id: ID!): Playlist}
对于addItemsToPlaylist
的突变,我们需要不止一个参数。
来自文档,我们需要以下参数:
playlist_id
- 播放列表的 ID,作为string
position
- 一个integer
,从零开始的索引,我们要在该位置插入曲目。uris
- 以逗号分隔的string
,包含我们要添加的轨道的uri
值。
我们可以将所有三个都用作参数,但是一个好的做法是将GraphQL的输入类型用作参数,用于字段。
在GraphQL 模式中,input
类型是一种特殊的对象类型,它将一组参数组合在一起,然后可以作为另一个字段的参数使用。
作为一种命名约定,我们在类型的名称后面添加Input
后缀,并赋予它与突变关联的相同名称。
在本例中,我们可以将参数命名为AddItemsToPlaylistInput
,用于addItemsToPlaylist
字段。让我们开始创建它吧!
AddItemsToPlaylistInput 类型
在Types
文件夹下,让我们添加一个新的类,用于AddItemsToPlaylistInput
。
namespace Odyssey.MusicMatcher;public class AddItemsToPlaylistInput{}
正如我们所提到的,一个常见的约定是在input
类型的名称末尾添加Input
。此外,当我们使用Input
后缀时,在幕后,Hot Chocolate 会将这个类转换为GraphQL的input
类型。当我们进入 Explorer 时,我们将看到这一点!
接下来,我们将添加属性。请记住,我们需要播放列表的 ID 和 URI 列表,至少。我们可以在播放列表中指定这些项目添加的位置,但这对于 REST API 来说不是必需的。默认情况下,曲目将追加到播放列表的末尾,因此我们可以从我们的GraphQL 模式中省略它。请记住,你的 GraphQL API 不需要完全匹配你的 REST API!
[ID][GraphQLDescription("The ID of the playlist.")]public string PlaylistId { get; set;}[GraphQLDescription("A comma-separated list of Spotify URIs to add.")]public List<string> Uris { get; set; }
不要忘记构造函数!
public AddItemsToPlaylistInput(string playlistId, List<string> uris){PlaylistId = playlistId;Uris = uris;}
更新解析器
现在让我们确保我们的解析器了解这个输入。回到Mutation.cs
中,我们可以将它添加到我们函数的参数中。我们将这个解析器的参数命名为input
。
请注意,input
参数可以命名为任何名称,例如playlistTracks
或tracksInput
!我们建议与团队协作,决定命名约定。使用input
作为GraphQL的参数名称是一个常见的约定。
public AddItemsToPlaylistPayload AddItemsToPlaylist(AddItemsToPlaylistInput input)
趁我们在这里,让我们也连接SpotifyService
的数据源,使这个函数异步,并将返回类型编辑为Task<T>
。
public async Task<AddItemsToPlaylistPayload> AddItemsToPlaylist(AddItemsToPlaylistInput input,SpotifyService spotifyService)
同样,不要忘记在顶部导入SpotifyWeb
命名空间!
using SpotifyWeb;
现在该使用我们的服务和输入类型了!在解析器函数的主体中,我们将使用AddTracksToPlaylistAsync
方法,该方法来自spotifyService
。
var snapshot_id = await spotifyService.AddTracksToPlaylistAsync(input.PlaylistId,null,string.Join(",", input.Uris));
使用方法签名作为指南,我们可以添加来自input
的参数的对应值,用于PlaylistId
,并将null
传递给position
。
对于最后一个参数,我们需要稍微调整一下格式。因为 input.Uris
是一个 list 类型的 string
,而方法需要一个以逗号分隔的 string
类型的值,所以我们会使用 string.Join
.
我们 await
方法的结果,并将其存储在一个名为 variable 的变量中,名为 snapshot_id
。这是方法的返回类型, 不是 我们架构期望的播放列表对象。
这仅仅是我们要使用的 REST 端点的特性。为了检索 playlist
对象,我们需要使用之前用过的 GetPlaylistAsync
方法进行后续调用。
但是我们应该在哪里进行调用呢?如果我们将它包含在这个 resolver 中,这意味着即使 playlist
field 没有包含在 GraphQL mutation 中,也会进行额外的 REST 调用!
我们之前已经遇到过这种情况,当时我们使用了 resolver 链。但是,在这种情况下,我们将保持 REST 调用包含在这个解析器中。考虑到客户端应用程序的需求,如果他们要向播放列表添加曲目,他们 很可能 会将播放列表及其曲目列表包含在 GraphQL operation 中!他们想要看到 结果 他们的 mutation 毕竟。
让我们继续。在 Playlist
resolver 中,我们将调用 spotifyService.GetPlaylistAsync
方法。
var response = await spotifyService.GetPlaylistAsync(input.PlaylistId);var playlist = new Playlist(response);
看起来熟悉吗?这是我们在 Query.Playlist
resolver 中使用的代码。
最后,让我们更新 AddItemsToPlaylistPayload
实例中的返回值,具体来说,用 playlist
variable 替换硬编码的播放列表。
return new AddItemsToPlaylistPayload("200",true,"Successfully added items to playlist",playlist);
成功路径已经处理好了!现在如果出现错误怎么办?让我们将到目前为止的代码包装在 try
块中,并 catch
任何抛出的 Exception
。
try{var snapshot_id = await spotifyService.AddTracksToPlaylistAsync(input.PlaylistId,input.Position,string.Join(",", input.Uris));var response = await spotifyService.GetPlaylistAsync(input.PlaylistId);var playlist = new Playlist(response);return new AddItemsToPlaylistPayload(200,true,"Successfully added items to playlist",playlist);}catch (Exception e){// return something}
如果出现错误,我们的返回值应该略有不同。我们仍然会返回 AddItemsToPlaylistPayload
类型,但这次代码将是 500
,成功状态将是 false
,我们还会返回异常包含的任何消息。 playlist
参数将被设置为 null
。
return new AddItemsToPlaylistPayload(500, false, e.Message, null);
探索时间!
在服务器运行了我们最新的更改之后,让我们打开一个新的工作区选项卡,从头开始构建我们的 mutation。随着 input
argument 的添加,当我们添加 addItemsToPlaylist
field 时, GraphQL operation 看起来有点不同了:
mutation AddItemsToPlaylist($input: AddItemsToPlaylistInput!) {addItemsToPlaylist(input: $input) {}}
我们还可以看到 变量 部分,其中在 JSON 对象中有一个 input
属性:
{"input": null}
让我们开始填充这个 input
对象。在 文档 面板中,点击 input
field,并添加 AddItemsToPlaylistInput
中的三个字段。Explorer 会自动更新你的 variables。
{"input": {"playlistId": null,"uris": null}}
我们只需要更新这些 null
!我们将使用一个新的播放列表 ID 并添加一个包含一个 URI 的数组。这些都是模拟数据,所以你可以随意放入任何你想放入的东西作为占位符。它不需要实际存在于 Spotify 数据库中。
{"input": {"playlistId": "6LB6g7S5nc1uVVfj00Kh6Z","uris": ["ASongAboutGraphQL"]}}
最后,让我们用我们需要的字段填充 mutation 的其余部分。添加 code
、success
和 message
fields。然后对于播放列表,添加其详细信息 以及 其曲目!
mutation AddItemsToPlaylist($input: AddItemsToPlaylistInput!) {addItemsToPlaylist(input: $input) {codemessagesuccessplaylist {idnametracks {idname}}}}
这是一个很大的 mutation,按播放运行!我们应该看到我们的数据成功返回。在 tracks
列表的底部,我们将看到 id
设置为我们传递给 variable 的 uri
,以及名称。
注意:你的响应看起来有点不同吗?因为 REST API 是所有 Odyssey 学习者共享的,你可能会在响应中看到更多或更少的曲目。这很可能是因为 其他 学习者将他们自己的曲目添加到播放列表中。我们也会定期重置数据以保持数据干净。
太棒了!现在看看如果你将播放列表 ID 更新为一个不存在的值会发生什么。
{"input": {"playlistId": "DoesNotExist","uris": ["ASongAboutGraphQL"]}}
当我们再次运行 mutation 时,我们仍然会收到数据,但这次我们得到了一个错误代码,一个失败消息,以及一个 null
播放列表。
我们的 mutation 工作了! 🎉
练习
input
类型?关键要点
- Mutations 在 GraphQL 中通常需要多个 arguments 来执行操作。为了将参数分组在一起,我们使用 GraphQL
input
类型来提高清晰度和可维护性。 - 当我们在类中添加 "Input" 后缀时,Hot Chocolate 会将这个 C# 类转换为 GraphQL
input
类型到 schema 中。 - 我们可以像访问其他任何 GraphQL 参数一样访问
input
argument 在 resolver 函数中。
结论
太棒了,您成功了,您构建了一个GraphQL API!您拥有一个运行良好的GraphQL 服务器,其中包含使用 Spotify REST API 作为数据源的播放列表和曲目。您已经编写了查询和变异,并在此过程中了解了一些常见的 GraphQL 约定。您已经探索了如何在 GraphQL 中使用参数、变量和输入类型进行模式设计。花点时间庆祝一下;您学到了很多东西!
但这仅仅是开始:当您准备好进一步提升您的GraphQL API 时,请进入本系列的下一门课程: 使用 C# 和 Hot Chocolate 进行联合。
如果您有任何想要看到的内容的请求,请在下面告诉我们!
分享您对本课的疑问和评论
本课程目前处于
您需要一个 GitHub 帐户才能在下面发布。没有帐户? 请在我们的 Odyssey 论坛上发布。