标签:post ips 中间 结束 实现原理 请求报文 情况 获取 wan
数据处理流程
下面展示下相关的代码
// 根据Content-Encoding判断是否解压,如需则调用相应解压函数 async function transformEncode(buffer, encode) { // ... } // charset转码 function transformCharset(buffer, charset) { // ... } // 根据content-type做最后的数据格式化 function formatData(str, contentType) { // ... } // 返回Promise function getRequestBody(req, res) { return new Promise(async (resolve, reject) => { const chunks = []; req.on(‘data‘, buf => { chunks.push(buf); }) req.on(‘end‘, async () => { let buffer = Buffer.concat(chunks); // 获取content-encoding const encode = req.headers[‘content-encoding‘]; // 获取content-type const { type, parameters } = contentType.parse(req); // 获取charset const charset = parameters.charset; // 解压缩 buffer = await transformEncode(buffer, encode); // 转换字符编码 const str = transformCharset(buffer, charset); // 根据类型输出不同格式的数据,如字符串或JSON对象 const result = formatData(str, type); resolve(result); }) }).catch(err => { throw err; }) }
function getRequestBody(req, res) { return new Promise(async (resolve, reject) => { // ... } }
const chunks = []; req.on(‘data‘, buf => { chunks.push(buf); })
const contentType = require(‘content-type‘); const iconv = require(‘iconv-lite‘); req.on(‘end‘, async () => { let buffer = Buffer.concat(chunks); // 获取content-encoding const encode = req.headers[‘content-encoding‘]; // 获取content-type const { type, parameters } = contentType.parse(req); // 获取charset const charset = parameters.charset; // 解压缩 buffer = await transformEncode(buffer, encode); // 转换字符编码 const str = transformCharset(buffer, charset); // 根据类型输出不同格式的数据,如字符串或JSON对象 const result = formatData(str, type); resolve(result); }
Content-Encoding可分为四种值:gzip,compress,deflate,br,identity
其中
所以我们需要处理解压的一共有三种数据类型
(注意!zlib.brotliDecompress方法在Node11.7以上版本才会支持,而且不要看到名字里有compress就误以为它是用来解压compress压缩的数据的,实际上它是用来处理br的)
代码如下,我们对zlib.gunzip等回调类方法通过promisify转成Promise编码风格
const promisify = util.promisify; // node 11.7版本以上才支持此方法 const brotliDecompress = zlib.brotliDecompress && promisify(zlib.brotliDecompress); const gunzip = promisify(zlib.gunzip); const inflate = promisify(zlib.inflate); const querystring = require(‘querystring‘); // 根据Content-Encoding判断是否解压,如需则调用相应解压函数 async function transformEncode(buffer, encode) { let resultBuf = null; debugger; switch (encode) { case ‘br‘: if (!brotliDecompress) { throw new Error(‘Node版本过低! 11.6版本以上才支持brotliDecompress方法‘) } resultBuf = await brotliDecompress(buffer); break; case ‘gzip‘: resultBuf = await gunzip(buffer); break; case ‘deflate‘: resultBuf = await inflate(buffer); break; default: resultBuf = buffer; break; } return resultBuf; }
我们采用iconv-lite对charset进行转码,代码如下
const iconv = require(‘iconv-lite‘); // charset转码 function transformCharset(buffer, charset) { charset = charset || ‘UTF-8‘; // iconv将Buffer转化为对应charset编码的String const result = iconv.decode(buffer, charset); return result; }
来!传送门
https://link.zhihu.com/?target=https%3A//www.npmjs.com/package/iconv-lite
具体的处理方式分三种情况:
代码如下
const querystring = require(‘querystring‘); // 根据content-type做最后的数据格式化 function formatData(str, contentType) { let result = ‘‘; switch (contentType) { case ‘text/plain‘: result = str; break; case ‘application/json‘: result = JSON.parse(str); break; case ‘application/x-www-form-urlencoded‘: result = querystring.parse(str); break; default: break; } return result; }
服务端
下面的代码你肯定知道要放在哪里了
// 省略其他代码 if (pathname === ‘/post‘) { // 调用getRequestBody,通过await修饰等待结果返回 const body = await getRequestBody(req, res); console.log(body); return; }
前端采用fetch进行测试
在下面的代码中,我们连续三次发出不同的POST请求,携带不同类型的body数据,看看服务端会输出什么
var iconv = require(‘iconv-lite‘); var querystring = require(‘querystring‘); var gbkBody = { data: "我是彭湖湾", contentType: ‘application/json‘, charset: ‘gbk‘ }; // 转化为JSON数据 var gbkJson = JSON.stringify(gbkBody); // 转为gbk编码 var gbkData = iconv.encode(gbkJson, "gbk"); var isoData = iconv.encode("我是彭湖湾,这句话采用UTF-8格式编码,content-type为text/plain", "UTF-8") // 测试内容类型为application/json和charset=gbk的情况 fetch(‘/post‘, { method: ‘POST‘, headers: { "Content-Type": ‘application/json; charset=gbk‘ }, body: gbkData }); // 测试内容类型为application/x-www-form-urlencoded和charset=UTF-8的情况 fetch(‘/post‘, { method: ‘POST‘, headers: { "Content-Type": ‘application/x-www-form-urlencoded; charset=UTF-8‘ }, body: querystring.stringify({ data: "我是彭湖湾", contentType: ‘application/x-www-form-urlencoded‘, charset: ‘UTF-8‘ }) }); // 测试内容类型为text/plain的情况 fetch(‘/post‘, { method: ‘POST‘, headers: { "Content-Type": ‘text/plain; charset=UTF-8‘ }, body: isoData });
服务端输出结果
{ data: ‘我是彭湖湾‘, contentType: ‘application/json‘, charset: ‘gbk‘ } { data: ‘我是彭湖湾‘, contentType: ‘application/x-www-form-urlencoded‘, charset: ‘UTF-8‘ } 我是彭湖湾,这句话采用UTF-8格式编码,content-type为text/plain
其实本质上来说,charset前端一般都是固定为utf-8的, 甚至在JQuery的AJAX请求中,前端请求charset甚至是不可更改,只能是charset,但是在使用fetch等API的时候,的确是可以更改charset的,这个工作尝试满足一些比较偏僻的更改charset需求。
一般情况下我们认为,考虑到前端发的AJAX之类的请求的数据量,是不需要做Gzip压缩的。但是向服务器发起请求的不一定只有前端,还可能是Node的客户端。这些Node客户端可能会向Node服务端传送压缩过后的数据流。 例如下面的代码所示
const zlib = require(‘zlib‘); const request = require(‘request‘); const data = zlib.gzipSync(Buffer.from("我是一个被Gzip压缩后的数据")); request({ method: ‘POST‘, url: ‘http://127.0.0.1:3000/post‘, headers: {//设置请求头 "Content-Type": "text/plain", "Content-Encoding": "gzip" }, body: data })
https://github.com/penghuwan/body-parser-promise
https://www.npmjs.com/package/body-parser-promise
Koa-bodyparser https://github.com/koajs/bodyparser
【完】
标签:post ips 中间 结束 实现原理 请求报文 情况 获取 wan
原文地址:https://www.cnblogs.com/penghuwan/p/11374268.html