有时候我们需要对返回到前端的数据做统一的格式处理,在实际开发中我们都会定义一个Result来封装返回的数据,一般会包含code、msg、data等等参数,不论请求是成功还是失败,都希望也需要返回这些统一的信息。例如:
如果我们不对返回信息做全局处理的话,当我们在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)标明需要捕获的异常类型,这里我是只捕获自定义异常,这里也可以改成其他的异常,可以写多个方法分别处理不同的异常