koa1.x獲取原始body內容

   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堆限制;

     簡單,容易實現。

  缺點:須要轉換成字符串,不會轉的還可能出現亂碼。

相關文章
相關標籤/搜索