Node版本比較老,koa1.x配合koa-body-parser,默認koa-body-parser會把請求數據轉成json對象,javascript
然而有的時候須要獲取原始的內容,不要轉換,看波koa-body-parser源碼,找到辦法。前端
辦法一:設置請求頭Content-type值爲:text/plainjava
這樣ctx.request.body就是一個字符串了.node
缺點:要毅端加東西,想到前端的懶惰,較困難,放棄。json
繼續摸索源碼,發現一個辦法,代碼:app
const koa = require('koa'); const router = require('koa-router'); const bodyParser = require('koa-body-parser'); const app = new koa(); const a = new router(); function injectRawBody(rawBodyName) { return function* (next) { const ctx = this; let _rawBody = yield function (done) { let received = ''; function onData(chunk) { received += chunk; } function onEnd() { ctx.req.removeListener('data', onData); ctx.req.removeListener('end', onEnd); done(null, received); } } ctx.request[rawBodyName] = _rawBody; yield next; } } app.use(injectRawBody('rawBody')); app.use(bodyParser(); const server = require('http').createServer(app.callback()); app.use(a.routes()); a.post('/a', function* () { const ctx = this; const body = ctx.request.body; console.log("ctx.request.body:", body); console.log("type of ctx.request.body:", typeof body); console.log("raw body:", Buffer.concat(ctx.request.rawBody).toString()); console.log("type of raw body:", typeof ctx.request.rawBody); yield tt.addOrCreate.apply(ctx); ctx.body = 'success'; }); server.listen(3000);
運行,看輸出,成功把原始內容掛在ctx.request.rawBody上,但ctx.request.body卻成了undefined,點解?koa
通過細查,發如今koa-body-parser/index.js有一句判斷:post
由於在上一個中間件,已經耗盡了req對象的數據,因此ended就是true,那也就不會進入以後的解析了。爲了一點小私,致使依賴body的功能不能用,不行不行。ui
既然我放在body-parser前面不行,那我能放到後面麼?像這樣:this
app.use(bodyParser());
app.use(injectRawBody('rawBody'));
答案是不行,由於在body-parser設置了req.once,並且即便沒有設置once,body-parser只有耗盡了req對象(end事件)纔會繼續下一個中間件,下一個中間件不能再從已經讀完的ReadableStream裏從新讀數據。
再細細看波源碼,co、koa-body-parser、co-body、raw-body真沒發現什麼好方法,這幾個庫之間的協做太無鏠了,基本上無法hack進去。
想過假裝一個req對象,跟ctx.req有同樣的接口,讓koa-body-parser裏操做的是這個假裝的對象,完了以後再恢復,但沒試出來,暫時放棄。
束手無措之際,忽然想起晚上在stackoverflow上看到有人問過一樣的問題,回憶了一下其中一個回答,當時以爲沒怎麼樣,如今想一想真是能夠用,代碼:
const koa = require('koa'); const router = require('koa-router'); const bodyParser = require('koa-body-parser');
const app = new koa(); const a = new router(); function injectRawBody(rawBodyName) { return function* (next) { const ctx = this; let received = []; function data(chunk) { received.push(chunk); } ctx.req.on('data', data); ctx.request[rawBodyName] = received; yield next; } } app.use(injectRawBody('rawBody')); app.use(bodyParser(); const server = require('http').createServer(app.callback()); app.use(a.routes()); a.post('/a', function* () { const ctx = this; //console.log(ctx.request); const body = ctx.request.body; console.log("ctx.request.body:", body); console.log("type of ctx.request.body:", typeof body); console.log("raw body:", Buffer.concat(ctx.request.rawBody).toString()); console.log("type of raw body:", typeof ctx.request.rawBody); yield tt.addOrCreate.apply(ctx); ctx.body = 'success'; }); server.listen(3000);
在injectRawBody中間件,綁定了data事件,接收數據。而body-parser只有在end事件發生時纔會resolve,全部在injectRawBody裏,獲取到的是完事的數據,只是存的是buffer,使用的時候須要轉換爲字符串。
優勢:沒有body-parser那樣的限制數據大小,buffer想放多少放多少,不受node堆限制;
簡單,容易實現。
缺點:須要轉換成字符串,不會轉的還可能出現亂碼。