Nuxt3代理/api/**路由后如何隐藏该路由并返回404?
问题描述
我有一个Nuxt3服务端插件myPlugin.server.js,通过await Promise.allSettled(apiRoutes.map(async (route) => {const data = await $fetch(route.url)...})映射所有外部后端的API路由,并在服务端填充Pinia状态。
同时配置了服务端中间件代理请求:
export default defineEventHandler(async (event) => { const url = event.node.req.url; if (url.includes('/api/') && url !== '/api/health' && url !== '/api/_sitemap-urls') { const pathRewrite = url.replace('/api/', '/'); await proxyRequest(event, (process.env.WP_API_URL || 'http://localhost:1222/wp-json') + pathRewrite); } });
现在需要实现:代理请求后,隐藏浏览器端的/api/**路由并返回404,但遇到以下问题:
- 代理后
/api/**路由不可访问,使用sendRedirect()会出现Can't send headers after they are sent错误; - 若在代理前返回404则会导致代理失效;
- 尝试创建
server/api/路由返回重定向、客户端路由及路由中间件使用navigateTo()均无效,因为这些路由已被代理,客户端无法访问。
请问该如何实现需求?
解决方案
核心思路是区分请求来源是服务端内部调用还是浏览器端发起,只允许服务端内部的/api/**请求走代理,浏览器端发起的直接返回404。
方法1:利用Nuxt内部请求头判断
Nuxt的$fetch在服务端内部调用时会自动添加x-nuxt-internal请求头,可直接用这个标识区分请求来源:
export default defineEventHandler(async (event) => { const url = event.node.req.url; const isInternalRequest = event.node.req.headers['x-nuxt-internal']; if (url.includes('/api/') && url !== '/api/health' && url !== '/api/_sitemap-urls') { // 仅服务端内部请求走代理逻辑 if (isInternalRequest) { const pathRewrite = url.replace('/api/', '/'); await proxyRequest(event, (process.env.WP_API_URL || 'http://localhost:1222/wp-json') + pathRewrite); } else { // 浏览器端直接访问时返回404 setResponseStatus(event, 404); return { status: 404, message: 'Not Found' }; } } });
方法2:自定义内部请求标识
如果担心默认头的兼容性,可在服务端插件的$fetch调用中添加自定义请求头,再在中间件校验:
- 修改服务端插件的
$fetch调用:
// myPlugin.server.js await Promise.allSettled(apiRoutes.map(async (route) => { const data = await $fetch(route.url, { headers: { 'x-internal-api-call': 'true' // 自定义内部请求标识 } }); // 填充Pinia状态的逻辑 }));
- 更新服务端中间件:
export default defineEventHandler(async (event) => { const url = event.node.req.url; const isInternalCall = event.node.req.headers['x-internal-api-call'] === 'true'; if (url.includes('/api/') && url !== '/api/health' && url !== '/api/_sitemap-urls') { if (isInternalCall) { const pathRewrite = url.replace('/api/', '/'); await proxyRequest(event, (process.env.WP_API_URL || 'http://localhost:1222/wp-json') + pathRewrite); } else { setResponseStatus(event, 404); return { status: 404, message: 'Not Found' }; } } });
关键说明
- 两种方法都不会影响服务端插件内部的
$fetch调用(会携带标识头),同时拦截浏览器端直接访问/api/**的请求; - 不要在
proxyRequest之后尝试修改响应头,因为代理已经发送了响应,这就是你遇到Can't send headers after they are sent错误的核心原因; - 保留的
/api/health和/api/_sitemap-urls可根据需求调整逻辑,比如同样限制为内部访问或保持公开。
内容的提问来源于stack exchange,提问作者Kirill
相关产品推荐
相关产品推荐

