RestFul风格-【编程不良人】笔记
RestFul风格
引言
REST全称是Representational State Transfer
,中文意思是表述(编者注:通常译为表征)性状态转移。 它首次出现在2000年Roy Fielding的博士论文中,Roy Fielding是HTTP规范的主要编写者之一。 他在论文中提到:"我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。REST指的是一组架构约束条件和原则。" 如果一个架构符合REST的约束条件和原则,我们就称它为RESTful架构。
RestFul: 是一种以网络为基础构架一种架构风格 一个架构符合Rest设计原则和约束成这个架构为RestFul。
Rest 词: 更新技术 组件 服务 让我们web请求能够利用web中标准 和 能力 更好描述架构
REST本身并没有创造新的技术、组件或服务,而隐藏在RESTful背后的理念就是使用Web的现有特征和能力, 更好地使用现有Web标准中的一些准则和约束。虽然REST本身受Web技术的影响很深, 但是理论上REST架构风格并不是绑定在HTTP上,只不过目前HTTP是唯一与REST相关的实例。 所以我们这里描述的REST也是通过HTTP实现的REST。
总结
Restful 一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
web开发: 实现所有功能
restful: 软件设计风格 标准 简洁 层次 优雅
rest设计原则 和 约束架构称之为restFul
URL定义
- 资源:互联网所有的事物都可以被抽象为资源
- 一首歌 一张图片 数据库一条记录
- 资源操作:使用POST(添加)、DELETE(删除)、PUT(修改)、GET(查询),使用不同请求方法对资源进行操作。
- 删除 delete
- 查询 get
- 添加 post
- 修改 put (修改全部字段)| patch(更新部分字段)
传统方式操作资源
-
http://127.0.0.1/item/deleteUser.action?id=1 删除,DELETE
注意:传统的操作是没有问题的,大神认为是有问题的,有什么问题呢?你每次请求的接口或者地址,都在做描述,例如查询的时候用了queryUser,新增的时候用了saveUser ,修改的时候用了updateUser,其实完全没有这个必要,我使用了get请求,就是查询.使用post请求,就是新增的请求,PUT就是修改,delete就是删除,我的意图很明显,完全没有必要做描述,这就是为什么有了restful.
使用RESTful操作资源
-
【GET】 /users # 查询用户信息列表
-
【GET】 /users/1001 # 查看某个用户信息
-
【POST】 /users # 新建用户信息
-
【PUT】 /users/1001 # 更新用户信息(全部字段)
-
【PATCH】 /users/1001 # 更新用户信息(部分字段)
-
【DELETE】 /users/1001 # 删除用户信息
Rest API设计风格原则
# 1.使用名词而不是动词
- 不要使用:
如:
/getAllUsers get /users get /users/002
/createNewUser post /users
/deleteAllUser delete /users/001
# 2.Get方法和查询参数不应该涉及状态改变
- 使用PUT, POST 和DELETE 方法 而不是 GET 方法来改变状态,不要使用GET 进行状态改变
# 3.使用复数名词
- 不要混淆名词单数和复数,为了保持简单,只对所有资源使用复数。
如:
/cars 而不是 /car
/users 而不是 /user
/products 而不是 /product
/settings 而不是 /setting
/orders 而不是 /order
# 4. 使用子资源表达关系
- 如果一个资源与另外一个资源有关系,使用子资源:
如:
GET /cars/711/drivers/ 返回 car 711的所有司机
GET /cars/711/drivers/4 返回 car 711的4号司机
GET /users/11/pets 返回 user 11的所有宠物
GET /users/11/pets/2 返回 user 11的2号宠物
# 5.使用Http头声明序列化格式
- 在客户端和服务端,双方都要知道通讯的格式,格式在HTTP-Header中指定
如:
Content-Type 定义请求格式
Accept 定义系列可接受的响应格式
# 6.为集合提供过滤 排序 选择和分页等功能
- Filtering过滤:使用唯一的查询参数进行
GET /cars?color=red 返回红色的cars
GET /cars?seats<=2 返回小于两座位的cars集合
- Sorting排序:允许针对多个字段排序
GET /cars?sort=-manufactorer,+model
这是返回根据生产者降序和模型升序排列的car集合
- Field selection
移动端能够显示其中一些字段,它们其实不需要一个资源的所有字段,给API消费者一个选择字段的能力,这会降低网络流量,提高API可用性。
GET /cars?fields=manufacturer,model,id,color
- Paging分页
使用 limit 和offset.实现分页,缺省limit=20 和offset=0;
GET /cars?offset=10&limit=5
为了将总数发给客户端,使用订制的HTTP头: X-Total-Count.
链接到下一页或上一页可以在HTTP头的link规定,遵循Link规定:
Link: <https://blog.mwaysolutions.com/sample/api/v1/cars?offset=15&limit=5>; rel="next",<https://blog.mwaysolutions.com/sample/api/v1/cars?offset=50&limit=3>; rel="last",<https://blog.mwaysolutions.com/sample/api/v1/cars?offset=0&limit=5>; rel="first",<https://blog.mwaysolutions.com/sample/api/v1/cars?offset=5&limit=5>; rel="prev",
# 7.版本化你的API 支付宝 v1 v2 v3
- 使得API版本变得强制性,不要发布无版本的API,使用简单数字,避免小数点如2.5.
一般在Url后面使用?v
/blog/api/v1
# 8. 使用Http状态码处理错误
- 如果你的API没有错误处理是很难的,只是返回500和出错堆栈不一定有用
- Http状态码提供70个出错,我们只要使用10个左右:
`200 – OK – 一切正常
`201 – OK – 新的资源已经成功创建
`204 – OK – 资源已经成功删除
`304 – Not Modified – 客户端使用缓存数据
`400 – Bad Request – 请求无效,需要附加细节解释如 "JSON无效"
`401 – Unauthorized – 请求需要用户验证
`403 – Forbidden – 服务器已经理解了请求,但是拒绝服务或这种请求的访问是不允许的。
`404 – Not found – 没有发现该资源
`422 – Unprocessable Entity – 只有服务器不能处理实体时使用,比如图像不能被格式化,或者重要字段丢失。
`500 – Internal Server Error – API开发者应该避免这种错误。
使用详细的错误包装错误: 状态码 数据 header头信息
{
"errors": [
{
"userMessage": "Sorry, the requested resource does not exist",
"internalMessage": "No car found in the database",
"code": 34,
"more info": "http://dev.mwaysolutions.com/blog/api/v1/errors/12345"
}
]
}
Rest API案例
@RestController
@RequestMapping("v1/users")
public class UserController {
private UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
/**
* 保存用户
*
* @param user
* @return
*/
@PostMapping
public ResponseEntity<Void> saveUser(@RequestBody User user) {
try {
userService.save(user);
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity<Void>(HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<Void>(HttpStatus.CREATED);
}
/**
* 删除用户
*
* @param id
* @return
*/
@DeleteMapping("{id}")
public ResponseEntity<Void> deleteUser(@PathVariable("id") Integer id) {
try {
userService.delete(id);
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity<Void>(HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}
/**
* 更新用户
*
* @param user
* @return
*/
@PutMapping
public ResponseEntity<Void> updateUser(@RequestBody User user) {
userService.update(user);
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}
/**
* 用户详情
*
* @param id
* @return
*/
@GetMapping("{id}")
public ResponseEntity<User> findUserById(@PathVariable("id") Integer id) {
User user = userService.findById(id);
if (user == null) {
return new ResponseEntity<User>(HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<User>(user, HttpStatus.OK);
}
/**
* 用户列表
*
* @return
*/
@GetMapping
public ResponseEntity<List<User>> users() {
List<User> users = null;
try {
users = userService.findAll();
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity<List<User>>(HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<List<User>>(users, HttpStatus.OK);
}
}
我的Rest API案例 测试
1.控制器
package xin.baizhiedu.controller.v1;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import xin.baizhiedu.entity.Pet;
import xin.baizhiedu.entity.User;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
//@Controller
@RestController // @RestController: 专用于restful 风格的注解 , @Controller:专用于传统开发注解
// @RestController = @Controller + @ResponseBody
@RequestMapping("/v1/users") // 符合 使用复数名词
public class UserController01 {
private static final Logger log = LoggerFactory.getLogger(UserController01.class);
// ResponseEntity: SpringMVC 封装了一个专用于restful 响应类 这个类在响应时可以提供相应的状态码,
// 同时还可以自定义响应头信息
// HttpStatus: SpringMVC 封装一个枚举类型 这个类中都是网络中的状态码
/**
* 查询某个用户详情
*
* @param id
* @return
*/
// @RequestMapping(value = "/{id}", method = RequestMethod.GET)
@GetMapping("/{id}") // @GetMapping是@RequestMapping的子类注解 使用@GetMapping代表只能使用GET方式访问到当前请求
// 注解 @PathVariable:代表在路径中获取请求参数
//@ResponseBody // 将控制器方法的返回值转为json
public ResponseEntity<User> getUser(@PathVariable("id") Integer id) {
log.info("本次id:{}", id);
User user = new User(id, "小陈", 4500.0, new Date());
return new ResponseEntity<>(user, HttpStatus.OK);
}
/**
* 用户列表
* @return
*/
@GetMapping("/")
//@ResponseBody
public ResponseEntity<List<User>> userList() {
List<User> userList = new ArrayList<>();
User user1 = new User(1001, "小陈", 4500.0, new Date());
User user2 = new User(1002, "张三", 7800.0, new Date());
User user3 = new User(1003, "李四", 9200.0, new Date());
userList.add(user1);
userList.add(user2);
userList.add(user3);
return new ResponseEntity<>(userList, HttpStatus.OK);
}
/**
* 添加用户
*/
@PostMapping("/")
//@ResponseBody // @ResponseBody: 将方法的返回值转为json格式数据 并响应请求(或者响应给浏览器),
// @RequestBody: 接收请求json格式数据 将json格式数据转为对象
public ResponseEntity<Void> saveUser(@RequestBody User user) {
log.info("用户信息:{}", user);
// 调用业务方法
return new ResponseEntity(HttpStatus.NO_CONTENT); // 没有内容
}
/**
* 修改用户
* @param user
*/
@PutMapping("/{id}")
//@ResponseBody
public ResponseEntity<Void> updateUser(@PathVariable("id") Integer id, @RequestBody User user) {
log.info("id= {}", id);
log.info("用户信息:{}", user);
// 调用业务方法
return new ResponseEntity(HttpStatus.NO_CONTENT); // 没有内容
}
/**
* 删除用户
* @param id
*/
@DeleteMapping("/{id}")
//@ResponseBody
public ResponseEntity<Void> deleteUser(@PathVariable("id") Integer id) {
log.info("被删除用户的id:{}", id);
// 调用业务方法
return new ResponseEntity(HttpStatus.NO_CONTENT); // 没有内容
}
/**
* 获得这个人的所有宠物
* @param id
* @return
*/
@GetMapping("/{id}/pets")
public ResponseEntity<List<Pet>> pets(@PathVariable("id") Integer id) {
log.info("查询所有宠物的那个人的id:{}", id);
List<Pet> petList = new ArrayList<>();
Pet pet1 = new Pet(1001, "小猫", 2);
Pet pet2 = new Pet(1002, "小兔", 1);
Pet pet3 = new Pet(1003, "小狗", 3);
petList.add(pet1);
petList.add(pet2);
petList.add(pet3);
return new ResponseEntity<>(petList, HttpStatus.OK);
}
/**
* 查询这个人的某个宠物
* @param id
* @param petId
* @return
*/
@GetMapping("/{id}/pets/{pid}")
public ResponseEntity<Pet> pet(@PathVariable("id") Integer id, @PathVariable("pid") Integer petId) {
log.info("查询那个人的id: {}", id);
log.info("查询那个人的宠物petId: {}", petId);
Pet pet = new Pet(2001, "小狗", 3);
return new ResponseEntity<>(pet, HttpStatus.OK);
}
}
2.User实体类
package xin.baizhiedu.entity;
import java.util.Date;
public class User {
private Integer id;
private String name;
private Double salary;
private Date birthday;
public User() {
}
public User(Integer id, String name, Double salary, Date birthday) {
this.id = id;
this.name = name;
this.salary = salary;
this.birthday = birthday;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", salary=" + salary +
", birthday=" + birthday +
'}';
}
}
3.Pet实体类
package xin.baizhiedu.entity;
public class Pet {
private Integer id;
private String name;
private Integer age;
public Pet() {
}
public Pet(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Pet{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
4.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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>xin.baizhiedu</groupId>
<artifactId>springboot_day08_restful</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot_day08_restful</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.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
5.测试
5.1 查询一个用户
5.2 查询所有用户
5.3 添加用户
5.4 修改用户
5.5 删除用户
5.6 查询某人的某只宠物
6.7 查询某人的所有宠物
原文地址:https://www.cnblogs.com/tomcat985/p/15140465.html
- 性能分析工具dotTrace
- struts2 异常处理3板斧
- UE4中的单映射:TMap容器
- Visual Studio 2015 前端开发工作流
- 易学易用的Windows PowerShell
- mongodb-java-driver基本用法
- 基于Redis的开源分布式服务Codis
- 利用Weblogic的iisproxy、iisforward插件实现IIS转发
- 帆软FineReport如何使用程序数据集
- etcd:用于服务发现的键值存储系统
- 如何使用HTTP压缩优化服务器
- "org.jboss.netty.internal.LoggerConfigurator".DESCRIBED is already registered 的解决办法
- ASP.NET SignalR 高可用设计
- JavaScript 和asp.net配合编码字符串
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- scrapy爬虫框架和selenium的使用:对优惠券推荐网站数据LDA文本挖掘
- 使用R语言进行Metroplis-in-Gibbs采样和MCMC运行分析
- R语言中的马尔科夫机制转换(Markov regime switching)模型
- R语言ARMA-EGARCH模型、集成预测算法对SPX实际波动率进行预测
- nginx快速入门
- R语言中进行期权定价的Heston模型
- 使用R语言随机波动模型SV处理时间序列中的随机波动率
- 20个ES6面试高频问题
- i++和++i傻傻分不清楚?这里给你最清楚的解答
- android APT 使用
- Flutter异步编程async与await的基本使用
- 教大家一个万能PPT图片排版技巧,太赞了!
- 重复读取 HttpServletRequest 中 InputStream 的方法
- 测试面试题集锦(三)| 计算机网络和数据库篇(附答案)
- 关于Scikit-Learn你(也许)不知道的10件事