You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用HotChocolate合并两个同Schema服务实现API组合网关?

问题:HotChocolate网关合并多同Schema服务的查询结果

我想用HotChocolate搭建API组合网关,拼接多个服务的GraphQL接口。当各服务Schema不同时运行正常,但遇到两个服务提供相同Schema的场景(比如两个图书服务都提供Book Schema),希望网关把Book Schema的请求转发到所有服务并合并结果,但目前HotChocolate只会调用其中一个Books端点。

现有代码

服务端配置

var builder = WebApplication.CreateBuilder();
builder.Services.AddGraphQLServer()
    .AddRemoteSchema(WellKnownSchemaNames.Books)
    .AddRemoteSchema(WellKnownSchemaNames.Authors)    
    .AddTypeExtensionsFromFile("./Stitching.graphql");

builder.Services.AddHttpClient(WellKnownSchemaNames.Books, c => c.BaseAddress = new Uri("http://localhost:7011/graphql"));
builder.Services.AddHttpClient(WellKnownSchemaNames.Authors, c => c.BaseAddress = new Uri("http://localhost:7012/graphql"));
// 注意此客户端Schema相同但端口不同
builder.Services.AddHttpClient(WellKnownSchemaNames.Books, c => c.BaseAddress = new Uri("http://localhost:7013/graphql"));

var app = builder.Build();
app.MapGraphQL();
app.Run($"http://localhost:7010");

Book的Schema与查询逻辑

public class Book
{
    public string Id { get; set; }    
    public string Title { get; set; }
}
public class BooksQuery
{    
    public static Book[] _books;  // 初始化时设置,两个端点的值不同
    public Book[] Books => _books;
    public Book GetBook(string id) => _books.Single(b => b.Id == id);
}

期望行为

  • books:查询两个端点,合并结果后返回
  • book(id: "x"):查询两个端点,若任意端点返回非错误结果则返回该结果

解决方案

问题出在重复添加同名的RemoteSchema,HotChocolate会覆盖之前的配置,只会保留最后一个。要实现多服务结果合并,需要给每个服务分配唯一标识,再通过自定义解析器处理请求转发和结果合并。

1. 配置唯一命名的RemoteSchema和HttpClient

给每个图书服务设置独立的Schema名称,避免覆盖:

var builder = WebApplication.CreateBuilder();

builder.Services.AddGraphQLServer()
    .AddRemoteSchema("BooksService1")
    .AddRemoteSchema("BooksService2")
    .AddRemoteSchema(WellKnownSchemaNames.Authors)
    .AddTypeExtensionsFromFile("./Stitching.graphql")
    .AddResolver<BookResolvers>(); // 注册自定义解析器

// 为每个服务配置独立HttpClient
builder.Services.AddHttpClient("BooksService1", c => c.BaseAddress = new Uri("http://localhost:7011/graphql"));
builder.Services.AddHttpClient("BooksService2", c => c.BaseAddress = new Uri("http://localhost:7013/graphql"));
builder.Services.AddHttpClient(WellKnownSchemaNames.Authors, c => c.BaseAddress = new Uri("http://localhost:7012/graphql"));

var app = builder.Build();
app.MapGraphQL();
app.Run($"http://localhost:7010");

2. 编写类型扩展文件(Stitching.graphql)

定义网关对外暴露的查询字段,并关联自定义解析器:

extend type Query {
  books: [Book!]! @resolver(name: "GetBooksAsync")
  book(id: String!): Book @resolver(name: "GetBookByIdAsync")
}

type Book @extends {
  id: String!
  title: String!
}

extend schema
  @link(
    url: "https://specs.apollo.dev/federation/v2.0"
    import: ["@extends"]
  )

3. 实现自定义解析器

编写C#类处理多服务调用和结果合并:

public class BookResolvers
{
    private readonly IQueryExecutor _queryExecutor;

    public BookResolvers(IQueryExecutor queryExecutor)
    {
        _queryExecutor = queryExecutor;
    }

    // 合并两个服务的books结果
    public async Task<Book[]> GetBooksAsync()
    {
        var serviceNames = new[] { "BooksService1", "BooksService2" };
        var tasks = serviceNames.Select(async name =>
        {
            var response = await _queryExecutor.ExecuteAsync(new QueryRequest
            {
                Query = "{ books { id title } }",
                SchemaName = name
            });
            return response.Data.GetFieldValue<Book[]>("books");
        });

        var allBooks = await Task.WhenAll(tasks);
        return allBooks.SelectMany(books => books).ToArray();
    }

    // 查询两个服务,返回第一个非空结果
    public async Task<Book?> GetBookByIdAsync(string id)
    {
        var serviceNames = new[] { "BooksService1", "BooksService2" };
        foreach (var name in serviceNames)
        {
            var response = await _queryExecutor.ExecuteAsync(new QueryRequest
            {
                Query = "{ book(id: $id) { id title } }",
                Variables = new Dictionary<string, object> { { "id", id } },
                SchemaName = name
            });

            var book = response.Data.GetFieldValue<Book?>("book");
            if (book != null)
            {
                return book;
            }
        }
        return null;
    }
}

内容的提问来源于stack exchange,提问作者EJHewy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 01:48:20