Skip to main content

中间件

warning

已弃用:中间件在版本 4.16.0 中已弃用。

¥Deprecated: Middleware is deprecated in version 4.16.0.

我们建议使用 Prisma 客户端扩展 query 组件类型 作为中间件的替代方案。Prisma 客户端扩展首次在 4.7.0 版本中引入预览版,并在 4.16.0 中全面可用。

¥We recommend using the Prisma Client extensions query component type as an alternative to middleware. Prisma Client extensions were first introduced into Preview in version 4.7.0 and made Generally Available in 4.16.0.

Prisma 客户端扩展允许你创建独立的 Prisma 客户端实例并将每个客户端绑定到特定的过滤器或用户。例如,你可以将客户端绑定到特定用户以提供用户隔离。Prisma 客户端扩展还提供端到端类型安全。

¥Prisma Client extensions allow you to create independent Prisma Client instances and bind each client to a specific filter or user. For example, you could bind clients to specific users to provide user isolation. Prisma Client extensions also provide end-to-end type safety.

中间件充当查询级生命周期钩子,允许你在查询运行之前或之后执行操作。使用 prisma.$use 方法添加中间件,如下:

¥Middlewares act as query-level lifecycle hooks, which allow you to perform an action before or after a query runs. Use the prisma.$use method to add middleware, as follows:

const prisma = new PrismaClient()

// Middleware 1
prisma.$use(async (params, next) => {
// Manipulate params here
const result = await next(params)
// See results here
return result
})

// Middleware 2
prisma.$use(async (params, next) => {
// Manipulate params here
const result = await next(params)
// See results here
return result
})

// Queries here
warning

使用 批量事务 时,请勿在中间件内多次调用 next。这将导致你中断事务并导致意想不到的结果。

¥Do not invoke next multiple times within a middleware when using batch transactions. This will cause you to break out of the transaction and lead to unexpected results.

params 代表中间件中可用的参数,例如查询的名称,next 代表 堆栈中的下一个中间件或原始 Prisma 客户端查询

¥params represent parameters available in the middleware, such as the name of the query, and next represents the next middleware in the stack or the original Prisma Client query.

中间件的可能用例包括:

¥Possible use cases for middleware include:

中间件还有更多用例 - 此列表为中间件旨在解决的问题类型提供了灵感。

¥There are many more use cases for middleware - this list serves as inspiration for the types of problems that middleware is designed to address.

样品

¥Samples

以下示例场景展示了如何在实践中使用中间件:

¥The following sample scenarios show how to use middleware in practice:

在哪里添加中间件

¥Where to add middleware

在请求处理程序的上下文之外添加 Prisma Client 中间件,否则每个请求都会将中间件的新实例添加到堆栈中。以下示例演示了在 Express 应用上下文中添加 Prisma Client 中间件的位置:

¥Add Prisma Client middleware outside the context of the request handler, otherwise each request adds a new instance of the middleware to the stack. The following example demonstrates where to add Prisma Client middleware in the context of an Express app:

import express from 'express'
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

prisma.$use(async (params, next) => {
// Manipulate params here
const result = await next(params)
// See results here
return result
})

const app = express()
app.get('/feed', async (req, res) => {
// NO MIDDLEWARE HERE
const posts = await prisma.post.findMany({
where: { published: true },
include: { author: true },
})
res.json(posts)
})

运行顺序和中间件堆栈

¥Running order and the middleware stack

如果你有多个中间件,则每个单独查询的运行顺序为:

¥If you have multiple middlewares, the running order for each separate query is:

  1. 每个中间件中 await next(params) 之前的所有逻辑,按降序排列

    ¥All logic before await next(params) in each middleware, in descending order

  2. 每个中间件中 await next(params) 之后的所有逻辑,按升序排列

    ¥All logic after await next(params) in each middleware, in ascending order

根据你在堆栈中的位置,await next(params) 可以:

¥Depending on where you are in the stack, await next(params) either:

  • 运行下一个中间件(在示例中的中间件 #1 和 #2 中)或

    ¥Runs the next middleware (in middlewares #1 and #2 in the example) or

  • 运行原始 Prisma 客户端查询(在中间件 #3 中)

    ¥Runs the original Prisma Client query (in middleware #3)

const prisma = new PrismaClient()

// Middleware 1
prisma.$use(async (params, next) => {
console.log(params.args.data.title)
console.log('1')
const result = await next(params)
console.log('6')
return result
})

// Middleware 2
prisma.$use(async (params, next) => {
console.log('2')
const result = await next(params)
console.log('5')
return result
})

// Middleware 3
prisma.$use(async (params, next) => {
console.log('3')
const result = await next(params)
console.log('4')
return result
})

const create = await prisma.post.create({
data: {
title: 'Welcome to Prisma Day 2020',
},
})

const create2 = await prisma.post.create({
data: {
title: 'How to Prisma!',
},
})

输出:

¥Output:

Welcome to Prisma Day 2020
1
2
3
4
5
6
How to Prisma!
1
2
3
4
5
6

性能和适当的用例

¥Performance and appropriate use cases

中间件针对每个查询执行,这意味着过度使用可能会对性能产生负面影响。为了避免增加性能开销:

¥Middleware executes for every query, which means that overuse has the potential to negatively impact performance. To avoid adding performance overheads:

  • 在中间件中尽早检查 params.modelparams.action 属性,以避免运行不必要的逻辑:

    ¥Check the params.model and params.action properties early in your middleware to avoid running logic unnecessarily:

    prisma.$use(async (params, next) => {
    if (params.model == 'Post' && params.action == 'delete') {
    // Logic only runs for delete action and Post model
    }
    return next(params)
    })
  • 考虑中间件是否适合你的场景。例如:

    ¥Consider whether middleware is the appropriate solution for your scenario. For example:

    • 如果需要填充字段,可以使用 @default 属性吗?

      ¥If you need to populate a field, can you use the @default attribute?

    • 如果需要设置 DateTime 字段的值,可以使用 now() 函数或 @updatedAt 属性吗?

      ¥If you need to set the value of a DateTime field, can you use the now() function or the @updatedAt attribute?

    • 如果你需要执行更复杂的验证,可以在数据库本身中使用 CHECK 约束吗?

      ¥If you need to perform more complex validation, can you use a CHECK constraint in the database itself?