需求:本地post請(qǐng)求到遠(yuǎn)程服務(wù)器http://www.imooc.com/data/che...,驗(yàn)證并返回html內(nèi)容
問題:發(fā)現(xiàn)一直報(bào)錯(cuò)504,檢查了很久,發(fā)現(xiàn)把bodyParser.urlencoded()注釋掉,或者調(diào)整下app.use(bodyParser.json())和app.use(bodyParser.urlencoded({extended:true}))到代理后都可以獲取到數(shù)據(jù)。
不明白,為什么bodyParser.urlencoded在代理前會(huì)有影響?我一開始在想是不是請(qǐng)求體的格式有問題。
客戶端:
==html===
extends ../layout
block content
.col-md-3.col-md-offset-3
.form-group.form-inline
label(for="") 檢驗(yàn)數(shù)字的奇偶性:
button.btn.btn-success#load 檢驗(yàn)
.form-group.form-inline
label(for="") 請(qǐng)輸入一個(gè)數(shù)字:
input#inputNumber.form-control
ul#infoList
script(src="/js/check_f.js")
===js=====
$(function () {
$("#load").bind("click", function () {
var $this = $(this);
//借助本地的代理服務(wù)器向目標(biāo)服務(wù)器發(fā)送數(shù)據(jù)
$.post("http://localhost:3000/data/check_f.php", {
num: $("#inputNumber").val()
},
function (data) {
$("#infoList").append("<li>你輸入的數(shù)字 <b>" + $("#inputNumber").val() + "</b>是<b>" + data + "</b></li>");
})
});
})
預(yù)期效果:
出問題前:
服務(wù)器端:
/**
*解析請(qǐng)求的消息體
*/
var bodyParser = require('body-parser');
app.use(bodyParser.json());//返回一個(gè)只解析json的中間件,最后保存的數(shù)據(jù)都放在req.body對(duì)象上
app.use(bodyParser.urlencoded({ extended: true }));//返回的對(duì)象為任意類型
/**
* proxy代理
*/
var proxy = require('http-proxy-middleware');//引入代理中間件
var dataProxy = proxy('/data', { target: "http://www.imooc.com/", changeOrigin: true });//將服務(wù)器代理到http://www.imooc.com上,本地服務(wù)器為localhost:3000
app.use('/data/*', dataProxy);//data子目錄下的都是用代理
console.log("dataProxy->next");
為什么會(huì)有沖突呢?
===========================分割線=========================
了解了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.保持出問題的服務(wù)器端代碼:
輸出結(jié)果:
分析:
1-10:可以看出app.use是按順序向下執(zhí)行的,此時(shí)啟動(dòng)了本地服務(wù)器。
接著發(fā)起了localhost:3000/check_f請(qǐng)求
11-13:走到j(luò)son.js中間件進(jìn)行驗(yàn)證,由于表單默認(rèn)的content-type是application/x-www-form-urlencoded,所以json.js走到驗(yàn)證請(qǐng)求體解析時(shí)直接next跳出了,參考if(!shouldParse(req))部分代碼。
14-19:繼續(xù)向下走到urlencoded.js中間件進(jìn)行驗(yàn)證,由于content-type是application/x-www-form-urlencoded,所以shouldParse(req)部分會(huì)繼續(xù)read下去,對(duì)請(qǐng)求體進(jìn)行解析。
20:回到index.js(http-proxy-middleware)進(jìn)行代理轉(zhuǎn)發(fā)
接著就遇到timeout 504的服務(wù)器錯(cuò)誤了。
原因分析:
1.app.use中間件是有序執(zhí)行的。
2.代理應(yīng)該就一個(gè)左手交右手的動(dòng)作,不該對(duì)請(qǐng)求體進(jìn)行解析。
3.$.post默認(rèn)提交的content-type是application/x-www-form-urlencoded格式的,這也就是為什么app.use(bodyParser.json())就算放前面不注釋也不干擾的原因。
解決方式:
1.app.use(bodyParser.urlencoded())仍然放代理前面,不過注釋掉-->不推薦,因?yàn)樵陧?xiàng)目中,不同模塊不同功能,但共享一個(gè)app.js的入口文件,刪掉此功能會(huì)對(duì)其它非跨域的表單請(qǐng)求產(chǎn)生影響。
2.請(qǐng)求體解析部分放在代理后面
修改后服務(wù)器端:
/**
* proxy代理
*/
var proxy = require('http-proxy-middleware');//引入代理中間件
var dataProxy = proxy('/data', { target: "http://www.imooc.com/", changeOrigin: true });//將服務(wù)器代理到http://www.imooc.com上,本地服務(wù)器為localhost:3000
app.use('/data/*', dataProxy);//data子目錄下的都是用代理
console.log("dataProxy->next");
/**
* 解析請(qǐng)求的消息體
*/
var bodyParser = require('body-parser');
app.use(bodyParser.json());//返回一個(gè)只解析json的中間件,最后保存的數(shù)據(jù)都放在req.body對(duì)象上
app.use(bodyParser.urlencoded({ extended: true }));//返回的對(duì)象為任意類型
測(cè)試結(jié)果:
分析:
1-10:可以看出app.use是按順序向下執(zhí)行的,此時(shí)啟動(dòng)了本地服務(wù)器。
接著發(fā)起了localhost:3000/check_f請(qǐng)求
完成代理轉(zhuǎn)發(fā)
11-12:走到j(luò)son.js/urlencoded.js中間件進(jìn)行驗(yàn)證,實(shí)際上不做處理了。
13-15:正常的驗(yàn)證返回
總結(jié):
實(shí)際上就是上面的3點(diǎn)原因分析。
另外:
這樣改是不會(huì)對(duì)本地非跨域的請(qǐng)求產(chǎn)生影響的,自己寫個(gè)本地的post響應(yīng)去驗(yàn)證就可以,懶得寫的可以參考post_l相關(guān)的。
代碼參考: https://github.com/yifon/WebL... 中的check_f相關(guān)的,其它的都是其它一些不相關(guān)的功能。
北大青鳥APTECH成立于1999年。依托北京大學(xué)優(yōu)質(zhì)雄厚的教育資源和背景,秉承“教育改變生活”的發(fā)展理念,致力于培養(yǎng)中國(guó)IT技能型緊缺人才,是大數(shù)據(jù)專業(yè)的國(guó)家
北大青鳥中博軟件學(xué)院創(chuàng)立于2003年,作為華東區(qū)著名互聯(lián)網(wǎng)學(xué)院和江蘇省首批服務(wù)外包人才培訓(xùn)基地,中博成功培育了近30000名軟件工程師走向高薪崗位,合作企業(yè)超4
中公教育集團(tuán)創(chuàng)建于1999年,經(jīng)過二十年潛心發(fā)展,已由一家北大畢業(yè)生自主創(chuàng)業(yè)的信息技術(shù)與教育服務(wù)機(jī)構(gòu),發(fā)展為教育服務(wù)業(yè)的綜合性企業(yè)集團(tuán),成為集合面授教學(xué)培訓(xùn)、網(wǎng)
達(dá)內(nèi)教育集團(tuán)成立于2002年,是一家由留學(xué)海歸創(chuàng)辦的高端職業(yè)教育培訓(xùn)機(jī)構(gòu),是中國(guó)一站式人才培養(yǎng)平臺(tái)、一站式人才輸送平臺(tái)。2014年4月3日在美國(guó)成功上市,融資1
曾工作于聯(lián)想擔(dān)任系統(tǒng)開發(fā)工程師,曾在博彥科技股份有限公司擔(dān)任項(xiàng)目經(jīng)理從事移動(dòng)互聯(lián)網(wǎng)管理及研發(fā)工作,曾創(chuàng)辦藍(lán)懿科技有限責(zé)任公司從事總經(jīng)理職務(wù)負(fù)責(zé)iOS教學(xué)及管理工作。
浪潮集團(tuán)項(xiàng)目經(jīng)理。精通Java與.NET 技術(shù), 熟練的跨平臺(tái)面向?qū)ο箝_發(fā)經(jīng)驗(yàn),技術(shù)功底深厚。 授課風(fēng)格 授課風(fēng)格清新自然、條理清晰、主次分明、重點(diǎn)難點(diǎn)突出、引人入勝。
精通HTML5和CSS3;Javascript及主流js庫,具有快速界面開發(fā)的能力,對(duì)瀏覽器兼容性、前端性能優(yōu)化等有深入理解。精通網(wǎng)頁制作和網(wǎng)頁游戲開發(fā)。
具有10 年的Java 企業(yè)應(yīng)用開發(fā)經(jīng)驗(yàn)。曾經(jīng)歷任德國(guó)Software AG 技術(shù)顧問,美國(guó)Dachieve 系統(tǒng)架構(gòu)師,美國(guó)AngelEngineers Inc. 系統(tǒng)架構(gòu)師。