首页
/ Strapi 无头 CMS:从 README 到源码,看懂请求流水线与快速上手路径

Strapi 无头 CMS:从 README 到源码,看懂请求流水线与快速上手路径

2026-09-06 11:02:29作者:贡沫苏Truman

本文以 Strapi 主仓库的 README 为骨架,系统讲解这个开源无头(headless)CMS 的核心定位、请求处理流水线(Routes → Middlewares → Controllers → Services)、快速安装与 Docker 部署方式,并结合仓库源码给出每一层的实现证据。读完后,你既能掌握从零创建 Strapi 项目的操作路径,也能理解自动生成的 REST API 背后的路由契约、内置中间件清单与默认行为,从而在二次开发(自定义路由、中间件、服务)时有的放矢。

一、Strapi 是什么:README 中的产品定位与能力清单

README 开篇给出的定义是:Strapi 是一个开源、可自托管的无头 CMS,让开发者快速构建内容 API,同时为内容编辑者提供友好的管理界面。你定义内容模型,Strapi 就生成一套完整的 API,可被任意前端、移动端或 IoT 设备消费(见 README.md)。

README 列举的核心能力,在当前仓库中都有对应的代码实体,逐条对照如下:

README 宣称的能力 仓库中的对应实现
可视化内容建模,无需写代码(Content-Type Builder) packages/core/content-type-builder
为每个内容类型自动生成 REST & GraphQL API 路由自动生成逻辑在 packages/core/core/src/core-api/routes/index.ts;GraphQL 插件在 packages/plugins/graphql
开箱即用的细粒度角色与权限 packages/plugins/users-permissions
内置媒体库(Media Library) packages/plugins/upload
国际化(i18n)与草稿/发布(Draft & Publish) packages/plugins/i18n;草稿发布的数据迁移逻辑见 packages/core/core/src/migrations/draft-publish.ts
一流 TypeScript 支持,数据库可选 SQLite / PostgreSQL / MySQL / MariaDB 数据库层在 packages/core/database;TypeScript 基础设施见 packages/core/types
可扩展的插件系统与可定制后台 插件加载器在 packages/core/core/src/loaders,官方插件示例在 examples/plugins/todo-example

需要说明的是:README 中的“Strapi AI”“Strapi Cloud 托管”“LaunchPad 演示模板”属于外部商业化/社区生态内容,不在本仓库源码范围内,本文不展开。

二、请求处理流水线:Routes → Middlewares → Controllers → Services

README 中最具技术价值的部分是一张请求流向说明:每一个进入的请求都会流经分层后端架构——Routes → Middlewares → Controllers → Services。下面逐层给出源码级验证。

2.1 路由层:API 路由是如何“自动生成”的

Strapi 的“零代码生成 API”并非魔术。从 packages/core/core/src/core-api/routes/index.ts 可以看到,路由工厂 createRoutes 会先判断内容类型是单例(single type)还是集合(collection type),然后返回一套带方法、路径、处理器和请求/响应契约(基于 Zod schema 的 request/response 字段)的路由定义。

集合类型(Collection Type)自动生成的 5 个端点(路径基于 info.pluralName 复数名):

操作 方法 路径 handler
列表查询 find GET /:pluralName api::xxx.findOne 对应的 find
单条查询 findOne GET /:pluralName/:id xxx.findOne
创建 create POST /:pluralName xxx.create
更新 update PUT /:pluralName/:id xxx.update
删除 delete DELETE /:pluralName/:id xxx.delete

单例类型(Single Type) 只生成 find(GET)、update(PUT)、delete(DELETE)三个端点,路径直接使用 info.singularName 单数名——因为不存在“多条记录”与“按 ID 定位”的概念。

查询参数同样由源码决定,而非口头约定:

  • 集合列表固定支持 fieldsfilters_qpaginationsortpopulate
  • 条件性参数由 getConditionalQueryParamsroutes/index.ts#L159-L173)按内容类型能力注入:开启 i18n 的本地化类型会追加 locale;开启草稿/发布的类型会追加 statuspublicationFilter,以及为兼容旧客户端保留的 hasPublishedVersion(源码注释明确其为 Deprecated,建议迁移到 publicationFilter)。

路由前缀则在运行时统一拼装:packages/core/core/src/services/content-api/index.tsgetRoutesMap 会把 api.rest.prefix(默认 /api)拼接到每条路由的 path 上,插件路由则额外加上插件名前缀。这解释了为什么默认端点形如 /api/articles

此外,Content API 还暴露了 addQueryParams / addInputParams 扩展点(content-api/index.ts#L181-L191),允许插件或应用注册自定义查询/输入参数(Zod schema 约束:查询参数必须是标量或标量数组,重名或占用 Strapi 保留键会在注册时直接抛错)。

2.2 中间件层:默认清单、必选清单与全局注册

Koa 应用本身在 packages/core/core/src/services/server/index.ts 中创建:它读取 server.proxy.*server.app.keys 等配置构建 Koa 实例,将每个请求上下文放入 requestCtx(用于后续生命周期钩子的数据传递),并注册了健康检查端点 ALL /_health——返回 204 状态码和 strapi: You are so French! 响应头,可作为容器编排的存活探针。

Strapi 内置的中间件工厂集中在 packages/core/core/src/middlewares/index.ts,共 14 个:compressioncorserrorsfaviconiploggerpoweredBybodyqueryresponseTimeresponsessecuritysessionpublic

而“哪些默认生效”由 packages/core/core/src/services/server/register-middlewares.ts 中的两份清单决定:

  • 默认配置middlewares 配置缺省时的取值):strapi::loggerstrapi::errorsstrapi::securitystrapi::corsstrapi::poweredBystrapi::sessionstrapi::querystrapi::bodystrapi::faviconstrapi::public
  • 必选清单errorssecuritycorsquerybodypublicfavicon 缺一不可,若用户在 config/middlewares 中移除任何一项,checkRequiredMiddlewares 会在启动阶段抛出错误;
  • 配置项本身支持两种写法:字符串("strapi::cors")或对象({ name, resolve, config }),对象形式可用于自定义中间件,注册前会用 Yup schema 严格校验格式。

这份源码证据恰好印证了 README 中“分层架构”的说法:中间件在路由之前全局挂载(strapi.server.use),负责错误兜底、安全头、CORS、请求体解析等横切关注点,请求随后才进入各路由的 Controller。

2.3 控制器与服务层:默认行为的“出厂设置”

控制器(controllers)与业务服务(services)按 API 目录约定加载。packages/core/core/src/loaders/apis.tsloadAPI 会并行读取每个 API 目录下的 configroutescontrollersservicespoliciesmiddlewarescontent-types 七个子目录,并把文件名规范化为 kebab-case 作为键。也就是说,你在应用 src/api 下新建一个 API 文件夹并遵循这套目录约定,路由与权限体系就能自动接管它。

对外查询的默认行为在 packages/core/core/src/core-api/service/core-service.ts 中一目了然:

export abstract class CoreService {
  getFetchParams(params = {}): any {
    return {
      status: 'published',
      ...params,
    };
  }
}

基类把 status: 'published' 作为默认查询条件——这正是“对外 API 只返回已发布内容”这一 Draft & Publish 语义的落地位置。集合/单例的控制器(core-api/controller/collection-type.tssingle-type.ts)继承该基类实现各自的 find/findOne/create/update/delete

三、快速上手:安装、环境要求与 Docker

3.1 用 Quickstart 创建项目

README 给出的最快创建方式:

npx create-strapi@latest my-project

该命令生成一个带默认功能集(认证、权限、内容管理、内容类型构建器、文件上传)的项目。对应源码即本仓库的 packages/cli/create-strapi-app(Quickstart 模板位于其 templates/ 目录)。若你想先看完整可运行项目,仓库自带的 examples/getstartedexamples/complexexamples/empty 三个示例分别展示了“带示例数据”“复杂建模”“纯净骨架”三种形态,其中 examples/complex/scripts 目录还提供了 SQLite、PostgreSQL、MySQL、MariaDB 四种数据库的一键切换脚本(如 db-sqlite.ts),与 README 宣称的数据库选型一一对应。

3.2 环境要求

以当前仓库根 package.json 为准:

  • Node.js>=20.0.0 <=26.x.x,npm >=6.0.0engines 字段);
  • 包管理器yarn@4.12.0packageManager 字段),且为 Yarn Workspaces monorepo,workspaces 覆盖 packages/*packages/*/*examples/* 等;
  • 仓库通过 isStrapiMonorepo: true 标记自身为 monorepo,构建/测试统一走 nx(buildtest:unittest:apitest:e2e 等脚本均基于 nx run-many)。

3.3 Docker 部署

README 明确说明:Strapi 不发布官方 Docker 镜像,需要基于自己的项目自行构建,并推荐社区工具:

npx @strapi-community/dockerize@latest

它会为你的项目生成定制的 Dockerfiledocker-compose.yml。仓库内也保留了开发用的 compose 文件供参考:docker-compose.dev.yml(开发)与 docker-compose.test.yml(测试)。生产环境则建议按官方 Docker 文档的思路自行维护镜像构建流程。

四、读懂仓库:monorepo 结构与贡献入口

README 的 “Repositories” 一节将 Strapi 生态分为三个仓库(核心 monorepo、Design System、LaunchPad 演示应用),而本文所在仓库正是核心 monorepo,其内部结构即 README.md 所述 CMS 本体的完整实现:

  • packages/corestrapi(CLI 入口与启动编排)、core(本文分析的请求流水线所在)、databaseadmincontent-managercontent-type-builderpermissionstypes 等核心能力包;
  • packages/pluginsgraphqli18nuploadusers-permissionssentrydocumentation 等可安装插件;
  • packages/providers:邮件(SES、Mailgun、SendGrid、Nodemailer、sendmail)与存储(AWS S3、Cloudinary、local)第三方 provider;
  • packages/clicreate-strapicreate-strapi-appcloud 等命令行工具;
  • packages/generators:基于 plop 的代码生成器;
  • examples:可直接运行的示例应用;
  • tests:API 集成测试(tests/api)、CLI 测试、Playwright E2E(tests/e2e)与版本迁移场景(tests/migration,根 package.jsontest:migrations* 脚本即驱动它)。

贡献与治理方面,仓库提供了 CONTRIBUTING.md(贡献指南)、SECURITY.md(安全漏洞负责任披露流程,对应 README 的 Security 一节)、LICENSECODE_OF_CONDUCT.md。官方文档站点源码也在本仓库内:docs/docs 下的 01-core05-utilsguidesrfcs 等目录覆盖了核心概念、工具函数指南与设计 RFC,是比 README 更深一层的阅读材料。

五、小结:一条请求的完整旅程

把 README 的一句话架构与源码证据串起来,一个 GET /api/articles?filters[title]=$contains[xxx] 请求的旅程是:

  1. Koa 全局中间件按默认清单依次执行(logger → errors → security → cors → … → body → public),见 register-middlewares.ts
  2. 请求命中 自动生成的路由契约core-api/routes/index.ts),查询参数被 Zod schema 校验,filters 等合法键放行,未声明键按 api.rest.strictParams 策略处理;
  3. 控制器(collection-type controller)接收请求,调用继承自 CoreService 的服务方法;
  4. 服务层status: 'published' 为默认参数查询数据库,再经 content-api.input/output 两级 sanitizer 与权限过滤后返回。

这正是 Strapi “定义模型即拥有 API”的完整技术闭环:路由可预测、中间件可裁剪、控制器与服务可覆写——三层扩展点分别对应 routesmiddlewarescontrollers/services 目录约定(loaders/apis.ts),也是后续所有后端定制开发的出发点。

登录后查看全文
热门项目推荐
相关项目推荐