钩子
‘钩子’ 是你声明的应用范围函数,SvelteKit 将响应特定事件调用这些函数,让你可以对框架的行为进行细粒度控制。
¥’Hooks’ are app-wide functions you declare that SvelteKit will call in response to specific events, giving you fine-grained control over the framework’s behaviour.
有三个钩子文件,都是可选的:
¥There are three hooks files, all optional:
src/hooks.server.js— 你应用的服务器钩子¥
src/hooks.server.js— your app’s server hookssrc/hooks.client.js— 你应用的客户端钩子¥
src/hooks.client.js— your app’s client hookssrc/hooks.js— 你应用的在客户端和服务器上运行的钩子¥
src/hooks.js— your app’s hooks that run on both the client and server
这些模块中的代码将在应用启动时运行,使其可用于初始化数据库客户端等。
¥Code in these modules will run when the application starts up, making them useful for initializing database clients and so on.
你可以使用
config.kit.files.hooks配置这些文件的位置。
服务器钩子(Server hooks)
¥Server hooks
以下钩子可以添加到 src/hooks.server.js:
¥The following hooks can be added to src/hooks.server.js:
handle
此函数每次 SvelteKit 服务器收到 request 时都会运行 - 无论是在应用运行时还是在 prerendering 期间发生 - 并确定 response。它接收一个代表请求的 event 对象和一个名为 resolve 的函数,该函数渲染路由并生成 Response。这允许你修改响应标头或正文,或完全绕过 SvelteKit(例如,以编程方式实现路由)。
¥This function runs every time the SvelteKit server receives a request — whether that happens while the app is running, or during prerendering — and determines the response. It receives an event object representing the request and a function called resolve, which renders the route and generates a Response. This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).
/** @type {import('@sveltejs/kit').Handle} */
export async function function handle({ event, resolve }: {
event: any;
resolve: any;
}): Promise<any>
handle({ event: anyevent, resolve: anyresolve }) {
if (event: anyevent.url.pathname.startsWith('/custom')) {
return new var Response: new (body?: BodyInit | null, init?: ResponseInit) => ResponseThis Fetch API interface represents the response to a request.
Response('custom response');
}
const const response: anyresponse = await resolve: anyresolve(event: anyevent);
return const response: anyresponse;
}import type { type Handle = (input: {
event: RequestEvent;
resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>;
}) => MaybePromise<...>
The handle hook runs every time the SvelteKit server receives a request and
determines the response.
It receives an event object representing the request and a function called resolve, which renders the route and generates a Response.
This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).
Handle } from '@sveltejs/kit';
export const const handle: Handlehandle: type Handle = (input: {
event: RequestEvent;
resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>;
}) => MaybePromise<...>
The handle hook runs every time the SvelteKit server receives a request and
determines the response.
It receives an event object representing the request and a function called resolve, which renders the route and generates a Response.
This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).
Handle = async ({ event: RequestEvent<Record<string, string>, string | null>event, resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve }) => {
if (event: RequestEvent<Record<string, string>, string | null>event.RequestEvent<Record<string, string>, string | null>.url: URLThe requested URL.
url.URL.pathname: stringpathname.String.startsWith(searchString: string, position?: number): booleanReturns true if the sequence of elements of searchString converted to a String is the
same as the corresponding elements of this object (converted to a String) starting at
position. Otherwise returns false.
startsWith('/custom')) {
return new var Response: new (body?: BodyInit | null, init?: ResponseInit) => ResponseThis Fetch API interface represents the response to a request.
Response('custom response');
}
const const response: Responseresponse = await resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve(event: RequestEvent<Record<string, string>, string | null>event);
return const response: Responseresponse;
};SvelteKit 不会处理静态资源(包括已预渲染的页面)的请求。
如果未实现,则默认为 ({ event, resolve }) => resolve(event)。
¥If unimplemented, defaults to ({ event, resolve }) => resolve(event).
在预渲染期间,SvelteKit 会抓取你的页面中的链接并渲染它找到的每条路由。渲染路由会调用 handle 函数(以及所有其他路由依赖,如 load)。如果你需要在此阶段排除某些代码的运行,请事先检查应用是否不是 building。
¥During prerendering, SvelteKit crawls your pages for links and renders each route it finds. Rendering the route invokes the handle function (and all other route dependencies, like load). If you need to exclude some code from running during this phase, check that the app is not building beforehand.
locals
要将自定义数据添加到请求中(该请求将传递给 +server.js 和服务器 load 函数中的处理程序),请填充 event.locals 对象,如下所示。
¥To add custom data to the request, which is passed to handlers in +server.js and server load functions, populate the event.locals object, as shown below.
/** @type {import('@sveltejs/kit').Handle} */
export async function function handle(input: {
event: RequestEvent;
resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>;
}): MaybePromise<...>
handle({ event: RequestEvent<Record<string, string>, string | null>event, resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve }) {
event: RequestEvent<Record<string, string>, string | null>event.RequestEvent<Record<string, string>, string | null>.locals: App.LocalsContains custom data that was added to the request within the server handle hook.
locals.App.Locals.user: Useruser = await const getUserInformation: (cookie: string | void) => Promise<User>getUserInformation(event: RequestEvent<Record<string, string>, string | null>event.RequestEvent<Record<string, string>, string | null>.cookies: CookiesGet or set cookies related to the current request
cookies.Cookies.get: (name: string, opts?: CookieParseOptions) => string | undefinedGets a cookie that was previously set with cookies.set, or from the request headers.
get('sessionid'));
const const response: Responseresponse = await resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve(event: RequestEvent<Record<string, string>, string | null>event);
// Note that modifying response headers isn't always safe.
// Response objects can have immutable headers
// (e.g. Response.redirect() returned from an endpoint).
// Modifying immutable headers throws a TypeError.
// In that case, clone the response or avoid creating a
// response object with immutable headers.
const response: Responseresponse.Response.headers: Headersheaders.Headers.set(name: string, value: string): voidset('x-custom-header', 'potato');
return const response: Responseresponse;
}import type { type Handle = (input: {
event: RequestEvent;
resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>;
}) => MaybePromise<...>
The handle hook runs every time the SvelteKit server receives a request and
determines the response.
It receives an event object representing the request and a function called resolve, which renders the route and generates a Response.
This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).
Handle } from '@sveltejs/kit';
export const const handle: Handlehandle: type Handle = (input: {
event: RequestEvent;
resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>;
}) => MaybePromise<...>
The handle hook runs every time the SvelteKit server receives a request and
determines the response.
It receives an event object representing the request and a function called resolve, which renders the route and generates a Response.
This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).
Handle = async ({ event: RequestEvent<Record<string, string>, string | null>event, resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve }) => {
event: RequestEvent<Record<string, string>, string | null>event.RequestEvent<Record<string, string>, string | null>.locals: App.LocalsContains custom data that was added to the request within the server handle hook.
locals.App.Locals.user: Useruser = await const getUserInformation: (cookie: string | void) => Promise<User>getUserInformation(event: RequestEvent<Record<string, string>, string | null>event.RequestEvent<Record<string, string>, string | null>.cookies: CookiesGet or set cookies related to the current request
cookies.Cookies.get: (name: string, opts?: CookieParseOptions) => string | undefinedGets a cookie that was previously set with cookies.set, or from the request headers.
get('sessionid'));
const const response: Responseresponse = await resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve(event: RequestEvent<Record<string, string>, string | null>event);
// Note that modifying response headers isn't always safe.
// Response objects can have immutable headers
// (e.g. Response.redirect() returned from an endpoint).
// Modifying immutable headers throws a TypeError.
// In that case, clone the response or avoid creating a
// response object with immutable headers.
const response: Responseresponse.Response.headers: Headersheaders.Headers.set(name: string, value: string): voidset('x-custom-header', 'potato');
return const response: Responseresponse;
};你可以定义多个 handle 函数并使用 sequence 辅助函数 执行它们。
¥You can define multiple handle functions and execute them with the sequence helper function.
resolve 还支持第二个可选参数,让你可以更好地控制响应的渲染方式。该参数是一个可以具有以下字段的对象:
¥resolve also supports a second, optional parameter that gives you more control over how the response will be rendered. That parameter is an object that can have the following fields:
transformPageChunk(opts: { html: string, done: boolean }): MaybePromise<string | undefined>— 将自定义转换应用于 HTML。如果done为真,则它是最终块。块不能保证是格式正确的 HTML(例如,它们可能包含元素的开始标记但不包含结束标记),但它们始终会在合理的边界处进行拆分,例如%sveltekit.head%或布局/页面组件。¥
transformPageChunk(opts: { html: string, done: boolean }): MaybePromise<string | undefined>— applies custom transforms to HTML. Ifdoneis true, it’s the final chunk. Chunks are not guaranteed to be well-formed HTML (they could include an element’s opening tag but not its closing tag, for example) but they will always be split at sensible boundaries such as%sveltekit.head%or layout/page components.filterSerializedResponseHeaders(name: string, value: string): boolean— 确定当load函数使用fetch加载资源时,序列化响应中应包含哪些标头。默认情况下,不会包含任何内容。¥
filterSerializedResponseHeaders(name: string, value: string): boolean— determines which headers should be included in serialized responses when aloadfunction loads a resource withfetch. By default, none will be included.preload(input: { type: 'js' | 'css' | 'font' | 'asset', path: string }): boolean— 确定应将哪些文件添加到<head>标签以预加载它。在构建代码块时,该方法会在构建时使用找到的每个文件调用 - 因此,例如,如果你的+page.svelte中有import './styles.css,则在访问该页面时将使用该 CSS 文件的解析路径调用preload。请注意,在开发模式下不会调用preload,因为它取决于构建时发生的分析。预加载可以通过更快地下载资源来提高性能,但如果不必要地下载太多资源,也会造成损害。默认情况下,js和css文件将被预加载。asset文件目前根本没有预加载,但我们可能会在评估反馈后添加此功能。¥
preload(input: { type: 'js' | 'css' | 'font' | 'asset', path: string }): boolean— determines what files should be added to the<head>tag to preload it. The method is called with each file that was found at build time while constructing the code chunks — so if you for example haveimport './styles.cssin your+page.svelte,preloadwill be called with the resolved path to that CSS file when visiting that page. Note that in dev modepreloadis not called, since it depends on analysis that happens at build time. Preloading can improve performance by downloading assets sooner, but it can also hurt if too much is downloaded unnecessarily. By default,jsandcssfiles will be preloaded.assetfiles are not preloaded at all currently, but we may add this later after evaluating feedback.
/** @type {import('@sveltejs/kit').Handle} */
export async function function handle({ event, resolve }: {
event: any;
resolve: any;
}): Promise<any>
handle({ event: anyevent, resolve: anyresolve }) {
const const response: anyresponse = await resolve: anyresolve(event: anyevent, {
transformPageChunk: ({ html }: {
html: any;
}) => any
transformPageChunk: ({ html: anyhtml }) => html: anyhtml.replace('old', 'new'),
filterSerializedResponseHeaders: (name: any) => anyfilterSerializedResponseHeaders: (name: anyname) => name: anyname.startsWith('x-'),
preload: ({ type, path }: {
type: any;
path: any;
}) => any
preload: ({ type: anytype, path: anypath }) => type: anytype === 'js' || path: anypath.includes('/important/')
});
return const response: anyresponse;
}import type { type Handle = (input: {
event: RequestEvent;
resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>;
}) => MaybePromise<...>
The handle hook runs every time the SvelteKit server receives a request and
determines the response.
It receives an event object representing the request and a function called resolve, which renders the route and generates a Response.
This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).
Handle } from '@sveltejs/kit';
export const const handle: Handlehandle: type Handle = (input: {
event: RequestEvent;
resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>;
}) => MaybePromise<...>
The handle hook runs every time the SvelteKit server receives a request and
determines the response.
It receives an event object representing the request and a function called resolve, which renders the route and generates a Response.
This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).
Handle = async ({ event: RequestEvent<Record<string, string>, string | null>event, resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve }) => {
const const response: Responseresponse = await resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve(event: RequestEvent<Record<string, string>, string | null>event, {
ResolveOptions.transformPageChunk?: ((input: {
html: string;
done: boolean;
}) => MaybePromise<string | undefined>) | undefined
Applies custom transforms to HTML. If done is true, it’s the final chunk. Chunks are not guaranteed to be well-formed HTML
(they could include an element’s opening tag but not its closing tag, for example)
but they will always be split at sensible boundaries such as %sveltekit.head% or layout/page components.
transformPageChunk: ({ html: stringhtml }) => html: stringhtml.String.replace(searchValue: string | RegExp, replaceValue: string): string (+3 overloads)Replaces text in a string, using a regular expression or search string.
replace('old', 'new'),
ResolveOptions.filterSerializedResponseHeaders?: ((name: string, value: string) => boolean) | undefinedDetermines which headers should be included in serialized responses when a load function loads a resource with fetch.
By default, none will be included.
filterSerializedResponseHeaders: (name: stringname) => name: stringname.String.startsWith(searchString: string, position?: number): booleanReturns true if the sequence of elements of searchString converted to a String is the
same as the corresponding elements of this object (converted to a String) starting at
position. Otherwise returns false.
startsWith('x-'),
ResolveOptions.preload?: ((input: {
type: "font" | "css" | "js" | "asset";
path: string;
}) => boolean) | undefined
Determines what should be added to the <head> tag to preload it.
By default, js and css files will be preloaded.
preload: ({ type: "font" | "css" | "js" | "asset"type, path: stringpath }) => type: "font" | "css" | "js" | "asset"type === 'js' || path: stringpath.String.includes(searchString: string, position?: number): booleanReturns true if searchString appears as a substring of the result of converting this
object to a String, at one or more positions that are
greater than or equal to position; otherwise, returns false.
includes('/important/')
});
return const response: Responseresponse;
};请注意,resolve(...) 永远不会抛出错误,它将始终返回带有适当状态代码的 Promise<Response>。如果在 handle 期间其他地方抛出错误,则该错误将被视为致命错误,SvelteKit 将根据 Accept 标头以 JSON 表示的错误或后备错误页面(可通过 src/error.html 自定义)进行响应。你可以阅读有关错误处理 此处 的更多信息。
¥Note that resolve(...) will never throw an error, it will always return a Promise<Response> with the appropriate status code. If an error is thrown elsewhere during handle, it is treated as fatal, and SvelteKit will respond with a JSON representation of the error or a fallback error page — which can be customised via src/error.html — depending on the Accept header. You can read more about error handling here.
handleFetch
此函数允许你修改(或替换)在端点 load、action、handle、handleError 或 reroute 内服务器上(或预渲染期间)运行的 event.fetch 调用的结果。
¥This function allows you to modify (or replace) the result of an event.fetch call that runs on the server (or during prerendering) inside an endpoint, load, action, handle, handleError or reroute.
例如,当用户执行到相应页面的客户端导航时,你的 load 函数可能会向公共 URL(如 https://api.yourapp.com)发出请求,但在 SSR 期间,直接访问 API 可能更有意义(绕过它与公共互联网之间的任何代理和负载平衡器)。
¥For example, your load function might make a request to a public URL like https://api.yourapp.com when the user performs a client-side navigation to the respective page, but during SSR it might make sense to hit the API directly (bypassing whatever proxies and load balancers sit between it and the public internet).
/** @type {import('@sveltejs/kit').HandleFetch} */
export async function function handleFetch({ request, fetch }: {
request: any;
fetch: any;
}): Promise<any>
handleFetch({ request: anyrequest, fetch: anyfetch }) {
if (request: anyrequest.url.startsWith('https://api.yourapp.com/')) {
// clone the original request, but change the URL
request: anyrequest = new var Request: new (input: RequestInfo | URL, init?: RequestInit) => RequestThis Fetch API interface represents a resource request.
Request(
request: anyrequest.url.replace('https://api.yourapp.com/', 'http://localhost:9999/'),
request: anyrequest
);
}
return fetch: anyfetch(request: anyrequest);
}import type { type HandleFetch = (input: {
event: RequestEvent;
request: Request;
fetch: typeof fetch;
}) => MaybePromise<Response>
The handleFetch hook allows you to modify (or replace) the result of an event.fetch call that runs on the server (or during prerendering) inside an endpoint, load, action, handle, handleError or reroute.
HandleFetch } from '@sveltejs/kit';
export const const handleFetch: HandleFetchhandleFetch: type HandleFetch = (input: {
event: RequestEvent;
request: Request;
fetch: typeof fetch;
}) => MaybePromise<Response>
The handleFetch hook allows you to modify (or replace) the result of an event.fetch call that runs on the server (or during prerendering) inside an endpoint, load, action, handle, handleError or reroute.
HandleFetch = async ({ request: Requestrequest, fetch: {
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>;
}
fetch }) => {
if (request: Requestrequest.Request.url: stringReturns the URL of request as a string.
url.String.startsWith(searchString: string, position?: number): booleanReturns true if the sequence of elements of searchString converted to a String is the
same as the corresponding elements of this object (converted to a String) starting at
position. Otherwise returns false.
startsWith('https://api.yourapp.com/')) {
// clone the original request, but change the URL
request: Requestrequest = new var Request: new (input: RequestInfo | URL, init?: RequestInit) => RequestThis Fetch API interface represents a resource request.
Request(
request: Requestrequest.Request.url: stringReturns the URL of request as a string.
url.String.replace(searchValue: string | RegExp, replaceValue: string): string (+3 overloads)Replaces text in a string, using a regular expression or search string.
replace('https://api.yourapp.com/', 'http://localhost:9999/'),
request: Requestrequest
);
}
return fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response> (+1 overload)fetch(request: Requestrequest);
};使用 event.fetch 发出的请求遵循浏览器的凭据模型 - 对于同源请求,除非 credentials 选项设置为 "omit",否则 cookie 和 authorization 标头都会被转发。对于跨域请求,如果请求 URL 属于应用的子域,则将包含 cookie — 例如,如果你的应用在 my-domain.com 上,而你的 API 在 api.my-domain.com 上,则请求中将包含 cookie。
¥Requests made with event.fetch follow the browser’s credentials model — for same-origin requests, cookie and authorization headers are forwarded unless the credentials option is set to "omit". For cross-origin requests, cookie will be included if the request URL belongs to a subdomain of the app — for example if your app is on my-domain.com, and your API is on api.my-domain.com, cookies will be included in the request.
有一个警告:如果你的应用和 API 位于同级子域(例如 www.my-domain.com 和 api.my-domain.com),则不会包含属于共同父域(例如 my-domain.com)的 Cookie,因为 SvelteKit 无法知道 Cookie 属于哪个域。在这些情况下,你需要使用 handleFetch 手动包含 cookie:
¥There is one caveat: if your app and your API are on sibling subdomains — www.my-domain.com and api.my-domain.com for example — then a cookie belonging to a common parent domain like my-domain.com will not be included, because SvelteKit has no way to know which domain the cookie belongs to. In these cases you will need to manually include the cookie using handleFetch:
/** @type {import('@sveltejs/kit').HandleFetch} */
export async function function handleFetch({ event, request, fetch }: {
event: any;
request: any;
fetch: any;
}): Promise<any>
handleFetch({ event: anyevent, request: anyrequest, fetch: anyfetch }) {
if (request: anyrequest.url.startsWith('https://api.my-domain.com/')) {
request: anyrequest.headers.set('cookie', event: anyevent.request.headers.get('cookie'));
}
return fetch: anyfetch(request: anyrequest);
}import type { type HandleFetch = (input: {
event: RequestEvent;
request: Request;
fetch: typeof fetch;
}) => MaybePromise<Response>
The handleFetch hook allows you to modify (or replace) the result of an event.fetch call that runs on the server (or during prerendering) inside an endpoint, load, action, handle, handleError or reroute.
HandleFetch } from '@sveltejs/kit';
export const const handleFetch: HandleFetchhandleFetch: type HandleFetch = (input: {
event: RequestEvent;
request: Request;
fetch: typeof fetch;
}) => MaybePromise<Response>
The handleFetch hook allows you to modify (or replace) the result of an event.fetch call that runs on the server (or during prerendering) inside an endpoint, load, action, handle, handleError or reroute.
HandleFetch = async ({ event: RequestEvent<Record<string, string>, string | null>event, request: Requestrequest, fetch: {
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>;
}
fetch }) => {
if (request: Requestrequest.Request.url: stringReturns the URL of request as a string.
url.String.startsWith(searchString: string, position?: number): booleanReturns true if the sequence of elements of searchString converted to a String is the
same as the corresponding elements of this object (converted to a String) starting at
position. Otherwise returns false.
startsWith('https://api.my-domain.com/')) {
request: Requestrequest.Request.headers: HeadersReturns a Headers object consisting of the headers associated with request. Note that headers added in the network layer by the user agent will not be accounted for in this object, e.g., the “Host” header.
headers.Headers.set(name: string, value: string): voidset('cookie', event: RequestEvent<Record<string, string>, string | null>event.RequestEvent<Record<string, string>, string | null>.request: RequestThe original request object.
request.Request.headers: HeadersReturns a Headers object consisting of the headers associated with request. Note that headers added in the network layer by the user agent will not be accounted for in this object, e.g., the “Host” header.
headers.Headers.get(name: string): string | nullget('cookie'));
}
return fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response> (+1 overload)fetch(request: Requestrequest);
};handleValidationError
当使用与提供的 标准架构 不匹配的参数调用远程函数时,会调用此钩子。它必须返回一个与 App.Error 形状匹配的对象。
¥This hook is called when a remote function is called with an argument that does not match the provided Standard Schema. It must return an object matching the shape of App.Error.
假设你有一个远程函数,它需要一个字符串作为参数……
¥Say you have a remote function that expects a string as its argument ...
import * as import vv from 'valibot';
import { function query<Output>(fn: () => MaybePromise<Output>): RemoteQueryFunction<void, Output> (+2 overloads)Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query } from '$app/server';
export const const getTodo: RemoteQueryFunction<string, void>getTodo = query<v.StringSchema<undefined>, void>(schema: v.StringSchema<undefined>, fn: (arg: string) => MaybePromise<void>): RemoteQueryFunction<string, void> (+2 overloads)Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query(import vv.function string(): v.StringSchema<undefined> (+1 overload)
export string
Creates a string schema.
string(), (id: stringid) => {
// implementation...
});...但如果调用时传入了与 schema 不匹配的参数(例如数字(例如 await getTodos(1))),则验证将失败,服务器将返回 400 状态码,并且函数将抛出 ‘错误请求’ 异常。
¥...but it is called with something that doesn’t match the schema — such as a number (e.g await getTodos(1)) — then validation will fail, the server will respond with a 400 status code, and the function will throw with the message ‘Bad Request’.
要自定义此消息并向错误对象添加其他属性,请实现 handleValidationError:
¥To customise this message and add additional properties to the error object, implement handleValidationError:
/** @type {import('@sveltejs/kit').HandleValidationError} */
export function function handleValidationError({ issues }: {
issues: any;
}): {
message: string;
}
handleValidationError({ issues: anyissues }) {
return {
message: stringmessage: 'No thank you'
};
}import type { type HandleValidationError<Issue extends StandardSchemaV1.Issue = StandardSchemaV1.Issue> = (input: {
issues: Issue[];
event: RequestEvent;
}) => MaybePromise<App.Error>
The handleValidationError hook runs when the argument to a remote function fails validation.
It will be called with the validation issues and the event, and must return an object shape that matches App.Error.
HandleValidationError } from '@sveltejs/kit';
export const const handleValidationError: HandleValidationErrorhandleValidationError: type HandleValidationError<Issue extends StandardSchemaV1.Issue = StandardSchemaV1.Issue> = (input: {
issues: Issue[];
event: RequestEvent;
}) => MaybePromise<App.Error>
The handleValidationError hook runs when the argument to a remote function fails validation.
It will be called with the validation issues and the event, and must return an object shape that matches App.Error.
HandleValidationError = ({ issues: StandardSchemaV1.Issue[]issues }) => {
return {
App.Error.message: stringmessage: 'No thank you'
};
};请仔细考虑在此处公开的信息,因为验证失败的最可能原因是有人向你的服务器发送了恶意请求。
¥Be thoughtful about what information you expose here, as the most likely reason for validation to fail is that someone is sending malicious requests to your server.
共享钩子(Shared hooks)
¥Shared hooks
以下内容可以添加到 src/hooks.server.js 和 src/hooks.client.js:
¥The following can be added to src/hooks.server.js and src/hooks.client.js:
handleError
如果在加载、渲染或从端点抛出 意外错误,则此函数将使用 error、event、status 代码和 message 调用。这允许两件事:
¥If an unexpected error is thrown during loading, rendering, or from an endpoint, this function will be called with the error, event, status code and message. This allows for two things:
你可以记录错误
¥you can log the error
你可以生成错误的自定义表示,可以安全地显示给用户,省略敏感细节,如消息和堆栈跟踪。返回的值(默认为
{ message })将成为$page.error的值。¥you can generate a custom representation of the error that is safe to show to users, omitting sensitive details like messages and stack traces. The returned value, which defaults to
{ message }, becomes the value of$page.error.
对于从你的代码(或你的代码调用的库代码)抛出的错误,状态将为 500,消息将为 “内部错误”。虽然 error.message 可能包含不应向用户公开的敏感信息,但 message 是安全的(尽管对普通用户来说毫无意义)。
¥For errors thrown from your code (or library code called by your code) the status will be 500 and the message will be “Internal Error”. While error.message may contain sensitive information that should not be exposed to users, message is safe (albeit meaningless to the average user).
要以类型安全的方式向 $page.error 对象添加更多信息,你可以通过声明 App.Error 接口(必须包含 message: string,以保证合理的后备行为)来自定义预期形状。这允许你(例如)附加跟踪 ID,以便用户在与你的技术支持人员通信时引用:
¥To add more information to the $page.error object in a type-safe way, you can customize the expected shape by declaring an App.Error interface (which must include message: string, to guarantee sensible fallback behavior). This allows you to — for example — append a tracking ID for users to quote in correspondence with your technical support staff:
declare global {
namespace App {
interface interface App.ErrorDefines the common shape of expected and unexpected errors. Expected errors are thrown using the error function. Unexpected errors are handled by the handleError hooks which should return this shape.
Error {
App.Error.message: stringmessage: string;
App.Error.errorId: stringerrorId: string;
}
}
}
export {};import * as module "@sentry/sveltekit"Sentry from '@sentry/sveltekit';
module "@sentry/sveltekit"Sentry.const init: (opts: any) => voidinit({/*...*/})
/** @type {import('@sveltejs/kit').HandleServerError} */
export async function function handleError(input: {
error: unknown;
event: RequestEvent;
status: number;
message: string;
}): MaybePromise<void | App.Error>
handleError({ error: unknownerror, event: RequestEvent<Record<string, string>, string | null>event, status: numberstatus, message: stringmessage }) {
const const errorId: `${string}-${string}-${string}-${string}-${string}`errorId = var crypto: Cryptocrypto.Crypto.randomUUID(): `${string}-${string}-${string}-${string}-${string}`Available only in secure contexts.
randomUUID();
// example integration with https://sentry.io/
module "@sentry/sveltekit"Sentry.const captureException: (error: any, opts: any) => voidcaptureException(error: unknownerror, {
extra: {
event: RequestEvent<Record<string, string>, string | null>;
errorId: `${string}-${string}-${string}-${string}-${string}`;
status: number;
}
extra: { event: RequestEvent<Record<string, string>, string | null>event, errorId: `${string}-${string}-${string}-${string}-${string}`errorId, status: numberstatus }
});
return {
App.Error.message: stringmessage: 'Whoops!',
errorId
};
}import * as module "@sentry/sveltekit"Sentry from '@sentry/sveltekit';
import type { type HandleServerError = (input: {
error: unknown;
event: RequestEvent;
status: number;
message: string;
}) => MaybePromise<void | App.Error>
The server-side handleError hook runs when an unexpected error is thrown while responding to a request.
If an unexpected error is thrown during loading or rendering, this function will be called with the error and the event.
Make sure that this function never throws an error.
HandleServerError } from '@sveltejs/kit';
module "@sentry/sveltekit"Sentry.const init: (opts: any) => voidinit({/*...*/})
export const const handleError: HandleServerErrorhandleError: type HandleServerError = (input: {
error: unknown;
event: RequestEvent;
status: number;
message: string;
}) => MaybePromise<void | App.Error>
The server-side handleError hook runs when an unexpected error is thrown while responding to a request.
If an unexpected error is thrown during loading or rendering, this function will be called with the error and the event.
Make sure that this function never throws an error.
HandleServerError = async ({ error: unknownerror, event: RequestEvent<Record<string, string>, string | null>event, status: numberstatus, message: stringmessage }) => {
const const errorId: `${string}-${string}-${string}-${string}-${string}`errorId = var crypto: Cryptocrypto.Crypto.randomUUID(): `${string}-${string}-${string}-${string}-${string}`Available only in secure contexts.
randomUUID();
// example integration with https://sentry.io/
module "@sentry/sveltekit"Sentry.const captureException: (error: any, opts: any) => voidcaptureException(error: unknownerror, {
extra: {
event: RequestEvent<Record<string, string>, string | null>;
errorId: `${string}-${string}-${string}-${string}-${string}`;
status: number;
}
extra: { event: RequestEvent<Record<string, string>, string | null>event, errorId: `${string}-${string}-${string}-${string}-${string}`errorId, status: numberstatus }
});
return {
App.Error.message: stringmessage: 'Whoops!',
errorId: `${string}-${string}-${string}-${string}-${string}`errorId
};
};import * as module "@sentry/sveltekit"Sentry from '@sentry/sveltekit';
module "@sentry/sveltekit"Sentry.const init: (opts: any) => voidinit({/*...*/})
/** @type {import('@sveltejs/kit').HandleClientError} */
export async function function handleError(input: {
error: unknown;
event: NavigationEvent;
status: number;
message: string;
}): MaybePromise<void | App.Error>
handleError({ error: unknownerror, event: NavigationEvent<Record<string, string>, string | null>event, status: numberstatus, message: stringmessage }) {
const const errorId: `${string}-${string}-${string}-${string}-${string}`errorId = var crypto: Cryptocrypto.Crypto.randomUUID(): `${string}-${string}-${string}-${string}-${string}`Available only in secure contexts.
randomUUID();
// example integration with https://sentry.io/
module "@sentry/sveltekit"Sentry.const captureException: (error: any, opts: any) => voidcaptureException(error: unknownerror, {
extra: {
event: NavigationEvent<Record<string, string>, string | null>;
errorId: `${string}-${string}-${string}-${string}-${string}`;
status: number;
}
extra: { event: NavigationEvent<Record<string, string>, string | null>event, errorId: `${string}-${string}-${string}-${string}-${string}`errorId, status: numberstatus }
});
return {
App.Error.message: stringmessage: 'Whoops!',
errorId
};
}import * as module "@sentry/sveltekit"Sentry from '@sentry/sveltekit';
import type { type HandleClientError = (input: {
error: unknown;
event: NavigationEvent;
status: number;
message: string;
}) => MaybePromise<void | App.Error>
The client-side handleError hook runs when an unexpected error is thrown while navigating.
If an unexpected error is thrown during loading or the following render, this function will be called with the error and the event.
Make sure that this function never throws an error.
HandleClientError } from '@sveltejs/kit';
module "@sentry/sveltekit"Sentry.const init: (opts: any) => voidinit({/*...*/})
export const const handleError: HandleClientErrorhandleError: type HandleClientError = (input: {
error: unknown;
event: NavigationEvent;
status: number;
message: string;
}) => MaybePromise<void | App.Error>
The client-side handleError hook runs when an unexpected error is thrown while navigating.
If an unexpected error is thrown during loading or the following render, this function will be called with the error and the event.
Make sure that this function never throws an error.
HandleClientError = async ({ error: unknownerror, event: NavigationEvent<Record<string, string>, string | null>event, status: numberstatus, message: stringmessage }) => {
const const errorId: `${string}-${string}-${string}-${string}-${string}`errorId = var crypto: Cryptocrypto.Crypto.randomUUID(): `${string}-${string}-${string}-${string}-${string}`Available only in secure contexts.
randomUUID();
// example integration with https://sentry.io/
module "@sentry/sveltekit"Sentry.const captureException: (error: any, opts: any) => voidcaptureException(error: unknownerror, {
extra: {
event: NavigationEvent<Record<string, string>, string | null>;
errorId: `${string}-${string}-${string}-${string}-${string}`;
status: number;
}
extra: { event: NavigationEvent<Record<string, string>, string | null>event, errorId: `${string}-${string}-${string}-${string}-${string}`errorId, status: numberstatus }
});
return {
App.Error.message: stringmessage: 'Whoops!',
errorId: `${string}-${string}-${string}-${string}-${string}`errorId
};
};在
src/hooks.client.js中,handleError的类型是HandleClientError而不是HandleServerError,event是NavigationEvent而不是RequestEvent。
此函数不会因预期错误(从 @sveltejs/kit 导入的 error 函数引发的错误)而调用。
¥This function is not called for expected errors (those thrown with the error function imported from @sveltejs/kit).
在开发过程中,如果由于 Svelte 代码中的语法错误而发生错误,则传入的错误会附加一个 frame 属性,高亮错误的位置。
¥During development, if an error occurs because of a syntax error in your Svelte code, the passed in error has a frame property appended highlighting the location of the error.
确保
handleError永远不会抛出错误
init
此函数在创建服务器或应用在浏览器中启动时运行一次,是执行异步工作(例如初始化数据库连接)的有用位置。
¥This function runs once, when the server is created or the app starts in the browser, and is a useful place to do asynchronous work such as initializing a database connection.
如果你的环境支持顶层 await,那么
init函数实际上与在模块顶层编写初始化逻辑没有什么不同,但某些环境(尤其是 Safari)不支持。
import * as import dbdb from '$lib/server/database';
/** @type {import('@sveltejs/kit').ServerInit} */
export async function function init(): Promise<void>init() {
await import dbdb.connect();
}import * as import dbdb from '$lib/server/database';
import type { type ServerInit = () => MaybePromise<void>The init will be invoked before the server responds to its first request
ServerInit } from '@sveltejs/kit';
export const const init: ServerInitinit: type ServerInit = () => MaybePromise<void>The init will be invoked before the server responds to its first request
ServerInit = async () => {
await import dbdb.connect();
};在浏览器中,
init中的异步工作会延迟 hydration,因此请谨慎处理你在其中放置的内容。
通用钩子(Universal hooks)
¥Universal hooks
以下内容可以添加到 src/hooks.js。通用钩子在服务器和客户端上运行(不要与特定于环境的共享钩子混淆)。
¥The following can be added to src/hooks.js. Universal hooks run on both server and client (not to be confused with shared hooks, which are environment-specific).
reroute
此函数在 handle 之前运行,并允许你更改 URL 转换为路由的方式。返回的路径名(默认为 url.pathname)用于选择路由及其参数。
¥This function runs before handle and allows you to change how URLs are translated into routes. The returned pathname (which defaults to url.pathname) is used to select the route and its parameters.
例如,你可能有一个 src/routes/[[lang]]/about/+page.svelte 页面,它应该可以作为 /en/about 或 /de/ueber-uns 或 /fr/a-propos 访问。你可以使用 reroute 实现这一点:
¥For example, you might have a src/routes/[[lang]]/about/+page.svelte page, which should be accessible as /en/about or /de/ueber-uns or /fr/a-propos. You could implement this with reroute:
/** @type {Record<string, string>} */
const const translated: {
'/en/about': string;
'/de/ueber-uns': string;
'/fr/a-propos': string;
}
translated = {
'/en/about': '/en/about',
'/de/ueber-uns': '/de/about',
'/fr/a-propos': '/fr/about',
};
/** @type {import('@sveltejs/kit').Reroute} */
export function function reroute({ url }: {
url: any;
}): any
reroute({ url: anyurl }) {
if (url: anyurl.pathname in const translated: {
'/en/about': string;
'/de/ueber-uns': string;
'/fr/a-propos': string;
}
translated) {
return const translated: {
'/en/about': string;
'/de/ueber-uns': string;
'/fr/a-propos': string;
}
translated[url: anyurl.pathname];
}
}import type { type Reroute = (event: {
url: URL;
fetch: typeof fetch;
}) => MaybePromise<string | void>
The reroute hook allows you to modify the URL before it is used to determine which route to render.
Reroute } from '@sveltejs/kit';
const const translated: Record<string, string>translated: type Record<K extends keyof any, T> = { [P in K]: T; }Construct a type with a set of properties K of type T
Record<string, string> = {
'/en/about': '/en/about',
'/de/ueber-uns': '/de/about',
'/fr/a-propos': '/fr/about',
};
export const const reroute: Reroutereroute: type Reroute = (event: {
url: URL;
fetch: typeof fetch;
}) => MaybePromise<string | void>
The reroute hook allows you to modify the URL before it is used to determine which route to render.
Reroute = ({ url: URLurl }) => {
if (url: URLurl.URL.pathname: stringpathname in const translated: Record<string, string>translated) {
return const translated: Record<string, string>translated[url: URLurl.URL.pathname: stringpathname];
}
};lang 参数将从返回的路径名中正确派生。
¥The lang parameter will be correctly derived from the returned pathname.
使用 reroute 不会更改浏览器地址栏的内容或 event.url 的值。
¥Using reroute will not change the contents of the browser’s address bar, or the value of event.url.
自 2.18 版以来,reroute 钩子可以是异步的,允许它(例如)从后端获取数据以决定重新路由到哪里。请谨慎使用并确保其速度快,否则会延迟导航。如果你需要获取数据,请使用提供的 fetch 作为参数。它将 相同的好处 作为提供给 load 函数的 fetch,但需要注意的是,由于路由尚不清楚,因此 params 和 id 对 handleFetch 不可用。
¥Since version 2.18, the reroute hook can be asynchronous, allowing it to (for example) fetch data from your backend to decide where to reroute to. Use this carefully and make sure it’s fast, as it will delay navigation otherwise. If you need to fetch data, use the fetch provided as an argument. It has the same benefits as the fetch provided to load functions, with the caveat that params and id are unavailable to handleFetch because the route is not yet known.
/** @type {import('@sveltejs/kit').Reroute} */
export async function function reroute({ url, fetch }: {
url: any;
fetch: any;
}): Promise<any>
reroute({ url: anyurl, fetch: anyfetch }) {
// Ask a special endpoint within your app about the destination
if (url: anyurl.pathname === '/api/reroute') return;
const const api: URLapi = new var URL: new (url: string | URL, base?: string | URL) => URLThe URL interface represents an object providing static methods used for creating object URLs.
URL class is a global reference for import { URL } from 'node:url'
https://nodejs.org/api/url.html#the-whatwg-url-api
URL('/api/reroute', url: anyurl);
const api: URLapi.URL.searchParams: URLSearchParamssearchParams.URLSearchParams.set(name: string, value: string): voidSets the value associated to a given search parameter to the given value. If there were several values, delete the others.
set('pathname', url: anyurl.pathname);
const const result: anyresult = await fetch: anyfetch(const api: URLapi).then(r: anyr => r: anyr.json());
return const result: anyresult.pathname;
}import type { type Reroute = (event: {
url: URL;
fetch: typeof fetch;
}) => MaybePromise<string | void>
The reroute hook allows you to modify the URL before it is used to determine which route to render.
Reroute } from '@sveltejs/kit';
export const const reroute: Reroutereroute: type Reroute = (event: {
url: URL;
fetch: typeof fetch;
}) => MaybePromise<string | void>
The reroute hook allows you to modify the URL before it is used to determine which route to render.
Reroute = async ({ url: URLurl, fetch: {
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>;
}
fetch }) => {
// Ask a special endpoint within your app about the destination
if (url: URLurl.URL.pathname: stringpathname === '/api/reroute') return;
const const api: URLapi = new var URL: new (url: string | URL, base?: string | URL) => URLThe URL interface represents an object providing static methods used for creating object URLs.
URL class is a global reference for import { URL } from 'node:url'
https://nodejs.org/api/url.html#the-whatwg-url-api
URL('/api/reroute', url: URLurl);
const api: URLapi.URL.searchParams: URLSearchParamssearchParams.URLSearchParams.set(name: string, value: string): voidSets the value associated to a given search parameter to the given value. If there were several values, delete the others.
set('pathname', url: URLurl.URL.pathname: stringpathname);
const const result: anyresult = await fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response> (+1 overload)fetch(const api: URLapi).Promise<Response>.then<any, never>(onfulfilled?: ((value: Response) => any) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<any>Attaches callbacks for the resolution and/or rejection of the Promise.
then(r: Responser => r: Responser.Body.json(): Promise<any>json());
return const result: anyresult.pathname;
};
reroute被视为纯幂等函数。因此,它必须始终对相同的输入返回相同的输出,并且不能有副作用。基于这些假设,SvelteKit 会在客户端缓存reroute的结果,因此每个唯一 URL 只会调用一次。
transport
这是一个传输器集合,允许你跨服务器/客户端边界传递自定义类型 - 从 load 和表单操作返回。每个传输器都包含一个 encode 函数,该函数在服务器上对值进行编码(或对任何非该类型实例的值返回假值),以及一个相应的 decode 函数:
¥This is a collection of transporters, which allow you to pass custom types — returned from load and form actions — across the server/client boundary. Each transporter contains an encode function, which encodes values on the server (or returns a falsy value for anything that isn’t an instance of the type) and a corresponding decode function:
import { import VectorVector } from '$lib/math';
/** @type {import('@sveltejs/kit').Transport} */
export const const transport: {
Vector: {
encode: (value: any) => false | any[];
decode: ([x, y]: [any, any]) => any;
};
}
transport = {
type Vector: {
encode: (value: any) => false | any[];
decode: ([x, y]: [any, any]) => any;
}
Vector: {
encode: (value: any) => false | any[]encode: (value: anyvalue) => value: anyvalue instanceof import VectorVector && [value: anyvalue.x, value: anyvalue.y],
decode: ([x, y]: [any, any]) => anydecode: ([x: anyx, y: anyy]) => new import VectorVector(x: anyx, y: anyy)
}
};import { import VectorVector } from '$lib/math';
import type { type Transport = {
[x: string]: Transporter<any, any>;
}
The transport hook allows you to transport custom types across the server/client boundary.
Each transporter has a pair of encode and decode functions. On the server, encode determines whether a value is an instance of the custom type and, if so, returns a non-falsy encoding of the value which can be an object or an array (or false otherwise).
In the browser, decode turns the encoding back into an instance of the custom type.
import type { Transport } from '@sveltejs/kit';
declare class MyCustomType {
data: any
}
// hooks.js
export const transport: Transport = {
MyCustomType: {
encode: (value) => value instanceof MyCustomType && [value.data],
decode: ([data]) => new MyCustomType(data)
}
};
Transport } from '@sveltejs/kit';
export const const transport: Transporttransport: type Transport = {
[x: string]: Transporter<any, any>;
}
The transport hook allows you to transport custom types across the server/client boundary.
Each transporter has a pair of encode and decode functions. On the server, encode determines whether a value is an instance of the custom type and, if so, returns a non-falsy encoding of the value which can be an object or an array (or false otherwise).
In the browser, decode turns the encoding back into an instance of the custom type.
import type { Transport } from '@sveltejs/kit';
declare class MyCustomType {
data: any
}
// hooks.js
export const transport: Transport = {
MyCustomType: {
encode: (value) => value instanceof MyCustomType && [value.data],
decode: ([data]) => new MyCustomType(data)
}
};
Transport = {
type Vector: {
encode: (value: any) => false | any[];
decode: ([x, y]: any) => any;
}
Vector: {
Transporter<any, any>.encode: (value: any) => anyencode: (value: anyvalue) => value: anyvalue instanceof import VectorVector && [value: anyvalue.x, value: anyvalue.y],
Transporter<any, any>.decode: (data: any) => anydecode: ([x: anyx, y: anyy]) => new import VectorVector(x: anyx, y: anyy)
}
};进一步阅读(Further reading)
¥Further reading