zoukankan      html  css  js  c++  java
  • Spring Security构建Rest服务-0300-Restful API异常处理

    SpringBoot默认的错误处理机制:

    一、测试需要的部分代码

    (完整代码放在了github https://github.com/lhy1234/spring-security):

    UserController:只对新增用户做测试,省略其他代码

    @RestController
    @RequestMapping("/user")
    public class UserController {
        
       /**
         * 创建
         * @Description: 
         * //@RequestBody:json映射到java
         *     @Valid 和User类上的@NotBlank注解一起做校验
         *  BindingResult存储的是校验错误信息
         * @param @param user
         * @param @return   
         * @return User  
         * @throws
         * @author lihaoyang
         * @date 2018年2月24日
         */
        @PostMapping
        public User create(@Valid @RequestBody User user){ //,BindingResult errors 先屏蔽BindingResult让springboot做错误处理,不屏蔽就能正常响应200了
            
    //        if(errors.hasErrors()){
    //            errors.getAllErrors().stream()
    //            .forEach(error -> System.err.println(error.getDefaultMessage()));
    //        }
            
            user.setId("1");
            System.err.println(user);
            return user;
        }

    //省略其他代码。。。
    }

    User类:使用valid注解校验

    public class User {
        
        public interface UserSimpleView {};  
        public interface UserDetailView extends UserSimpleView{}; //继承
        
        private String id;
        
        @NotBlank(message="用户名不能为空")
        private String username;
        
        @NotBlank(message="密码不能为空")
        private String password;
        @Past(message="生日只能是过去的日期")
        private Date birthday;
    
        //省略get set
        
    }

    一、对于进入controller之前就被打回的错误处理

     1、对于url不存在的400错误处理

    springboot对浏览器和app的请求,处理是不一样的:

      1.1,使用浏览器,响应式的body一段html:

      使用Springboot,启动服务,使用浏览器访问一个不存在的url后,出现html空白页:

      

      浏览器发出的请求:请求头Content-Type:text/html;

      

      1.2,使用chrome插件restlet client(https://restlet.com/modules/client/)模拟app访问:

      

      使用restlet client模拟app访问不存在的url,响应体body是一个json:

      

                          图一           

    从上图可以看出,使用app发出的请求请求头是contentType是json类型

    springboot错误处理类控制器 BasicErrorController代码片段:

    这个控制器处理的url是 /error ,但是会根据请求头里类型的不同,做不同的处理,浏览器发的请求走上边的,返回时html;app发的请求,走下边的方法,返回是一个json。

    这里提供了一种机制:同一个url,不同的情况做不同的处理。

    上边是对url不存在的400错误的处理,从图一可以看出,springboot的响应信息很简单:

    错误码 400,加上响应体body:

    {
    "timestamp": 1519608427470,
    "status": 404,
    "error": "Not Found",
    "message": "No message available",
    "path": "/xxx"
    }

    2,对于不符合校验规则的错误处理

      User类的username和password是不能为空的,使用restlet client发出post请求,请求体为{},空的json,这样就不符合校验规则了(host是变量127.0.0.1):

      

    响应头:

    响应体:

     会吧错误信息返回。

    上边这两种情况,都是在进入controller之前就被spring打回的,一般我们需要在进入controller方法后,调用service过程中有异常来进行处理,下边模拟这种情况:

    二、进入controller方法后发生了异常

    在getInfo里直接抛出运行时异常模拟调用service发生错误:

    /**
         * 详情
         * @Description: TODO
         * @param @param id
         * @param @return   
         * @return User  
         * @throws
         * @author lihaoyang
         * @date 2018年2月24日
         */
        @GetMapping("{id:\d+}") //{}里可以是正则,匹配数字
    //    @GetMapping("detail/{id}")
        @JsonView(User.UserDetailView.class)
        public User getInfo(@PathVariable(value="id",required=true) String id){
            
            throw new RuntimeException("query deltail interface has error!");
            
    //        System.err.println(id);
    //        User user = new User();
    //        user.setUsername("tom");
    //        user.setPassword("123456");
    //        user.setId("1");
    //        return user;
        }

    使用浏览器访问:

    对于浏览器也可以自定义错误页面,在src/main/resources下新建resources/error目录,可以自定义错误页面,这个对于app访问是无效的,app还是返回json

    模拟app访问:错误中只包含抛出的异常信息

    大部分情况,这种springboot对rest服务做的错误处理已经够用了。

    如果还不够用也可以自己定义错误处理机制:

    三、自定义异常

    自定义异常:

    package com.imooc.exception;
    
    /**
     * 用户不存在异常
     * ClassName: UserNotExistException 
     * @Description: TODO
     * @author lihaoyang
     * @date 2018年2月26日
     */
    public class UserNotExistException extends RuntimeException{
        /**
         * @Fields serialVersionUID : TODO
         */
        private static final long serialVersionUID = 1L;
    
        private String id;
        
        public UserNotExistException(String id){
            super("user not exist!");
            this.id = id;
        }
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
        
    }

    controller方法:

        @GetMapping("{id:\d+}") //{}里可以是正则,匹配数字
    //    @GetMapping("detail/{id}")
        @JsonView(User.UserDetailView.class)
        public User getInfo(@PathVariable(value="id",required=true) String id){
            
    //        throw new RuntimeException("query deltail interface has error!");
            throw new UserNotExistException(id);
            
    //        System.err.println(id);
    //        User user = new User();
    //        user.setUsername("tom");
    //        user.setPassword("123456");
    //        user.setId("1");
    //        return user;
        }

    此时浏览器访问,返回自定义的错误页

    区别就在于app访问,exception类已经是自定义的了,但是异常信息message还不够详细,下面介绍怎么处理

    统一异常处理类:

    package com.imooc.web.controller;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import org.springframework.http.HttpStatus;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.ResponseStatus;
    
    import com.imooc.exception.UserNotExistException;
    
    /**
     * @ControllerAdvice:处理其他controller抛出的异常,都会到这里处理
     */
    @ControllerAdvice
    public class ControllerExceptionHandler {
    
        @ExceptionHandler(UserNotExistException.class)//处理哪个异常类
        @ResponseBody //返回json
        @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)//状态码 500
        public Map<String,Object> handlerUserNotExistException(UserNotExistException ex){
            Map<String,Object> result = new HashMap<>();
            //通过ex对象拿到异常信息 
            result.put("id", ex.getId()); 
            result.put("message", ex.getMessage());
            return result;
        }
    }

    app访问,可以打印自己想要的异常信息。

  • 相关阅读:
    Linux中将两块新硬盘合并成一个,挂载到/data目录下
    linux将硬盘格式化为xfs文件系统
    nginx配置文件
    centos 添加新硬盘,lvm对根目录扩容
    centos7重新调整分区大小
    Linux 一种调用蜂鸣器的方法
    mybatis中 keyProperty="id" 的作用
    MySQL实战45讲
    常用正则表达式最强整理(速查手册)
    linux下nacos的1.1.3版本集群部署
  • 原文地址:https://www.cnblogs.com/lihaoyang/p/8466943.html
Copyright © 2011-2022 走看看