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

Apollo Supergraph调用Subgraph传递Upload标量类型失败问题

解决Apollo Supergraph转发Upload类型请求到gqlgen Subgraph的错误问题

问题分析

Apollo Supergraph(Node.js网关)默认会解析前端发送的multipart格式文件上传请求,将Upload类型解析为内部对象后以JSON格式转发给Go语言gqlgen编写的Subgraph。但gqlgen的Subgraph期望接收符合GraphQL multipart规范的请求,而非JSON结构中的普通对象,因此抛出错误"message": "map[string]interface {} is not an Upload"。直接调用Subgraph正常,说明Subgraph本身的文件上传逻辑无问题,问题出在Supergraph的请求转发处理上。

可行解决方案

方案一:配置Supergraph透传multipart请求(推荐)

让Supergraph不对文件上传请求做解析,直接将原始的multipart请求体转发给对应Subgraph,保留请求的原始格式,符合gqlgen的接收要求。

  1. 修改Apollo Gateway的服务构建逻辑:
const { ApolloGateway } = require('@apollo/gateway');
const { fetch } = require('cross-fetch');

const gateway = new ApolloGateway({
  serviceList: [/* 填入你的Subgraph服务列表,例如{ name: 'upload-service', url: 'http://your-subgraph-url' } */],
  buildService({ name, url }) {
    return {
      async request({ request, context }) {
        // 判断是否为文件上传的Mutation请求
        const isUploadRequest = request.query.includes('mutation') && request.query.includes('Upload');
        if (isUploadRequest && context.req) {
          // 透传原始multipart请求
          const response = await fetch(url, {
            method: 'POST',
            headers: {
              ...context.req.headers,
              // 强制保留multipart请求的Content-Type
              'Content-Type': context.req.headers['content-type'],
            },
            body: context.req.body,
          });
          return response.json();
        }
        // 非上传请求按默认逻辑处理
        return fetch(url, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(request),
        }).then(res => res.json());
      },
    };
  },
});
  1. 将原始请求对象传入Context:
    在Apollo Server的配置中,把前端请求的req对象注入到context中,让网关能获取到原始请求体:
const { ApolloServer } = require('apollo-server');

const server = new ApolloServer({
  gateway,
  context: ({ req }) => ({ req }),
});

server.listen().then(({ url }) => {
  console.log(`Supergraph running at ${url}`);
});

方案二:修改gqlgen Subgraph兼容JSON格式的Upload对象

如果无法修改Supergraph配置,可以在Subgraph端自定义Upload类型的解析器,将Supergraph转发的JSON结构转换为gqlgen可识别的类型。

  1. 配置gqlgen自定义解析器:
    在gqlgen.yml中指定Upload类型的自定义解析逻辑:
models:
  Upload:
    model: github.com/99designs/gqlgen/graphql.Upload
  1. 编写自定义解析函数:
package graph

import (
	"context"
	"mime/multipart"
	"os"

	"github.com/99designs/gqlgen/graphql"
)

// 解析Supergraph转发的JSON格式Upload对象
func UnmarshalUpload(v interface{}) (multipart.File, *multipart.FileHeader, error) {
	fileData, ok := v.(map[string]interface{})
	if !ok {
		return nil, nil, graphql.NewError("invalid Upload format from Supergraph")
	}
	fileInfo, ok := fileData["file"].(map[string]interface{})
	if !ok {
		return nil, nil, graphql.NewError("missing file info in Upload")
	}

	// 此处需要注意:Supergraph转发时仅传递了文件元数据,未传递文件内容
	// 若要使用此方案,需修改Supergraph在转发时同时传递文件的base64内容,再在此处解码为临时文件
	// 示例(假设Supergraph添加了content字段):
	// contentBase64 := fileInfo["content"].(string)
	// content, _ := base64.StdEncoding.DecodeString(contentBase64)
	// tempFile, _ := os.CreateTemp("", "upload-*")
	// tempFile.Write(content)
	// tempFile.Seek(0, 0)
	// fh := &multipart.FileHeader{Filename: fileInfo["filename"].(string)}
	// return tempFile, fh, nil

	// 注:此方案需Supergraph配合修改,复杂度较高,优先推荐方案一
	return nil, nil, graphql.NewError("file content not found in Upload data")
}

func MarshalUpload(f multipart.File, fh *multipart.FileHeader) interface{} {
	return map[string]interface{}{
		"file": map[string]interface{}{
			"filename": fh.Filename,
			"mimetype": fh.Header.Get("Content-Type"),
			"encoding": "7bit",
		},
	}
}

方案三:预上传文件至S3,传递URL替代Upload类型

绕开GraphQL的multipart上传机制,先将文件上传至S3等存储服务,再将文件URL作为字符串参数传递给Supergraph,Subgraph通过URL下载文件处理。

  1. 前端流程:
  • 调用后端接口获取S3预签名上传URL
  • 将文件上传至S3,获取文件的公开访问URL
  • 调用GraphQL Mutation,将URL作为参数传入
  1. Subgraph处理:
    接收URL参数后,发起HTTP请求下载文件,进行后续业务处理。

此方案无需处理GraphQL Upload类型的兼容性问题,但需要额外的存储服务配置和前端逻辑改造。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 00:12:18