RESTful API介绍
一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
上文应用于百度百科:https://baike.baidu.com/item/RESTful/4406165?fr=aladdin
RESTful API的示例
序号 | restful | 请求方式 | 表示 |
---|---|---|---|
① | /users | GET | 查询所有user对象 |
② | /user/{id} | GET | 根据id查询单个user对象 |
③ | /user | POST | 保存user对象 |
④ | /user | PUT | 更新user对象 |
⑤ | /user/name | PATCH | 更新user对象(部分) |
⑥ | /user/{id} | DELETE | 根据id删除单个user对象 |
RESTful API在springboot中的应用
(一)关于注解的介绍
大家在spring项目中会使用@RequestMapping注解,通过它来指定控制器可以处理哪些URL请求。若不指定请求方式,默认GET、POST都支持,根据前端方式自动适应。所以,大家为了区分,有的时候会这样使用注解:
@RequestMapping(value = “/xxx”,method = RequestMethod.GET)
当然,上面的注解的使用方式也是正确的,但是在springboot中有更加方便的写法。接下来,就给大家一一列出。
序号 | spring中的注解 | springboot中的注解 |
---|---|---|
① | @RequestMapping(value = "/url",method = RequestMethod.GET) | @GetMapping(value="/url") |
② | @RequestMapping(value = "/url",method = RequestMethod.POST) | @PostMapping(value="/url") |
③ | @RequestMapping(value = "/url",method = RequestMethod.PUT) | @PutMapping(value="/url") |
④ | @RequestMapping(value = "/url",method = RequestMethod.PATCH) | @PatchMapping(value="/url") |
⑤ | @RequestMapping(value = "/url",method = RequestMethod.DELETE) | @DeleteMapping(value="/url") |
HEAD:类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头。
CONNECT:HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
OPTIONS:允许客户端查看服务器的性能。
TRACE:回显服务器收到的请求,主要用于测试或诊断。
因为本人基本上没有用到上面这几个请求方式,所以,感兴趣的朋友,可以根据相关资料,自行研究。
(一)RESTful API实践
为了大家能直观的看数据的变化,所以这里我使用了spring-jdbc来操作数据库。至于连接数据库的这部分知识,后面也会慢慢讲到。
- pom.xml中导入相关的jar依赖
<!-- jdbc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
- 修改配置文件
修改配置文件,前面章节我们讲了配置文件的使用,这里我就不详细说了,不清楚的朋友,可以参考我之前的文章: https://blog.csdn.net/qq_32101993/article/details/84628396
spring:
datasource:
url: jdbc:mysql://localhost:3306/springboot_1?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
- 创建表
CREATE TABLE `book` (
`id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`name` varchar(255) DEFAULT NULL COMMENT '书名',
`author` varchar(255) DEFAULT NULL COMMENT '作者',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
- 创建Book对象
package com.project.parent.springbootrestful.pojo;
import lombok.Data;
import java.io.Serializable;
/**
* @ClassName: Book
* @Author: 清风一阵吹我心
* @Description: TODO
* @Date: 2018/12/18 11:22
* @Version 1.0
**/
@Data
public class Book implements Serializable {
private Long id;
private String name;
private String author;
//这里使用了lombok来完成自动生成setter、getter方法,需要导入相关的jar依赖。
//然后使用@data注解,就能自动生成了。
}
需要使用lombok的朋友,可以在pom.xml中加入以下依赖:
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>compile</scope>
</dependency>
- 返回统一接口的实体类封装
上一篇文章我们讲了,怎么使用springboot返回对象、集合、自定义对象。但是为了规范化,这里我们返回统一的对象。所以需要书写下面几个类。
ResultCode:定义了成功,失败的标志,推荐使用枚举,我这里就简化了。
public class ResultCode {
public static final Integer SUCCESS = 200;
public static final Integer FAIL = -1;
}
Result:这就是我们需要返回的对象
package com.project.parent.springbootrestful.pojo;
import lombok.Data;
import java.io.Serializable;
/**
* @ClassName: Result
* @Author: 清风一阵吹我心
* @Description: TODO
* @Date: 2018/12/18 10:34
* @Version 1.0
**/
@Data
public class Result<T> implements Serializable {
private static final long serialVersionUID = 874200365941306385L;
private Integer code;
private String msg;
private T data;
//同样使用了lombok
}
ResultBean:操作Result的对象
public class ResultBean {
public static Result success() {
Result result = new Result();
result.setCode(ResultCode.SUCCESS);
return result;
}
public static Result success(Object data) {
Result result = new Result();
result.setCode(ResultCode.SUCCESS);
result.setData(data);
return result;
}
public static Result success(Object data, String msg) {
Result result = new Result();
result.setCode(ResultCode.SUCCESS);
result.setData(data);
result.setMsg(msg);
return result;
}
public static Result failure() {
Result result = new Result();
result.setCode(ResultCode.FAIL);
return result;
}
public static Result failure(String msg) {
Result result = new Result();
result.setCode(ResultCode.FAIL);
result.setMsg(msg);
return result;
}
- service层的实现
因为博主偷懒,这里就没有dao层了,所有操作都在service层完成,这是一个不好的习惯,但是由于时间原因,所以希望大家理解。
BookService:接口
public interface BookService {
/**
* 增加
* @param book
* @return
*/
int insert(Book book);
/**
* 根据删除
* @param id
* @return
*/
int delete(Long id);
/**
* 更新全部
* @param book
* @return
*/
int update(Book book);
/**
* 更新部分
* @param book
* @return
*/
int updateName(Book book);
/**
* 根据id查询
* @param id
* @return
*/
Book findOne(Long id);
/**
* 查询所有
* @return
*/
List<Book> findAll();
}
BookServiceImpl :接口的实现类
@Service
public class BookServiceImpl implements BookService {
//因为使用的是spring-jdbc,所以这里使用JdbcTemplate来完成增删改查操作
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public int insert(Book book) {
String sql = "INSERT INTO book(name,author) VALUES(?,?)";
Object[] params = {book.getName(), book.getAuthor()};
return jdbcTemplate.update(sql, params);
}
@Override
public int delete(Long id) {
String sql = "DELETE FROM book WHERE id=?";
return jdbcTemplate.update(sql, id);
}
@Override
public int update(Book book) {
String sql = "UPDATE book SET name=?,author=? WHERE id=?";
Object[] params = {book.getName(), book.getAuthor(), book.getId()};
return jdbcTemplate.update(sql, params);
}
@Override
public int updateName(Book book) {
String sql = "UPDATE book SET name=? WHERE id=?";
Object[] params = {book.getName(), book.getId()};
return jdbcTemplate.update(sql, params);
}
@Override
public Book findOne(Long id) {
String sql = "SELECT * FROM book WHERE id=?";
return jdbcTemplate.queryForObject(sql, new Object[]{id}, new BeanPropertyRowMapper<Book>(Book.class));
}
@Override
public List<Book> findAll() {
String sql = "SELECT * FROM book";
//返回list集合,需要我们自己实现RowMapper接口,从结果集里得到数据
return jdbcTemplate.query(sql, new BookMapper());
}
}
class BookMapper implements RowMapper<Book> {
@Override
public Book mapRow(ResultSet rs, int i) throws SQLException {
//从结果集里把数据得到
long id = rs.getLong("id");
String name = rs.getString("name");
String author = rs.getString("author");
Book book = new Book();
book.setId(id);
book.setName(name);
book.setAuthor(author);
return book;
}
}
- controller层的实现
package com.project.parent.springbootrestful.controller;
import com.project.parent.springbootrestful.pojo.Book;
import com.project.parent.springbootrestful.pojo.Result;
import com.project.parent.springbootrestful.pojo.ResultBean;
import com.project.parent.springbootrestful.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @ClassName: BookController
* @Author: 清风一阵吹我心
* @Description: TODO
* @Date: 2018/12/18 11:45
* @Version 1.0
**/
@RestController
@RequestMapping("/")
public class BookController {
@Autowired
private BookService bookService;
@PostMapping(value = "book")
public Result insert(Book book){
return ResultBean.success(bookService.insert(book),"插入成功");
}
@GetMapping(value = "book/{id}")
public Result findOne(@PathVariable Long id){
return ResultBean.success(bookService.findOne(id),"查询成功");
}
@GetMapping(value = "books")
public Result findAll(){
return ResultBean.success(bookService.findAll(),"查询成功");
}
@DeleteMapping(value = "book/{id}")
public Result delete(@PathVariable Long id){
return ResultBean.success(bookService.delete(id),"删除成功");
}
@PutMapping(value = "book")
public Result update(Book book){
return ResultBean.success(bookService.update(book),"更新成功");
}
@PatchMapping(value = "book")
public Result patch(Book book){
return ResultBean.success(bookService.updateName(book),"更新成功(部分)");
}
//这里使用了所有我们列出来的请求:get、post、delete、put、patch
}
- 测试环节
本来可以直接使用Postman来测试,但是为了直观的反应各个请求。所以下面我会使用MockMvc来进行测试,MockMvc也能发起各种http请求,测试很方便。
当然,想用测试工具测试的朋友,也可以忽略底下的篇幅。
RestfulTest :测试类
package com.project.parent.springbootrestful;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.context.WebApplicationContext;
/**
* @ClassName: ResultfulTest
* @Author: 清风一阵吹我心
* @Description: TODO
* @Date: 2018/12/18 13:11
* @Version 1.0
**/
@RunWith(SpringRunner.class)
@SpringBootTest
//测试环境使用,用来表示测试环境使用的ApplicationContext将是WebApplicationContext类型的
@WebAppConfiguration
public class RestfulTest {
@Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
//使用日志来记录
private static final Logger LOGGER = LoggerFactory.getLogger(RestfulTest.class);
//定义出所有请求的路径
private String insertUrl = "/book";
private String oneUrl = "/book/{id}";
private String allUrl = "/books";
private String updateUrl ="/book";
private String updateNameUrl = "/book";
private String deleteUrl = "/book/{id}";
//@Before初始化方法 对于每一个测试方法都要执行一次
@Before
public void setUp() {
//指定WebApplicationContext,将会从该上下文获取相应的控制器并得到相应的MockMvc;
mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build();
}
/**
* 1、mockMvc.perform执行一个请求。
* 2、MockMvcRequestBuilders.get("XXX")构造一个请求
* 3、ResultActions.param添加请求传值
* 4、ResultActions.accept(MediaType.TEXT_HTML_VALUE))设置返回类型
* 5、ResultActions.andExpect添加执行完成后的断言
* 6、ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情
* 比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息
* 7、ResultActions.andReturn表示执行完成后返回相应的结果
*
* @throws Exception
*/
@Test
public void insert() throws Exception {
final MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("name", "魔道祖师");
params.add("author", "墨香铜臭");
String content = mockMvc.perform(MockMvcRequestBuilders.post(insertUrl).params(params))
.andReturn().getResponse().getContentAsString();
LOGGER.info("content:{}", content);
}
@Test
public void findOne() throws Exception {
String book = mockMvc.perform(MockMvcRequestBuilders.get(oneUrl, 1))
.andReturn().getResponse().getContentAsString();
LOGGER.info("book:{}",book);
}
@Test
public void findAll() throws Exception{
String list = mockMvc.perform(MockMvcRequestBuilders.get(allUrl))
.andReturn().getResponse().getContentAsString();
LOGGER.info("list:{}",list);
}
@Test
public void update() throws Exception {
final MultiValueMap<String,String> params = new LinkedMultiValueMap<>();
params.add("id","1");
params.add("name","武动乾坤");
params.add("author","天蚕土豆");
String content = mockMvc.perform(MockMvcRequestBuilders.put(updateUrl).params(params))
.andReturn().getResponse().getContentAsString();
LOGGER.info("content:{}", content);
}
@Test
public void patch() throws Exception {
final MultiValueMap<String,String> params = new LinkedMultiValueMap<>();
params.add("id","1");
params.add("name","星辰变");
String content = mockMvc.perform(MockMvcRequestBuilders.patch(updateNameUrl).params(params))
.andReturn().getResponse().getContentAsString();
LOGGER.info("content:{}", content);
}
@Test
public void delete() throws Exception {
String content = mockMvc.perform(MockMvcRequestBuilders.delete(deleteUrl, "1"))
.andReturn().getResponse().getContentAsString();
LOGGER.info("content:{}", content);
}
}
使用日志来记录测试结果,给大家放几张图:
基本上整个RESTful API的设计,到这里就讲完了,关于MockMvc的知识,我们后面再讲解。如有不明白的朋友,可以给我留言,我一定尽自己所能,给大家解决问题。
你为什么那么努力?
因为我想要的东西很贵……
我想去的地方很远……
我喜欢的人很优秀……