在Deno/Oak中如何读取含数组的多部分表单数据?
Oak 表单数据数组处理方案
问题根源
Oak 默认对表单数据的解析逻辑和 Express 不同:
application/x-www-form-urlencoded编码返回URLSearchParams对象,需手动提取多字段值为数组multipart/form-data编码需遍历所有 parts 收集同名字段,默认不会自动组装数组
1. 处理 URL 编码表单(application/x-www-form-urlencoded)
直接使用 URLSearchParams.getAll() 可获取同名字段的数组值,也可以封装一个转换函数,自动将多字段转为数组,实现类似 Express 的 req.body 格式:
转换函数实现
function parseUrlencodedBody(params: URLSearchParams): Record<string, string | string[]> { const body: Record<string, string | string[]> = {}; for (const [key, value] of params.entries()) { if (body[key]) { // 已有值则转为数组或追加元素 body[key] = Array.isArray(body[key]) ? [...(body[key] as string[]), value] : [body[key] as string, value]; } else { body[key] = value; } } return body; }
路由中使用
import { Application, Router, Context } from "https://deno.land/x/oak@v12.6.0/mod.ts"; const router = new Router(); router.post("/submit-urlencoded", async (ctx: Context) => { // 解析URL编码表单 const params = await ctx.request.body({ type: "form" }).value; // 转换为类似Express的body格式 const body = parseUrlencodedBody(params); // 示例:body.genre 会是数组(如果勾选多个复选框) ctx.response.body = { success: true, data: body }; }); const app = new Application(); app.use(router.routes()); app.use(router.allowedMethods()); await app.listen({ port: 8000 });
2. 处理 Multipart 表单(multipart/form-data)
Multipart 表单的每个字段是独立的 Part 对象,需遍历所有 parts 收集同名字段值:
解析函数实现
async function parseMultipartBody(ctx: Context): Promise<Record<string, string | string[]>> { const body: Record<string, string | string[]> = {}; const parts = ctx.request.body({ type: "form-data" }).value; for await (const part of parts) { if (!part.filename) { // 跳过文件字段,如需处理可单独添加逻辑 const value = await part.text(); if (body[part.name]) { body[part.name] = Array.isArray(body[part.name]) ? [...(body[part.name] as string[]), value] : [body[part.name] as string, value]; } else { body[part.name] = value; } } else { // 处理文件字段示例:可保存文件到本地或获取文件信息 body[part.name] = { filename: part.filename, contentType: part.contentType, // 如需读取文件内容:await part.arrayBuffer() }; } } return body; }
路由中使用
router.post("/submit-multipart", async (ctx: Context) => { const body = await parseMultipartBody(ctx); ctx.response.body = { success: true, data: body }; });
3. 统一中间件(自动适配编码类型)
封装一个中间件,自动根据请求的 Content-Type 解析表单,直接在 ctx.state.body 中获取类似 Express 的格式化数据:
async function formBodyMiddleware(ctx: Context, next: () => Promise<unknown>) { const contentType = ctx.request.headers.get("content-type") || ""; if (contentType.includes("application/x-www-form-urlencoded")) { const params = await ctx.request.body({ type: "form" }).value; ctx.state.body = parseUrlencodedBody(params); } else if (contentType.includes("multipart/form-data")) { ctx.state.body = await parseMultipartBody(ctx); } await next(); } // 注册中间件 const app = new Application(); app.use(formBodyMiddleware); app.use(router.routes()); app.use(router.allowedMethods()); // 路由中直接使用 router.post("/submit", async (ctx: Context) => { ctx.response.body = { success: true, data: ctx.state.body }; });
这样处理后,你就能像在 Express 中使用 req.body 一样,直接获取包含字符串和数组的表单数据,无需手动处理不同编码的差异。
内容的提问来源于stack exchange,提问作者Sachin
相关产品推荐
相关产品推荐

