首页 / Astro 教程 / Endpoints / API 路由

Astro 教程

Endpoints / API 路由

本教程共 56 篇 · 第 34 篇 · 更新于 2026-08-07 · 约 9 分钟阅读

AstroAstro 教程EndpointAPI 路由SSR动态路由

本节目标:学会用 Astro 的端点(Endpoint)生成静态数据文件,或写出能在服务端处理请求的真实 API。

上一章我们讲的是”Astro 去别的接口要数据”。这一章反过来:让你的 Astro 项目自己成为接口,对外提供数据或处理请求。在 Astro 里,这件事靠**端点(Endpoint)**完成。端点既可以生成静态文件,也能当成一个真正的服务端 API 路由(API Route)。

什么是端点

**端点(Endpoint)**就是放在 src/pages/ 目录下的 .js.ts 文件。它导出一个函数(通常是 GET),返回一个标准的 Response 对象。Astro 会在合适的时候调用它,把你返回的内容变成浏览器能访问的地址。

它和 .astro 页面很像,都放在 pages 里、都能被访问;但端点不返回 HTML 页面,而是返回数据(JSON、图片、文本等)。你可以拿它来:

  • 生成一个 sitemap.xmldata.json 之类的静态文件;
  • 做一个 RSS 订阅源;
  • 写成一组 API,让前端或别的程序来调用。
Note

文件名里的扩展名会被去掉,换成你想要的扩展名。比如 src/pages/data.json.ts 构建后访问地址是 /data.json。所以写端点时,文件名要带”最终想要的后缀”。

静态文件端点

最基础的用法:在 pages 里建一个 .ts 文件,导出一个 GET 函数,返回一个 Response

// src/pages/builtwith.json.ts
// 构建后访问:/builtwith.json
export function GET({ params, request }) {
  return new Response(
    JSON.stringify({
      name: "Astro",
      url: "https://astro.build/",
    })
  );
}

在静态站点里,这个函数在构建时被调用一次,返回的内容直接写成 /builtwith.json 文件。访客访问时拿到的就是构建好的那份。

从 Astro v3.0 起,返回的 Response 不再需要写 encoding 字段。所以生成二进制内容(比如图片)也很简单:

// src/pages/astro-logo.png.ts
export async function GET({ params, request }) {
  const response = await fetch(
    "https://docs.astro.build/assets/full-logo-light.png"
  );
  return new Response(await response.arrayBuffer());
}

如果你想写类型更安全的端点,可以用 APIRoute 类型配合 satisfies

import type { APIRoute } from "astro";

export const GET = (async ({ params, request }) => {
  // ...
}) satisfies APIRoute;
Tip

带文件后缀的端点(如 sitemap.xml.ts)只能用”不带斜杠”的地址访问,比如 /sitemap.xml,跟你 build.trailingSlash 怎么配置无关。这是个小坑,部署后记得用对的地址测一下。

动态路由的端点

端点也支持动态路由(dynamic routing)——就是文件名用方括号包住参数,再导出 getStaticPaths() 列出所有可能的值。

// src/pages/api/[id].json.ts
import type { APIRoute } from "astro";

const usernames = ["Sarah", "Chris", "Yan", "Elian"];

export const GET = (({ params, request }) => {
  const id = Number(params.id);
  return new Response(
    JSON.stringify({
      name: usernames[id],
    })
  );
}) satisfies APIRoute;

export function getStaticPaths() {
  return [
    { params: { id: "0" } },
    { params: { id: "1" } },
    { params: { id: "2" } },
    { params: { id: "3" } },
  ];
}

构建时,它会一次性生成四个 JSON:/api/0.json/api/1.json/api/2.json/api/3.json。逻辑和页面的动态路由完全一样。在静态模式下,你还能通过 getStaticPaths() 给端点传 props;但到了按需渲染模式,端点是个函数不是组件,就不支持传 props 了。

服务端端点(API 路由)

上面都是静态的。一旦你开启按需渲染(第 36 章),端点就升级成”活的”服务端端点:每次收到请求才执行。这下它能干的事一下子多起来——收表单、查数据库、按条件返回不同结果,而且敏感代码只在服务器跑,不暴露给浏览器。

server 模式下,路由默认就是按需渲染的。在 static 模式下,你得给端点单独加一句 export const prerender = false 让它走服务端。

服务端端点能直接用 params,不用导出 getStaticPaths();还能自由设置状态码和请求头:

// src/pages/[id].json.js
import type { APIRoute } from "astro";
import { getProduct } from "../db";

export const GET = (async ({ params }) => {
  const id = params.id;
  const product = await getProduct(id);

  if (!product) {
    return new Response(null, {
      status: 404,
      statusText: "Not found",
    });
  }

  return new Response(JSON.stringify(product), {
    status: 200,
    headers: {
      "Content-Type": "application/json",
    },
  });
}) satisfies APIRoute;

访问 /helmet.json 时,params.id 就是 "helmet"。能查到就返回商品 JSON 和 200;查不到就返回 404。这正是真实 API 的样子。

支持多种 HTTP 方法

端点不只能处理 GET。你可以导出任意 HTTP 方法 名的函数:POSTPUTDELETEPATCH 等等。收到请求时,Astro 看方法名调用对应的函数。还能导出 ALL 来兜底所有没单独定义的方法。

// src/pages/methods.json.ts
import type { APIRoute } from "astro";

export const GET = (({ params, request }) => {
  return new Response(JSON.stringify({ message: "This was a GET!" }));
}) satisfies APIRoute;

export const POST = (({ request }) => {
  return new Response(JSON.stringify({ message: "This was a POST!" }));
}) satisfies APIRoute;

export const DELETE = (({ request }) => {
  return new Response(JSON.stringify({ message: "This was a DELETE!" }));
}) satisfies APIRoute;

export const ALL = (({ request }) => {
  return new Response(JSON.stringify({ message: `This was a ${request.method}!` }));
}) satisfies APIRoute;

如果你只定义了 GET 没定义 HEAD,Astro 会自动用 GET 的结果,只把响应体去掉,当作 HEAD 的回复。

Note

HEAD 是一种”只要响应头、不要正文”的请求,常用来检查资源是否存在。Astro 帮你自动处理了,省心。

读取请求里的数据

在按需渲染模式下,端点拿到的 request 是一个完整的 Request 对象。你能读请求头、读请求体。下面这个例子处理 POST,从 JSON 请求体里取名字再回传:

// src/pages/test-post.json.ts
import type { APIRoute } from "astro";

export const POST = (async ({ request }) => {
  if (request.headers.get("Content-Type") === "application/json") {
    const body = await request.json();
    const name = body.name;

    return new Response(
      JSON.stringify({
        message: "Your name was: " + name,
      }),
      { status: 200 }
    );
  }

  return new Response(null, { status: 400 });
}) satisfies APIRoute;

端点里做重定向

端点上下文还提供了一个 redirect() 工具,和页面里的 Astro.redirect 用法一致。比如根据短链 ID 跳到真实地址:

// src/pages/links/[id].js
import type { APIRoute } from "astro";
import { getLinkUrl } from "../db";

export const GET = (async ({ params, redirect }) => {
  const { id } = params;
  const link = await getLinkUrl(id);

  if (!link) {
    return new Response(null, { status: 404, statusText: "Not found" });
  }

  return redirect(link, 307);
}) satisfies APIRoute;

端点返回的内容不限于 JSON

端点返回什么,全看你构造的 Response。除了 JSON,也能返回纯文本、HTML,甚至图片的二进制数据。比如返回一个简单的 HTML 片段:

// src/pages/hello.ts
import type { APIRoute } from "astro";

export const GET = (async () => {
  return new Response("<h1>Hello from endpoint</h1>", {
    headers: { "Content-Type": "text/html" },
  });
}) satisfies APIRoute;

关键点就一个:通过 Responseheaders 里的 Content-Type,告诉浏览器”这是什么类型”。设对了,浏览器按对应方式处理;设错了,可能把本该显示的 HTML 当成文件下载下来。

静态端点和服务端端点怎么选

两种端点写法很像,区别在于”什么时候跑”。一句话判断:

  • 内容固定、不依赖访客 → 用静态端点,构建时生成一次,省钱又快。比如站点地图、RSS、很少变的配置。
  • 内容因人因时而异、要读请求 → 用服务端端点(开按需渲染),每次请求现算。比如用户资料、搜索、表单提交。

在静态模式里,端点拿到的 request 其实只有 request.url 能用——它返回当前端点的完整网址,用法和页面里的 Astro.request.url 一样。真正的请求体、请求头要等到按需渲染才完整可用。

Tip

端点文件一旦带 .json.xml 这类后缀,访问地址就不能带结尾斜杠。比如 sitemap.xml.ts 只能以 /sitemap.xml 访问。部署后拿这个地址测一下,别被斜杠坑了。

端点也要优雅地处理错误

服务端端点跑在真实流量下,出错概率比构建时高不少。别让一个没兜住的异常变成 500 白页。下面这个例子在取数失败时返回 500 和一句人话:

// src/pages/api/product.ts
import type { APIRoute } from "astro";

export const GET = (async ({ params }) => {
  try {
    const product = await getProduct(params.id);
    if (!product) {
      return new Response("找不到该商品", { status: 404 });
    }
    return new Response(JSON.stringify(product), {
      status: 200,
      headers: { "Content-Type": "application/json" },
    });
  } catch (err) {
    return new Response("服务暂时不可用", { status: 500 });
  }
}) satisfies APIRoute;

返回什么状态码,最好和语义对上:找不到是 404、参数不对是 400、服务器自己崩了是 500。前端或别的程序调用你的端点时,正是靠这些码来判断下一步怎么走。状态码给得准,调用方才好写逻辑。

小结

端点就是 pages 里的 .js/.ts 文件,导出一个 GET(或别的 HTTP 方法)函数,返回一个 Response。静态模式下它在构建时跑一次、生成文件;按需渲染模式下它变成活的 API 路由,能收请求、读请求体、设状态码、做重定向。动态路由、多方法、重定向都支持。

下一章我们聊环境变量,它是存放接口密钥、切换开发/生产配置的必备工具。先把端点想成”返回数据的页面”,静态给文件、按需给 API,就不容易迷路。