该文件是服务器端的入口点,控制 React Router 应用程序如何在服务器上生成 HTTP 响应。
该模块应使用带有当前请求的 `context` 和 `url` 的 <ServerRouter>
元素来呈现当前页面的标记。一旦 JavaScript 在浏览器中加载,该标记将(可选地)使用 客户端入口模块 进行再水合(re-hydrated)。
entry.server.tsx
默认情况下,React Router 将为您处理 HTTP 响应的生成。您可以通过以下方式显示默认的入口服务器文件:
npx react-router reveal
default
该模块的 `default` 导出是一个函数,它允许您创建响应,包括 HTTP 状态、头信息和 HTML,让您完全控制标记的生成和发送到客户端的方式。
import { PassThrough } from "node:stream";
import type { EntryContext } from "react-router";
import { createReadableStreamFromReadable } from "@react-router/node";
import { ServerRouter } from "react-router";
import { renderToPipeableStream } from "react-dom/server";
export default function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
routerContext: EntryContext
) {
return new Promise((resolve, reject) => {
const { pipe, abort } = renderToPipeableStream(
<ServerRouter
context={routerContext}
url={request.url}
/>,
{
onShellReady() {
responseHeaders.set("Content-Type", "text/html");
const body = new PassThrough();
const stream =
createReadableStreamFromReadable(body);
resolve(
new Response(stream, {
headers: responseHeaders,
status: responseStatusCode,
})
);
pipe(body);
},
onShellError(error: unknown) {
reject(error);
},
}
);
});
}
streamTimeout
如果您正在流式传输响应,您可以导出一个可选的 `streamTimeout` 值(以毫秒为单位),该值将控制服务器在拒绝未完成的 Promise 并关闭流之前等待流式 Promise 解决的时间。
建议将此值与您中止 React 渲染器的超时值分离开来。您应始终将 React 渲染超时设置为一个更高的值,以便它有时间从您的 `streamTimeout` 中流式传输底层的拒绝信息。
// Reject all pending promises from handler functions after 10 seconds
export const streamTimeout = 10000;
export default function handleRequest(...) {
return new Promise((resolve, reject) => {
// ...
const { pipe, abort } = renderToPipeableStream(
<ServerRouter context={routerContext} url={request.url} />,
{ /* ... */ }
);
// Abort the streaming render pass after 11 seconds to allow the rejected
// boundaries to be flushed
setTimeout(abort, streamTimeout + 1000);
});
}
handleDataRequest
您可以导出一个可选的 `handleDataRequest` 函数,该函数将允许您修改数据请求的响应。这些请求不渲染 HTML,而是在客户端水合发生后将 `loader` 和 `action` 数据返回到浏览器。
export function handleDataRequest(
response: Response,
{
request,
params,
context,
}: LoaderFunctionArgs | ActionFunctionArgs
) {
response.headers.set("X-Custom-Header", "value");
return response;
}
handleError
默认情况下,React Router 会将遇到的服务器端错误记录到控制台。如果您想更多地控制日志记录,或者希望将这些错误报告给外部服务,那么您可以导出一个可选的 `handleError` 函数,该函数将为您提供控制权(并将禁用内置的错误日志记录)。
export function handleError(
error: unknown,
{
request,
params,
context,
}: LoaderFunctionArgs | ActionFunctionArgs
) {
if (!request.signal.aborted) {
sendErrorToErrorReportingService(error);
console.error(formatErrorForJsonLogging(error));
}
}
请注意,当请求被中止时,您通常希望避免日志记录,因为 React Router 的取消和竞争条件处理可能会导致大量请求被中止。
流式渲染错误
当您通过 renderToPipeableStream
或 renderToReadableStream
流式传输 HTML 响应时,您自己的 `handleError` 实现将只处理初始 shell 渲染期间遇到的错误。如果在后续的流式渲染期间遇到渲染错误,您将需要手动处理这些错误,因为 React Router 服务器在那时已经发送了响应。
对于 renderToPipeableStream
,您可以在 onError
回调函数中处理这些错误。您需要在 onShellReady
中切换一个布尔值,以便知道错误是 shell 渲染错误(可以忽略)还是异步错误。
有关示例,请参阅 Node 的默认 entry.server.tsx
文件。
抛出的响应
请注意,这不会处理从您的 `loader`/`action` 函数中抛出的 `Response` 实例。此处理程序的目的是查找导致意外抛出错误的代码中的 bug。如果您检测到某种情况并在您的 `loader`/`action` 中抛出一个 401/404/等 `Response`,那么这是一个由您的代码处理的预期流程。如果您也希望记录或将这些发送到外部服务,那应该在您抛出响应时完成。