【问题标题】:Respond to Koa request immediately, continue middleware chain立即响应 Koa 请求,继续中间件链
【发布时间】:2020-03-11 13:20:29
【问题描述】:

我正在为我的应用程序中的 API 端点编写中间件,这些端点响应来自其他应用程序的 webhook,并且对 Koa 相对较新,因此并不完全熟悉它的模式。

我想按如下方式构建我的中间件:

exports.updateReceived = async (ctx, next) => {
  // Respond to server issuing the webhook
  ctx.res.body = "ok";
  ctx.res.statusCode = 200;

  // Grab what we need from the request
  const { headers, state, request } = ctx;
  const { body } = request;

  // Do some async work
  const { example } = await doSomeAsyncWork(ctx);

  // Prepare a database query
  const query = { aValue: anId };

  // Run the DB query
  const result = await Thing.findOne(query);

  // Add data to the request
  state.thing = result;

  // Move on...
  return next();
};

但是,这似乎不起作用,因为我的任何异步方法中的错误都可能导致路由出错。

我的目标是让这个端点总是(立即)响应“是的,好的”,这意味着它只是由应用程序来处理任何错误状态。

我对此进行了很好的研究,并且遇到了这种模式:

app.use(async ctx => {
  db.fetch() // Assuming a Promise is returned
    .then(() => { ... })
    .catch(err => {
      log(err)
    })

  // status 200 will be returned regardless of if db.fetch() resolves or rejects.
  ctx.status = 200
})

但是,这不能满足我的需求,因为中间件没有使用next,所以据我所知,这并不是一个真正有用的模式。

谁能告诉我我忽略了什么?

【问题讨论】:

  • 关键是你的中间件需要返回before调用next。接下来是中间件链的封装。

标签: node.js koa koa2


【解决方案1】:

next() 调用下游中间件链并返回一个在所有下游中间件/处理程序完成后解析的承诺。

这意味着您可以简单地实现自己的上游错误处理程序来捕获任何错误并始终确保 200 OK 响应。

const Koa = require('koa')
const app = new Koa()

app.use(async (ctx, next) => {
  next().catch((err) => {
    // Print error for our own records / debugging
    console.error(err)

    // But ensure that outgoing response is always a smile
    ctx.status = 200
    ctx.body = ':)'
  })
})

app.use(async (ctx) => {
  // Do your webhook / biz logic here, but for demonstration
  // let's make it always throw an error. Thus upstream next()
  // will be a rejected promise.
  throw new Error('this middleware will always bubble up a rejected promise')
})

app.listen(3000, () => {
  console.log('listening on 3000')
})

【讨论】:

  • 这看起来是处理错误的好方法,但它并不能确保及时的服务器响应。访问我的端点的服务器对 webhook 响应时间有非常严格的要求,由于 API 调用,我的服务器上要做大量(非常繁重)的工作,我不能保证我的服务器总是会在他们之前响应非常短的响应/超时要求。因此,虽然这确实处理了我问题的错误部分,但它似乎并没有解决“立即响应”部分。有什么想法吗?
【解决方案2】:

只是为了将解决方案转移到不同的方面,您可以考虑将您的工作添加到某种 MessageQueue 中,然后让另一个进程为您完成该任务。基本上是异步的,但你仍然很重要。这种模式适合你的要求。

您可以考虑使用许多消息传递系统,例如 AWS SQS。这样,您的 api 将非常轻巧,它会做它需要做的事情并向您的消息传递系统发送命令来做额外的事情。您基本上将核心逻辑和后台处理的事情分开,这也很好地扩展了。

【讨论】:

  • 当然,但是当我只想让服务器执行服务器执行的第 1 件事时,这似乎是一大笔开销:响应。
【解决方案3】:

注意:我们不等待 next(),所以我们可以立即结束请求。但是链中的下一个处理程序仍然有机会处理逻辑

app.use((ctx, next) => {
  next()
  ctx.status = 200
})

app.use( async ctx =>{
    db.fetch()
        .then(() => { ... })
        .catch(err => log(err))
    }
}

【讨论】:

    猜你喜欢
    • 2014-02-12
    • 1970-01-01
    • 1970-01-01
    • 2017-02-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-30
    • 1970-01-01
    相关资源
    最近更新 更多