中间件
已弃用:中间件在版本 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
使用 批量事务 时,请勿在中间件内多次调用 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:
-
设置或覆盖字段值 - 例如,设置博客文章评论的上下文语言
¥Setting or overwriting a field value - for example, setting the context language of a blog post comment
-
验证输入数据 - 例如,通过外部服务检查用户输入是否存在不当语言
¥Validating input data - for example, check user input for inappropriate language via an external service
-
拦截
delete
查询并将其更改为update
以执行 软删除¥Intercept a
delete
query and change it to anupdate
in order to perform a soft delete
中间件还有更多用例 - 此列表为中间件旨在解决的问题类型提供了灵感。
¥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:
中间件示例:软删除
以下示例使用 中间件 执行软删除。软删除意味着通过将 deleted 等字段更改为 true 来将记录标记为已删除,而不是实际从数据库中删除。使用软删除的原因包括:
中间件示例:logging
以下示例记录 Prisma 客户端查询运行所需的时间:
中间件示例:会话数据
以下示例将每个 Post 的 language 字段设置为上下文语言(例如,取自会话状态):
在哪里添加中间件
¥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:
-
每个中间件中
await next(params)
之前的所有逻辑,按降序排列¥All logic before
await next(params)
in each middleware, in descending order -
每个中间件中
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.model
和params.action
属性,以避免运行不必要的逻辑:¥Check the
params.model
andparams.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 thenow()
function or the@updatedAt
attribute? -
如果你需要执行更复杂的验证,可以在数据库本身中使用
CHECK
约束吗?¥If you need to perform more complex validation, can you use a
CHECK
constraint in the database itself?
-