1

Background

I am using koa2 with some middlewares to build a basic api framework. But when I use "ctx.body" to send response in my router, the client side always receive "Not Found"

My code

./app.js

const Koa = require('koa');
const app = new Koa();
const config = require('./config');

//Middlewares
const loggerAsync = require('./middleware/logger-async')
const bodyParser = require('koa-bodyparser')
const jsonp = require('koa-jsonp')
app.use(loggerAsync())
app.use(bodyParser())
app.use(jsonp());

//Router
const gateway = require('./router/gateway')
app.use(gateway.routes(), gateway.allowedMethods());

app.use(async(ctx, next) => {
    await next();
    ctx.response.body = {
        success: false,
        code: config.code_system,
        message: 'wrong path'
    }
});

app.listen(3000);

./router/gateway.js

/**
 * Created by Administrator on 2017/4/11.
 */
const Router = require('koa-router');
const gateway = new Router();
const df = require('../db/data-fetcher');
const config = require('../config');
const moment = require('moment');
const log4js = require('log4js');
// log4js.configure({
//     appenders: { cheese: { type: 'file', filename: 'cheese.log' } },
//     categories: { default: { appenders: ['cheese'], level: 'error' } }
// });
const logger = log4js.getLogger('cheese');
logger.setLevel('ERROR');


gateway.get('/gateway', async(ctx, next) => {
    let time = ctx.query.time;
    if (!time) {
        ctx.body = {
            success: false,
            code: config.code_system,
            message: 'Please input running times'
        }
    } else {
        try {
            let r = await df(`insert into gateway (g_time, g_result, g_date) values (${time}, '',now())`);
            return ctx.body = {
                success: true,
                code: config.code_success
            }
        } catch (error) {
            logger.error(error.message);
        }
    }
});

module.exports = gateway;

Then a db wrapper(mysql)

./db/async-db.js

const mysql = require('mysql');
const config = require('../config');

const pool = mysql.createPool({
    host: config.database.HOST,
    user: config.database.USERNAME,
    password: config.database.PASSWORD,
    database: config.database.DATABASE
})

let query = (sql, values) => {
    return new Promise((resolve, reject) => {
        pool.getConnection(function (err, connection) {
            if (err) {
                reject(err)
            } else {
                connection.query(sql, values, (err, rows) => {

                    if (err) {
                        reject(err)
                    } else {
                        resolve(rows)
                    }
                    connection.release()
                })
            }
        })
    })
}

module.exports = query

./db/data-fetcher.js

const query = require('./async-db')
async function performQuery(sql) {
    let dataList = await query(sql)
    return dataList
}

module.exports = performQuery;

My running result

When I launch server on port 3000 then accesss via http://localhost:3000/gateway?time=5, it always returns "Not found". But as I can see I have already used

return ctx.body = {
                success: true,
                code: config.code_success
            }

to send response. I debugged and found that the database processing was done well, the new data was inserted well.

when I remove that db inserting line, it works well and returns success info.

let r = await df(`insert into gateway (g_time, g_result, g_date) values (${time}, '',now())`);

Is there anything wrong?

Thanks a lot!

Update 2017/04/27

Now I have found the problem. It's due to my custom middleware

const loggerAsync = require('./middleware/logger-async')

Code are like following -

function log( ctx ) {
    console.log( ctx.method, ctx.header.host + ctx.url )
}

module.exports = function () {

    return function ( ctx, next ) {

        return new Promise( ( resolve, reject ) => {

            // 执行中间件的操作
            log( ctx )

            resolve()

            return next()

        }).catch(( err ) => {

            return next()
        })
    }

}

I changed it to async/await way then everything is working well.

Could anyone please tell me what's wrong with this middleware?

J.Lyu
  • 932
  • 7
  • 16

2 Answers2

0

I guess, your problem is the ./db/data-fetcher.js function. When you are calling

let r = await df(`insert ....`)

your df - function should return a promise.

So try to rewrite your ./db/data-fetcher.js like this (not tested):

const query = require('./async-db')

function performQuery(sql) {
    return new Promise((resolve, reject) => {
        query(sql).then(
            result => {
                resolve(result)
            }
        )
    }
}

module.exports = performQuery;

Hope that helps.

Sebastian Hildebrandt
  • 2,661
  • 1
  • 14
  • 20
  • Hi Thanks for your help. But it still returns "Not found" – J.Lyu Apr 20 '17 at 03:58
  • One more thing, that I see: in your `./router/gateway.js` after the `let r = await df(...)` you have `return ctx.body = { ...`. Here it should be `ctx.body = {` ... so without the `return` – Sebastian Hildebrandt Apr 23 '17 at 12:55
0

correct middleware:

function log( ctx ) {
    console.log( ctx.method, ctx.header.host + ctx.url )
}

module.exports = function () {
    return function ( ctx, next ) {
        log( ctx );
        return next()
    }
}

reason: when resolve involved; promise chain was completed; response has been sent to client. although middleware remained will involved, but response has gone!

try to understand It seems that if you want to use a common function as middleware, you have to return the next function

nodejs(koa):Can't set headers after they are sent

kwoktung
  • 572
  • 1
  • 4
  • 12