PassNAT LogoPassNAT

ReDoc 接口文档:NAS 上用 Docker 部署,给同事一个地址就能查 OpenAPI

作者

小编

发布时间

Curving abstract shapes with an orange and blue gradient

项目介绍

ReDoc 是一个开源的接口文档渲染器。你只要写好一份 OpenAPI 的 YAML,它就把所有接口排成一整页:左边是分组目录,点开接口,中间列每个路径参数、查询参数和请求体,右边直接给返回的 JSON 示例。

页面是静态的,不用登录,也不需要数据库。把 YAML 和编排放在 NAS 上,同事用浏览器打开一个地址就能查;接口改了,换掉 YAML,页面跟着更新,不用再发文件、翻聊天记录。

这篇用一份「家庭藏书 API」的示例 YAML 演示:四个图书接口、一个借阅接口,返回三本书。换成你自己的接口,格式照这个写就行。

项目地址:点我跳转

部署安装

只要能跑 Docker 的 NAS 都能装,下面用飞牛 NAS 演示:先用 帕斯内网穿透 拿到一个公网地址,再用 Docker Compose 把 ReDoc 装起来。装完用公网地址打开,同事在外面、用手机也能看同一页文档。

一、先用帕斯内网穿透拿到公网地址

飞牛的「应用中心」里搜 PassNAT,装上这个穿透插件,打开它,点「创建隧道」。隧道名字随便起(不超过 12 个字符),接入节点和传输协议留默认,本地 IP 留默认的 127.0.0.1,本地端口填 3652(就是下面编排里冒号左边那个端口)。

建好之后点开这条隧道,按一下电源,状态变成运行中,详情里就能看到分配好的公网地址(形如 IP:端口),先记下来。

不是飞牛的系统,PassNAT 文档的「连接使用」里有其他系统的连接教程:点我跳转。

二、准备接口说明文件

在存放编排的文件夹里新建一个文件,名字就叫 openapi.yaml,把下面这份示例整个贴进去。注意缩进是空格,不要用 Tab。

1openapi: 3.0.3
2info:
3 title: 家庭藏书 API
4 version: 1.0.0
5 description: |
6 给家里的书架做一份接口说明:查书、看详情、登记借出。
7 数据只存在 NAS 上,局域网内用浏览器打开就能看。
8servers:
9 - url: https://nas.local/api
10 description: NAS 上的本地服务
11tags:
12 - name: 图书
13 description: 查看和登记书架上的书
14 - name: 借阅
15 description: 谁把书借走了
16paths:
17 /books:
18 get:
19 tags: [图书]
20 summary: 列出书架上的书
21 description: 支持按书名搜索、按书柜筛选,默认一次返回 20 条。
22 parameters:
23 - name: q
24 in: query
25 description: 书名或作者关键词
26 schema: { type: string }
27 example: 三体
28 - name: shelf
29 in: query
30 description: 书柜编号
31 schema: { type: string }
32 example: A-03
33 - name: limit
34 in: query
35 schema: { type: integer, default: 20, maximum: 100 }
36 responses:
37 '200':
38 description: 图书列表
39 content:
40 application/json:
41 schema:
42 type: array
43 items: { $ref: '#/components/schemas/Book' }
44 example:
45 - id: 1024
46 title: 三体
47 author: 刘慈欣
48 shelf: A-03
49 status: 在架
50 - id: 1025
51 title: 人类简史
52 author: 尤瓦尔·赫拉利
53 shelf: B-01
54 status: 在架
55 - id: 1026
56 title: 设计中的设计
57 author: 原研哉
58 shelf: A-03
59 status: 借出
60 post:
61 tags: [图书]
62 summary: 登记一本新书
63 requestBody:
64 required: true
65 content:
66 application/json:
67 schema: { $ref: '#/components/schemas/BookInput' }
68 example:
69 title: 置身事内
70 author: 兰小欢
71 shelf: C-02
72 responses:
73 '201':
74 description: 登记成功
75 content:
76 application/json:
77 schema: { $ref: '#/components/schemas/Book' }
78 /books/{id}:
79 get:
80 tags: [图书]
81 summary: 查看一本书的详情
82 parameters:
83 - name: id
84 in: path
85 required: true
86 schema: { type: integer }
87 example: 1024
88 responses:
89 '200':
90 description: 图书详情
91 content:
92 application/json:
93 schema: { $ref: '#/components/schemas/Book' }
94 '404':
95 description: 没有这本书
96 content:
97 application/json:
98 schema: { $ref: '#/components/schemas/Error' }
99 delete:
100 tags: [图书]
101 summary: 从书架移除一本书
102 parameters:
103 - name: id
104 in: path
105 required: true
106 schema: { type: integer }
107 responses:
108 '204':
109 description: 已移除
110 /books/{id}/lend:
111 post:
112 tags: [借阅]
113 summary: 登记借出
114 description: 记下借给了谁,书架上的状态会变成「借出」。
115 parameters:
116 - name: id
117 in: path
118 required: true
119 schema: { type: integer }
120 example: 1024
121 requestBody:
122 required: true
123 content:
124 application/json:
125 schema: { $ref: '#/components/schemas/Lend' }
126 example:
127 borrower: 小林
128 due_date: '2026-11-02'
129 responses:
130 '200':
131 description: 借出登记成功
132 content:
133 application/json:
134 schema: { $ref: '#/components/schemas/Book' }
135components:
136 schemas:
137 Book:
138 type: object
139 description: 书架上的书
140 required: [id, title, author, shelf, status]
141 properties:
142 id:
143 type: integer
144 example: 1024
145 title:
146 type: string
147 example: 三体
148 author:
149 type: string
150 example: 刘慈欣
151 shelf:
152 type: string
153 description: 书柜与层号
154 example: A-03
155 status:
156 type: string
157 enum: [在架, 借出]
158 example: 在架
159 BookInput:
160 type: object
161 required: [title, author, shelf]
162 properties:
163 title:
164 type: string
165 example: 置身事内
166 author:
167 type: string
168 example: 兰小欢
169 shelf:
170 type: string
171 example: C-02
172 Lend:
173 type: object
174 required: [borrower]
175 properties:
176 borrower:
177 type: string
178 description: 借书人
179 example: 小林
180 due_date:
181 type: string
182 format: date
183 description: 约定归还日期
184 example: '2026-11-02'
185 Error:
186 type: object
187 properties:
188 code:
189 type: integer
190 example: 404
191 message:
192 type: string
193 example: 没有这本书

几个位置按自己的情况改:title 写你的服务名;description 写一两句给同事看的说明;servers 里的 https://nas.local/api 换成你的接口地址,或者直接删掉这一行;下面的 paths 和 components 换成你自己的接口。不想手写也可以先拿这份示例跑通,看到效果再改。

三、新建 Compose 项目

打开飞牛的「Docker」应用,切到「Compose」标签,点右上角的新增项目。项目名称填 redoc,路径选刚才放 openapi.yaml 的那个文件夹。来源选「创建 docker-compose.yml」,把下面这段整个贴进去:

1services:
2 redoc:
3 image: redocly/redoc:latest
4 container_name: redoc
5 restart: unless-stopped
6 ports:
7 - "3652:80"
8 environment:
9 SPEC_URL: openapi.yaml
10 PAGE_TITLE: 家庭藏书 API
11 volumes:
12 - ./openapi.yaml:/usr/share/nginx/html/openapi.yaml:ro

这段编排要看两处:

SPEC_URL: openapi.yaml:告诉 ReDoc 去读同目录下的哪份文件。文件名改了,这里跟着改。

volumes 那一行:把宿主上的 openapi.yaml 挂进容器里 nginx 的网站目录,只读。冒号右边是容器里的路径,不要改;冒号左边是文件名,要跟 SPEC_URL 对上。

端口 3652 被占用的话,把冒号左边的数字换一个,隧道里的本地端口也跟着改。勾上「创建项目后立即启动」,镜像下完就起来了。这个镜像很小,只有几十兆,几秒钟就能拉完。

四、打开看看

用公网地址打开,先看到文档标题和简介。左边是「图书」「借阅」两组目录,点开列出接口;点 GET /books,中间是 q、shelf、limit 三个查询参数,右边是 200 返回的 JSON,三条示例数据整整齐齐。

手机上打开同一个地址,排版会自动收成一列:目录、参数和返回示例都在,不用缩放也能看。把地址发到工作群,同事点开就能查,不用登录,也不用装任何东西。

五、接口改了怎么办

改完自己的接口,把新的 openapi.yaml 覆盖上去,刷新浏览器就是新页面,容器不用重启。如果改了文件名,记得同步改编排里的 SPEC_URL 和 volumes。

接口文档跟着代码走:写接口的时候先把 YAML 写好,前端后端都照这一页来,改动也有据可查。比在聊天记录里翻「这个字段到底叫啥」省事得多。

几点说明

它只负责展示。 ReDoc 读 YAML、排版、显示示例,不提供接口本身,也不做鉴权。文档页面谁拿到地址都能看,所以别把口令、密钥写进 YAML 的示例里。

数据就是那份 YAML。 没有数据库,也没有要备份的卷。把 openapi.yaml 和 compose.yml 两个文件存好,换一台 NAS 照样能起来。

镜像标签用 latest。 升级时重新拉取镜像、重启项目即可;升级前先把两个文件备份一份,万一新版对 YAML 的要求变了,还能退回去。

和 Swagger UI 的区别。 同样是读 OpenAPI,ReDoc 更偏「读文档」:三栏排版、搜索、右侧常驻返回示例;需要在线调试接口的话,另装一个 Swagger UI 更合适。这篇只讲 ReDoc。