1. 简介
Representational State Transfer,简称为REST, 即表现层状态转化
- 资源 Resources
指的是网络上的某个数据, 如一个文件、一种服务等 - 表现层 Representational
资源的表现层,指的是资源的具体呈现形式,如HTML、JSON等 - 状态转化 State Transfer
指的是状态变化,通过HTTP方法来实现:
GET 获取资源
POST 新建资源
PUT 更新资源
DELETE 删除资源
简单来说,客户端通过HTTP方法对服务器的资源进行操作, 实现表现层状态转化
2. 设计原则
Restful 是目前最流行的 API 设计规范,用于 Web 数据接口的设计
Restful API设计原则:
- 尽量将API 部署在一个专用的域名下,如
http://api.itany.com
- API的版本应该在URL中体现,如
http://api.itany.com/v2
- URL中不要使用动词,应使用资源名词,且使用名词的复数形式,如
注:简单来说,可以使用同一个 URL ,通过约定不同的 HTTP 方法来实施不同的业务
- 服务器响应时返回JSON对象,应包含HTTP状态码、消息、结果等,如
{code:200,message:"success",result:{id:1001,name:"tom"}}
- 最好在返回的结果中提供链接, 指向其他的API方法,使得用户不用查文档, 就能知道下一步该怎么做
3. 用法
cloud-common
提供基本传输对象,给多个服务之间传输提供基石
- 依赖配置
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
</dependencies>
- 公共响应体
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResponeResult {
private int code;
private String msg;
private Object obj;
public static ResponeResult createSuccessResult(Object obj){
return new ResponeResult(200,"",obj);
}
public static ResponeResult createSuccessResult(){
return new ResponeResult(200,"",null);
}
public static ResponeResult createErrorResult(String msg){
return new ResponeResult(400,msg,null);
}
public static ResponeResult createErrorResult(){
return new ResponeResult(400,"操作失败",null);
}
public static ResponeResult createServerErrorResult(){
return new ResponeResult(500,"服务器异常",null);
}
public static ResponeResult createServerErrorResult(String msg){
return new ResponeResult(500,msg,null);
}
}
- 业务数据对象
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String phone;
}
cloud-provider
构建Restful API服务
- 服务依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>spring-cloud-provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-provider</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR12</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>spring-cloud-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 服务配置
application.yml
server:
port: 8001
- 控制层接口
@RestController
@RequestMapping("/user")
public class UserController {
static Map<Integer, User> userMap = new ConcurrentHashMap<>();
static AtomicInteger nextId = new AtomicInteger();
static {
userMap.put(1,new User(1,"张三","18623232323"));
userMap.put(2,new User(2,"李四","18623232323"));
userMap.put(3,new User(3,"王五","18623232323"));
userMap.put(4,new User(4,"赵六","19515785248"));
nextId.set(5);
}
@GetMapping
public ResponeResult userList(){
return ResponeResult.createSuccessResult(userMap);
}
@GetMapping("/{id}")
public ResponeResult user(@PathVariable("id")Integer id){
return ResponeResult.createSuccessResult(userMap.get(id));
}
/**
*
*
*/
@PostMapping("/form")
public ResponeResult addForm(User user){
user.setId(nextId.getAndAdd(1));
userMap.put(user.getId(),user);
return ResponeResult.createSuccessResult(user);
}
@PostMapping("/json")
public ResponeResult addJson(@RequestBody User user){
user.setId(nextId.getAndAdd(1));
userMap.put(user.getId(),user);
return ResponeResult.createSuccessResult(user);
}
@DeleteMapping("/{id}")
public ResponeResult delete(@PathVariable("id") Integer id){
User user = userMap.get(id);
if(Objects.isNull(user)){
return ResponeResult.createErrorResult(String.format("没有id为%s的用户!",id));
}
userMap.remove(user.getId());
return ResponeResult.createSuccessResult(user);
}
@PutMapping
public ResponeResult update(@RequestBody User user){
User oldUser = userMap.get(user.getId());
if(Objects.isNull(oldUser)){
return ResponeResult.createErrorResult(String.format("没有id为%s的用户!",user.getId()));
}
userMap.put(user.getId(),user);
return ResponeResult.createSuccessResult(oldUser);
}
}
cloud-consumer
- 使用RestTemplate调用Rest服务
- RestTemplate是Spring提供的用于访问Rest服务的客户端,提供了访问远程Http服务的方法
- 服务依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>spring-cloud-customer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-customer</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>spring-cloud-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 配置文件
server:
port: 8080
- Spring服务配置类
@Configuration
public class SpringConfig {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
- 访问端
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
RestTemplate restTemplate;
static final String BASEURL = "http://localhost:8001/user";
@GetMapping
public ResponeResult userList(){
return restTemplate.getForObject(BASEURL,ResponeResult.class);
}
@GetMapping("/{id}")
public ResponeResult user(@PathVariable("id")Integer id){
return restTemplate.getForObject(BASEURL+"/{id}",ResponeResult.class,id);
}
/**
* 发送对方使用x-www-form-urlencode方式接收数据
* @param user
* @return
*/
@PostMapping("/form")
public ResponeResult addForm(User user){
MultiValueMap params = new LinkedMultiValueMap();
params.add("name",user.getName());
params.add("phone",user.getPhone());
return restTemplate.postForObject(BASEURL+"/form",params,ResponeResult.class);
}
/**
* 发送对方使用application/json接受数据方式
* @param user
* @return
*/
@PostMapping("/json")
public ResponeResult addJson(@RequestBody User user){
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add("Accept",MediaType.APPLICATION_JSON.toString());
HttpEntity entity = new HttpEntity<User>(user,headers);
return restTemplate.postForObject(BASEURL+"/json",entity,ResponeResult.class);
}
@DeleteMapping("/{id}")
public ResponeResult delete(@PathVariable("id") Integer id){
ResponseEntity<ResponeResult> entity= restTemplate.exchange(BASEURL+"/{id}",
HttpMethod.DELETE,null,ResponeResult.class,id);
return entity.getBody();
}
@PutMapping
public ResponeResult update(@RequestBody User user){
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add("Accept",MediaType.APPLICATION_JSON.toString());
HttpEntity entity = new HttpEntity<User>(user,headers);
ResponseEntity<ResponeResult> responseEntity= restTemplate.exchange(BASEURL,
HttpMethod.PUT,entity,ResponeResult.class);
return responseEntity.getBody();
}
}
消费者的访问接口与生产者的访问接口相同,只是 消费者的数据依赖生产者的数据。
4. 使用postman
Postman是一款非常优秀的调试工具,可以用来模拟发送各类HTTP请求,进行接口测试。
5. 使用Swagger2
通常情况下,我们会创建一份Restful API文档来记录所有的接口细节,供其他开发人员使用提供的接口服务,但会存在以下的问题:
- 接口众多,并且细节复杂
- 需要根据接口的变化,不断修改API文档,非常麻烦,费时费力
Swagger2的出现就是为了解决上述的这些问题,减少创建API文档的工作量
- 后端人员在代码里添加接口的说明内容,就能够生成可预览的API文档,无须再维护Word文档
- 让维护文档和修改代码整合为一体,在修改代码逻辑的同时方便的修改文档说明
- 提供了强大的页面测试功能,便于对接口进行测试
使用步骤:
- 添加Swagger2依赖
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.21</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.5.21</version>
</dependency>
- 创建Swagger2配置类
@Configuration
@EnableSwagger2 // 启用Swagger2
public class Swagger2Config {
/**
* 创建Restful API文档内容
*/
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
// 指定要暴露给Swagger来展示的接口所在的包
.apis(RequestHandlerSelectors.basePackage("com.itany.controller"))
.paths(PathSelectors.any())
.build();
}
/**
* 创建API的基本信息,这些信息会展现在文档页面中
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
// 标题
.title("使用Swagger2构建Restful API文档")
// 描述
.description("欢迎访问后端API接口文档")
// 联系人
.contact(new Contact("tangxiaoyang","https://github.com/tangyang8942","1049901079@qq.com"))
// 版本号
.version("1.0")
.build();
}
}
- 添加文档内容
使用Swagger2提供的注解对接口进行说明,常用注解:
- @Api 标注在类上,对类进行说明
- @ApiOperation 标注在方法上,对方法进行说明
- @ApiImplicitParams 标注在方法上,对方法的多个参数进行说明
- @ApiImplicitParam 标注在方法上,对方法的一个参数进行说明
- @ApiModel 标注在模型Model上,对模型进行说明
- @ApiModelProperty 标注在属性上,对模型的属性进行说明
- @ApiIgnore 标注在类或方法上,表示忽略这个类或方法
- 查看Restful API的页面,并测试接口
启动SpringBoot程序,访问http://localhost:端口/swagger-ui.html