需求:本地post请求到远程服务器http://www.imooc.com/data/che...,验证并返回html内容
问题:发现一直报错504,检查了很久,发现把bodyParser.urlencoded()注释掉,或者调整下app.use(bodyParser.json())和app.use(bodyParser.urlencoded({extended:true}))到代理后都可以获取到数据。
不明白,为什么bodyParser.urlencoded在代理前会有影响?我一开始在想是不是请求体的格式有问题。
客户端:
==html===
extends ../layout
block content
.col-md-3.col-md-offset-3
.form-group.form-inline
label(for="") 检验数字的奇偶性:
button.btn.btn-success#load 检验
.form-group.form-inline
label(for="") 请输入一个数字:
input#inputNumber.form-control
ul#infoList
script(src="/js/check_f.js")
===js=====
$(function () {
$("#load").bind("click", function () {
var $this = $(this);
//借助本地的代理服务器向目标服务器发送数据
$.post("http://localhost:3000/data/check_f.php", {
num: $("#inputNumber").val()
},
function (data) {
$("#infoList").append("<li>你输入的数字 <b>" + $("#inputNumber").val() + "</b>是<b>" + data + "</b></li>");
})
});
})
预期效果:
出问题前:
服务器端:
/**
*解析请求的消息体
*/
var bodyParser = require('body-parser');
app.use(bodyParser.json());//返回一个只解析json的中间件,最后保存的数据都放在req.body对象上
app.use(bodyParser.urlencoded({ extended: true }));//返回的对象为任意类型
/**
* proxy代理
*/
var proxy = require('http-proxy-middleware');//引入代理中间件
var dataProxy = proxy('/data', { target: "http://www.imooc.com/", changeOrigin: true });//将服务器代理到http://www.imooc.com上,本地服务器为localhost:3000
app.use('/data/*', dataProxy);//data子目录下的都是用代理
console.log("dataProxy->next");
为什么会有冲突呢?
===========================分割线=========================
了解了app.use,代理作用,以及在bodyParser.json,bodyParser.urlencode,http-proxy-middleware中加了debug code后,知道了原因。
在bodyParser组件的json.js,urlencode,read.js中加debug code,在http-proxy-middleware组件index.js中debug code.
json.js:
function json(options) {
var opts = options || {}
.....//省略
//June[debug]
console.log("json-type:"+opts.type);
console.log("json-verify:"+opts.verify);
var type = opts.type || 'application/json'
var verify = opts.verify || false
.....//省略
console.log('shouldParse-typeof type:'+(typeof type));
// create the appropriate type checking function
var shouldParse = typeof type !== 'function'
? typeChecker(type)
: type
.....//省略
return function jsonParser(req, res, next) {
if (req._body) {
//June[debug]
console.log("json-:body already parsed");
return debug('body already parsed'), next()
}
req.body = req.body || {}
// skip requests without bodies
if (!typeis.hasBody(req)) {
//June[debug]
console.log("json-:skip empty body");
return debug('skip empty body'), next()
}
debug('content-type %j', req.headers['content-type'])
// determine if request should be parsed
if (!shouldParse(req)) {
//June[debug]
console.log("json-:"+req);
console.log("json-:skip parsing");
return debug('skip parsing'), next()
}
// assert charset per RFC 7159 sec 8.1
var charset = getCharset(req) || 'utf-8'
if (charset.substr(0, 4) !== 'utf-') {
//June[debug]
console.log("json-:invalid charset");
.....//省略
}
//June[debug]
console.log("json-:go to read");
// read
read(req, res, next, parse, debug, {
.....//省略
})
}
}
urlencode.js:
function urlencoded(options) {
var opts = options || {}
.....//省略
//June[debug]
console.log("urlencode:"+opts.type);
var type = opts.type || 'application/x-www-form-urlencoded'
var verify = opts.verify || false
.....//省略
console.log('urlencode-typeof type:'+(typeof type));
// create the appropriate type checking function
var shouldParse = typeof type !== 'function'
? typeChecker(type)
: type
.....//省略
return function urlencodedParser(req, res, next) {
if (req._body) {
//June[debug]
console.log('body already parsed');
return debug('body already parsed'), next()
}
req.body = req.body || {}
// skip requests without bodies
if (!typeis.hasBody(req)) {
//June[debug]
console.log('skip empty body');
return debug('skip empty body'), next()
}
debug('content-type %j', req.headers['content-type'])
// determine if request should be parsed
if (!shouldParse(req)) {
//June[debug]
console.log('skip parsing');
return debug('skip parsing'), next()
}
// assert charset
var charset = getCharset(req) || 'utf-8'
if (charset !== 'utf-8') {
//June[debug]
console.log('invalid charset');
.....//省略
}
//June[debug]
console.log('urlencoded, go to read');
// read
read(req, res, next, parse, debug, {
.....//省略
})
}
}
read.js
function read(req, res, next, parse, debug, options) {
//June[debug]
console.log('req:'+req);
.....//省略
}
index.js(http-proxy-middleware)
function middleware(req, res, next) {
if (shouldProxy(config.context, req)) {
console.log("shouldProxy");//June
.....//省略
}
1.保持出问题的服务器端代码:
输出结果:
分析:
1-10:可以看出app.use是按顺序向下执行的,此时启动了本地服务器。
接着发起了localhost:3000/check_f请求
11-13:走到json.js中间件进行验证,由于表单默认的content-type是application/x-www-form-urlencoded,所以json.js走到验证请求体解析时直接next跳出了,参考if(!shouldParse(req))部分代码。
14-19:继续向下走到urlencoded.js中间件进行验证,由于content-type是application/x-www-form-urlencoded,所以shouldParse(req)部分会继续read下去,对请求体进行解析。
20:回到index.js(http-proxy-middleware)进行代理转发
接着就遇到timeout 504的服务器错误了。
原因分析:
1.app.use中间件是有序执行的。
2.代理应该就一个左手交右手的动作,不该对请求体进行解析。
3.$.post默认提交的content-type是application/x-www-form-urlencoded格式的,这也就是为什么app.use(bodyParser.json())就算放前面不注释也不干扰的原因。
解决方式:
1.app.use(bodyParser.urlencoded())仍然放代理前面,不过注释掉-->不推荐,因为在项目中,不同模块不同功能,但共享一个app.js的入口文件,删掉此功能会对其它非跨域的表单请求产生影响。
2.请求体解析部分放在代理后面
修改后服务器端:
/**
* proxy代理
*/
var proxy = require('http-proxy-middleware');//引入代理中间件
var dataProxy = proxy('/data', { target: "http://www.imooc.com/", changeOrigin: true });//将服务器代理到http://www.imooc.com上,本地服务器为localhost:3000
app.use('/data/*', dataProxy);//data子目录下的都是用代理
console.log("dataProxy->next");
/**
* 解析请求的消息体
*/
var bodyParser = require('body-parser');
app.use(bodyParser.json());//返回一个只解析json的中间件,最后保存的数据都放在req.body对象上
app.use(bodyParser.urlencoded({ extended: true }));//返回的对象为任意类型
测试结果:
分析:
1-10:可以看出app.use是按顺序向下执行的,此时启动了本地服务器。
接着发起了localhost:3000/check_f请求
完成代理转发
11-12:走到json.js/urlencoded.js中间件进行验证,实际上不做处理了。
13-15:正常的验证返回
总结:
实际上就是上面的3点原因分析。
另外:
这样改是不会对本地非跨域的请求产生影响的,自己写个本地的post响应去验证就可以,懒得写的可以参考post_l相关的。
代码参考: https://github.com/yifon/WebL... 中的check_f相关的,其它的都是其它一些不相关的功能。
不对的地方欢迎指出