nodejs学习笔记——body-parser实现解析

前言

body-parser是非常常用的一个express中间件,作用是对http请求体进行解析。
(如果对http请求不熟悉可以参考个人事先整理的关于http事务剖析)


body-parser主要做了什么

// bodyParser.json([options])
// bodyParser.raw([options])
// bodyParser.text([options])
// bodyParser.urlencoded([options])
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

官方提供了这样几种api应用范例,通过几种方法名可以看出都是针对改类型的请求体的。至于options中,可以应对不同的编码格式、压缩类型等等。
因此可以看出body-parser完成了以下几点功能:

  • 处理不同类型的请求体:text、json、urlencoded、raw,对应的报文主体的格式不同。
  • 处理不同的编码:比如utf8、gbk等。
  • 处理不同的压缩类型:比如gzip、deflare等。
  • 其他边界、异常的处理。

解析不同类型请求体

源代码非常清晰,根据4个类型在lib/type文件夹下单独存放方法。

解析text/plain

(raw的解析方式基本一致,只是针对options做了不同的代码处理)

// 客户端代码
var http = require('http');
var options = {
    hostname: '127.0.0.1',
    port: '3000',
    path: '/test',
    method: 'POST',
    headers: {
        'Content-Type': 'text/plain',
        'Content-Encoding': 'identity'
    }
};
var client = http.request(options, (res) => {
    res.pipe(process.stdout);
});
client.end('hello');
// 服务端代码
var http = require('http');
var parsePostBody = function (req, done) {
    var arr = [];
    var chunks;
    req.on('data', buff => {
        arr.push(buff);
    });
    req.on('end', () => {
        chunks = Buffer.concat(arr);
        done(chunks);
    });
};
var server = http.createServer(function (req, res) {
    parsePostBody(req, (chunks) => {
        var body = chunks.toString();
        res.end(`You said ${body}`)
    });
});
server.listen(3000);
解析application/json
// 客户端代码
// 更换了Content-Type
var http = require('http');
var options = {
    // 配置options
    headers: {
        'Content-Type': 'application/json',
        'Content-Encoding': 'identity'
    }
};
var client = http.request(options, (res) => {
    res.pipe(process.stdout);
});
client.end( JSON.stringify( say : 'hello' }) );
// 服务端代码
var http = require('http');
var parsePostBody = function (req, done) {
    // 同上
};
var server = http.createServer(function (req, res) {
    parsePostBody(req, (chunks) => {
        var json = JSON.parse( chunks.toString() ); // 先用JSON.parse解析
        res.end(`You said ${json.say}`)
    });
});
server.listen(3000);
解析application/x-www-form-urlencoded
// 客户端代码
var http = require('http');
var qs = require('qs');
var options = {
    // 配置options
    headers: {
        'Content-Type': 'form/x-www-form-urlencoded'
    }
};
var client = http.request(options, (res) => {
    res.pipe(process.stdout);
});
client.end( qs.stringify({ say : 'hello' }) );
// 服务端代码
var http = require('http');
var qs = require('qs');
var parsePostBody = function (req, done) {
    // 同上
};
var server = http.createServer(function (req, res) {
    parsePostBody(req, (chunks) => {
        var body = qs.parse( chunks.toString() ); // 先借助qs解析
        res.end(`You said ${body.say}`)
    });
});
server.listen(3000);

处理不同编码

有两个要点:

  • 编码声明:在Content-Type最后加上 ;charset=gbk
  • 请求体编码:这里借助了iconv-lite,对请求体进行编码
    iconv.encode('你好', 'gbk')
    注:nodejs本身不支持gbk编码,所以请求发送前,需要先进行编码
// 客户端代码
var http = require('http');
var iconv = require('iconv-lite');
var encoding = 'gbk'; // 请求编码
var options = {
    // options设置
    headers: {
        'Content-Type': 'text/plain; charset=' + encoding,
        'Content-Encoding': 'identity',        
    }
};
var buff = iconv.encode('你好', encoding);
var client = http.request(options, (res) => {
    res.pipe(process.stdout);
});
client.end(buff, encoding);

服务端代码如下,这里多了两个步骤:编码判断、解码操作。首先通过Content-Type获取编码类型gbk,然后通过iconv-lite进行反向解码操作。

// 服务端代码
var http = require('http');
var contentType = require('content-type');
var iconv = require('iconv-lite');
var parsePostBody = function (req, done) {
    var obj = contentType.parse(req.headers['content-type']);
    var charset = obj.parameters.charset; // 编码判断:这里获取到的值是 'gbk'
    var arr = [];
    var chunks;
    req.on('data', buff => {
        arr.push(buff);
    });
    req.on('end', () => {
        chunks = Buffer.concat(arr);
        var body = iconv.decode(chunks, charset);  // 解码操作
        done(body);
    });
};
// 以下和先前一致
var server = http.createServer(function (req, res) {
    parsePostBody(req, (body) => {
        res.end(`You said ${body}`)
    });
});
server.listen(3000);

处理不同压缩类型

和上述处理编码的方式类似,借助了zlib模块。
服务端先判断Content-Encoding,例如gzip格式压缩,然后对请求提进行解压操作

stream = zlib.createGunzip()
req.pipe(stream)

心得总结

body-parser的核心实现并不复杂,很大篇幅是在处理异常和边界情况,尤其是一些定制化的options处理。
另外对于POST请求,有一个非常常见的Content-Type是multipart/form-data,这个的处理相对复杂些,body-parser好像并不打算对其进行支持。


参考

事先整理的关于http事务剖析
expressjs/body-parser仓库地址
程序猿小卡的nodejs学习笔记github

你可能感兴趣的:(nodejs学习笔记——body-parser实现解析)