快速设置多部分/表单数据错误:“身体必须是对象”
我正在使用Fastify-multer和JSON模式提交可能包含文件的多部分表单数据。不管我做什么,factify都会给我一个不好的响应错误:
{
"statusCode": 400,
"error": "Bad Request",
"message": "body must be object"
}
这是我的index.ts
:
const server = fastify();
server.register(require("@fastify/cors"));
server.register(multer.contentParser).after(() => {
if (!isProdEnv) {
server.register(require("@fastify/swagger"), {
/* ... */
});
}
server.register(require("@fastify/auth")).after(() => {
server.decorate("authenticateRequest", authenticateRequest);
server.decorate("requireAuthentication", requireAuthentication);
server.addHook("preHandler", server.auth([server.authenticateRequest]));
server.register(indexRouter);
server.register(authRouter, { prefix: "/auth" });
server.register(usersRouter, { prefix: "/users" });
server.register(listsRouter, { prefix: "/lists" });
server.register(postsRouter, { prefix: "/posts" });
server.register(searchRouter, { prefix: "/search" });
server.register(settingsRouter, { prefix: "/settings" });
});
});
server.setErrorHandler((err, req, res) => {
req.log.error(err.toString());
res.status(500).send(err);
});
和/posts/create/create
endpoint:
const postsRouter = (server: FastifyInstance, options: FastifyPluginOptions, next: HookHandlerDoneFunction) => {
server.post(
"/create",
{
schema: {
consumes: ["multipart/form-data"],
body: {
content: {
type: "string"
},
media: {
type: "string",
format: "binary"
},
"media-description": {
type: "string"
}
}
},
preHandler: [server.auth([server.requireAuthentication]), uploadMediaFileToCloud]
},
postsController.createPost
);
next();
};
export default postsRouter;
request curl:
curl -X 'POST' \
'http://localhost:3072/posts/create' \
-H 'accept: */*' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJoYW5kbGUiOiJ1bGtrYSIsInVzZXJJZCI6IjYyNGQ5NmY4NzFhOTI2OGY2YzNjZWExZCIsImlhdCI6MTY1NzEwNTg5NCwiZXhwIjoxNjU3NDA1ODk0fQ.A5WO3M-NhDYGWkILQLVCPfv-Ve-e_Dlm1UYD2vj5UrQ' \
-H 'Content-Type: multipart/form-data' \
-F 'content=Test.' \
-F '[email protected];type=image/png' \
-F 'media-description=' \
为什么是这样不起作用?
I'm using fastify-multer and JSON Schema to submit multipart form data that may include a file. No matter what I do, Fastify keeps giving me a bad response error:
{
"statusCode": 400,
"error": "Bad Request",
"message": "body must be object"
}
Here is my index.ts
:
const server = fastify();
server.register(require("@fastify/cors"));
server.register(multer.contentParser).after(() => {
if (!isProdEnv) {
server.register(require("@fastify/swagger"), {
/* ... */
});
}
server.register(require("@fastify/auth")).after(() => {
server.decorate("authenticateRequest", authenticateRequest);
server.decorate("requireAuthentication", requireAuthentication);
server.addHook("preHandler", server.auth([server.authenticateRequest]));
server.register(indexRouter);
server.register(authRouter, { prefix: "/auth" });
server.register(usersRouter, { prefix: "/users" });
server.register(listsRouter, { prefix: "/lists" });
server.register(postsRouter, { prefix: "/posts" });
server.register(searchRouter, { prefix: "/search" });
server.register(settingsRouter, { prefix: "/settings" });
});
});
server.setErrorHandler((err, req, res) => {
req.log.error(err.toString());
res.status(500).send(err);
});
And the /posts/create
endpoint:
const postsRouter = (server: FastifyInstance, options: FastifyPluginOptions, next: HookHandlerDoneFunction) => {
server.post(
"/create",
{
schema: {
consumes: ["multipart/form-data"],
body: {
content: {
type: "string"
},
media: {
type: "string",
format: "binary"
},
"media-description": {
type: "string"
}
}
},
preHandler: [server.auth([server.requireAuthentication]), uploadMediaFileToCloud]
},
postsController.createPost
);
next();
};
export default postsRouter;
Request CURL:
curl -X 'POST' \
'http://localhost:3072/posts/create' \
-H 'accept: */*' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJoYW5kbGUiOiJ1bGtrYSIsInVzZXJJZCI6IjYyNGQ5NmY4NzFhOTI2OGY2YzNjZWExZCIsImlhdCI6MTY1NzEwNTg5NCwiZXhwIjoxNjU3NDA1ODk0fQ.A5WO3M-NhDYGWkILQLVCPfv-Ve-e_Dlm1UYD2vj5UrQ' \
-H 'Content-Type: multipart/form-data' \
-F 'content=Test.' \
-F '[email protected];type=image/png' \
-F 'media-description=' \
Why is this not working?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
编辑2:显然,有一个非常简单的解决方案:在
prevalidation
挂钩中使用multer,而不是prehandler
。因此,一个工作代码看起来像这样:编辑:在下面发布答案后,我可以找到解决方案。为可能遇到同一问题的其他人更新我的答案。
首先,我从
fastify-multer
切换到@fastify/multipart
。然后,我从媒体
字段中删除了type
属性。之后,我在注册
@fastify/multipart
时添加了Option{AddTobody:true}
。这些更改后,字段
媒体
在request.body
中可用。旧答案:
似乎如今,我必须在这里回答自己的问题。无论如何,我弄清楚了发生了什么。 Fastify的内置模式验证与
Multipart/form-data
相关。我玩了架构规范,以确保情况如此。因此,我从所有路线中删除了模式验证。我的用例是从Expressjs移植API以进行快速化,所以我使用 Express-Oas-Generator 躺在周围。我用它来产生Swagger UI,一切正常。我希望Fastify将其行为共同解决,并解决这个问题。EDIT 2: Apparently, there is a really easy solution for this: Use multer in the
preValidation
hook instead ofpreHandler
. So, a piece of working code will look like this:EDIT: After I posted the answer below, I was able to find a solution for this. Updating my answer for anyone else who might encounter the same issue.
First, I switched to
@fastify/multipart
fromfastify-multer
. Then I removed thetype
property from themedia
field.After this, I added the option
{ addToBody: true }
when registering@fastify/multipart
.After these changes, the field
media
became available inrequest.body
.OLD ANSWER:
Seems like these days I have to answer my own questions here. Anyway, I figured out what's happening. Fastify's built-in schema validation doesn't play well with
multipart/form-data
. I played around with the schema specification to make sure that this is the case. So I removed schema validation from all routes. My use case here was porting an API from ExpressJS to Fastify, so I had a nice Swagger JSON spec generated using express-oas-generator lying around. I used that to generate Swagger UI and everything worked fine. I hope Fastify gets its act together and sorts out this issue.您现在可以使用
@fastify/multipart
来完成此操作,要求它将所有多部分表单字段连接到对象上,就像是JSON一样,使用魔术值“ keyValues”:将文件标记为文件(s)在架构中键入“对象”:
然后,每个表单字段将是
request.body
对象的字段:您可以通过curl:
You can now do this with
@fastify/multipart
by asking it to attach all multipart form fields to an object as if it was JSON, using the magic value "keyValues":Mark the file(s) as type "object" in the schema:
Then each form field will be a field on the
request.body
object:And you can see the object via curl: