SpringBoot全局异常处理

释放双眼,带上耳机,听听看~!

     有时候我们需要对返回到前端的数据做统一的格式处理,在实际开发中我们都会定义一个Result来封装返回的数据,一般会包含code、msg、data等等参数,不论请求是成功还是失败,都希望也需要返回这些统一的信息。例如:

SpringBoot全局异常处理SpringBoot全局异常处理SpringBoot全局异常处理

      如果我们不对返回信息做全局处理的话,当我们在service层处理异常逻辑的时候就必须手动判断并封装数据到Result,重复代码比较多而且看起来很臃肿。在一般的项目中,全局异常处理不需要像网上其他文章那样定义那么详细,每个业务模块只需要自定义一个异常就基本够用了。

1.controller和service

只是做一个简单的测试,controller和service中能制造异常就行了。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
1package com.wzk.demo.controller;
2
3import com.wzk.demo.common.Result;
4import com.wzk.demo.pojo.User;
5import com.wzk.demo.service.MyExceptionService;
6import org.springframework.beans.factory.annotation.Autowired;
7import org.springframework.validation.BindingResult;
8import org.springframework.web.bind.annotation.RequestBody;
9import org.springframework.web.bind.annotation.RequestMapping;
10import org.springframework.web.bind.annotation.RestController;
11
12import javax.validation.Valid;
13
14
15/**
16 * @author WangZhiKai
17 * @date 2019/9/12 11:27
18 */
19@RestController
20@RequestMapping("/exp")
21public class MyExceptionController {
22
23    @Autowired
24    private MyExceptionService myExceptionService;
25
26    /**
27     *  登录
28     */
29    @RequestMapping("/login")
30    public Result testExp(@RequestBody @Valid User user, BindingResult bindingResult) {
31        if (bindingResult.hasErrors()) {
32            return Result.error(bindingResult.getFieldErrors().get(0).getDefaultMessage());
33        }
34        return myExceptionService.login(user);
35    }
36
37    /**
38     *  注册
39     */
40    @RequestMapping("/registry")
41    public Result registry(@RequestBody @Valid User user, BindingResult bindingResult) {
42        if (bindingResult.hasErrors()) {
43            return Result.error(bindingResult.getFieldErrors().get(0).getDefaultMessage());
44        }
45        return myExceptionService.registry(user);
46    }
47}
48
49

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
1package com.wzk.demo.service.impl;
2
3import com.wzk.demo.common.Result;
4import com.wzk.demo.common.ResultEnum;
5import com.wzk.demo.exception.MyException;
6import com.wzk.demo.pojo.User;
7import com.wzk.demo.service.MyExceptionService;
8import org.springframework.stereotype.Service;
9import org.springframework.util.StringUtils;
10
11/**
12 * @author WangZhiKai
13 * @date 2019/9/12 14:18
14 */
15@Service
16public class MyExceptionServiceImpl implements MyExceptionService {
17
18    @Override
19    public Result login(User user) {
20        if (!"wangzk".equals(user.getUsername())) {
21            throw new MyException(ResultEnum.EXCEPTION_PARAM_ERROR);
22        }else if (!"66666".equals(user.getPassword())) {
23            throw new MyException(ResultEnum.EXCEPTION_DB_ERROR);
24        }else {
25            return new Result(ResultEnum.OK);
26        }
27    }
28
29
30    @Override
31    public Result registry(User user) {
32        if (!StringUtils.hasLength(user.getTelephone())) {
33            throw new MyException(ResultEnum.EXCEPTION_PHONE_MISS);
34        }else if (!"18100000000".equals(user.getTelephone())) {
35            throw new MyException(ResultEnum.EXCEPTION_PHONE_ERROR);
36        }else {
37            return new Result(ResultEnum.OK);
38        }
39    }
40}
41
42

 1.controller方法参数里面有一个 BindingResult 参数,这个是spring检查被@valid注解修饰的对象入参合法性的,可以先忽略,      如果感兴趣可以在网上搜@valid+BindingResult实现参数校验的文章,这里不细述;

2.Result是我自定义的返回对象,可以看一下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1@Data
2public class Result {
3
4    private String code;
5    private String msg;
6    private Object data;
7
8    public Result() {
9    }
10
11    public Result(ResultEnum resultEnum) {
12        this.code = resultEnum.getCode();
13        this.msg = resultEnum.getMsg();
14    }
15
16    public static Result error() {
17        return error("0", "未知异常!");
18    }
19
20    public static Result error(String msg) {
21        return error("1",msg);
22    }
23
24    private static Result error(String code, String msg) {
25        Result result = new Result();
26        result.code = code;
27        result.msg = msg;
28        return result;
29    }
30}
31

3.Result里面有一个构造方法传入ResultEnum枚举对象,这个是为了简化传参,code和msg放在枚举中就不用分别设值,例如:


1
2
1return new Result(ResultEnum.OK);
2

 顺便看一下ResultEnum:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
1package com.wzk.demo.common;
2
3/**
4 * @author WangZhiKai
5 * @date 2019/9/12 11:35
6 */
7public enum ResultEnum {
8
9    OK("0","操作成功!"),
10    EXCEPTION_PARAM_ERROR("PARAM-1001","参数错误,操作失败!"),
11    EXCEPTION_DB_ERROR("PARAM-1002","数据库操作异常,操作失败!"),
12    EXCEPTION_PHONE_MISS("PARAM-1003","参数缺失,操作失败!电话号不能为空!"),
13    EXCEPTION_PHONE_ERROR("PARAM-1004","参数错误,操作失败!电话号错误!");
14
15    private String code;
16    private String msg;
17
18
19    public String getCode() {
20        return code;
21    }
22
23    public String getMsg() {
24        return msg;
25    }
26
27    ResultEnum(String code, String msg) {
28        this.code = code;
29        this.msg = msg;
30    }
31}
32
33

2.pojo类


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
1package com.wzk.demo.pojo;
2
3import lombok.Data;
4
5import javax.validation.constraints.Max;
6import javax.validation.constraints.Min;
7import javax.validation.constraints.NotNull;
8import javax.validation.constraints.Pattern;
9import javax.validation.constraints.Size;
10
11/**
12 * @author WangZhiKai
13 * @date 2019/9/12 11:30
14 */
15@Data
16public class User {
17
18    public static final String REGEXP_PHONE =
19            "^((13[0-9])|(14[5,7,9])|(15([0-3]|[5-9]))|(166)|(17[0,1,3,5,6,7,8])|(18[0-9])|(19[8|9]))\\d{8}$";
20
21    @NotNull(message = "用户名不能为空!")
22    private String username;
23
24    @NotNull(message = "密码不能为空!")
25    @Size(min = 1, max = 12, message = "密码长度不能超过12位字符")
26    private String password;
27
28    @Max(value = 150,message = "年龄不在合法范围!")
29    @Min(value = 0,message = "年龄不在合法范围!")
30    private Integer age;
31
32    @Pattern(regexp = REGEXP_PHONE, message = "手机号码格式错误!")
33    private String telephone;
34
35}
36
37

参数上面的注解都是为了避免在service层进行繁琐的参数校验,service只需要关注业务逻辑即可,具体的使用可以在网上看看  其他的文章;

3.自定义异常类MyException


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1package com.wzk.demo.exception;
2
3import com.wzk.demo.common.ResultEnum;
4import lombok.Data;
5import lombok.EqualsAndHashCode;
6
7/**
8 * @author WangZhiKai
9 * @date 2019/9/12 15:08
10 */
11@EqualsAndHashCode(callSuper = true)
12@Data
13public class MyException extends RuntimeException{
14
15    private String code;
16    private String msg;
17
18
19    public MyException(String msg) {
20        this.code = "0";
21        this.msg = msg;
22    }
23
24    public MyException(ResultEnum resultEnum) {
25        super(resultEnum.getMsg());
26        this.code = resultEnum.getCode();
27        this.msg = resultEnum.getMsg();
28    }
29}
30
31

这里同样有一个传入ResultEnum的构造方法,方便对异常信息做统一处理,例如:


1
2
1throw new MyException(ResultEnum.EXCEPTION_PARAM_ERROR);
2

4.全局异常处理器 MyExceptionHandler


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
1package com.wzk.demo.exception;
2
3import com.wzk.demo.common.Result;
4import org.slf4j.Logger;
5import org.slf4j.LoggerFactory;
6import org.springframework.http.HttpStatus;
7import org.springframework.http.ResponseEntity;
8import org.springframework.web.bind.annotation.ExceptionHandler;
9import org.springframework.web.bind.annotation.RestControllerAdvice;
10import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
11
12import javax.servlet.http.HttpServletRequest;
13
14/**
15 * @author WangZhiKai
16 * @date 2019/9/12 16:40
17 */
18@RestControllerAdvice
19public class MyExceptionHandler extends ResponseEntityExceptionHandler {
20
21    private static Logger logger = LoggerFactory.getLogger(MyExceptionHandler.class);
22
23    @ExceptionHandler(value = MyException.class)
24    public ResponseEntity<Result> handleMyException(MyException e, HttpServletRequest request) {
25
26        logger.error(e.getMsg(),e);
27
28        Result result = new Result();
29        result.setCode(e.getCode());
30        result.setMsg(e.getMsg());
31        result.setData(false);
32
33        return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
34    }
35}
36
37

1.@RestControllerAdvice注解是标明它只会捕获被@RestController修饰的类中的异常,而且不能在里面做try catch,否则不会被捕获,要注意的是,在service中的异常也是可以捕获到的,但是也不能进行try catch。如果controller层使用的是@Controller注解,这里的@RestControllerAdvice也要改成@ControllerAdvice,否则无效;

2.@ExceptionHandler(value = MyException.class)标明需要捕获的异常类型,这里我是只捕获自定义异常,这里也可以改成其他的异常,可以写多个方法分别处理不同的异常

给TA打赏
共{{data.count}}人
人已打赏
安全技术

c++ list, vector, map, set 区别与用法比较

2022-1-11 12:36:11

气候知识

2012年第7号台风“卡努”

2012-7-6 21:18:33

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索