昨天我们使用的网关转发数据时出了点问题!

情景是这样的,另一方以Post的形式向我的node.js服务推送JSON数据。但是使用bodyParser中间件后,在req.body中拿不到任何信息。

代码如下:

    app.use(bodyParser.json());
app.post('/api/*',function(req,res){
_option.onMessage({url:req.url,query:req.query,body:req.body},function(error,data){
res.header('Access-Control-Allow-Origin','*');
res.header('Access-Control-Allow-Headers','X-Requested-With, Content-Type');
console.log(req.params);console.log(req.body.data);
res.send(data);
});

当时百思不得其解,后来发现推送方的Post请求中请求头的Content-Type的值为"text/xml"。当时觉得和这个值有很大的关系,经过检测果然不出所料。

自己模拟一个Post请求时将Content-type设置为"application/json"那么传过来的JSON数据就可以通过bodyParser中间件后由req.body拿到。但是另一方并没有以Content-type="application/json"的形式进行传送,我们只能从自己的node.js服务下手,坚信一定会想到办法。

在网上大概找了30多分钟的资料但是没有任何眉目!!!   无奈之下只能自己读bodyParser源码,而且我始终相信读别人的代码是最快的进步方式。

果然功夫不负有心人,在长达半个多小时与bodyParser源码的”作战中“,找到了解决问题的方法!

源代码分析如下:

bodyParser一定是对Content-Type进行了校验,那么我们就从这里入手,首先找到了bodyParser.json方法,代码如下:

function json(options) {
options = options || {} var limit = typeof options.limit !== 'number'
? bytes(options.limit || '100kb')
: options.limit
var inflate = options.inflate !== false
var reviver = options.reviver
var strict = options.strict !== false
var type = options.type || 'json'
var verify = options.verify || false if (verify !== false && typeof verify !== 'function') {
throw new TypeError('option verify must be function')
} function parse(body) {
if (body.length === 0) {
// special-case empty json body, as it's a common client-side mistake
// TODO: maybe make this configurable or part of "strict" option
return {}
} if (strict) {
var first = firstchar(body) if (first !== '{' && first !== '[') {
throw new Error('invalid json')
}
} return JSON.parse(body, reviver)
} return function jsonParser(req, res, next) {
if (req._body) return next()
req.body = req.body || {} if (!typeis(req, type)) return next() // RFC 7159 sec 8.1
var charset = typer.parse(req).parameters.charset || 'utf-8'
if (charset.substr(0, 4).toLowerCase() !== 'utf-') {
var err = new Error('unsupported charset')
err.status = 415
next(err)
return
} // read
read(req, res, next, parse, {
encoding: charset,
inflate: inflate,
limit: limit,
verify: verify
})
}
}

那么上述代码的 if (!typeis(req, types_))则为关键点,如果if执行了next()那下面的语句就将无法执行,我们也就无法进行read方法中,所以这个typeis方法即为突破口。

而typeis方法的位置在type-is模块中,关键代码如下:

function typeis(value, types_) {
var i
var types = types_ // remove parameters and normalize
value = typenormalize(value) // no type or invalid
if (!value) {
return false
} // support flattened arguments
if (types && !Array.isArray(types)) {
types = new Array(arguments.length - 1)
for (i = 0; i < types.length; i++) {
types[i] = arguments[i + 1]
}
} // no types, return the content type
if (!types || !types.length) return value; var type
for (i = 0; i < types.length; i++) {
if (mimeMatch(normalize(type = types[i]), value)) {
return type[0] === '+' || ~type.indexOf('*')
? value
: type
}
} // no matches
return false;
}
................
function typenormalize(value) {
try {
var type = typer.parse(value)
delete type.parameters
return typer.format(type)
} catch (err) {
return null
}
}

上述代码的value实则为传进来的req对象,也就是请求对象。那么请求对象通过'media-typer'模块中的parse方法将req判断为对象类型。代码如下:

function parse(string) {
if (!string) {
throw new TypeError('argument string is required')
} // support req/res-like objects as argument
if (typeof string === 'object') {
string = getcontenttype(string)
} if (typeof string !== 'string') {
throw new TypeError('argument string is required to be a string')
}
................................. function getcontenttype(obj) {
if (typeof obj.getHeader === 'function') {
// res-like
return obj.getHeader('content-type')
} if (typeof obj.headers === 'object') {
// req-like
return obj.headers && obj.headers['content-type']
}
}

之后在通过getcontenttype方法取出content-type的值将其返回,最后由typeis方法中的value接收。在红色的for循环中可以看出typeis函数中的types_可以为数组类型,并且会循环与value进行匹配,如果相同则直接返回。那么返回之后就不会再去执行next(),这样我们就可以继续执行下面的代码,我们就可以利用bodyParser中间件从req.body中获取数据了至于types_数组中写几种content-type值就由你的需要决定!

解决问题代码例如:

app.use(bodyParser.json( { type: ['text/xml','application/json'] } ));
app.post('/api/*',function(req,res){
_option.onMessage({url:req.url,query:req.query,body:req.body},function(error,data){
res.header('Access-Control-Allow-Origin','*');
res.header('Access-Control-Allow-Headers','X-Requested-With, Content-Type');
console.log(req.params);console.log(req.body.data);
res.send(data);
});

使用node.js的bodyParser中间件读取post数据解析的更多相关文章

  1. node.js - 路由、中间件、mysql

    这几天天天搞到这么晚,我看今天的内容看起不多啊,不知道为什么学着学着就到了这么晚.今天的内容还是有点多哈,有点自我矛盾了,再次一一道来. 1. 首先今天先看到路由的概念,什么叫做路由? 路由就是映射关 ...

  2. node.js之excel文件读取

    金天:学习一个新东西,就要持有拥抱的心态,如果固守在自己先前的概念体系,就会有举步维艰的感觉.node.js解析excel, 读取记录. 业务需求,从excel (xlsx, xls)导入数据. 备选 ...

  3. 【node.js】readline (逐行读取)

    官网链接:http://nodejs.cn/api/readline#readline_readline require('readline') 模块提供了一个接口,用于从可读流(如 process. ...

  4. electron node.js 实现文件拖动读取文件

    css/styles.css .for_file_drop { width: 100%; height: 100px; background-color: blueviolet; } index.ht ...

  5. node.js http.get 和http.post 数据

    http.get('http://codestudy.sinaapp.com', function (response) {}); node.js http post 样例: var reqData= ...

  6. node.js爬虫杭州房产销售及数据可视化

    现在年轻人到25岁+,总的要考虑买房结婚的问题,2016年的一波房价大涨,小伙伴们纷纷表示再也买不起上海的房产了,博主也得考虑考虑未来的发展了,思考了很久,决定去杭州工作.买房.定居.生活,之前去过很 ...

  7. node.js 从文件流中读写数据及管道流

    读取数据 // 引入 fs 模块 const fs = require('fs'); // 创建可读流 let readStream = fs.createReadStream('index.txt' ...

  8. 【Node.js】 bodyparser实现原理解析

    为什么我们需要body-parser 也许你第一次和bodyparser相遇是在使用Koa框架的时候.当我们尝试从一个浏览器发来的POST请求中取得请求报文实体的时候,这个时候,我们想,这个从Koa自 ...

  9. 【Node.js】'readline' 逐行读取、写入文件内容

    [转]运用readline逐行读取的两种实现 效果图如下: 左边1.log 为源文件 右边1.readline.log为复制后的文件 下边为命令行输出 实现方式一: [javascript] view ...

随机推荐

  1. 简单几何(凸包) POJ 2187 Beauty Contest

    题目传送门 题意:求两点的距离平方的最大值 分析:凸包模板题 /************************************************ * Author :Running_T ...

  2. 数学 ACdream 1196 KIDx's Triangle

    题目传送门 /* 这道题花了好长时间AC,思路有,但是表达式少写了括号一直乱码,囧! 注意:a==0时要特判:) */ #include <cstdio> #include <alg ...

  3. cocos2d ccmenulabel

    // // TestMenu.hpp // mario // // Created by sun on 15/12/22. // // #ifndef TestMenu_hpp #define Tes ...

  4. BZOJ2388 : 旅行规划

    考虑分块,每块维护两个标记$ts,td$. 那么对于块中一个位置$i$,它的实际值为$i\times td+ts+v_i$. 修改的时候,对于整块,直接打标记,对于零散的暴力修改,然后重构凸壳,时间复 ...

  5. BZOJ3103 : Palindromic Equivalence

    用Manacher可以推出O(n)对相等和不等关系. 将相等的用并查集维护,不等的连边. 然后从1到n,如果该等价类还没被考虑过,则ans*=26-与它不等的考虑过的等价类个数. #include&l ...

  6. BZOJ 1856 字符串(组合)

    题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1856 题意:有n个1和m个0组成的串,使得任意前k个中1的个数不少于0的个数.有多少种这 ...

  7. mysql的jdbc入门学习小结

    转自:专注JavaWeb开发 http://www.javaweb1024.com/data/MySQL/2015/04/25/618.html 一.jdbc基本概念jdbc : Java Datab ...

  8. Linux下java获取CPU、内存、磁盘IO、网络带宽使用率

    一.CPU 使用proc文件系统,"proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间.它以文件系统的方式为访问系统内核数据的操作提供接口.用户和应用程序可以通过proc得 ...

  9. Java导出Highcharts生成的图表为图片源码

    本文转载自:http://blog.csdn.net/dengsilinming/article/details/7352054 需要的jar包: 需要的js文件:(可以通过http://www.hi ...

  10. 适合码农工作时玩的游戏:Scrum

    适合码农工作时玩的游戏:Scrum 昨天遇到一个来自微软的面试者,在面试的最后,我简单介绍了一下我们团队使用一周一次的 Scrum 来做项目管理.他回答说:” 我在微软也用 Scrum,不过我们一周两 ...