查询 HotChocolate 时出错 "The ID `1` has an invalid format"

Error "The ID `1` has an invalid format" when querying HotChocolate

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

有一个部分,其中 id 标记为 IDAttribute 的查询参数。

ID 类型说明如下:

The ID scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as "4") or integer (such as 4) input value will be accepted as an 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] 属性并将其用作 GraphQL 查询中的 'Int!',它可以正常工作。

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

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

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 需要更多 in-place 解释这些东西的目的。可以找到here.

首先,ID scalar type是GraphQL标准的一部分,定义为:

In GraphQL the ID scalar type represents a unique identifier, often used to refetch an object or as the key for a cache. The ID type is serialized in the same way as a String; however, defining it as an ID signifies that it is not intended to be human‐readable.

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

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

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

即使您在 HotChocolate 中启用了“中继支持”,您也不必使用中继,您仍然可以使用任何 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 source。如果您尝试使用 base64 解码此字符串:

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

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

这个查询应该有效:

query getEmployeeById {
  employeeById(id: "TGFuZ3VhZ2UKaTE=") {
    familyName
  }
}