标头主要通过路由模块 headers
导出定义。您也可以在 entry.server.tsx
中设置标头。
import { Route } from "./+types/some-route";
export function headers(_: Route.HeadersArgs) {
return {
"Content-Security-Policy": "default-src 'self'",
"X-Frame-Options": "DENY",
"X-Content-Type-Options": "nosniff",
"Cache-Control": "max-age=3600, s-maxage=86400",
};
}
您可以返回一个 Headers
实例或 HeadersInit
。
当标头依赖于加载器数据时,加载器和操作也可以设置标头。
data
中import { data } from "react-router";
export async function loader({ params }: LoaderArgs) {
let [page, ms] = await fakeTimeCall(
await getPage(params.id),
);
return data(page, {
headers: {
"Server-Timing": `page;dur=${ms};desc="Page query"`,
},
});
}
headers
导出返回来自加载器和操作的标头不会自动发送。您必须从 headers
导出中明确返回它们。
function hasAnyHeaders(headers: Headers): boolean {
return [...headers].length > 0;
}
export function headers({
actionHeaders,
loaderHeaders,
}: HeadersArgs) {
return hasAnyHeaders(actionHeaders)
? actionHeaders
: loaderHeaders;
}
一个显著的例外是 Set-Cookie
标头,它会自动从父路由的 headers
、loader
和 action
中保留下来,即使子路由没有导出 headers
。
考虑这些嵌套路由
route("pages", "pages-layout-with-nav.tsx", [
route(":slug", "page.tsx"),
]);
如果两个路由模块都想设置标头,将发送最深层匹配路由的标头。
当您需要同时保留父标头和子标头时,您需要在子路由中合并它们。
最简单的方法是直接追加到父标头。这样可以避免覆盖父级可能设置的标头,并且两者都很重要。
export function headers({ parentHeaders }: HeadersArgs) {
parentHeaders.append(
"Permissions-Policy: geolocation=()",
);
return parentHeaders;
}
有时覆盖父标头很重要。使用 set
而不是 append
来实现
export function headers({ parentHeaders }: HeadersArgs) {
parentHeaders.set(
"Cache-Control",
"max-age=3600, s-maxage=86400",
);
return parentHeaders;
}
您可以通过仅在“叶子路由”(索引路由和没有子路由的子路由)中定义标头,而不在父路由中定义,来避免合并标头的需要。
entry.server.tsx
handleRequest
导出接收来自路由模块的标头作为参数。您可以在这里追加全局标头。
export default async function handleRequest(
request,
responseStatusCode,
responseHeaders,
routerContext,
loadContext,
) {
// set, append global headers
responseHeaders.set(
"X-App-Version",
routerContext.manifest.version,
);
return new Response(await getStream(), {
headers: responseHeaders,
status: responseStatusCode,
});
}
如果您没有 entry.server.tsx
,请运行 reveal
命令
react-router reveal