1 RESTful基础
1.1 RESTful是什么?
RESTful不是一种协议,也不是一种文件格式,更不是一种开发框架。它是一系列设计约束的集合:无状态、将超媒体作为应用状态的引擎等。
1.2 RESTful的基本术语
以RESTful的风格访问web服务:客户端向标识资源的URL发起一系列HTTP请求,然后服务器在响应中向客户端发送表述。随着时间的推移,客户端通过这些表述建立起了一个资源状态的全景图。最后,客户端发起一个意义重大的PUT、POST或者PATCH请求,将一个表述发送回服务器从而更改资源的状态。
读完这句话你肯定有以下几点疑问:
- 什么是资源?
- 什么是资源状态?
- 什么是表述?
- 什么是状态的转换?
下面我们一一解答各个术语的含义:
1.资源
资源一般是可以保存到计算机里面的事物。比如网页,电子文档,数据库的一条记录。在Web中,我们使用URL来为每个资源提供一个全球唯一的地址。将一个事物赋以URL,它就会成为一个资源。
2.资源状态
资源状态指的是服务器中资源的状态。例如,服务器中有一个拥有三个邮件的邮件列表,那么邮件列表的状态就是拥有三个邮件的邮件列表。如果用户又发送了一条邮件,那么邮件列表的状态就变为拥有四个邮件的邮件列表。
3.表述
当客户端对一个资源发起一个GET请求的时候,服务器会以一种有效的方式提供一个采集了资源信息的文档作为回应。这就是表述——一种以机器可读的方式对资源当前状态的说明。对于数据库中的一条记录,服务器可以用XML文档、JSON对象、逗号分隔的数值或者用来生成它的SQL INSERT语句来描述它。
4.状态的转换
我们通常都认为表述是服务器发送给客户端的东西,这是由于在我们上网的时候,发送的大部分的请求都是GET请求,我们一直都在请求获取表述。但是实际上,在POST、PUT或者PATCH请求中,客户端也会向服务器端发送表述,服务器随后的工作就是改变资源状态,这种情况下的表述反映的是将来的表述。
2 RESTful实现交互
尽管任何事物都可以成为一个资源,但是客户端并不能随心所欲地对资源进行任意的操作。所能进行的操作是有规定的。在一个RESTful系统里,客户端和服务器端只能通过相互发送遵循预定义协议的消息来进行交互。
6种不同类型的消息:
- GET:获取资源的某个表述
- DELETE:销毁一个资源。
- POST:基于给定的表述信息,在当前资源的下一级创建新的资源。
- PUT:用给定的表述信息替换资源的当前状态。
- HEAD:获取服务器发送过来的报头信息(不是资源的表述),这些报头信息是在服务器发送资源的表述的时候被一起发送过来的。
- OPTIONS:获取这个资源所能响应的HTTP方法列表。
响应协议的结构:
从上图可以看出HTTP响应由三部分构成:
- 状态码:这部分是由三位数字组成的,它简要说明了请求目前的进展。
- 响应报头:响应报头的发送顺序排在状态码和实体消息体之间,通常是一系列用于描述实体消息体和HTTP响应的键值对。
- 响应体:这部分是一个采用某种数据格式书写成的文档,并且我们预期该文档是可以被客户端所理解的。
3 RESTful的特点
- 每一个URI代表1种资源。
- 客户端使用GET、POST、PUT、DELETE4个表示操作方式的动词对服务端资源进行操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。
- 通过操作资源的表现形式来操作资源。
- 资源的表现形式是XML或者HTML。
- 通过操作资源的表现形式来操作资源。
- 客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息。
4 SpringMVC如何支持RESTful
根据RESTful的特点,我们可以得到RESTful的风格,并分析出SpringMVC是如何支持RESTful风格的。
5 基于Rest的Controller(控制器)
我们的 REST API :
- GET 方式请求 /api/user/ 返回用户列表
- GET 方式请求 /api/user/1返回id为1的用户
- POST 方式请求 /api/user/ 通过user对象的JSON 参数创建新的user对象
- PUT 方式请求 /api/user/3 更新id为3的发送json格式的用户对象
- DELETE 方式请求/api/user/4删除 ID为 4的user对象
- DELETE 方式请求/api/user/删除所有user
package com.xuecheng.restful.controller;
import com.xuecheng.restful.api.UserControllerApi;
import com.xuecheng.restful.model.domain.ucenter.User;
import com.xuecheng.restful.service.UserService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping(value = "/user/")
public class UserController implements UserControllerApi {
@Autowired
UserService userService;
@RequestMapping(value = "/list", method = RequestMethod.GET)
public List<User> findAllUsers() {
return userService.findAllUser();
}
@RequestMapping(value = "/id", method = RequestMethod.GET)
public ResponseEntity<User> findById(@PathVariable("id") String id) {
User user = userService.findById(id);
if (user == null) {
return new ResponseEntity<User>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<User>(user,HttpStatus.OK);
}
@RequestMapping(value = "/save", method = RequestMethod.POST)
public ResponseEntity<Void> saveUser(@RequestBody User user) {
if(userService.isUserExist(user)){
return new ResponseEntity<>(HttpStatus.CONFLICT);
}
userService.saveUser(user);
return new ResponseEntity<>(HttpStatus.OK);
}
@RequestMapping(value = "/update", method = RequestMethod.PUT)
public ResponseEntity<Void> updateUser(@RequestBody User user) {
User user1 = userService.findById(user.getId());
if(user1 == null){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
BeanUtils.copyProperties(user,user1);
userService.updateUser(user);
return new ResponseEntity<>(HttpStatus.OK);
}
@RequestMapping(value = "/delete", method = RequestMethod.DELETE)
public ResponseEntity<Void> deleteUserById(@PathVariable("id") String id) {
User user = userService.findById(id);
if(user == null){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
userService.deleteUserById(id);
return new ResponseEntity<>(HttpStatus.OK);
}
public ResponseEntity<Void> deleteAllUsers() {
userService.deleteAllUsers();
return new ResponseEntity<>(HttpStatus.OK);
}
public boolean isUserExist(@RequestBody User user) {
return false;
}
}
springmvc注解详解
@RestController
此注解避免了每个方法都要加上@ResponseBody注解。也就是说@RestController 自己戴上了 @ResponseBody注解,看以看作是
@RequestBody
@ResponseBody
@ResponseEntity: 是一个真实数据.它代表了整个 HTTP 响应(response). 它的好处是你可以控制任何对象放到它内部。
你可以指定状态码、头信息和响应体。它包含你想要构建HTTP Response 的信息。
@PathVariable: 此注解意味着一个方法参数应该绑定到一个url模板变量[在’{}’里的一个]中
一般来说你,要实现REST API in Spring 4 需要了解@RestController , @RequestBody, ResponseEntity 和 @PathVariable 这些注解 .另外, spring 也提供了一些支持类帮助你实现一些可定制化的东西。
6 发布和测试API
就以查询所有用户为例进行测试
7 根据RestTemplate 写REST Client
Spring的 RestTemplate随之出现。RestTemplate 提供了高级方法,来响应者6种主要的HTTP方法。
HTTP 方法和对应的 RestTemplate方法:
- HTTP GET : getForObject, getForEntity
- HTTP PUT : put(String url, Object request, String…urlVariables)
- HTTP DELETE : delete
- HTTP POST : postForLocation(String url, Object request, String… urlVariables), postForObject(String url, Object request, ClassresponseType, String… uriVariables)
- HTTP HEAD : headForHeaders(String url, String… urlVariables)
- HTTP OPTIONS : optionsForAllow(String url, String… urlVariables)
- HTTP PATCH and others : exchange execute
定义 Rest client , 定义REST services
package com.xuecheng.restful;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestSpringMVCRest {
public static final String REST_SERVICE_URI = "http://localhost:31001/user/";
@Autowired
RestTemplate restTemplate;
/* GET */
@Test
public void listAllUsers() {
ResponseEntity<List> users = restTemplate.getForEntity(REST_SERVICE_URI + "list", List.class);
if (users != null) {
System.out.println(users.getBody());
} else {
System.out.println("No user exist----------");
}
}
}
8 完整的项目
8.1 项目结构
8.2 pom.xml添加项目依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>xc-framework-parent</artifactId>
<groupId>com.xuecheng</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../xc-framework-parent/pom.xml</relativePath>
</parent>
<groupId>com.xuecheng</groupId>
<artifactId>test-restful</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.0.12.Final</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
</dependencies>
</project>
8.3 User Service
package com.xuecheng.restful.service;
import com.xuecheng.restful.model.domain.ucenter.User;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
@Service
public class UserService {
private static List<User> users;
static {
users = populateDummyUsers();
}
private static List<User> populateDummyUsers() {
List<User> users = new ArrayList<User>();
users.add(new User(UUID.randomUUID().toString(), "Sam", 30, 70000));
users.add(new User(UUID.randomUUID().toString(), "Tom", 40, 50000));
users.add(new User(UUID.randomUUID().toString(), "Jerome", 45, 30000));
users.add(new User(UUID.randomUUID().toString(), "Silvia", 50, 40000));
return users;
}
public List<User> findAllUser() {
return users;
}
public User findById(String id) {
for (User user : users) {
if (user.getId().equals(id)) {
return user;
}
}
return null;
}
public User findByName(String name) {
for (User user : users) {
if (user.getName().equals(name)) {
return user;
}
}
return null;
}
public void saveUser(User user) {
user.setId(UUID.randomUUID().toString());
users.add(user);
}
public void updateUser(User user) {
int index = users.indexOf(user);
users.set(index, user);
}
public void deleteUserById(String id) {
Iterator<User> iterable = users.iterator();
while (iterable.hasNext()){
if(iterable.next().getId().equals(id)){
users.remove(iterable.next());
}
}
}
public void deleteAllUsers() {
users.clear();
}
public boolean isUserExist(User user) {
return findByName(user.getName())!=null;
}
}
8.4 Model (模型)类
package com.xuecheng.restful.model.domain.ucenter;
import lombok.Data;
import lombok.ToString;
import javax.persistence.Entity;
@Data
@ToString
@Entity
public class User {
private String id;
private String name;
private int age;
private double salary;
public User(String id, String name, int age, double salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
}
8.5 配置类
server:
port: ${PORT:31001}
spring:
application:
name: test-restful
8.6 初始化类(启动springboot)
package com.xuecheng.restful;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.client.RestTemplate;
/**
* @author Administrator
* @version 1.0
* @create 2018-09-12 17:13
**/
@SpringBootApplication
@ComponentScan(basePackages = {"com.xuecheng.restful"})//扫描本项目下的所有类
public class ManageCmsApplication {
public static void main(String[] args) {
SpringApplication.run(ManageCmsApplication.class, args);
}
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
8.7 源代码下载地址