跳转到内容

Astro 适配器 API

Astro 旨在轻松部署到任何云服务商进行按需渲染,也称为服务器端渲染(SSR)。此功能由适配器提供,适配器是集成的一种。请参阅按需渲染指南来学习如何使用现有适配器。

适配器是一种特殊的集成,它为请求时的服务器渲染提供了一个入口点。适配器做两件事:

  • 实现用于处理请求的主机特定 API。
  • 根据主机约定配置构建过程。

适配器是一种集成,它可以做集成能做的任何事情。

适配器必须astro:config:done 钩子中调用 setAdapter API,如下所示:

my-adapter.mjs
export default function createIntegration() {
return {
name: '@example/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@example/my-adapter',
serverEntrypoint: '@example/my-adapter/server.js',
supportedAstroFeatures: {
staticOutput: 'stable'
}
});
},
},
};
}

传递给 setAdapter 的对象定义如下:

interface AstroAdapter {
name: string;
serverEntrypoint?: string;
previewEntrypoint?: string;
exports?: string[];
args?: any;
adapterFeatures?: AstroAdapterFeatures;
supportedAstroFeatures: AstroAdapterFeatureMap;
}
export interface AstroAdapterFeatures {
/**
* Creates an edge function that will communicate with the Astro middleware.
*/
edgeMiddleware: boolean;
/**
* Determine the type of build output the adapter is intended for. Defaults to `server`;
*/
buildOutput?: 'static' | 'server';
}
export type AdapterSupportsKind = 'unsupported' | 'stable' | 'experimental' | 'deprecated' | 'limited';
export type AdapterSupportWithMessage = {
support: Exclude<AdapterSupportsKind, 'stable'>;
message: string;
suppress?: 'default' | 'all';
};
export type AdapterSupport = AdapterSupportsKind | AdapterSupportWithMessage;
export type AstroAdapterFeatureMap = {
/**
* The adapter is able to serve static pages
*/
staticOutput?: AdapterSupport;
/**
* The adapter is able to serve pages that are static or rendered via server
*/
hybridOutput?: AdapterSupport;
/**
* The adapter is able to serve pages rendered on demand
*/
serverOutput?: AdapterSupport;
/**
* The adapter is able to support i18n domains
*/
i18nDomains?: AdapterSupport;
/**
* The adapter is able to support `getSecret` exported from `astro:env/server`
*/
envGetSecret?: AdapterSupport;
/**
* The adapter supports the Sharp image service
*/
sharpImageService?: AdapterSupport;
};

这些属性是:

  • name:适配器的唯一名称,用于日志记录。
  • serverEntrypoint:按需服务器渲染的入口点。
  • exports:与 createExports(下文解释)结合使用时的命名导出数组。
  • adapterFeatures:一个用于启用必须由适配器支持的特定功能的对象。这些功能将改变构建输出,适配器必须实现适当的逻辑来处理不同的输出。
  • supportedAstroFeatures:Astro 内置功能的映射。这允许 Astro 确定适配器无法或不愿支持哪些功能,以便提供适当的错误消息。

Astro 的适配器 API 旨在与任何类型的主机协同工作,并提供一种灵活的方式来遵循主机的 API。

一些无服务器主机希望你导出一个函数,例如 handler

export function handler(event, context) {
// ...
}

通过适配器 API,你可以在你的 serverEntrypoint 中实现 createExports 来达到这个目的:

import { App } from 'astro/app';
export function createExports(manifest) {
const app = new App(manifest);
const handler = (event, context) => {
// ...
};
return { handler };
}

然后在你的集成中,在你调用 setAdapter 的地方,在 exports 中提供这个名称:

my-adapter.mjs
export default function createIntegration() {
return {
name: '@example/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@example/my-adapter',
serverEntrypoint: '@example/my-adapter/server.js',
exports: ['handler'],
});
},
},
};
}

入门

启动

一些主机希望你自己启动服务器,例如通过监听一个端口。对于这类主机,适配器 API 允许你导出一个 start 函数,该函数将在运行打包脚本时被调用。

import { App } from 'astro/app';
export function start(manifest) {
const app = new App(manifest);
addEventListener('fetch', event => {
// ...
});
}

该模块用于渲染通过 astro build 预构建的页面。Astro 使用标准的 RequestResponse 对象。对于具有不同请求/响应 API 的主机,应在其适配器中将它们转换为这些类型。

import { App } from 'astro/app';
import http from 'http';
export function start(manifest) {
const app = new App(manifest);
addEventListener('fetch', event => {
event.respondWith(
app.render(event.request)
);
});
}

提供以下方法:

类型: (request: Request, options?: RenderOptions) => Promise<Response>

此方法调用与请求匹配的 Astro 页面,渲染它,并返回一个指向 Response 对象的 Promise。这也适用于不渲染页面的 API 路由。

const response = await app.render(request);

类型: {addCookieHeader?: boolean; clientAddress?: string; locals?: object; prerenderedErrorPageFetch?: (url: ErrorPagePath) => Promise<Response>; routeData?: RouteData;}

app.render() 方法接受一个强制的 request 参数,以及一个可选的 RenderOptions 对象,用于设置 addCookieHeaderclientAddresslocalsprerenderedErrorPageFetchrouteData

类型: boolean
默认值: false

是否自动将所有由 Astro.cookie.set() 写入的 cookie 添加到响应头中。

当设置为 true 时,它们将作为逗号分隔的键值对添加到响应的 Set-Cookie 头中。你可以使用标准的 response.headers.getSetCookie() API 来单独读取它们。当设置为 false(默认值)时,cookie 将仅能从 App.getSetCookieFromResponse(response) 中获取。

const response = await app.render(request, { addCookieHeader: true });

类型: string
默认值: request[Symbol.for("astro.clientAddress")]

客户端 IP 地址,将在页面中作为 Astro.clientAddress 提供,在 API 路由和中间件中作为 ctx.clientAddress 提供。

下面的示例读取 x-forwarded-for 标头并将其作为 clientAddress 传递。该值将作为 Astro.clientAddress 提供给用户。

const clientAddress = request.headers.get("x-forwarded-for");
const response = await app.render(request, { clientAddress });

类型: object

用于在请求生命周期中存储和访问信息的 context.locals 对象

下面的示例读取名为 x-private-header 的标头,尝试将其解析为对象并传递给 locals,然后可以将其传递给任何中间件函数

const privateHeader = request.headers.get("x-private-header");
let locals = {};
try {
if (privateHeader) {
locals = JSON.parse(privateHeader);
}
} finally {
const response = await app.render(request, { locals });
}

类型: (url: ErrorPagePath) => Promise<Response>
默认值: fetch

添加于: astro@5.6.0

一个允许你为获取预渲染的错误页面提供自定义实现的函数。

这用于覆盖默认的 fetch() 行为,例如,当 fetch() 不可用或你无法从服务器自身调用服务器时。

以下示例从磁盘读取 500.html404.html,而不是执行 HTTP 调用:

return app.render(request, {
prerenderedErrorPageFetch: async (url: string): Promise<Response> => {
if (url.includes("/500")) {
const content = await fs.promises.readFile("500.html", "utf-8");
return new Response(content, {
status: 500,
headers: { "Content-Type": "text/html" },
});
}
const content = await fs.promises.readFile("404.html", "utf-8");
return new Response(content, {
status: 404,
headers: { "Content-Type": "text/html" },
});
});

如果未提供,Astro 将回退到其默认的获取错误页面的行为。

类型: RouteData
默认值: app.match(request)

如果你已经知道要渲染的路由,请为 integrationRouteData 提供一个值。这样做将绕过对 app.match 的内部调用来确定要渲染的路由。

const routeData = app.match(request);
if (routeData) {
return app.render(request, { routeData });
} else {
/* adapter-specific 404 response */
return new Response(..., { status: 404 });
}

类型: (request: Request) => RouteData | undefined

此方法用于确定请求是否与 Astro 应用的路由规则匹配。

if(app.match(request)) {
const response = await app.render(request);
}

你通常可以调用 app.render(request) 而不使用 .match,因为如果你提供了一个 404.astro 文件,Astro 会处理 404。如果你想以不同的方式处理 404,请使用 app.match(request)

astro add 命令允许用户轻松地将集成和适配器添加到他们的项目中。如果你希望你的适配器能通过此工具安装,请将 astro-adapter 添加到你的 package.jsonkeywords 字段中

{
"name": "example",
"keywords": ["astro-adapter"],
}

一旦你将你的适配器发布到 npm,运行 astro add example 将安装你的包以及 package.json 中指定的任何同级依赖项。我们还会指导用户手动更新他们的项目配置。

添加于: astro@3.0.0

Astro 功能是适配器告知 Astro 它们是否能够支持某个功能以及其支持级别的一种方式。

使用这些属性时,Astro 将:

  • 运行特定的验证;
  • 向日志中发出上下文信息;

这些操作的运行基于所支持或不支持的功能、它们的支持级别、所需的日志记录量以及用户自己的配置。

以下配置告诉 Astro,此适配器对由 Sharp 驱动的内置图像服务提供实验性支持:

my-adapter.mjs
export default function createIntegration() {
return {
name: '@example/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@example/my-adapter',
serverEntrypoint: '@example/my-adapter/server.js',
supportedAstroFeatures: {
sharpImageService: 'experimental'
}
});
},
},
};
}

如果使用了 Sharp 图像服务,Astro 将根据你的适配器的支持情况,在终端中记录警告和错误:

[@example/my-adapter] The feature is experimental and subject to issues or changes.
[@example/my-adapter] The currently selected adapter `@example/my-adapter` is not compatible with the service "Sharp". Your project will NOT be able to build.

此外,还可以提供一条消息,为用户提供更多上下文:

my-adapter.mjs
export default function createIntegration() {
return {
name: '@example/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@example/my-adapter',
serverEntrypoint: '@example/my-adapter/server.js',
supportedAstroFeatures: {
sharpImageService: {
support: 'limited',
message: 'This adapter has limited support for Sharp. Certain features may not work as expected.'
}
}
});
},
},
};
}

类型: 'default' | 'all'

添加于: astro@5.9.0

一个用于阻止显示部分或全部关于适配器对某功能支持情况的日志消息的选项。

如果 Astro 的默认日志消息是多余的,或者与你的自定义 message 结合使用时对用户造成困惑,你可以使用 suppress: "default" 来抑制默认消息,只记录你的消息:

my-adapter.mjs
export default function createIntegration() {
return {
name: '@example/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@example/my-adapter',
serverEntrypoint: '@example/my-adapter/server.js',
supportedAstroFeatures: {
sharpImageService: {
support: 'limited',
message: 'The adapter has limited support for Sharp. It will be used for images during build time, but will not work at runtime.',
suppress: 'default' // custom message is more detailed than the default
}
}
});
},
},
};
}

你还可以使用 suppress: "all" 来抑制所有关于该功能支持情况的消息。当这些消息在特定上下文中对用户无用时(例如,当他们的配置设置意味着他们不使用该功能时),这很有用。例如,你可以选择阻止从你的适配器记录任何关于 Sharp 支持的消息:

my-adapter.mjs
export default function createIntegration() {
return {
name: '@example/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@example/my-adapter',
serverEntrypoint: '@example/my-adapter/server.js',
supportedAstroFeatures: {
sharpImageService: {
support: 'limited',
message: 'This adapter has limited support for Sharp. Certain features may not work as expected.',
suppress: 'all'
}
}
});
},
},
};
}

一组改变已生成文件输出的功能。当适配器选择使用这些功能时,它们将在特定的钩子中获得额外的信息。

类型: boolean

定义在构建时是否会打包任何按需渲染的中间件代码。

启用后,这将防止中间件代码在构建过程中被所有页面打包和导入:

my-adapter.mjs
export default function createIntegration() {
return {
name: '@example/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@example/my-adapter',
serverEntrypoint: '@example/my-adapter/server.js',
adapterFeatures: {
edgeMiddleware: true
}
});
},
},
};
}

然后,使用astro:build:ssr钩子,它会给你一个 middlewareEntryPoint,即指向文件系统上物理文件的 URL

my-adapter.mjs
export default function createIntegration() {
return {
name: '@example/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@example/my-adapter',
serverEntrypoint: '@example/my-adapter/server.js',
adapterFeatures: {
edgeMiddleware: true
}
});
},
'astro:build:ssr': ({ middlewareEntryPoint }) => {
// remember to check if this property exits, it will be `undefined` if the adapter doesn't opt in to the feature
if (middlewareEntryPoint) {
createEdgeMiddleware(middlewareEntryPoint)
}
}
},
};
}
function createEdgeMiddleware(middlewareEntryPoint) {
// emit a new physical file using your bundler
}

类型: AdapterSupportsKind

这是一个功能,允许你的适配器检索用户在 env.schema 中配置的密钥。

通过将任何有效的 AdapterSupportsKind 值传递给适配器来启用该功能:

my-adapter.mjs
export default function createIntegration() {
return {
name: '@example/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@example/my-adapter',
serverEntrypoint: '@example/my-adapter/server.js',
adapterFeatures: {
envGetSecret: 'stable'
}
});
},
},
};
}

astro/env/setup 模块允许你为 getSecret() 提供一个实现。在你的服务器入口点中,尽快调用 setGetEnv()

import { App } from 'astro/app';
import { setGetEnv } from "astro/env/setup"
setGetEnv((key) => process.env[key])
export function createExports(manifest) {
const app = new App(manifest);
const handler = (event, context) => {
// ...
};
return { handler };
}

如果你支持密钥,当你的环境变量与请求相关联时,请确保在调用 getSecret() 之前调用 setGetEnv()

import type { SSRManifest } from 'astro';
import { App } from 'astro/app';
import { setGetEnv } from 'astro/env/setup';
import { createGetEnv } from '../utils/env.js';
type Env = {
[key: string]: unknown;
};
export function createExports(manifest: SSRManifest) {
const app = new App(manifest);
const fetch = async (request: Request, env: Env) => {
setGetEnv(createGetEnv(env));
const response = await app.render(request);
return response;
};
return { default: { fetch } };
}

类型: 'static' | 'server'

添加于: astro@5.0.0

此属性允许你为构建强制指定一个特定的输出形式。这对于只适用于特定输出类型的适配器很有用,例如,你的适配器可能期望一个静态网站,但使用适配器来创建特定于主机的文​​件。如果未指定,则默认为 server

my-adapter.mjs
export default function createIntegration() {
return {
name: '@example/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@example/my-adapter',
serverEntrypoint: '@example/my-adapter/server.js',
adapterFeatures: {
buildOutput: 'static'
}
});
},
},
};
}

类型: true | false

添加于: astro@5.9.3

启用此功能后,Astro 将返回一个由静态页面发出的 Headers 的映射。这个 experimentalRouteToHeaders 映射在 astro:build:generated 钩子中可用。

标头的值可能会根据应用程序启用/使用的功能而改变。

例如,如果启用了 CSP,<meta http-equiv="content-security-policy"> 元素不会被添加到静态页面中。相反,它的 content 会在 experimentalRouteToHeaders 映射中可用。

my-adapter.mjs
export default function createIntegration() {
return {
name: '@example/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@example/my-adapter',
serverEntrypoint: '@example/my-adapter/server.js',
adapterFeatures: {
experimentalStaticHeaders: true,
},
});
},
'astro:build:generated': ({ experimentalRouteToHeaders }) => {
// use `experimentalRouteToHeaders` to generate a configuration file
// for your virtual host of choice
},
},
};
}
贡献 社区 赞助