Koa: ctx.body overwrites status set by ctx.assert

Created on 17 Nov 2017  路  2Comments  路  Source: koajs/koa

Sometimes you may want to wrap your middleware in try - catch statement.

If one uses ctx.assert -method inside a try statement, which sets the ctx.status, one might come across with counter intuitive behavior.

For example:

(ctx, next) => {
  try {
    ctx.assert(false, '400', 'this is bad')
  }
  catch (error) {
    console.log(ctx.status) // 400
    ctx.body = 'whoops, the status will be 200 after setting me'
    console.log(ctx.status) // 200
  }
}

However, this one works as expected:

(ctx, next) => {
  try {
    ctx.assert(false, '400', 'this is bad')
  }
  catch (error) {
    ctx.status = ctx.status // This sets the _explicitStatus to true
    console.log(ctx.status) // 400
    ctx.body = 'now works'
    console.log(ctx.status) // 400 as expected
  }
}

Most helpful comment

I guess there is nothing wrong in this behavior. ctx.assert does not set ctx.status. It throws Error object with status key set to the value you've specified. After that it's as per the user to decide what to do with it. So error throwing and status handling are kind of decoupled.

Here's koa context default error handler source code:
https://github.com/koajs/koa/blob/841844ee2fd0cf91478b5ed8d2f401e8b97eb4a2/lib/context.js#L143-L144
Then it uses error status to set context status:
https://github.com/koajs/koa/blob/841844ee2fd0cf91478b5ed8d2f401e8b97eb4a2/lib/context.js#L149
Btw assert test checks error object, not context.status:
https://github.com/koajs/koa/blob/841844ee2fd0cf91478b5ed8d2f401e8b97eb4a2/test/context/assert.js#L15

If you raise the error without handling it in try/catch you'll get the correct response code.

Here's a source example of 3 cases:

  • koa default error handling
  • try/catch without setting context status
  • try/catch with setting context status
const
  CODE = 400,
  MSG  = "this is bad";

const handlerDefault = (ctx) => {
  ctx.assert(false, CODE, MSG);
};

const handlerCustom = (ctx) => {
  try
  {
    ctx.assert(false, CODE, MSG);
  }
  catch (err)
  {
    console.log(`Hit custom error handler, err.status: ${err.status}`);
  }
};

const handlerCustomCode = (ctx) => {
  try
  {
    ctx.assert(false, CODE, MSG);
  }
  catch (err)
  {
    ctx.status = err.status || 500;
    console.log(`Hit custom error handler, ctx.status set to err.status: ${ctx.status}`);
  }
};

You'll get:

  • 400 as koa propagates it in default error handler
  • 404 as a default HTTP response code (or 200 if body is set)
  • 400 as context status was set explicitly

Full example source code here.

It's also worth checking documentation.

All 2 comments

I guess there is nothing wrong in this behavior. ctx.assert does not set ctx.status. It throws Error object with status key set to the value you've specified. After that it's as per the user to decide what to do with it. So error throwing and status handling are kind of decoupled.

Here's koa context default error handler source code:
https://github.com/koajs/koa/blob/841844ee2fd0cf91478b5ed8d2f401e8b97eb4a2/lib/context.js#L143-L144
Then it uses error status to set context status:
https://github.com/koajs/koa/blob/841844ee2fd0cf91478b5ed8d2f401e8b97eb4a2/lib/context.js#L149
Btw assert test checks error object, not context.status:
https://github.com/koajs/koa/blob/841844ee2fd0cf91478b5ed8d2f401e8b97eb4a2/test/context/assert.js#L15

If you raise the error without handling it in try/catch you'll get the correct response code.

Here's a source example of 3 cases:

  • koa default error handling
  • try/catch without setting context status
  • try/catch with setting context status
const
  CODE = 400,
  MSG  = "this is bad";

const handlerDefault = (ctx) => {
  ctx.assert(false, CODE, MSG);
};

const handlerCustom = (ctx) => {
  try
  {
    ctx.assert(false, CODE, MSG);
  }
  catch (err)
  {
    console.log(`Hit custom error handler, err.status: ${err.status}`);
  }
};

const handlerCustomCode = (ctx) => {
  try
  {
    ctx.assert(false, CODE, MSG);
  }
  catch (err)
  {
    ctx.status = err.status || 500;
    console.log(`Hit custom error handler, ctx.status set to err.status: ${ctx.status}`);
  }
};

You'll get:

  • 400 as koa propagates it in default error handler
  • 404 as a default HTTP response code (or 200 if body is set)
  • 400 as context status was set explicitly

Full example source code here.

It's also worth checking documentation.

@garcia556, thanks for your extensive answer!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

TheRav3n picture TheRav3n  路  3Comments

imkimchi picture imkimchi  路  4Comments

ilkkao picture ilkkao  路  4Comments

SteveCruise picture SteveCruise  路  3Comments

rowild picture rowild  路  4Comments