koajs / router

Router middleware for Koa. Maintained by @forwardemail and @ladjs.
MIT License
845 stars 172 forks source link

A catch-all 404 handler for koa? #158

Open slavafomin opened 1 year ago

slavafomin commented 1 year ago

Hello!

I'm trying to implement a pretty trivial thing — a catch-all handler that will return 404 when request handler is not found for the request in question, however, I can't find any related examples in the docs and the Internet. What is the best practice for this?

The example below won't work because the router.all('/(.*)') will intercept all requests after they were handled. Of course I can do something like context.handled = true and then check for this flag in the catch-all handler, but this will be a boilerplate code that should actually be handled by the router itself. Avoiding a call to next() in the request handler will also be a mistake, because it will break the middleware that do request post-processing (e.g. response transformation, etc). If I change the order of handler registration then I will need to set proper status in all the request handlers.

router.get('/health', (context, next) => {
  context.body = {
    status: 'ok',
  };
  return next();
});

router.all('/(.*)', (context, next) => {
  context.status = 404;
  context.body = {
    message: 'Not found',
  };
  return next();
});

I believe the router should provide a special method for handling unhandled requests.

ThaDaVos commented 1 year ago

Sadly running into the same issue - did you find a solution?

breavyn commented 1 year ago

I handle this in the applications error middleware.

import * as createError from "http-errors";

const errorMiddleware = async (ctx, next) => {
  try {
    await next();

    if (ctx.status === 404) {
      throw createError(404);
    }
  } catch (err) {
    // handle error
  }
};

If any of my handled routes need to return 404, they do so by throwing an error. If await next(); completes without error, and the status is set to 404, I can assume the router has done this.

zacanger commented 1 year ago

No need for the extra http-errors package, and also no need to even check the status, not really — the default is 404, so if you add a 404 handler after everything else (except for the listen or callback or whatever), you're all set:

app.use(async (ctx, next) => {
  ctx.status = 404
  ctx.body = 'Not found'
})
fewbadboy commented 3 months ago

yesterday, i also meet same problem too! the key is use wrong way of the next().this is my code example.

router/index.js

const router = new Router({
  prefix: '/api/v1'
})

router.get('/', (ctx, next) => {
  ctx.user = 'Hello'
  next()
}, (ctx, next) => {
  ctx.body = ctx.user
})

router.use(errorRouter.routes(), errorRouter.allowedMethods())

router.all(':pattern(.*)*',(ctx, next) => {
  console.log(ctx.params.pattern)
  ctx.redirect('/api/v1/error/404')
  ctx.status = 301
})

export default router

router/error/index,js

const router = new Router({
  prefix: '/error'
})

router
  .all('/404',(ctx, next) => {
    ctx.status = 404
    ctx.body = '404 Error Page'
  })

export default router