什么是RESTful?
REST是Roy Thomas Fielding(HTTP协议(1.0版和1.1版)的主要设计者、Apache服务器软件的作者之一、Apache基金会的第一任主席)在2000年的博士论文(地址:http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm)中提出的。REST是Representational State Transfer的缩写。通常翻译为“表现层状态转化”,实际上用“”表达更为准确。如果一个架构符合REST原则,就称它为RESTful架构。
API如何设计才能满足RESTful的要求?
协议。API是基于Http协议。
版本。API要有版本信息。当客户端数量较多或者提供给第三方使用时,很难控制客户端的兼容性,一个比较好的做法就是当已发布的服务变更时,通过版本来控制兼容性。当然,版本不能演进太快,最好的版本化就是无须版本。例如http://api.xxx.com/v1/;
路径。API要用资源(resource)来表示,URL中不能出现动词。要理解REST首先要理解什么是资源(Resources),REST开发又被称作“面向资源的开发”。资源是服务端可命名的一个抽象的概念,只要客户端容易理解,可以随意抽象。通常可以把资源看成是一个实体,例如用户、邮件、图片等等,用URI(统一资源定位符)指向它,经验告诉我们,往往这里的资源和数据库的表名是对应关系。一种观点认为DDD可以和REST API很好的契合起来,因为REST的资源可以很好地与DDD的实体映射起来。定义资源的时候,这里推荐用复数,假设我们要获取用户的信息,大概是这样http://api.xxx.com/v1/users/;
方法。一般允许的方法主要包括:
GET:读取资源,一个或多个(常用)
POST:创建资源(常用)
PUT:修改资源,客户端提供修改后的完整资源(常用)
PATCH:对已知资源进行局部更新,客户端只需要提供改变的属性
DELETE:删除、回收资源(常用)
HEAD:读取资源的元数据(不常用)
OPTIONS:读取对资源的访问权限(不常用)
一般情况下GET、POST、PUT、DELETE已经足以。甚至有一种观点认为,只需要使用GET、POST。
例如:GET /users/1,获取用户ID为1的用户信息。
GET /users/1/orders,获取用户ID为1的用户拥有的所有订单。
参数。参数可以放到API路径中,也可以放到?后面。
例如GET /users/1/orders
GET /orders?user_id=1
编码。虽然RESTful并没有限制资源的表达格式,HTML/Xml/Json/纯文本/图片/视频/音频等等都可以。但是通常,服务端和客户端通过Json传递信息。
状态码。用HTTP Status Code传递Server的状态信息。
常用的状态码如下:
100 Continue
200 OK。GET,获取信息成功
201 Created。POST/PUT/PATCH,新建或修改数据成功
202 Accepted
204 NO Content。DELETE,删除数据成功
400 Bad Request。POST/PUT/PATCH,无效的请求
401 Unauthorized。没有权限
403 Forbidden。访问禁止
404 Not Found。没有发现资源
405 Method Not Allowed
406 Not Acceptable
409 Conflict
410 Gone
412 Precondition Failed
429 Too many requests
500 Internal Server Error。服务端错误
501 Not Implemented
503 Service Unavailable
完整信息可以参考:https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
RESTful中比较重要的几个约束:
单一职责。尽量保持接口职责单一,应留给客户端足够的操作空间,以满足不同的业务需求。对于接口粒度的大小,需要考虑的包括:性能(合并请求性能更高)、一致性、灵活性以及客户端使用时的易用程度。
幂等性。一次和多次请求某一个资源应该具有同样的作用,客户端能够重复发起请求而不必担心造成副作用。
无状态。多次请求之间不应该存在状态耦合,无须关联过去、现在和将来的请求或者响应。
客户端发起。一般通信方式都是由客户端发起,服务端是被调用。
原子性。保证所有操作是一个不可分割的单元,要么全部成功,要么全部失败。
易用。需要提供详尽的文档,参数说明,示例等,API定义的url、变量名需要通俗易懂,最好是英文,尽量减少自定义的缩写,让开发者容易调试和管理。
SLA。需要提供响应时间、吞吐量、可用性等关键指标。
RESTful的优点是:
更加轻量级,简单。
容易调试。http请求可以直接基于浏览器调试。
跨语言。
通过Swagger实现RESTful
传统的API设计方式都是通过写代码的方式,然后另外写一份说明文档。这种方式效率比较低,文档和代码缺乏关联性,比较麻烦。后来通过javadoc注解的方式。把文档和注释关联起来了,提升了效率,但是由于javadoc需要不断的生成,文档难免和代码存在不一致。Swagger的诞生正是由于上述问题的存在。Swagger是一个简单、功能非常强大、非常流行的API表达工具。基于Swagger生成API,我们可以得到交互式文档,自动生成代码的SDK以及API的发现方式等。
Swagger是基于OpenAPI的,OpenAPI支持YAML和Json格式描述API。YAML相对Json来说更加简洁。YAML是“YAML Ain't a Markup Language”(YAML不是一种置标语言)的递归缩写。YAML比较适合做简洁的序列化和配置文件。OpenAPI规范是Linux基金会的一个项目,试图通过定义一种用来描述API格式或API定义的语言,来规范RESTful服务开发过程。目前最新的版本是3.0,目前swagger支持到2.0,规范地址:https://swagger.io/specification。由于OpenAPI规范比较复杂,Arnaud Lauret为了简化OpenAPI规范的阅读,维护了一个openapi-specification-visual-documentation的项目,通过动态导图的方式把规范完整呈现。地址:http://openapi-specification-visual-documentation.apihandyman.io/
编写YAML文档推荐使用SwaggerEditor,它提供了语法高亮、自动完成、即时预览等功能。使用编辑器可以在本地,也可以在线。
YAML的数据结构可以用类似大纲的缩排方式呈现,结构通过缩进来表示,连续的项目通过减号“-”来表示,map结构里面的key/value对用冒号“:”来分隔。
下面我们来看如何基于swagger构建API?
基于swagger editor设计API。可以直接在线编辑API,也可以在本地安装,本文以在线编辑器为例。
访问官方editor编辑器http://editor2.swagger.io
编辑yaml文件,如下所示。
swagger:"2.0"
info:
version:1.0.0
title:Product API
description:Product API for test
schemes:
- https
host:localhost
basePath:/product
paths:{}
下面我简单说明一下这个文档。
swagger:"2.0"
表示OpenAPI的版本是2.0
info:
version:1.0.0
title:Product API
description:Product API fortest
表示API的描述信息,包括如API文档版本(version)、API文档名称(title)和描述信息(description)。
schemes:
- https
host:localhost
basePath:/product
表示API的URL,采用https协议,主机名(host),根路径(basePath)。
下面我们来看一个稍复杂一点的。
swagger:'2.0'
info:
version:'1.0'
title:Swagger构建RESTful API
host:'localhost:8080'
basePath:/
tags:
-name:product-controller
description:Product Controller
paths:
/products:
get:
tags:
- product-controller
summary:获取产品列表
description:获取产品列表
operationId:getProductListUsingGET
consumes:
- application/json
produces:
-'*/*'
responses:
'200':
description:OK
schema:
type:array
items:
$ref:'#/definitions/Product'
'401':
description:Unauthorized
'403':
description:Forbidden
'404':
description:Not Found
/products/:
get:
tags:
-product-controller
summary:获取产品详细信息
description:根据url的id来获取产品详细信息
operationId:getProductUsingGET
consumes:
- application/json
produces:
-'*/*'
parameters:
-name:id
in:path
description:产品ID
required:true
type:integer
responses:
'200':
description:OK
schema:
$ref:'#/definitions/Product'
'401':
description:Unauthorized
'403':
description:Forbidden
'404':
description:Not Found
definitions:
Product:
type:object
properties:
count:
type:integer
format:int32
desc:
type:string
id:
type:integer
format:int32
name:
type:string
在上面的示例中我们定义了两个API,一个是获取Product的列表,一个是根据id获取Product的详情。
当我们编辑完成的时候,我们可以得到如下所示的文档。
然后点击file-download yaml,就可以下载yaml文件。
然后点击generate server下载服务端,generate client下载客户端,可以分别生成相应语言的SDK工程。
通过SpringBoot、Springfox、Swagger实现RESTful
上面我们介绍了通过Swagger Editor实现API设计,先写yaml文件,然后生成服务端和客户端。下面我们来介绍另外一种方法,直接写代码,通过注解自动生成相关文档。
首先,SpringBoot已经家喻户晓了,Swagger我们上面已经介绍了,Springfox是什么?Marty Pitt曾经编写了一个基于Spring的组件swagger-springmvc,用于将Swagger集成到springmvc中,Springfox则是从这个组件发展而来。下面我们通过一个简单的实例来说明一下。
实例基于maven构建,在pom中引入相应的jar包。引入SpringBoot和Springfox的相关包。
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-web
io.springfox
springfox-swagger2
2.7.0
io.springfox
springfox-swagger-ui
2.7.0
编写Swagger配置类。
@Configuration
@EnableSwagger2
public classSwagger2 {
@Bean
publicDocket createRestApi() {
return newDocket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.cloudnative.rest"))
.paths(PathSelectors.any())
.build();
}
privateApiInfo apiInfo() {
return newApiInfoBuilder()
.title("Swagger构建RESTful API")
.description("")
.termsOfServiceUrl("")
.version("1.0")
.build();
}
}
实现dto类。
public classProduct {
private intid;
privateStringname;
private intcount;
privateStringdesc;
publicString getName() {
returnname;
}
public voidsetName(String name) {
this.name= name;
}
public intgetCount() {
returncount;
}
public voidsetCount(intcount) {
this.count= count;
}
publicString getDesc() {
returndesc;
}
public voidsetDesc(String desc) {
this.desc= desc;
}
public intgetId() {
returnid;
}
public voidsetId(intid) {
this.id= id;
}
@Override
publicString toString() {
return"Product{"+
"id="+id+
", name='"+name+'\''+
", count="+count+
", desc='"+desc+'\''+
'}';
}
}
基于注解编写接口实现。
@RestController
@RequestMapping(value="/products")//通过这里配置使下面的映射都在/products下
public classProductController {
privateList
productList;
//初始化
publicProductController(){
productList=newArrayList
();
for(inti =; i
Product product =newProduct();
product.setId(i);
product.setCount(i+10);
product.setName("watch"+i);
product.setDesc("watch desc"+i);
productList.add(product);
}
}
@ApiOperation(value="获取产品列表", notes="获取产品列表")
@RequestMapping(value={""}, method= RequestMethod.GET)
publicList
getProductList() {
returnproductList;
}
@ApiOperation(value="获取产品详细信息", notes="根据url的id来获取产品详细信息")
@ApiImplicitParam(name ="id", value ="产品ID", required =true, dataType ="Integer",paramType="path")
@RequestMapping(value="/", method=RequestMethod.GET)
publicProduct getProduct(@PathVariableInteger id) {
returnproductList.get(id);
}
}
基于Main方法启动。
@SpringBootApplication
public classApplication {
public static voidmain(String[] args) {
SpringApplication.run(Application.class, args);
}
}
访问http://localhost:8080/swagger-ui.html。这就是自动生成的文档。
并且可以基于这个界面点击tryit out进行测试。
访问http://localhost:8080/v2/api-docs可以获取接口的json描述文件,可以直接到Swagger官网转换为yaml。
领取专属 10元无门槛券
私享最新 技术干货