Astro 的中间件(Middleware)是一个强大的功能,允许你在请求到达页面之前拦截和处理请求,实现认证、重定向、修改响应等功能。
核心概念:
中间件在服务器端运行,可以访问请求对象,并在请求处理链中执行自定义逻辑。
创建中间件:
typescript// src/middleware.ts import { defineMiddleware } from 'astro:middleware'; import type { MiddlewareResponseHandler } from 'astro'; export const onRequest: MiddlewareResponseHandler = async (context, next) => { // 在这里处理请求 // 调用 next() 继续处理链 const response = await next(); // 可以修改响应 response.headers.set('X-Custom-Header', 'Custom Value'); return response; };
中间件上下文:
typescript// src/middleware.ts export const onRequest = async (context, next) => { // context 包含以下属性: console.log(context.request); // Request 对象 console.log(context.url); // URL 对象 console.log(context.cookies); // Cookies console.log(context.locals); // 本地数据存储 console.log(context.site); // 站点配置 const response = await next(); return response; };
使用场景:
- 认证和授权:
typescript// src/middleware.ts export const onRequest = async (context, next) => { const protectedRoutes = ['/dashboard', '/settings', '/profile']; const currentPath = context.url.pathname; if (protectedRoutes.some(route => currentPath.startsWith(route))) { const token = context.cookies.get('auth-token'); if (!token) { return context.redirect('/login'); } // 验证 token const user = await verifyToken(token.value); if (!user) { return context.redirect('/login'); } // 将用户信息存储在 locals 中 context.locals.user = user; } return next(); };
- 重定向管理:
typescript// src/middleware.ts export const onRequest = async (context, next) => { const redirects = { '/old-url': '/new-url', '/old-page': '/new-page', }; const currentPath = context.url.pathname; if (redirects[currentPath]) { return context.redirect(redirects[currentPath], 301); } return next(); };
- 国际化(i18n):
typescript// src/middleware.ts export const onRequest = async (context, next) => { const supportedLocales = ['en', 'zh', 'ja']; const defaultLocale = 'en'; // 从 URL 获取语言 const pathSegments = context.url.pathname.split('/'); const locale = pathSegments[1]; // 检查是否是支持的语言 if (supportedLocales.includes(locale)) { context.locals.locale = locale; return next(); } // 从 Accept-Language 头获取语言 const acceptLanguage = context.request.headers.get('accept-language'); const browserLocale = acceptLanguage?.split(',')[0].split('-')[0] || defaultLocale; const targetLocale = supportedLocales.includes(browserLocale) ? browserLocale : defaultLocale; return context.redirect(`/${targetLocale}${context.url.pathname}`); };
- 日志记录:
typescript// src/middleware.ts export const onRequest = async (context, next) => { const startTime = Date.now(); const response = await next(); const duration = Date.now() - startTime; console.log(`${context.request.method} ${context.url.pathname} - ${duration}ms`); return response; };
- CORS 处理:
typescript// src/middleware.ts export const onRequest = async (context, next) => { const response = await next(); response.headers.set('Access-Control-Allow-Origin', '*'); response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE'); response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization'); return response; };
条件中间件:
typescript// src/middleware.ts export const onRequest = async (context, next) => { // 只对特定路径应用中间件 if (context.url.pathname.startsWith('/api/')) { console.log('API request:', context.url.pathname); } return next(); };
中间件优先级:
Astro 支持在不同层级定义中间件:
- 全局中间件:
src/middleware.ts- 应用于所有请求 - 路由中间件:在特定路由目录中定义 - 应用于该路由及其子路由
typescript// src/middleware.ts (全局) export const onRequest = async (context, next) => { console.log('Global middleware'); return next(); };
typescript// src/pages/dashboard/middleware.ts (路由特定) export const onRequest = async (context, next) => { console.log('Dashboard middleware'); return next(); };
使用 locals 传递数据:
typescript// src/middleware.ts export const onRequest = async (context, next) => { // 在中间件中设置数据 context.locals.user = await getUser(context); context.locals.theme = 'dark'; const response = await next(); return response; };
astro--- // 在页面中使用 locals const user = Astro.locals.user; const theme = Astro.locals.theme; --- <h1>欢迎, {user?.name}</h1> <p>当前主题: {theme}</p>
错误处理:
typescript// src/middleware.ts export const onRequest = async (context, next) => { try { return await next(); } catch (error) { console.error('Middleware error:', error); return new Response('Internal Server Error', { status: 500, headers: { 'Content-Type': 'text/plain' }, }); } };
性能优化:
typescript// src/middleware.ts export const onRequest = async (context, next) => { // 缓存频繁访问的数据 const cacheKey = `user:${context.cookies.get('user-id')?.value}`; const cachedUser = await cache.get(cacheKey); if (cachedUser) { context.locals.user = cachedUser; return next(); } const user = await fetchUser(context.cookies.get('user-id')?.value); await cache.set(cacheKey, user, { ttl: 3600 }); context.locals.user = user; return next(); };
最佳实践:
- 保持中间件简洁高效
- 避免在中间件中执行耗时操作
- 使用缓存优化性能
- 合理使用 locals 传递数据
- 实现适当的错误处理
- 考虑中间件的执行顺序
- 只在必要时使用重定向
Astro 的中间件功能为构建复杂的应用提供了强大的请求处理能力,特别适合需要认证、授权、国际化等功能的项目。