// URL编码方式 简单的解码方法实现 function decode (qs, sep = '&', eq = '=') { const obj = {} qs = qs.split(sep) for (let i = 0, max = qs.length; i < max; i++) { const item = qs[i] const index = item.indexOf(eq) let key, value if (~index) { key = item.substr(0, index) value = item.substr(index + 1) } else { key = item value = '' } key = decodeURIComponent(key) value = decodeURIComponent(value) if (!obj.hasOwnProperty(key)) { obj[key] = value } } return obj } console.log(decode('a=1&b=2&c=3')) // { a: '1', b: '2', c: '3' }
URL编码方式适合处理简单的键值对数据,并且很多框架的Ajax中的Content-Type默认值都是它,但是对于复杂的嵌套对象就不太好处理了,这时就需要JSON编码方式大显身手了。
客户端发送请求主体时,只需要采用JSON.stringify进行编码。服务器端只需要采用JSON.parse进行解码即可:
const strictJSONReg = /^[\x20\x09\x0a\x0d]*(\[|\{)/; function parse(str) { if (!strict) return str ? JSON.parse(str) : str; // 严格模式下,总是返回一个对象 if (!str) return {}; // 是否为合法的JSON字符串 if (!strictJSONReg.test(str)) { throw new Error('invalid JSON, only supports object and array'); } return JSON.parse(str); }
除了上述两种字符串编码方式,koa-bodyparser还支持不采用任何字符串编码方式的普通字符串。
三种字符串编码的处理方式由【co-body】模块提供,koa-bodyparser中通过判断当前Content-Type类型,调用不同的处理方式,将获取到的结果挂载在ctx.request.body:
return async function bodyParser(ctx, next) { if (ctx.request.body !== undefined) return await next(); if (ctx.disableBodyParser) return await next(); try { // 最重要的一步 将解析的内容挂载到koa的上下文中 const res = await parseBody(ctx); ctx.request.body = 'parsed' in res ? res.parsed : {}; if (ctx.request.rawBody === undefined) ctx.request.rawBody = res.raw; // 保存原始字符串 } catch (err) { if (onerror) { onerror(err, ctx); } else { throw err; } } await next(); }; async function parseBody(ctx) { if (enableJson && ((detectJSON && detectJSON(ctx)) || ctx.request.is(jsonTypes))) { return await parse.json(ctx, jsonOpts); // application/json等json type } if (enableForm && ctx.request.is(formTypes)) { return await parse.form(ctx, formOpts); // application/x-www-form-urlencoded } if (enableText && ctx.request.is(textTypes)) { return await parse.text(ctx, textOpts) || ''; // text/plain } return {}; } };
其实还有一种比较常见的Content-type,当采用表单上传时,报文主体中会包含多个实体主体:
------WebKitFormBoundaryqsAGMB6Us6F7s3SF Content-Disposition: form-data;; filename="image.png" Content-Type: image/png ------WebKitFormBoundaryqsAGMB6Us6F7s3SF Content-Disposition: form-data; ------WebKitFormBoundaryqsAGMB6Us6F7s3SF--
这种方式处理相对比较复杂,koa-bodyparser中并没有提供该Content-Type的解析。(下一篇中应该会介绍^_^)
五、总结
以上便是koa-bodyparser的核心实现原理,其中涉及到很多关于HTTP的基础知识,对于HTTP不太熟悉的同学,可以推荐看一波入门级宝典【图解HTTP】。
最后留图一张: