鍍金池/ 問答/網(wǎng)絡(luò)安全  HTML/ 同時(shí)使用bodyParser.json()和bodyParser.urlenco

同時(shí)使用bodyParser.json()和bodyParser.urlencode()結(jié)果post請(qǐng)求得到504錯(cuò)誤

需求:本地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)的功能。

回答
編輯回答
巫婆

不對(duì)的地方歡迎指出

2017年7月26日 13:40