11. 播放列表的曲目
5m

概述

我们还缺少一些东西:播放列表的曲目 - 我们实际想听的歌曲!

在本课中,我们将

  • 创建Track 类型
  • 编写 函数,用于播放列表的曲目

The Track 对象

Mockup design of a playlist's tracks

花点时间研究上面的模型,并开始思考我们需要哪些数据,以及它们可能是什么类型。为了简化操作,我们现在将忽略艺术家信息。

你可能还想查看 GET /playlists/{playlist_id}/tracks 端点,以更好地了解 API 返回的内容以及它们对某些属性的命名。

慢慢来!

当你准备好后,将其与我们的结果进行比较

Mockup design of a playlist's tracks

  • 曲目的名称是 string.
  • “E” 标签表示曲目是否为显式曲目。我们可以将其设置为 bool 类型,并让客户端使用逻辑来显示“E” 标签或不显示。
  • 曲目的持续时间。模型显示的格式为分钟和秒之间用冒号隔开,因此我们可能需要将其设置为 string 类型,或者客户端团队可能希望控制他们自己的格式,我们应该将其作为 double 类型返回,持续时间以毫秒为单位。REST 端点返回后者,因此我们现在坚持使用它。
  • 虽然模型上没有显示,但为对象设置一个标识符非常有用,因此我们将确保也返回曲目的 ID。
  • 有一个选项可以复制曲目链接以与他人共享,以便他们也可以在 Spotify 上打开它。因此,我们可能需要将该链接作为 string 返回。在 REST API 中,他们将此命名为 uri
  • 我们将使所有这些 不可为空,因为 REST API 也这样做。

你的设计可能与我们的略有不同,但考虑到这些部分,让我们继续创建我们的 Track 类!请记住,我们将所有 类型一起组织在 Types 文件夹下。

你应该拥有所需的一切知识,可以尝试自己编写!如果你需要参考,请随意使用下面的内容

Types/Track.cs
namespace Odyssey.MusicMatcher;
[GraphQLDescription("A single audio file, usually a song.")]
public class Track
{
[ID]
[GraphQLDescription("The ID for the track.")]
public string Id { get; }
[GraphQLDescription("The name of the track.")]
public string Name { get; set; }
[GraphQLDescription("The track length in milliseconds.")]
public double DurationMs { get; set; }
[GraphQLDescription(
"Whether or not the track has explicit lyrics (true = yes it does; false = no it does not OR unknown)"
)]
public bool Explicit { get; set; }
[GraphQLDescription("The URI for the track, usually a Spotify link.")]
public string Uri { get; set; }
public Track(string id, string name, string uri)
{
Id = id;
Name = name;
Uri = uri;
}
}

提示:在选择模式中的 名称时,尽量做到描述性且简洁。例如,我们可以选择 duration 作为其中一个 名称。但是,durationMs 对返回以毫秒为单位的格式更具描述性。我们也可以选择 durationInMilliseconds 以便进一步说明。如果我们还想支持返回格式化的字符串,我们可以添加一个名为 durationString 的新 。使用 描述也有助于清晰度。 了解有关 Apollo 文档中模式命名约定的更多信息

不要忘记使用 [GraphQLDescription] 属性向模式添加描述。

将曲目连接到播放列表

在我们的 Track 类全部设置好后,我们现在可以添加期待已久的 tracks Playlist 中。

Playlist.cs
[GraphQLDescription("The playlist's tracks.")]
public List<Track> Tracks { get; set; }

让我们考虑一下这个特定的 函数。到目前为止,Playlist 类包含简单的属性 (用于 IdNameDescription)。请记住,在幕后,Hot Chocolate 将具有 get 访问器的属性转换为

我们是否可以对我们的 Tracks 做同样的事情?好吧,让我们检查一下我们的数据来自哪里。对于下一节,我们强烈建议使用代码编辑器的功能来 Cmd/Ctrl + 点击特定类型以导航到其类型导航!

特定播放列表的详细信息来自 Query.Playlist 函数,该函数使用 SpotifyService 方法 GetPlaylistAsync

如果我们在我们的 SpotifyService.cs 文件中跟随路径,我们可以看到响应返回一个 SpotifyWeb.Playlist 类型。此类型具有一个名为 Tracks 的属性,类型为 PaginatedOfPlaylistTrackPaginatedOfPlaylistTrack 然后具有许多与分页相关的其他属性,以及一个名为 Items 的属性,它是一个 PlaylistTrack 类型的集合。

跟随 that 类型,我们得到更多与播放列表曲目的元数据相关的属性,例如谁添加了它以及何时添加,以及一个名为 Track 的属性,类型为 PlaylistTrackItemFinally,这看起来像我们正在寻找的 Track 信息!在其他属性中,它具有 IdNameExplicitDuration_ms 属性,与我们 ourTrack 类在我们的 中的属性完全匹配。

哇!所有这些都说明,我们必须深入挖掘几层才能返回我们需要的价值。

但是,我们实际上已经完成了第一级。我们将 SpotifyWeb.Playlist 类型转换成了我们 自己Playlist 类,使用构造函数。

Playlist.cs
public Playlist(SpotifyWeb.Playlist obj)
{
Id = obj.Id;
Name = obj.Name;
Description = obj.Description;
}

所以我们可以扩展这个构造函数,并使用 SpotifyWeb.Playlistobj 来初始化 Playlist.Tracks 属性

让我们一步一步来!确保依赖你的代码编辑器的 IntelliSense,以便更好地了解每个级别可用的属性。

Playlist(SpotifyWeb.Playlist obj) 构造函数中,我们首先提取分页轨道的集合。

Playlist.cs
var paginatedTracks = obj.Tracks.Items;

这个 paginatedTracks 目前是一个 SpotifyWeb.PlaylistTrackItem 对象的集合。这非常接近我们需要的!我们需要我们 自己Track 类。这应该是一个熟悉的模式,我们之前已经使用过两次了。

我们将创建一个新的、额外的 Track 构造函数,它接受一个 SpotifyWeb.PlaylistTrackItem 对象,并根据该对象的属性初始化其 。跳转到 Track.cs 文件:

Track.cs
public Track(PlaylistTrackItem obj)
{
Id = obj.Id;
Name = obj.Name;
DurationMs = obj.Duration_ms;
Explicit = obj.Explicit;
Uri = obj.Uri;
}

不要忘记在顶部导入 SpotifyWeb 命名空间,因为 PlaylistTrackItem 来自该包。

Track.cs
using SpotifyWeb;

我们现在可以在我们的 Playlist 构造函数中使用这个 Track 构造函数。我们将遍历这些项目,并返回一个新的集合,从 item.Track 创建一个 Track 实例。

Playlist.cs
var trackObjects = paginatedTracks.Select(item => new Track(item.Track));

最后,我们将转换为 List 类型,因为这是 Tracks 属性期望的类型。

Playlist.cs
Tracks = trackObjects.ToList();

将所有内容放在一起,新的扩展 Playlist 构造函数如下所示:

Playlist.cs
public Playlist(SpotifyWeb.Playlist obj)
{
Id = obj.Id;
Name = obj.Name;
Description = obj.Description;
var paginatedTracks = obj.Tracks.Items;
var trackObjects = paginatedTracks.Select(item => new Track(item.Track));
Tracks = trackObjects.ToList();
}

想要更简洁一点吗?我们可以简化为单行代码

Playlist.cs
public Playlist(SpotifyWeb.Playlist obj)
{
Id = obj.Id;
Name = obj.Name;
Description = obj.Description;
Tracks = obj.Tracks.Items.Select(item => new Track(item.Track)).ToList();
}

探索时间!

写了这么多代码,但我们应该有足够的代码来 播放列表的曲目了!确保我们的服务器正在运行最新的更改。

回到 Explorer 中,让我们在原始 中添加播放列表轨道的

GraphQL 操作
query GetPlaylistDetails($playlistId: ID!) {
playlist(id: $playlistId) {
id
name
description
tracks {
id
name
durationMs
explicit
uri
}
}
}

变量 部分设置为:

变量
{
"playlistId": "6Fl8d6KF0O4V5kFdbzalfW"
}
https://studio.apollographql.com/sandbox/explorer

Explorer - get playlist's tracks

哇,这个播放列表有这么多曲目!

任务!

另一种途径

那么 featuredPlaylists 路径呢?它是我们架构的另一个入口点,它返回一个 Playlist 类型的列表,然后可以访问其 tracks 。让我们试试。

GraphQL 操作
query GetFeaturedPlaylists {
featuredPlaylists {
id
name
description
tracks {
id
name
explicit
uri
}
}
}

当我们运行这个 时,我们得到了一个包含消息“无法为非空字段返回 null”的 errors 数组。糟糕!

关键要点

  • 函数可能涉及遍历多个数据级别,特别是在嵌套对象的情况下。
  • 我们建议为架构选择描述性但简洁的 名称。

下一步

让我们调查一下错误的来源,并修复它。

上一页

分享你关于本课的问题和评论

本课程目前处于

测试版
.你的反馈帮助我们改进!如果你卡住了或困惑,请告诉我们,我们会帮助你。所有评论都是公开的,必须遵守 Apollo 行为准则。请注意,已解决或已处理的评论可能会被删除。

你需要一个 GitHub 帐户才能在下面发帖。没有吗? 改为在我们 Odyssey 论坛上发帖。