如何在最新版SvelteKit中实现基于Accept头的路由穿透?
在SvelteKit中基于Accept头实现通用API路由方案
需求说明
需要实现一套依赖Accept请求头的通用API路由,具体规则:
- GET /[schema] - 返回包含{schema}条目列表的HTML页面
- GET /[schema](携带
Accept: application/json头)- 返回{schema}条目列表的JSON格式数据 - POST /[schema](携带
Accept: application/json头)- 保存{schema}条目 - GET /[schema]/[id] - 返回包含{schema}条目的HTML详情页
- GET /[schema]/[id](携带
Accept: application/json头)- 返回{schema}条目的JSON格式数据
限制条件
- 不能使用
/[schema]/+server.js直接响应所有请求,需仅过滤带Accept: application/json的请求 - 不能/不愿使用中间件(搭配node/vite开发服务器),避免影响
+page.js的load函数使用相对URL - 原路由穿透功能已移除,URL matchers无法获取请求头且不支持异步
实现方案
核心思路是通过+server.js校验Accept头处理API请求,同时页面的load函数携带指定头请求同一路由获取数据,既满足API的头校验要求,又不影响页面路由的正常渲染。
1. 通用列表路由(/[schema])
src/routes/[schema]/+page.svelte
HTML页面渲染组件,接收load函数返回的数据:
<script> export let data; </script> <h1>{$params.schema} 列表</h1> <ul> {#each data.items as item} <li><a href="/{$params.schema}/{item.id}">{item.name}</a></li> {/each} </ul>
src/routes/[schema]/+page.js
页面数据加载逻辑,携带Accept: application/json头请求同一路由获取JSON数据:
/** @type {import('./$types').PageLoad} */ export async function load({ fetch, params }) { const res = await fetch(`/${params.schema}`, { headers: { Accept: 'application/json' } }); return { data: await res.json() }; }
src/routes/[schema]/+server.js
API请求处理逻辑,仅当Accept头包含application/json时响应,否则返回404交由页面路由处理:
import { error } from '@sveltejs/kit'; /** @type {import('./$types').RequestHandler} */ export async function GET({ request, params }) { const acceptHeader = request.headers.get('Accept'); if (!acceptHeader?.includes('application/json')) { throw error(404, 'Not Found'); } // 替换为实际业务逻辑:从数据库获取{schema}列表 const items = [ { id: 1, name: `${params.schema} 条目1` }, { id: 2, name: `${params.schema} 条目2` } ]; return new Response(JSON.stringify({ items }), { headers: { 'Content-Type': 'application/json' } }); } /** @type {import('./$types').RequestHandler} */ export async function POST({ request, params }) { const acceptHeader = request.headers.get('Accept'); if (!acceptHeader?.includes('application/json')) { throw error(404, 'Not Found'); } // 替换为实际业务逻辑:解析请求体并保存数据 const body = await request.json(); return new Response(JSON.stringify({ ...body, id: Date.now() }), { headers: { 'Content-Type': 'application/json' }, status: 201 }); }
2. 详情路由(/[schema]/[id])
src/routes/[schema]/[id]/+page.svelte
详情页渲染组件:
<script> export let data; </script> <h1>{$params.schema} 详情</h1> <p>ID: {data.item.id}</p> <p>名称: {data.item.name}</p> <a href="/{$params.schema}">返回列表</a>
src/routes/[schema]/[id]/+page.js
详情页数据加载逻辑:
/** @type {import('./$types').PageLoad} */ export async function load({ fetch, params }) { const res = await fetch(`/${params.schema}/${params.id}`, { headers: { Accept: 'application/json' } }); return { data: await res.json() }; }
src/routes/[schema]/[id]/+server.js
详情API处理逻辑:
import { error } from '@sveltejs/kit'; /** @type {import('./$types').RequestHandler} */ export async function GET({ request, params }) { const acceptHeader = request.headers.get('Accept'); if (!acceptHeader?.includes('application/json')) { throw error(404, 'Not Found'); } // 替换为实际业务逻辑:根据ID从数据库获取条目 const item = { id: params.id, name: `${params.schema} 条目${params.id}` }; return new Response(JSON.stringify({ item }), { headers: { 'Content-Type': 'application/json' } }); }
方案说明
- 用户直接访问
/[schema]时,SvelteKit优先匹配+page.svelte渲染HTML页面,+page.js中的fetch会调用同一路由的+server.js(因携带指定头,API逻辑正常响应JSON) - 外部客户端携带
Accept: application/json头请求时,+server.js直接返回JSON;未携带指定头时返回404,符合仅处理API请求的需求 - 该方式既规避了中间件的问题,又利用
+server.js实现请求头校验,同时页面load函数可正常使用相对URL
内容的提问来源于stack exchange,提问作者bytesnz
相关产品推荐
相关产品推荐

