【问题标题】:Error "The ID `1` has an invalid format" when querying HotChocolate查询 HotChocolate 时出现错误“ID `1` 的格式无效”
【发布时间】:2020-10-06 13:25:10
【问题描述】:

尝试制作自己的项目,以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` has an invalid format”。

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

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

【问题讨论】:

    标签: hotchocolate


    【解决方案1】:

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

    在 GraphQL 中,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 source。如果你尝试base64解码这个字符串:

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

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

    这个查询应该可以工作:

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

    【讨论】:

    • 顺便说一句,我通过在 HotChocolate 的 Slack 频道中提问找到了答案。
    • 一件事,ID 类型将在客户端显示为“字符串”类型。因此,在使用 int id 作为参数执行每个查询之前,您需要将其转换为输入类型“数字”(TypeScript)。也许将 ID 的关联设置为string | number 会很好。我们需要看看会发生什么。
    • 您还可以通过扩展 IIdSerializer 并使用 .AddGrahQLSever().AddIdSerializer() 在 Hot Chocolate 中添加自定义 ID 序列化程序
    【解决方案2】:

    发现 IDAttribute 用于 Relay(因为它位于 HotChocolate.Types.Relay 命名空间中)。所以需要启用和配置 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 需要更多就地解释这些事情的目的。可以找到here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-06
      • 2017-11-16
      • 2020-04-21
      • 1970-01-01
      • 2015-08-28
      • 2020-05-25
      相关资源
      最近更新 更多