Apollo v4文件上传报错:POST body缺失等问题求助
Apollo v4 文件上传问题解决方案
错误原因
- 你遇到的第一个错误是因为文件上传请求采用
multipart/form-data格式,但服务端配置了bodyParser.json(),无法解析该格式,导致Apollo无法识别请求体。 - 第二个错误是手动强制设置
Content-Type: application/json后,multipart请求的边界标识(如------WebKit...)无法被JSON解析器识别,引发格式错误。
解决步骤
1. 安装依赖
安装multer用于解析multipart类型的请求:
npm install multer
2. 修改服务端配置(Server.js)
替换原有的bodyParser.json(),改用multer处理multipart请求,并将上传的文件映射到GraphQL变量中:
// Server.js import { ApolloServer } from '@apollo/server'; import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer'; import { expressMiddleware } from '@apollo/server/express4'; import express from 'express'; import http from 'http'; import cors from 'cors'; import multer from 'multer'; import { makeExecutableSchema } from '@graphql-tools/schema'; import { typeDefs, resolvers } from "./schema.js"; // 配置multer使用内存存储,临时存放上传文件 const storage = multer.memoryStorage(); const upload = multer({ storage: storage }); async function startServer() { const schema = makeExecutableSchema({ resolvers, typeDefs }); const app = express(); const httpServer = http.createServer(app); const server = new ApolloServer({ typeDefs, resolvers, plugins: [ApolloServerPluginDrainHttpServer({ httpServer })], }); await server.start(); // 自定义中间件,区分处理multipart和普通JSON请求 app.use('/', cors(), (req, res, next) => { if (req.is('multipart/form-data')) { upload.any()(req, res, () => { // 解析GraphQL multipart格式的operations和map字段 const operations = JSON.parse(req.body.operations); const map = JSON.parse(req.body.map); // 将上传文件映射到对应的GraphQL变量 Object.keys(map).forEach(fileKey => { const variablePath = map[fileKey][0]; const [, varName] = variablePath.split('.'); const file = req.files.find(f => f.fieldname === fileKey); if (file && operations.variables) { operations.variables[varName] = file; } }); // 替换请求体为解析后的operations,供Apollo处理 req.body = operations; next(); }); } else { // 非文件上传请求使用JSON解析 express.json()(req, res, next); } }); app.use( '/', expressMiddleware(server, { context: async ({ req }) => ({ token: req.headers.token }), }), ); await new Promise((resolve) => httpServer.listen({ port: 4000 }, resolve)); console.log(`🚀 Server ready at http://localhost:4000/`); } startServer();
3. 完善Upload标量解析(resolvers)
在resolvers中添加Upload标量的解析逻辑,确保文件对象能正确传递到mutation中:
// mutation.js export default { Upload: { __parseValue: (value) => value, // 直接返回multer处理后的文件对象 __serialize: (value) => value, __parseLiteral: (ast) => null, // 字面量解析无需处理,返回null }, Mutation: { createPost: async (_, { title, content, userId, categoryId, file }) => { // file为multer的File对象,包含buffer、originalname、size等属性 console.log('上传的文件信息:', file); // 此处可添加文件存储、数据库写入等业务逻辑 return { ok: true }; } } }
4. Altair测试注意事项
- 不要手动设置
Content-Type,Altair在选择文件后会自动设置为multipart/form-data - 在变量面板中,将
file字段的类型设置为File,并选择本地文件 - 使用如下mutation模板测试:
mutation CreatePost($title: String!, $content: String!, $userId: Int!, $categoryId: Int!, $file: Upload) { createPost(title: $title, content: $content, userId: $userId, categoryId: $categoryId, file: $file) { ok } }
内容的提问来源于stack exchange,提问作者tr_korea
相关产品推荐
相关产品推荐

