美文网首页
nodejs学习笔记——body-parser实现解析

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

作者: AmazRan | 来源:发表于2019-07-28 17:57 被阅读0次

    前言

    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实现解析

          本文链接:https://www.haomeiwen.com/subject/euqcrctx.html