在 Next.js 使用 connect 中间件

Posted Randy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在 Next.js 使用 connect 中间件相关的知识,希望对你有一定的参考价值。

本文节选自 Next.js 应用开发实践

Next.js 没有中间件机制。首先让我简单解释一下什么是中间件,为什么我们需要中间件。

在 Express/Koa, 我们可以用中间件进入一个请求的生命周期,一个典型的中间件是一个函数,它接受 req, resnext 参数:

function exampleMiddleware(req, res, next) {
  if (/** ...*/) {
    req.foo = \'bar\'
      next()
  } else {
    res.statusCode = 403
    res.send(\'forbiddon\')
  }
}
这个中间件的模式起源于一个 Node.js Web 框架 connect, 早期的 Express 也基于 Connect 开发,于是很多框架也兼容了这种模式,所以这种中间件模式我们通常称为 connect 中间件。

在中间件里,我们可以:

  • req 对象注入一些属性,这些属性可以被下一个中间件或者 controller 获取到。
  • 可以通过不执行 next() 来中止请求,同时修改 res 的属性从而改变 response 的状态。

这使得中间件可以很好地使代码在不同的路由之间重用。假设我们需要在一个路由跟据 cookies 获取用户信息,我们可以把这个获取用户信息的方法写成中间件,然后把用户信息注入到 req.user,这样所以使用了这个中间件的路由可以通过 req.user 取得用户信息。而且在中间件中,如果判断用户没有登录,可以中止这个请求,并返回 403.

下面是 Express 编写和使用中间件的例子:

function authMiddleware(req, res, next) {
  // 假设 cookies 中用 `token` 保存用户信息
  if (req.cookies.token) {
    const user = getUserByToken(req.cookies.token)
    req.user = user
    next()
  } else {
    // cookies.token 不存在,中止请求并返回 403
    res.statusCode = 403
    res.send(\'please sign in first\')
  }
}

// 不使用这个中间件的路由
app.get(\'/\', (req, res) => {
  res.send(\'hello world\')
})

// 使用这个中间件的路由
app.get(\'/profile\', authMiddleware, (req, res) => {
  // 可以通过 `req.user` 取得用户信息
  res.send(`welcome! ${req.user.name}`)
})

如果在 Next.js 要做同样的事,我们会这么做:

// pages/api/example.ts

function auth(req, res) {
  if (req.cookies.token) {
    const user = getUserByToken(req.cookies.token)
    return user
  } else {
    // 用户未登录
    res.status(403)
    res.send(\'please sign in first\')
  }
}

export default (req, res) => {
  if (req.method === \'GET\') {
    res.send(\'hello\')
  } else if (req.method === \'POST\') {
    const user = auth(req, res)
    console.log(\'do other things\')
      res.send(`welcome! ${user.name}`)
  }
}

但在 Next.js, 我们没有任何办法中止请求。理论上 console.log(\'do other things\') 在用户未登录时不应该被执行。

使用 next-connect

要在 Next.js 中像 Express/Koa 这样使用 connect 中间件,我们可以使用 next-connect 这个库。

安装 next-connect:

$ yarn add next-connect

现在,让我们用 next-connect 重写上面的例子:

// pages/api/example.ts

import nc from \'next-connect\'

function authMiddleware(req, res, next) {
  res.status(403)
  res.send(\'please sign in first\')
}

// 用 `nc()` 创建一个 api handler
const handler = nc()
  .get((req, res) => {
    res.send(\'hello\')
  })
  .post(authMiddleware, (req,res) => {
    res.send(\'hello\')
  })

export default handler

可以看到,现在我们在 Next.js 的API route 可以像在 Express 一样使用中间件。

authMiddleware中,我们返回了一个 403,并且没有执行 next(), 模拟了用户未登录的情况。由于 next() 没有执行,这个 POST 请求不会执行这个 POST handler 的代码。

next-connect 的另一个好处是,我们可以用.get(), .post(), put() 这样的 helper 来创建对应的 handler, 而不需要用 if (req.method === XXX) 这样的判断。让代码更好读。

因为 next-connect 兼容 connect 中间件,所以我们可以直接用社区上成熟的 connect 中间件,例如用于修改跨域设置的中间件 cors:

安装 cors:

$ yarn add cors
// pages/api/example.ts

import nc from \'next-connect\'
+ import * as cors from \'cors\'

const corsOptions = {
  origin: \'http://example.com\',
  optionsSuccessStatus: 200
}

const handler = nc()
+    .use(cors(corsOptions))
  .get((req, res) => {
    res.send(\'hello\')
  })
  .post(authMiddleware, (req,res) => {
    res.send(\'hello\')
  })

export default handler

以上是关于在 Next.js 使用 connect 中间件的主要内容,如果未能解决你的问题,请参考以下文章

尝试使用 next-connect 在 Next.js 中从 http://localhost:3000 重定向到 https://api.twitter.com 时出现 CORS 错误?

Next.js + 云开发Webify 打造绝佳网站

在 getServerSideProps() 中直接导入 next.js API 端点

Next.js 默认是同站点来源,但我仍然可以访问它

next.js 的缺点超过 create react app + redux + s-s-r [关闭]

myBatis连接MySQL报异常:No operations allowed after connection closed.Connection was implicitly closed(示例代