springboot 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 一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

URL定义

  • 资源:互联网所有的事物都可以被抽象为资源
    • 一首歌 一张图片 数据库一条记录
  • 资源操作:使用POST(添加)、DELETE(删除)、PUT(修改)、GET(查询),使用不同请求方法对资源进行操作。
    • 删除 delete
    • 查询 get
    • 添加 post
    • 修改 put (修改全部字段)| patch(更新部分字段)

传统方式操作资源

  • http://localhost/item/queryUser.action?id=1 查询,GET

  • http://localhost/item/saveUser.action 新增,POST

  • http://localhost/item/updateUser.action 更新,PUT

  • http://localhost/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实际使用

编写对应controller

@RestController
@RequestMapping("/v1/users")
public class UserController {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());


    /**
     *  获取单个对象
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable("id") String id) {
        logger.info("用户id:{}",id);
        User user = new User("张三",22);
        ResponseEntity response = new ResponseEntity(user, HttpStatus.OK);
        return response;
    }


    /**
     *  获取用户列表
     * @return
     */
    @GetMapping
    public ResponseEntity<List<User>> getAllUsers() {
        List<User> userList = Arrays.asList(new User("张三",22)
            ,new User("李四",62)
            ,new User("王五",23)
            ,new User("赵六",25));
        User user = new User("张三",22);
        ResponseEntity response = new ResponseEntity(userList, HttpStatus.OK);
        return response;
    }


    /**
     *  添加用户
     * @param user
     * @return
     */
    @PostMapping
    public ResponseEntity<Void> addUser(@RequestBody User user) {
        logger.info("name:{},age:{}",user.getName(),user.getAge());
        // 业务方法调用
        ResponseEntity<Void> entity = new ResponseEntity<>(HttpStatus.NO_CONTENT);
        return entity;
    }


    /**
     *  更新用户
     * @param user
     * @return
     */
    @PutMapping("/{id}")
    public ResponseEntity<Void> updUser(@PathVariable("id")String id,@RequestBody User user) {
        logger.info("更新用户id:{}",id);
        logger.info("name:{},age:{}",user.getName(),user.getAge());
        // 业务方法调用
        ResponseEntity<Void> entity = new ResponseEntity<>(HttpStatus.NO_CONTENT);
        return entity;
    }



    /**
     *  删除用户
     * @return
     */
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delUser(@PathVariable("id")String id) {
        logger.info("删除用户,本次id:{}",id);
        // 业务方法调用
        ResponseEntity<Void> entity = new ResponseEntity<>(HttpStatus.NO_CONTENT);
        return entity;
    }


    /**
     *  获取个人下的所有宠物
     * @return
     */
    @GetMapping("/{id}/pets")
    public ResponseEntity<List<Pet>> getPets() {
        List<Pet> petList = Arrays.asList(new Pet("小鸟","张三")
                ,new Pet("小狗","张三")
                ,new Pet("小猪","张三"));
        ResponseEntity response = new ResponseEntity(petList, HttpStatus.OK);
        return response;
    }


    /**
     *  获取个人下面单个宠物
     * @return
     */
    @GetMapping("/{id}/pets/{pid}")
    public ResponseEntity<Pet> getPetById(@PathVariable("id")String id,@PathVariable("pid")String pid) {
        Pet pet = new Pet("小鸟", "张三");
        ResponseEntity response = new ResponseEntity(pet, HttpStatus.OK);
        return response;
    }

}

对应测试类

使用forapi工具测试接口

1. 获取单个对象数据

http://127.0.0.1:8081/v1/users/123

在这里插入图片描述

2. 获取全部数据

http://127.0.0.1:8081/v1/users

在这里插入图片描述

3. 新增用户信息

http://127.0.0.1:8081/v1/users

在这里插入图片描述

4. 更新用户信息

http://127.0.0.1:8081/v1/users/123

在这里插入图片描述

5、 删除用户

http://127.0.0.1:8081/v1/users/123

在这里插入图片描述


三、全局异常处理

当我们没有手动定义异常时,出现错误会出现500错误的页面,这对用户不友好,如果我们在每个controller 里面去做一个 try catch 的话,那么代码就会很冗余,我们使用统一的异常处理机制,但是这个机制也分两种情况,一种是页面跳转,还有一种的ajax请求的错误;

modeAndView 传统开发页面跳转处理方案

我们可以根据异常的类型来判断是否是自己指定的异常情况,从而进行不同页面的跳转;

@Component
public class GlobalException implements HandlerExceptionResolver {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler, Exception ex) {
        logger.info("统一异常拦截到了信息");
        logger.info("异常信息:{}",ex);
        logger.info("handler:{}",handler);
        if (ex instanceof NullPointerException) {
            
        }
        
        ModelAndView mv = new ModelAndView();
        mv.setViewName("error");
        mv.addObject("msg",ex.getMessage());

        return mv;
    }
}

ajax 开发处理

当出现错误时候,我们统一返回错误的状态码,定义错误信息返回到前端

@ControllerAdvice
public class RestFulException {


    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResponseEntity<String> exceptionHandler(Exception ex) {
        System.out.println("来restfull风格里面了");
        if (ex instanceof NullPointerException) {

        }
        
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星空寻流年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值