天天看点

swagger 学习

swagger 学习

本文引用 https://learnku.com/laravel/t/7430/how-to-write-api-documents-based-on-swagger-php#747b67 参考学习,感觉非常实用,收藏个人学习记录

前言

编写目的

本文介绍如何使用 Swagger 编写 API 文档。通过阅读本文,你可以:

了解 swagger 是什么

掌握使用 swagger 编写 API 文档的基本方法

第 1 章 简介

1.1 Swagger

Swagger(丝袜哥)给人第一印象就是【最(hen)流(niu)行(bai)】,不懂 Swagger 咱就 out 了。它的官方网站是 http://swagger.io/。

Swagger 是一个简单但功能强大的 API 表达工具。它具有地球上最大的 API 工具生态系统,数以千计的开发人员,使用几乎所有的现代编程语言,都在支持和使用 Swagger。使用 Swagger 生成 API,我们可以得到交互式文档,自动生成代码的 SDK 以及 API 的发现特性等。

现在,Swagger 已经帮助包括 Apigee, Getty 图像,Intuit, LivingSocial, McKesson, 微软,Morningstar 和 PayPal 等世界知名企业建立起了一套基于 RESTful API 的完美服务系统。

2.0 版本已经发布,Swagger 变得更加强大。值得感激的是,Swagger 的源码 100% 开源在 github。

1.2 OpenAPI 规范

OpenAPI 规范是 Linux 基金会的一个项目,试图通过定义一种用来描述 API 格式或 API 定义的语言,来规范 RESTful 服务开发过程。OpenAPI 规范帮助我们描述一个 API 的基本信息,比如:

有关该 API 的一般性描述

可用路径(/ 资源)

在每个路径上的可用操作(获取 / 提交…)

每个操作的输入 / 输出格式

目前 V2.0 版本的 OpenAPI 规范(也就是 SwaggerV2.0 规范)已经发布并开源在 github 上。该文档写的非常好,结构清晰,方便随时查阅。关于规范的学习和理解,本文最后还有个彩蛋。

1.3 为啥要使用 OpenAPI 规范?

OpenAPI 规范这类 API 定义语言能够帮助你更简单、快速的表述 API,尤其是在 API 的设计阶段作用特别突出

根据 OpenAPI 规范编写的二进制文本文件,能够像代码一样用任何 VCS 工具管理起来

一旦编写完成,API 文档可以作为:

需求和系统特性描述的根据

前后台查询、讨论、自测的基础

部分或者全部代码自动生成的根据

其他重要的作用,比如开放平台开发者的手册…

1.4 如何编写 API 文档?

1.4.1 语言 JSON vs YAML

1.4.2 基于 PHP 注释的方式

第 2 章 从零开始

这一章主要介绍 API 的基本组成部分,包括提供给 API 消费者(所有可能访问 API 的个体,下简称 “消费者”)的的不同 HTTP 请求方法、路径,请求和消息体中的参数,以及返回给消费者的不同 HTTP 状态及响应消息体。

<?php 这个文档的内容分成四部分,下面分别来说明。 2.1.1 OpenAPI 规范的版本号 首先我们要通过一个 swagger 属性来声明 OpenAPI 规范的版本。 <?php 你没看错,是 swagger,上面已经介绍了,OpenAPI 规范是基于 Swagger 的,在未来的版本中,这个属性可能会换成别的。 目前这个属性的值,暂时只能填写为 2.0。 2.1.2 API 描述信息 然后我们需要说明一下 API 文档的相关信息,比如 API 文档版本(注意不同于上面的规范版本)、API 文档名称已经可选的描述信息。 <?php version:API 文档版本 title:API 文档标题 description:API 文档描述 2.1.3 API 的 URL 作为 web API,一个很重要的信息就是用来给消费者使用的根URL,可以用协议(http 或者 https)、主机名、根路径来描述: <?php 这这个例子中,消费者把 https://tcmapi.emao.com/v1 作为根节点来访问各种 API。因为和具体环境有关,不涉及 API 描述的根本内容,所以这部分信息是可选的。 schemes:协议,可以是多个 host:主机名 basePath:根路径 2.2 定义一个 API 操作 如果我们要展示一组用户信息,可以这样描述: <?php Get:请求的 HTTP 方法,支持 GET/POST/PUT/DELETE 等 HTTP 标准请求方法 path:请求的路径 summary:接口简介 tags:接口标签,可以是多个 description:接口描述,支持 Markdown 语法 operationId:操作的 ID,需要唯一 2.2.1 添加一个 路径 (path) 我们添加一个 /persons 的 路径,用来访问一组用户信息 2.2.2 在路径中添加一个 HTTP 方法 在每个路径中,我们可以添加任意的 HTTP动词,如 GET/POST/PUT/DELETE 等来操作所需要的资源。 比如需要展示一组用户信息,我们可以在 /person 路径中添加 get 方法,同时还可以填写一些简单的描述信息 (summary) 或者说明该主主法的一段长篇大论 (description) <?php 这样一来,我们调 get https://tcmapi.emao.com/v1/persons 方法就能获取一个用户信息列表了。 2.2.3 定义响应 (response) 类型 对于每个方法(或操作),我们都可以在响应 (responses) 中添加任意的 HTTP状态码(比如 200 OK 或者 404 Not Found 等)。这个例子中我们添加上 200 的响应: <?php 2.2.4 定义响应内容 get /persons 这个接口返回一组用户信息,我们通过响应消息中的模式(schema)属性来描述清楚具体的返回内容。 一组用户信息就是一个用户信息对象的数组(array),每一个数组元素则是一个用户信息对象(object),该对象包含三个 string 类型的属性:姓氏、名字、用户名,其中用户名必须提供(required)。 <?php 2.3 定义 请求参数 (query parameteers) 用户太多,我们不想一股脑全部输出出来。这个时候,分页输出是个不错的选择,我们可以通过添加请求参数来实现。 <?php 2.3.1 在 get 方法中增加请求参数 <?php 2.3.2 添加分页参数 在参数列表中,我们添加两个名字(name)分别叫做 pageSize 和 pageNumber 的整型(integer)参数,并作简单描述: <?php 这样一来,消费者就可以通过 get /persons?pageSize=20&pageNumber=2 来访问第 2 页的用户信息(不超过 20 条)了。 2.4 定义 路径参数 (path parameter) 有时候我们想要根据用户名来查找用户信息,这时我们需要增加一个接口操作,比如可以添加一个类似 /persons/{username} 的操作来获取用户信息。注意,{username} 是在请求路径中的参数。 <?php 2.4.1 添加一个 get /persons/{username} 操作 首先我们在 /persons 路径后面,增加一个 /persons/{username} 的路径,并定义一个 get (操作)方法。 <?php 2.4.2 定义路径参数 username 因为 {username} 是路径参数,我们需要先像请求参数一样将它添加到 parameters 属性中,注意名称应该同上面大括号( { } ) 里面的名称一致。并通过 in 这个属性,来表示它是一个路径(path)参数。 <?php 定义路径参数时很容易出现的问题就是忘记:required: true,Swagger 的自动完成功能中没有包含这个属性定义。 如果没有写 require 属性,默认值是 false,也就是说 username 参数时可选的。可事实上,作为路径参数,它是必需的。 2.4.3 定义响应消息 别忘了获取单个用户信息也需要填写 200 响应消息,响应消息体的内容就是之前描述过的用户信息(用户信息列表中的一个元素): <?php 当然,API 的提供者会对 username 进行校验,如果查无此人,应该返回 404 的异常状态。所以我们再加上 404 状态的响应: <?php 2.5 定义 消息体参数 (body parameter) 当我们需要添加一个用户信息时,我们需要一个能够提供 post /persons 的 API 操作。 <?php 代码已被折叠,点此展开 2.5.1 添加一个 post /persons 操作 首页在 /persons 路径下添加一个 Post 操作: <?php 2.5.2 定义消息体参数 接下来我们给 post 方法添加参数,通过 in 属性显式说明参数是在 body 中的。参数的定义参考 get /persons/{username} 的 200 响应消息体参数,也就是包含用户的姓氏、名字、用户名。 <?php 2.5.3 定义响应消息 最后不要忘记定义 post 操作的响应消息。 <?php 第 3 章 文档瘦身 现在我们已经学会了编写 API 文档的基本方法。不过上面的例子中存在一些重复,这对于程序员的嗅觉来说,就是代码的 “坏味道”。这一章我们一起学习如何通过抽取可重用的定义(definitions)来简化 API 文档。 3.1 简化数据模型 我们认真观察第 2 章最后输出的 API 文档,很容易发现 Person 的定义出现了三次,非常的不 DRY☹。 现在,我们通过可重用的 定义 (definition)来重构这个文档: <?php 文档简化了很多。这得益于 OpenAPI 规范中关于定义(definition)的章节中允许我们 “一处定义,处处使用”。 3.1.1 添加 定义 (definitions) 项 我们首先在 API 文档的尾部添加一个 定义 (definitions)项(其实它也可以放在文档的任意位置,只不过大家习惯放在文档末尾): <?php 3.1.2 增加一个可重用的(对象)定义 然后我们增加一个 Person 对象的定义: <?php 3.1.3 引用一个 定义 来增加另一个 定义 在定义项中,我们可以立即引用刚才定义好的 Person 来增加另一个 定义,Persons。Persons 是一个 Person 对象的数组。与之前直接定义的不同之处是,我们增加了一个 引用(reference)属性,也就是 ref 来引用 Person 。 <?php 3.1.4 在响应消息中使用 定义 一旦定义好了 Person ,我们可以把原来在响应消息中相应的定义字段替换掉。 3.1.4.1 get/persons 原来: <?php 现在: <?php 3.1.4.2 get/persons/{username} 原来: <?php 现在: <?php 3.1.5 在参数中使用 定义 不仅仅在消息中可以使用 定义,在参数中也可以使用。 3.1.5.1 post /persons 原来: <?php 现在: <?php 3.2 简化响应消息 我们看到了 引用 ($ref)的作用,接下来我们再把它用到响应消息的定义中: 3.2.1 定义可重用的 HTTP 500 响应 发生 HTTP 500 错误时,假如我们希望每一个 API 操作都返回一个带有错误码(error code)和描述信息(message)的响应,我们可以这样做: <?php 3.2.2 增加一个 Error 定义 按照 “一处定义、处处引用” 的原则,我们可以在定义项中增加 Error 的定义: <?php 而且我们也学会了使用 引用($ref),所以我们可以这样写: <?php 3.2.3 定义一个可重用的响应消息 上面的文档中,还是有一些重复的内容。我们可以根据 OpenAPI 规范中的 responses 章节的描述,通过定义一个可重用的响应消息,来进一步简化文档。 <?php 注意:响应消息中引用了 Error 的定义。 3.2.4 使用已定义的响应消息 我们还是通过 引用($ref)来使用一个已经定义好的响应消息,比如: 3.2.4.1 get /users <?php 略去一部份不重要的。 第 4 章 深入了解一下 通过前面的练习,我们可以写出一篇结构清晰、内容精炼的 API 文档了。可是 OpenAPI 规范还给我们提供了更多的便利和惊喜,等着我们去了解和掌握。这一章主要介绍用于定义属性和数据模型的高级方法。 4.1 私人定制 使用 JSON Schema Draft 4,我们可以定义任意类型的各种属性,举例说明。 4.1.1 定符串 (strings) 长度和格式 当定义个字符串属性时,我们可以定制它的长度及格式: 属性 类型 描述 minLength number 字符串最小长度 maxLength number 字符串最大长度 pattern string 正则表达式 如果我们规定用户名是长度介于 8~64,而且只能由小写字母和数字来构成,那么我们可以这样写: <?php 4.1.2 日期和时间 日期和时间的处理参考 RFC 3339 ,我们唯一要做的就是写对格式: 格式 属性包含内容 属性示例 date ISO8601 full-date 2016-04-01 dateTime ISO8601 date-time 2016-04-16T16:06:05Z 如果我们在 Person 的定义中增加 生日 和 上次登录时间 时间戳,我们可以这样写: <?php 4.1.3 数字类型与范围 当我们定义一个数字类型的属性时,我们可以规定它是一个整型、长型、浮点型或者双浮点型。 名称 类型 格式 integer integer int32 long integer int64 float number float double number double 和字符串一样,我们也可以定义数字属性的范围,比如: 属性 类型 描述 minimum number 最小值 maximum number 最大值 exclusiveMinimum boolean 数值必须 > 最小值 exclusiveMaximum boolean 数值必须 < 最大值 multipleOf number 数值必须是 multipleOf 的整数倍 如果我们规定 pageSize 必须是整数,必须 > 0 且 <=100,还必须是 10 的整数倍,可以这样写: <?php 4.1.4 枚举类型 我们还可以定义枚举类型,比如定义 Error 时,我们可以这样写: <?php code 的值只能从三个枚举值中选择。 4.1.5 数值的大小和唯一性 数字的大小和唯一性通过下面这些属性来定义: 属性 类型 描述 minItems number 数值中的最小元素个数 maxItem number 数值中的最大元素个数 uniqueItems boolean 标示数组中的元素是否唯一 比如我们定义一个用户数组 Persons,希望返回的用户信息条数介于 10~100 之间,而且不能有重复的用户信息,我们可以这样写: <?php 4.1.6 二进制数据 可以用 string 类型来表示二进制数据: 格式 属性包含 byte Base64 编码字符 binary Base64 任意十进制的数据序列字符 比如我们需要在用户信息中增加一个头像属性(avatarBase64PNG)用 base64 编码的 PNG 图片来表示,可以这样写: <?php 4.2 高级数据定义 4.2.1 读写操作同一定义的数据 有时候我们读取资源信息的内容会比我们写入资源信息的内容(属性)更多,这很常见。是不是意味着我们必须专门为读取资源和写入资源分别定义不同的数据模型呢?幸运的是,OpenAPI 规范中提供了 readOnly 字段来帮我们解决整问题。比如: <?php 上面这个例子中,上次在线时间(lastTimeOnline )是 Person 的一个属性,我们获取用户信息时需要这个属性。但是很明显,在创建用户时,我们不能把这个属性 post 到服务器。于是我们可以把它标记为 readOnly。 4.2.2 组合定义确保一致性 一致性设计是在编写 API 文档时需要重点考虑的问题。比如我们在获取一组用户信息时,需要同时获取页面信息(totalItems, totalPage, pageSize, currentPage)等,而且这些信息 必须 在根节点上。 怎么办呢?首先想到的做法就是: <?php 如果其他 API 操作也需要这些 页面信息,那就意味着这些属性必须一遍又一遍的定义。不仅重复体力劳动,而且还很危险:比如忘记了其中的一两个属性,或者需要添加一个新的属性进来,那就是霰弹式的修改,想想都很悲壮。 稍微好一点的做法,就是根据前面学习的内容,把这几个属性抽取出来,建立一个 Paging 模型,“一处定义、处处使用”: <?php 但是,页面属性都不再位于 根节点!与我们前面设定的要求不一样了。怎么破? JSON Schema v4 property 中定义的 allOf,能帮我们解围: <?php 上面这个例子表示,PagedPersons 根节点下,具有将 Persons 和 Paging 展开 后的全部属性。 allOf 同样可以使用行内的数据定义,比如 <?php 4.2.3 数据模型的继承(TODO) 目前各工具支持程度不高,待续 第 5 章 输入输出模型 这一章主要介绍如何定义高度精确化的参数和响应消息等。 5.1 高级参数定义 5.1.1 必带参数和可选参数 我们已经知道使用关键字 required 来定义一个必带参数。 5.1.1.1 定义必带参数和可选参数 在一个参数中,required 是一个 boolean 型的可选值。它的默认值是 false 。 比如在某个操作中,username 是必填参数: <?php 5.1.1.2 定义必带属性和可选属性 根据定义,required 是一个字符串列表,列表中包含各必带参数名。如果某个参数在这张列表中找不到,那就说明它不是必带参数。如果没有定义 required ,就说明所有参数都是可选。如果 required 定义在一个 HTTP 请求上,这说明所有的请求参数都是必填。 在 POST 、persons 中有 Person 的定义,在这里 username 这个属性是必带的,我们可以指定它为 required ,其他非必带字段则不指定: <?php 5.1.2 带默认值的参数 通过关键字 default,我们可以定义一个参数的默认值。当这个参数不可得(请求未带或者服务器未返回)时,这个参数就取默认值。因此设定了某个参数的默认值后,它是否 required 就没意义了。 5.1.2.1 定义参数的默认值 我们定义参数 pageSize 的默认值为 20 ,那么如果请求时没有填写 pageSize ,服务器也会默认返回 20 个元素。 <?php 5.1.2.2 定义属性的默认值 同参数,使用关键字 default 即可。 5.1.3 带空值的参数 在 GET /persons 时,如果我们想添加一个参数来过滤 “是否通过实名认证” 的用户,应该怎么做呢?首先想到的是这样:GET /persons?page=2&includeVerifiedUsers=true ,问题是 includeVerifiedUsers 语义已经如此清晰,而让 “=true” 显得很多余。我们能不能直接用:GET /persons?page=2&includeVerifiedUsers 呢? 要做到这种写法,我们需要一个关键字 allowEmptyValue 。我们定义 includeVerifiedUsers 时允许它为空。那么如果我们请求 GET /persons?page=2&includeVerifiedUsers 则表示需要过滤 “实名认证” 用户,如果我们直接请求 GET /persons?page=2 则表示不过滤: <?php 5.1.4 参数组 设计 API 的时候,我们经常会遇到在 GET 请求中需要携带一组请求参数的情况。如何在 API 文档章呈现呢?很简单,我们只需要设定 参数类型(type) 为 array,并选择合适的 组合格式(collectionFormat) 就行了。 组合格式 描述 csv (default value) Comma separated values(逗号分隔) foo,bar ssv Space separated values(空格分隔) foo bar tsv Tab separated values(反斜杠分隔) foo\bar pipes Pipes separated values(竖线分隔) foo|bar multi 单属性可以取多个值,比如 foo=bar&foo=baz. 只适用于查询参数和表单参数。 比如我们想根据多种参数(username , firstname , lastname , lastTimeOnline )等来对 Person 进行带排序的查询。我们需要一个这样的 API 请求: GET /persons?sort=-lastTimeOnline|+firtname|+lastname 。用于排序的参数是 sort ,+ 表示升序,- 表示降序。 相应的 API 文档,可以这样写: <?php 现在我们就能搞定 GET /persons?sort=-lastTimeOnline|+firtname|+lastname 这种请求了。当然,我们还可以指定排序的默认值,锦上添花。 <?php 5.1.5 消息头 (Header) 参数 参数,按照位置来分,不仅仅包含路径参数、请求参数和消息体参数,还包括消息头参数和表单参数等。比如我们可以在 HTTP 请求的消息头上加一个 User-Agent (用于跟踪、调试或者其他),可以这样定义它: <?php 然后像使用其他参数一样使用它: <?php 5.1.6 表单参数 有些 js-less-browser 的老浏览器不支持 POST JSON 数据,比如在创建用户时,只能够以这样个格式请求: POST /js-less-persons username=apihandyman&firstname=API&lastname=Handyman 没有问题,丝袜哥可以搞定。我们只需要把各个属性的 in 关键字定义为 formData ,然后设置 consumes 的媒体类型为 application/x-www-form-urlencoded 即可。 <?php 5.1.7 文件参数 当我们要处理一个请求,输入类型是 文件 时,我们需要: 使用 multipart/form-data 媒体类型; 设置参数的 in 关键字为 formData; 设置参数的 类型(type) 为 file。 比如: <?php 有时候我们想限定输入文件的类型(后缀),很不幸的是,根据现在 V2.0 的规范暂时还做不到☹ 5.1.8 参数的媒体类型 一个 API 可以消费各种不同的媒体类型,比如说最常见的是 application/json 类型的数据,当然这不是 API 唯一支持的类型。我们可以在 文档的根节点 或者 一个操作的根节点 下添加关键字 consumes,来定义这个操作能够消费的媒体类型。 比如我们的 API 全部都接受 JSON 和 YAML 的数据,那我们可以在文档的根节点下添加: <?php 如果某个操作(比如上传图片的操作)很特殊,它可以通过自己添加 consumes 来覆盖全局设置: <?php 5.2 高级响应消息定义 5.2.1 不带消息体的响应消息 不带消息体的响应很常见,比如 HTTP 204 状态响应本身就表示服务器返回不带任何消息内容的成功消息。 要定义一个不带消息体的响应很简单,我们只需要写响应状态和描述就行了: <?php 5.2.2 响应消息中的必带参数和可选参数 与请求消息中类似,我们使用 required 参数来表示,比如请求一个用户信息时, 服务器必须返回 username。 5.2.3 响应消息头 API 的返回结果不仅仅体现下 HTTP 状态和响应消息体,还可以在响应消息头上做文章。比如我们可以限定一个 API 的使用次数和使用时间段,在响应消息头中,增加一个属性 X-Rate-Limit-Remaining 来表示 API 可调用的剩余次数,增加另一个属性 X-Rate-Limit-Reset 来表示 API 的有效截止时间。 <?php 5.2.4 默认响应消息 我们在定义响应消息时,通常会列举不同的 HTTP 状态结果。如果有些状态不在我们 API 文档的定义范围(比如服务器需要返回 993 的状态),该怎么处理呢?这时需要通过关键字 default 来定义一个默认响应消息,用于各种 定义之外 的状态响应,比如: <?php 目前这个配置也不支持 “一次定义,处处使用” 。☹ 5.2.5 响应消息的媒体类型 与请求消息一样,我们也可以定义响应消息所支持的媒体类型,不同的是我们要用到关键字 produces(与请求消息中的 consumes 相对,由此可见,API 文档描述的主体是服务提供者)。 比如,我们可以在文档的根路径下全局设置: <?php 也可以在某个操作的根路径下覆盖设置。 5.3 定义某个参数只存在于响应消息中 如前章节 4.2.1 中已经提到的,定义一个对象,其中某个属性我们只希望在响应消息中携带,而不希望在请求消息中携带,应该用 readOnly 关键字来表示 第 6 章 不要让 API 裸奔 略 第 7 章 让文档的可读性更好 7.1 分类标签 (Tags) 通过关键字 tags 我们可以对文档中接口进行归类,tags 的本质是一个字符串列表。tags 定义在文档的根路径下。 7.1.1 单标签 比如说 GET /persons 属于用户(Person) 这个分类的,那么我们可以给它贴个标签: <?php 7.1.2 多标签 一个操作也可以同时贴几个标签,比如: <?php 贴上标签后,在 Swagger Editor 和 Swagger UI 中能够自动归类,我们可以按照标签来筛选接口,试试吧? 7.2 无处不在的描述文字(Descriptions) description 这个属性几乎是无处不在,为了提高文档的可读性,我们应该在必要的地方都加上描述文字。 7.2.1 安全项的描述 7.2.2 模式 (Schema) 的描述 每一种模式(Schema),都会有一个标题(title)和一段描述,比如: <?php 7.2.3 属性的描述 比如: <?php 7.2.4 参数的描述 比如: 7.2.5 操作的概述 (summary)、描述和操作 ID (operationId) 一个操作(Operation)通常都会包含概述和描述信息。而且我们还可以添加一个关键字 operationId,这个关键字通常用来指示服务提供者对这个操作的 处理函数 的函数名。比如: <?php 7.2.6 响应消息的描述 <?php 7.2.7 响应消息头的描述 7.2.8 标签的描述 我们在 API 文档的根路径下添加了 tags 的定义,对于其中的每一个标签,我们都可以添加描述信息: <?php 7.3 在描述中使用 Markdown 语法 在绝大部份的 description 中,我们可以使用 GFM (Github Flavored Markdown)语法。 7.3.1 多行描述 使用符号 | 然后在新行中打一个 tab(注意:YAML 的 tab 是两个空格 ),就可以编辑多行描述,比如: <?php 7.3.2 简单使用 GFM 比如我们要强调,可以这样写: 略 7.3.3 带信息组的描述 略 7.3.4 带代码的描述 略 7.4 示例数据 (Examples) 我们已经知道了用 Schema 来描述参数和属性,有的时候,用示例数据更有表现了。我们可以使用关键字 example 来给原子属性或者对象添加示例数据。 7.4.1 原子属性的示例数据 <?php 7.4.2 对象属性的示例数据 待补充 7.4.3 定义的示例数据 待补充 7.4.4 响应消息的示例数据 待补充 7.4.5 示例数据的优先级 如果我们在各个级别(比如参数、对象、定义、响应消息)都添加了示例数据。支持 OpenAPI 规范的各解析工具 都是 以 最高级别 的定义为准。 7.5 标记为弃用 我们可以通过关键字 deprecated 置为 true 来标记接口的 弃用 状态,比如: <?php 7.6 链接到外部文档 一般来说,项目中不光只有一篇 API 文档,还应该有些描述 application key,测试用例,操作链以及其他内容的文档,这些文档一般是单独成篇的。如果在描述某个接口时,我们想链接这些文档,可以通过关键字 externalDoc 来添加,例如: <?php 第 8 章 分而治之 根据前面几张的知识,我们已经可以轻松的构建一个复杂的 API 文档了。可是作为一个学过 Clean Code 的程序员,我们并不希望所有的接口、定义都在一个大而全的上帝文件里。这一章我们一起来学习拆分文件。 ———————————————— 原文作者:maxincai 转自链接:https://learnku.com/laravel/t/7430/how-to-write-api-documents-based-on-swagger-php#747b67 版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请保留以上作者信息和原文链接。