需求:实习中,我们新版本探针的下发使用的是直接调用scan-apiserver接口的方式,现在需要新增加一个服务scan-scheduler,来对下载任务进行管控,需要使用swagger。
什么是Restful风格
概念
Restful风格指的是网络应用中就是资源定位和资源操作的风格。不是标准也不是协议。 Rest即Representational State Transfer的缩写,可译为"表现层状态转化”。Restful风格最大的特点为:资源、统一接口、URI和无状态。 这种风格设计的软件,可以更简洁,更有层次,更易于实现缓存等机制。
基于HTTP,可以使用 XML 格式定义或 JSON 格式定义。
特点
资源:互联网所有的事务都可以被抽象为资源,例如:.txt .html .jpg .mp3 .mp4等 RESTful 架构风格是围绕资源展开的,资源操作都是统一接口的: GET(SELECT):从服务器取出资源(一项或多项)。 POST(CREATE):在服务器新建一个资源。 PUT(UPDATE):在服务器更新资源(客户端提供完整资源数据)。 PATCH(UPDATE):在服务器更新资源(客户端提供需要修改的资源数据)。 DELETE(DELETE):从服务器删除资源。 URI:每一个URI(统一资源定位符)指向一个特定的资源。通过URI来访问资源。最典型的URI就是URL。@RequestMapping的path/value属性表示的就是URL的一部分。 无状态:所有的资源,都可以通过URI定位,而且这个定位与其他资源无关。例如无需登录就可以通过URL查看,就是无状态。需要登录才能查看,是有状态。
操作资源方式
传统
http://127.0.0.1/item/queryUser.action?id=1 查询,GET http://127.0.0.1/item/saveUser.action 新增,POST http://127.0.0.1/item/updateUser.action 更新,POST http://127.0.0.1/item/deleteUser.action?id=1 删除,GET或POST
Restful
【GET】 /users # 查询用户信息列表
【GET】 /users/1001 # 查看某个用户信息
【POST】 /users # 新建用户信息
【PUT】 /users/1001 # 更新用户信息(全部字段)
【PATCH】 /users/1001 # 更新用户信息(部分字段)
【DELETE】 /users/1001 # 删除用户信息
之前的操作每次请求的接口或者地址,都在做描述,例如查询的时候用了queryUser,新增的时候用了saveUser ,修改的时候用了updateUser,其实完全没有这个必要, 使用了get请求,就是查询.使用post请求,就是新增的请求,PUT就是修改,delete就是删除,
意图很明显,完全没有必要做描述,这就是为什么有了restful.
go swagger
restful 是这些年的高频词汇了,各大互联网公司也都纷纷推出了自己的 restful api,其实 restful 和 thrift,grpc 类似,就是一种协议,但是这种协议有点特殊的就是使用 http 接口,返回的对象一般是 json 格式,这样有个好处,就是可以供前端的 js 直接调用,使用非常方便,但 http 本身并不是一个高效的协议,后端的内部通信还是使用 grpc 或者 thrift 可以获得更高的性能
其实如果只是要用 http 返回 json 本身并不是一件很难的事情,不用任何框架,golang 本身也能很方便做到,但是当你有很多 api 的时候,这些 api 的维护和管理就会变得很复杂,你自己都无法记住这些 api 应该填什么参数,返回什么,当然你可以花很多时间去维护一份接口文档,这样不仅耗时而且很难保证文档的即时性,准确性以及一致性
swagger 有一整套规范来定义一个接口文件,类似于 thrift 和 proto 文件,定义了服务的请求内容和返回内容,同样也有工具可以生成各种不同语言的框架代码,在 golang 里面我们使用 go-swagger 这个工具,这个工具还提供了额外的功能,可以可视化显示这个接口,方便阅读
go-swagger使用方法
api定义文件
首先需要写一个 api 定义文件
swagger: '2.0'
info:
description: 不想依赖第三方的统计和评论,自己开发一个点赞评论系统
version: 1.0.0
title: comment_like
contact:
email: hatlonely@foxmail.com
license:
name: Apache 2.0
url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
host: hatlonely.com
basePath: /api
tags:
- name: view
description: 浏览
- name: like
description: 点赞
- name: comment
description: 评论
schemes:
- http
paths:
/doview:
get:
tags:
- view
summary: 浏览
description: ''
operationId: doView
consumes:
- application/json
produces:
- application/json
parameters:
- name: title
in: query
description: 文章标题
required: true
type: string
responses:
'200':
description: 成功
schema:
$ref: '#/definitions/ErrorModel'
'500':
description: 内部错误
schema:
$ref: '#/definitions/ErrorModel'
/countview:
get:
tags:
- view
summary: 浏览
description: ''
operationId: countView
consumes:
- application/json
produces:
- application/json
parameters:
- name: title
in: query
description: 文章标题
required: true
type: string
responses:
'200':
description: 成功
schema:
$ref: '#/definitions/CountViewModel'
'500':
description: 内部错误
schema:
$ref: '#/definitions/ErrorModel'
/dolike:
get:
tags:
- like
summary: 点赞
description: ''
operationId: doLike
consumes:
- application/json
produces:
- application/json
parameters:
- name: title
in: query
description: 文章标题
required: true
type: string
responses:
'200':
description: 成功
schema:
$ref: '#/definitions/ErrorModel'
'500':
description: 内部错误
schema:
$ref: '#/definitions/ErrorModel'
/dounlike:
get:
tags:
- like
summary: 取消赞
description: ''
operationId: doUnlike
consumes:
- application/json
produces:
- application/json
parameters:
- name: title
in: query
description: 文章标题
required: true
type: string
responses:
'200':
description: 成功
schema:
$ref: '#/definitions/ErrorModel'
'500':
description: 内部错误
schema:
$ref: '#/definitions/ErrorModel'
/showlike:
get:
tags:
- like
summary: 点赞否
description: ''
operationId: showLike
consumes:
- application/json
produces:
- application/json
parameters:
- name: title
in: query
description: 文章标题
required: true
type: string
responses:
'200':
description: 成功
schema:
$ref: '#/definitions/ShowLikeModel'
'500':
description: 内部错误
schema:
$ref: '#/definitions/ErrorModel'
/countlike:
get:
tags:
- like
summary: 有多少赞
description: ''
operationId: countLike
consumes:
- application/json
produces:
- application/json
parameters:
- name: title
in: query
description: 文章标题
required: true
type: string
responses:
'200':
description: 成功
schema:
$ref: '#/definitions/CountLikeModel'
'500':
description: 内部错误
schema:
$ref: '#/definitions/ErrorModel'
/docomment:
get:
tags:
- comment
summary: 评论
description: ''
operationId: doComment
consumes:
- application/json
produces:
- application/json
parameters:
- name: title
in: query
description: 文章标题
required: true
type: string
- name: content
in: query
description: 评论内容
required: true
type: string
- name: nickname
in: query
description: 用户昵称
required: false
type: string
- name: mail
in: query
description: 用户邮箱
required: false
type: string
responses:
'200':
description: 成功
schema:
$ref: '#/definitions/ErrorModel'
'500':
description: 内部错误
schema:
$ref: '#/definitions/ErrorModel'
/showcomment:
get:
tags:
- comment
summary: 显示评论
description: ''
operationId: showComment
consumes:
- application/json
produces:
- application/json
parameters:
- name: title
in: query
description: 文章标题
required: true
type: string
responses:
'200':
description: 成功
schema:
$ref: '#/definitions/ShowCommentModel'
'500':
description: 内部错误
schema:
$ref: '#/definitions/ErrorModel'
definitions:
CountViewModel:
type: object
properties:
count:
type: integer
title:
type: string
example: golang json 性能分析
ShowLikeModel:
type: object
properties:
ip:
type: string
example: 127.0.0.1
ua:
type: string
example: >-
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
title:
type: string
example: golang json 性能分析
islike:
type: boolean
CountLikeModel:
type: object
properties:
count:
type: integer
title:
type: string
example: golang json 性能分析
ShowCommentModel:
type: object
properties:
comments:
type: array
items:
$ref: '#/definitions/CommentModel'
CommentModel:
type: object
properties:
content:
type: string
example: 写得很好
nickname:
type: string
example: sonic
mail:
type: string
example: sonic@foxmail.com
ErrorModel:
type: object
properties:
message:
type: string
example: error message
code:
type: integer
example: 400
这个是 yaml 语法,有点像去掉了括号的 json
这里完整地定义了请求方法、请求参数、正常返回接口、异常返回结果,有了这个文件只需要执行下面命令就能生成框架代码了
swagger generate server -f api/comment_like/comment_like.yaml
还可以下面这个命令可视化查看这个接口文件
swagger serve api/comment_like/comment_like.yaml
这个命令依赖 swagger 工具,可以通过下面命令获取
go get -u github.com/go-swagger/go-swagger/cmd/swagger
export PATH=$GOPATH/bin:$PATH
执行完了之后,你发现多了几个文件夹,其中 cmd
目录里面包含 main 函数,是整个程序的入口,restapi
文件夹下面包含协议相关代码,其中 configure_xxx.go
是需要特别关注的,你需要在这个文件里面实现你具体的业务逻辑
现在你就其实已经可以运行程序了,go run cmd/comment-like-server/main.go
,在浏览器里面访问一下你的 api,会返回一个错误信息,告诉你 api 还没有实现,下面就来实现一下吧
api.LikeCountLikeHandler = like.CountLikeHandlerFunc(func(params like.CountLikeParams) middleware.Responder {
count, err := comment_like.CountLike(params.Title)
if err != nil {
return like.NewCountLikeInternalServerError().WithPayload(&models.ErrorModel{
Code: http.StatusInternalServerError,
Message: err.Error(),
})
}
return like.NewCountLikeOK().WithPayload(&models.CountLikeModel{
Count: count,
Title: params.Title,
})
})
你只需要在这些 handler 里面实现自己的业务逻辑即可,这里对协议的封装非常好,除了业务逻辑以及打包返回,没有多余的逻辑
再次运行,现在返回已经正常了
统一处理
如果你对请求有一些操作需要统一处理,比如输出统一的日志之类的,可以重写这个函数,也在 configure_xxx.go
这个文件中
func setupGlobalMiddleware(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
handler.ServeHTTP(w, r)
})
}