3

试图制作自己的项目,以ChilliCream/graphql-workshop作为例子。

有一部分,其中id查询的参数标有IDAttribute

ID类型的描述如下:

ID 标量类型表示唯一标识符,通常用于重新获取对象或作为缓存的键。ID 类型以字符串形式出现在 JSON 响应中;但是,它并非旨在供人类阅读。当期望作为输入类型时,任何字符串(例如“4”)或整数(例如 4)输入值都将被接受为 ID。

我的 C# 查询源看起来像

[ExtendObjectType(Name = GraphqlQueryNames.Query)]
public class EmployeeQuery
{
    public async Task<Employee> GetEmployeeByIdAsync(
        [ID] int id,
        [Service] IEmployeeRepository employeeRepository,
        CancellationToken token)
    {
        return await employeeRepository.GetEmployeeByIdAsync(id, token);
    }
}

在操场上:

# 1 passed as value of $id

query getEmployeeById($id: ID!) {
  employeeById(id: $id) {
    familyName
  }
}

无论值是字符串还是数字,服务器都会抛出相同的错误“ID `1` 的格式无效”。

如果我们从 C# 中删除 [ID] 属性并将其用作 'Int!' 在 GraphQL 查询中,它工作正常。

ID 有什么问题以及为什么它存在于示例 (AttendeeQueries.cs) 中?热巧克力 10.5.3

4

2 回答 2

3

首先,ID 标量类型是 GraphQL 标准的一部分,定义为:

在 GraphQL 中,ID 标量类型表示唯一标识符,通常用于重新获取对象或作为缓存的键。ID类型的序列化方式与String相同;但是,将其定义为 ID 意味着它不适合人类阅读。

Relay是一个用于 React 应用程序的 GraphQL 客户端 JavaScript 框架。Apollo GraphQL 是另一种选择。

HotChocolate 有一些帮助程序来启用“中继式 GraphQL API”。这些助手将 id 字段转换为 base64 编码的哈希,序列化为字符串。这是一件好事,因为:

  • 它有助于缓存,所有实体都有一个唯一的ID
  • 对用户隐藏实际 ID (?)

即使您在 HotChocolate 中启用了“中继支持”,您也不必使用Relay,您仍然可以使用任何 GraphQL 客户端(我最喜欢 Apollo 客户端)

现在,如果您只想使用 GraphQL ID 标量类型,您可以按照我的建议尝试一下:

首先从查询中删除 ID 属性:

[ExtendObjectType(Name = GraphqlQueryNames.Query)]
public class EmployeeQuery
{
    public async Task<Employee> GetEmployeeByIdAsync(
        int id,
        [Service] IEmployeeRepository employeeRepository,
        CancellationToken token)
    {
        return await employeeRepository.GetEmployeeByIdAsync(id, token);
    }
}

然后像这样指定 ID 标量类型:

public class EmployeeType : ObjectType<Employee>
{
    protected override void Configure(IObjectTypeDescriptor<Employee> descriptor)
    {
        descriptor.Field(r => r.Id).Type<IdType>;
    }
}

但是,如果您想在 HotChocolate 中启用“中继支持”,请按照 Arsync 的回答(我将其更改为 HotChocolate v11,其语法略有不同):

public class Startup
{
  public void ConfigureServices(IServiceCollection services) 
  {
    services.AddGraphQLServer().EnableRelaySupport();
  }
}

Hot Chocolate v12 的更新:现在可以启用全局识别,但没有其他与中继相关的内容:

public class Startup
{
  public void ConfigureServices(IServiceCollection services) 
  {
    services.AddGraphQLServer().AddGlobalObjectIdentification();
  }
}

然后在您的类型定义中:

public class EmployeeType : ObjectType<Employee>
{
    protected override void Configure(IObjectTypeDescriptor<Employee> descriptor)
    {
        // Relay ID support. Retailer.Id will be a hash. the real id / int is available below when passed to DataLoader
        descriptor
            .ImplementsNode()
            .IdField(c => c.Id)
            .ResolveNode(((context, id) => context.DataLoader<EmployeeByIdDataLoader>().LoadAsync(id, context.RequestAborted)));
    }
}

或者使用 v12 更简单:

public class EmployeeType : ObjectType<Employee>
{
    protected override void Configure(IObjectTypeDescriptor<Employee> descriptor)
    {
        descriptor
            .Field(f => f.Id).ID(nameof(Employee));
    }
}

如果您现在尝试查询员工,您将看到 id 不是整数,而是哈希。类似“TGFuZ3VhZ2UKaTE=”的东西。此哈希由 HotChocolate 生成,请参阅IdSerializer 源代码。如果您尝试对这个字符串进行 base64 解码:

$ echo "TGFuZ3VhZ2UKaTE=" | base64 -d
Employee
i1

您收到“ID1格式无效”的错误消息是因为它现在需要一个散列字符串,而不是整数。

此查询应该有效:

query getEmployeeById {
  employeeById(id: "TGFuZ3VhZ2UKaTE=") {
    familyName
  }
}
于 2021-03-17T14:38:48.370 回答
2

发现 IDAttribute 用于 Relay(因为它位于HotChocolate.Types.Relay命名空间中)。所以需要启用和配置中继支持(来源):

ISchema schema = SchemaBuilder.New()
    .EnableRelaySupport()
    ...
    .Create();

在 ObjectType 中:

public class MyObjectType
    : ObjectType<MyObject>
{
    protected override void Configure(IObjectTypeDescriptor<MyObject> descriptor)
    {
        descriptor.AsNode()
            .IdField(t => t.Id)
            .NodeResolver((ctx, id) =>
                ctx.Service<IMyRepository>().GetMyObjectAsync(id));
        ...
    }
}

似乎示例项目graphql-workshop需要对这些事情的目的进行更多的就地解释。可以在这里找到。

于 2020-10-07T07:37:08.343 回答