> Discover all available pages from the documentation index: https://mastra.zisheng.pro/zh-HK/llms.txt # Middleware Mastra 伺服器可以在叫用 API 路由處理器之前或之後執行自訂 middleware 函數。這適用於身份驗證、記錄日誌、注入請求特定的 context,或加入 CORS header 等情況。 Middleware 會接收 [Hono](https://hono.dev) `Context`(`c`)和 `next` 函數。如果它傳回 `Response`,請求便會短路。呼叫 `next()` 會繼續處理下一個 middleware 或路由處理器。 ```typescript 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() }, ], }, }) ``` 如要將 middleware 附加至單一路由,請將 `middleware` 選項傳遞給 `registerApiRoute`: ```typescript 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!' }) }, }) ``` ## 常見範例 ### 使用 `RequestContext` 你可以在 runtime 伺服器 middleware 中從請求擷取資料,以填入 `RequestContext`。在此範例中,`temperature-unit` 會根據 Cloudflare `CF-IPCountry` header 設定,確保回應符合用戶的地區設定。 ```typescript 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() }, ], }, }) ``` ### 身份驗證 ```typescript { 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/*', } ``` ### 授權(用戶隔離) 身份驗證會核實用戶身份;授權則控制他們可以存取的內容。如果沒有按資源 ID 限定範圍,已通過身份驗證的用戶便可能透過猜測 ID 或操控 `resourceId` 參數,存取其他用戶的 thread。 要將 memory 和 thread 的範圍限定於已通過身份驗證的用戶,最簡單的方法是使用 auth config 中的 `mapUserToResourceId` callback: ```typescript 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`。傳回值會在 request context 上設為 `MASTRA_RESOURCE_ID_KEY`,並適用於所有伺服器 adapter(Hono、Express、Next.js 等)。 資源 ID 不一定要是 `user.id`。常見模式包括: ```typescript // Org-scoped mapUserToResourceId: user => `${user.orgId}:${user.id}` // From a JWT claim mapUserToResourceId: user => user.tenantId // Composite key mapUserToResourceId: user => `${user.workspaceId}:${user.projectId}:${user.id}` ``` 設定資源 ID 後,伺服器會自動: - **篩選 thread 列表**,只傳回用戶擁有的 thread - **驗證 thread 存取權限**,如嘗試存取其他用戶的 thread,則傳回 403 - **強制建立 thread** 時使用已通過身份驗證的用戶 ID - **驗證訊息操作**(包括刪除),確保訊息屬於用戶擁有的 thread 即使用戶端傳遞 `?resourceId=other-user-id`,由 auth 設定的值仍會優先採用。嘗試存取其他用戶擁有的 thread 或訊息時,系統會傳回 403 錯誤。 #### 進階:在 middleware 中設定資源 ID 對於較複雜的情況(例如從數據庫查找資源 ID),你可以直接在 middleware 中設定 `MASTRA_RESOURCE_ID_KEY`: ```typescript 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 檢查之前執行。當 middleware 需要已通過身份驗證的用戶時,請呼叫 `getAuthenticatedUser()`,透過已設定的 auth provider 解析用戶,而不會變更預設的路由 auth 流程。 #### 使用 `MASTRA_THREAD_ID_KEY` 你亦可以設定 `MASTRA_THREAD_ID_KEY`,覆寫用戶端提供的 thread ID: ```typescript 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 支援 ```typescript { 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(); }, } ``` ### 請求日誌記錄 ```typescript { 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`); }, } ``` # 相關內容 - [Request Context](https://mastra.zisheng.pro/zh-HK/docs/server/request-context) - [保留鍵](https://mastra.zisheng.pro/zh-HK/docs/server/request-context)