跳到主要内容
非官方简体中文翻译,内容版权归 Mastra 所有,遵循 Apache-2.0 许可。

Middleware

Mastra 服务器可以在调用 API 路由处理器之前或之后执行自定义中间件函数。这适用于身份验证、日志记录、注入请求特定上下文或添加 CORS 头等场景。

中间件接收 Hono 的 Context(c)和一个 next 函数。如果它返回 Response,请求会被短路。调用 next() 会继续处理下一个中间件或路由处理器。

备注

中间件处理器使用 Hono 的签名,因此它们运行在基于 Hono 的服务路径上:mastra dev 和 mastra build、Hono 适配器,以及基于它构建的适配器,例如 @mastra/next 和 @mastra/tanstack-start。其他框架的适配器(Express、Fastify、Koa)无法运行 Hono 处理器,并且在配置了中间件时会在启动时记录警告。在这些框架中,请通过框架自己的 API 注册中间件。

用户中间件永远不会在通过 requiresAuth: false 声明为公开的路由上运行,因此它无法阻止框架需要保持可访问的端点,例如 Studio 登录路由。

import { Mastra } from '@mastra/core'

export const mastra = new Mastra({
server: {
middleware: [
{
handler: async (c, next) => {
// Example: Add authentication check
const authHeader = c.req.header('Authorization')
if (!authHeader) {
return new Response('Unauthorized', { status: 401 })
}

await next()
},
path: '/api/*',
},
// Add a global request logger
async (c, next) => {
console.log(`${c.req.method} ${c.req.url}`)
await next()
},
],
},
})

要将中间件附加到单个路由,请向 registerApiRoute 传入 middleware 选项:

registerApiRoute('/my-custom-route', {
method: 'GET',
middleware: [
async (c, next) => {
console.log(`${c.req.method} ${c.req.url}`)
await next()
},
],
handler: async c => {
const mastra = c.get('mastra')
return c.json({ message: 'Hello, world!' })
},
})

常见示例
常见示例的直接链接

阻止内置路由组
阻止内置路由组的直接链接

Mastra 不提供移除内置路由或将其加入允许列表的配置选项。在基于 Hono 的服务路径上,你可以通过返回响应而不调用 next() 来使选定的路由组不可用:

src/mastra/index.ts
import { Mastra } from '@mastra/core'

const notFound = async () => new Response('Not Found', { status: 404 })

export const mastra = new Mastra({
server: {
middleware: [
{ path: '/api/memory/*', handler: notFound },
{ path: '/api/logs/*', handler: notFound },
{ path: '/api/observability/*', handler: notFound },
],
},
})

每个通配符模式会同时阻止路由组本身及其嵌套路由。例如,/api/logs/* 会阻止 /api/logs 和 /api/logs/transports。其他路由组保持可用。

生成的服务器会将 server.apiPrefix 传给 Hono 适配器的 prefix 构造函数选项,该选项会为内置路由添加前缀。中间件路径按原样注册,因此必须显式包含配置的前缀。例如,使用 apiPrefix: '/api/v2' 时,应使用 /api/v2/memory/*。自定义 API 路由必须位于配置的 API 前缀之外,并且不会被这些模式阻止。

此方法无法阻止通过 requiresAuth: false 声明为公开的路由,因为 Mastra 会跳过这些路由的用户中间件。使用非 Hono 服务器适配器时,请通过服务器框架注册等效的中间件。

在 mastra dev 期间,可以在 OpenAPI 规范中检查完整的内置路由列表(包括 MCP 路由)。API 探索和生产设置请参阅服务器概览。

如果你使用中间件进行身份验证而没有使用 server.auth,请优先采用默认拒绝的中间件,在允许访问之前对请求进行身份验证,而不是枚举受保护路径。请包含完整的 API 前缀,以便覆盖与 MCP 传输并列的 REST 路由。

使用 RequestContext
使用-requestcontext的直接链接

你可以在运行时服务器中间件中通过从请求提取信息来填充 RequestContext。在此示例中,temperature-unit 根据 Cloudflare 的 CF-IPCountry 头设置,以确保响应匹配用户的区域设置。

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { RequestContext } from '@mastra/core/request-context'
import { testWeatherAgent } from './agents/test-weather-agent'

export const mastra = new Mastra({
agents: { testWeatherAgent },
server: {
middleware: [
async (context, next) => {
const country = context.req.header('CF-IPCountry')
const requestContext = context.get('requestContext')

requestContext.set('temperature-unit', country === 'US' ? 'fahrenheit' : 'celsius')

await next()
},
],
},
})

身份验证
身份验证的直接链接

{
handler: async (c, next) => {
const authHeader = c.req.header('Authorization');
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return new Response('Unauthorized', { status: 401 });
}

// Validate token here
await next();
},
path: '/api/*',
}

授权(用户隔离)
授权(用户隔离)的直接链接

身份验证确认用户是谁,而授权控制他们可以访问什么。

如果没有服务器强制执行的 resource 作用域或其他授权策略,已认证的调用方可以列出跨 resource 的 thread,并访问其消息和工作记忆。调用方无需猜测 thread ID:未限定范围的列表可能暴露它们。对于按用户或按租户隔离的私有记忆,请配置 mapUserToResourceId 或在中间件中设置受信任的 resource ID。

在共享应用中,跨 resource 访问可能是有意为之。resource ID 可以标识用户、租户、项目或其他分组。请选择与可以访问这些资源的人员相匹配的授权策略。

将记忆和 thread 限定到已认证用户的最简单方式是在 auth 配置中使用 mapUserToResourceId 回调:

import { Mastra } from '@mastra/core'

export const mastra = new Mastra({
server: {
auth: {
authenticateToken: async token => {
return verifyToken(token) // { id: 'user-123', orgId: 'org-456', ... }
},
mapUserToResourceId: user => user.id,
},
},
})

身份验证成功后,会以已认证用户对象调用 mapUserToResourceId。返回的值会设置为请求上下文上的 MASTRA_RESOURCE_ID_KEY,这在所有服务器适配器(Hono、Express、Next.js 等)中均有效。

配置后,回调必须返回非空字符串。如果它抛出错误或返回 null、undefined、空字符串或仅含空白的字符串,或非字符串值,身份验证中间件会在路由运行之前以 500 错误拒绝请求。这适用于所有运行身份验证的请求,包括非记忆路由。客户端提供的 resource ID 无法覆盖失败的映射。

使用 CompositeAuth 时,此要求适用于对请求进行身份验证的提供商。没有映射器的提供商保持其现有行为,即使复合中的另一个提供商有映射器。相同的验证适用于已配置的 Studio auth 映射器。

没有映射器时,身份验证不会启用用户隔离:你需要负责在中间件中设置受信任的 resource ID,或以其他方式强制执行授权。

resource ID 不必是 user.id。常见模式:

// Per-user within an organization
mapUserToResourceId: user => `${user.orgId}:${user.id}`

// From a JWT claim
mapUserToResourceId: user => user.tenantId

// Composite key
mapUserToResourceId: user => `${user.workspaceId}:${user.projectId}:${user.id}`

设置 resource ID 后,服务器会自动:

  • 过滤 thread 列表,仅返回配置的 resource 作用域内的 thread
  • 验证 thread 访问,如果访问属于其他 resource 的 thread,则返回 403
  • 强制 thread 创建使用配置的 resource ID
  • 验证消息操作,包括删除,确保消息属于配置的 resource 作用域内的 thread

即使客户端传入 ?resourceId=other-resource-id,服务器设置的值也会优先。尝试访问属于其他 resource 的 thread 或消息会返回 403 错误。映射到同一 resource ID 的用户共享该 resource 作用域。

高级:在中间件中设置 resource ID
高级:在中间件中设置 resource ID的直接链接

对于更复杂的场景(例如从数据库查找 resource ID),你可以直接在中间件中设置 MASTRA_RESOURCE_ID_KEY:

import { Mastra } from '@mastra/core'
import { MASTRA_RESOURCE_ID_KEY } from '@mastra/core/request-context'
import { getAuthenticatedUser } from '@mastra/server/auth'

export const mastra = new Mastra({
server: {
auth: {
authenticateToken: async token => verifyToken(token),
},
middleware: [
{
path: '/api/*',
handler: async (c, next) => {
const token = c.req.header('Authorization')
if (!token) {
return c.json({ error: 'Unauthorized' }, 401)
}

const user = await getAuthenticatedUser<{ id: string }>({
mastra: c.get('mastra'),
token,
request: c.req.raw,
})
const requestContext = c.get('requestContext')

if (!user) {
return c.json({ error: 'Unauthorized' }, 401)
}

requestContext.set(MASTRA_RESOURCE_ID_KEY, user.id)
return next()
},
},
],
},
})

server.middleware 在 Mastra 的逐路由 auth 检查之前运行。当中间件需要已认证用户时,调用 getAuthenticatedUser() 从配置的 auth 提供商解析,而不会更改默认的路由 auth 流程。

使用 MASTRA_THREAD_ID_KEY
使用-mastra_thread_id_key的直接链接

你还可以设置 MASTRA_THREAD_ID_KEY 来覆盖客户端提供的 thread ID:

import { MASTRA_RESOURCE_ID_KEY, MASTRA_THREAD_ID_KEY } from '@mastra/core/request-context'

// Force operations to use a specific thread
requestContext.set(MASTRA_THREAD_ID_KEY, validatedThreadId)

当你希望将操作限制为通过其他方式验证过的特定 thread 时,这非常有用。

CORS 支持
CORS 支持的直接链接

{
handler: async (c, next) => {
c.header('Access-Control-Allow-Origin', '*');
c.header(
'Access-Control-Allow-Methods',
'GET, POST, PUT, DELETE, OPTIONS',
);
c.header(
'Access-Control-Allow-Headers',
'Content-Type, Authorization',
);

if (c.req.method === 'OPTIONS') {
return new Response(null, { status: 204 });
}

await next();
},
}

请求日志
请求日志的直接链接

{
handler: async (c, next) => {
const start = Date.now();
await next();
const duration = Date.now() - start;
console.log(`${c.req.method} ${c.req.url} - ${duration}ms`);
},
}

相关内容

On this page