使用 Swagger 为你的 HTTP API 写文档
尝试过用 Wiki 和 Swagger 等工具写基于 HTTP 协议的 API 的 文档,虽然有提供 curl 示例,但接口调用者使用起来还是觉得不方便,毕竟不是所有人都习惯命令行.
直到了解到 Swagger, 简直发现了写 HTTP API 文档的神器啊. 现已捐赠给 Open API Initiative (OAI) , 和 OpenAPI 2.0 Specification 合并了.
Swagger 简介
详情介绍可以直接看官网.
按个人的理解. Swagger 提供一种简单的方式为 HTTP API 写文档,同时又方便 API 调用者测试.
Swagger 本身是由标准与工具组成的.
Swagger 标准
现已和 OpenAPI 2.0 Specification 是同一个标准了.
这个标准,有点像是 XML 和 XML Schema 的关系.
XML 是非结构化数据,根本就不清楚 A 接点下面包含的是 B 还是 C 接口. 而 XML Schema 就是用来告诉我们, A 接点下面包含什么接点,同时还支持数据验证等功能.
我们写出来的 HTTP API 接口,如果没有文档,调用者根本就知道要传什么参数,返回什么数据. 而 OpenAPI Specification 就是这样一种标准,告诉我们应该怎样描述我们的接口,描述接口要传什么参数,返回什么数据.
OpenAPI Specification 最终是 JSON 或 YAML 数据格式表示, Specification 本身是告诉我们应该生成怎样的 JSON 或 YAML 数据.
主要描述请求的主机是什么,路径是什么,请求是 GET 还是 POST 等; 传参是 QUERY STRING 还是 BODY 等, 需要传什么头,返回什么头. 返回的数据是什么格式.
Swagger 工具
- Swagger UI: 把 Swagger 标准的 JSON 数据,显示成友好可操作的 HTML 文档,方便调用者查看与调试接口.
- Swagger Editor: 一个在线 YAML 编辑器,方便编写 Swagger 标准的接口描述数据,并能生成JSON格式的数据,同时能生成本地客户端,方便文档分发.
- Sdk Generators: 根据 Swagger 标准的数据生成接口代码.
在 Rails 里使用 Swagger
上面三个工具,只用到 Swagger UI, 用它把写的接口描述JSON数据显示成友好的 HTML 界面.
ruby 中,如果用 grape 写 HTTP API,那配合 grape-swagger ,可以同步生成好文档,非常方便.
但个人习惯 Rails 了,觉得用 grape 要自己管理数据迁移脚本之类的,太麻烦了.
Ruby 里面的 Swagger 库我选 Swagger::Blocks ,纯 Ruby 实现,代码只有一个文件,700多行,简单. 其本身只是一个生成 Swagger 标准的 JSON 数据的 DSL . 调用 Swagger::Blocks.build_root_json
方法,最终生成的只是 json 字符串而已.与 Web 框架无关,只要把此 json 数据做为 response 数据返回即可.
下面创建一个 Rails 项目,对 Swagger 主要点进行演示,算是个人踩过坑后的一点心得.
1. 创建 Rails 演示项目
参考 Getting Started with Rails 创建一个 Rails 项目,并带有简单的 CURD 接口.
gem install rails
rails new -B swagger_demo
cd swagger_demo
vi Gemfile # 编辑 Gemfile 文件,把第一行的 https://rubygems.org 替换成 https://gems.ruby-china.org
bundle install
bundle exec rails g scaffold Article title:string text:text # 生成 Article 的 model 和 controller
bundle exec rake db:create db:migrate # 创建数据库,运行迁移
代码: https://github.com/mangege/swagger_demo/tree/step1
2. 建立 swagger 初始文件
vi Gemfile # 添加 gem 'swagger-blocks' 到最后一行
bundle install
bundle exec rails g controller Apidocs index
vi app/controllers/apidocs_controller.rb # 复制此段内容 https://github.com/fotinakis/swagger-blocks#docs-controller ,还需要再编辑此文件内容,最终请看仓库代码
vi config/routes.rb # 删除掉 get 'apidocs/index' ,添加 resources :apidocs, only: [:index]
cd /tmp; git clone https://github.com/swagger-api/swagger-ui.git
cp -R /tmp/swagger-ui/dist ~/workspace/swagger_demo/public/ # 复制 swagger 的静态文件到 rails 项目的 public 目录下.
cd ~/workspace/swagger_demo/public/; mv dist swagger-ui # 重命名 dist 文件夹为 swagger-ui
vi public/swagger-ui/index.html # 替换 http://petstore.swagger.io/v2/swagger.json 为 /apidocs.json
打开浏览器,访问 http://localhost:3000/swagger-ui/ 即可.
代码: https://github.com/mangege/swagger_demo/tree/step2
3. 为 Article 接口添加文档
按照 Swagger::Blocks 的示例,一般是在 model 或 controller 文件里写文档.但这样有可能导致 model 或 controller 文件行数过长.
通过分析源码了解,我们随便建一个类也可以.所以我们在 app 目录下建立专门的 swagger 目录.
mkdir app/swagger
vi config/application.rb # 添加 config.autoload_paths << Rails.root.join('app/swagger') ,改了此文件记得重启 rails server
vi app/swagger/app/swagger/article_swagger.rb
添加以下内容
class ArticleSwagger
include Swagger::Blocks
swagger_schema :Article do
key :required, [:id]
property :id do
key :type, :integer
end
property :title do
key :type, :string
key :description, '标题'
end
property :text do
key :type, :string
key :description, '正文'
end
end
end
vi app/controllers/apidocs_controller.rb # 在 SWAGGERED_CLASSES 添加 ArticleSwagger
vi app/swagger/articles_controller_swagger.rb
添加以下内容
class ArticlesControllerSwagger
include Swagger::Blocks
swagger_path '/articles' do
operation :get do
key :description, 'article list'
key :operationId, 'articleIndex'
key :tags, [
'article'
]
response 200 do
key :description, 'article response'
schema do
key :type, :array
items do
key :'$ref', :Article
end
end
end
response :default do
key :description, 'unexpected error'
schema do
key :'$ref', :ErrorModel
end
end
end
operation :post do
key :description, 'create article'
key :operationId, 'articleCreate'
key :tags, [
'article'
]
parameter do
key :name, :article
key :in, :body
key :required, true
schema do
key :'$ref', :Article
end
end
response 200 do
key :description, 'article response'
schema do
key :'$ref', :Article
end
end
response :default do
key :description, 'unexpected error'
schema do
key :'$ref', :ErrorModel
end
end
end
end
swagger_path '/articles/{id}' do
operation :get do
key :description, 'article show'
key :operationId, 'articleShow'
key :tags, [
'article'
]
parameter do
key :name, :id
key :in, :path
key :required, true
key :type, :integer
end
response 200 do
key :description, 'article response'
schema do
key :'$ref', :Article
end
end
response :default do
key :description, 'unexpected error'
schema do
key :'$ref', :ErrorModel
end
end
end
operation :patch do
key :description, 'article update'
key :operationId, 'articleUpdate'
key :tags, [
'article'
]
parameter do
key :name, :id
key :in, :path
key :required, true
key :type, :integer
end
parameter do
key :name, :article
key :in, :body
key :required, true
schema do
key :'$ref', :Article
end
end
response 200 do
key :description, 'article response'
schema do
key :'$ref', :Article
end
end
response :default do
key :description, 'unexpected error'
schema do
key :'$ref', :ErrorModel
end
end
end
operation :delete do
key :description, 'article destroy'
key :operationId, 'articleDestroy'
key :tags, [
'article'
]
parameter do
key :name, :id
key :in, :path
key :required, true
key :type, :integer
end
response 204 do
schema do
key :type, :string
end
end
response :default do
key :description, 'unexpected error'
schema do
key :'$ref', :ErrorModel
end
end
end
end
end
vi app/swagger/error_model_swagger.rb
添加以下内容
module ErrorModelSwagger
include Swagger::Blocks
swagger_schema :ErrorModel do
key :description, '错误定义'
key :required, [:code, :message]
property :code do
key :type, :integer
key :description, '错误代码. 401 没有登录, 403 没有权限, 422 表单数据有误'
end
property :message do
key :type, :string
key :description, '错误消息'
end
property :errors do
key :type, :object
key :description, '错误详情. 键为出错的属性名.值为出错信息,值是字符串数组.'
end
end
end
vi app/controllers/apidocs_controller.rb # 在 SWAGGERED_CLASSES 添加 ErrorModelSwagger 和 ArticlesControllerSwagger
vi app/controllers/application_controller.rb # 替换 exception 为 null_session ,注意,如果项目还有普通的web页面,不要把此改成 null_session ,而是新建一个 api_controller.rb 文件,在新建的文件里设置为 null_session ,然后所有的 api controller 都继承与它.
好了, Article 的 CRUD 操作的接口文档都已经编写完成,现在我们打开浏览器,访问 http://localhost:3000/swagger-ui/ ,即可通过 swagger ui 来阅读文档,并测试接口了.