有时候我们需要对返回到前端的数据做统一的格式处理,在实际开发中我们都会定义一个Result来封装返回的数据,一般会包含code、msg、data等等参数,不论请求是成功还是失败,都希望也需要返回这些统一的信息。例如:
如果我们不对返回信息做全局处理的话,当我们在service层处理异常逻辑的时候就必须手动判断并封装数据到Result,重复代码比较多而且看起来很臃肿。在一般的项目中,全局异常处理不需要像网上其他文章那样定义那么详细,每个业务模块只需要自定义一个异常就基本够用了。
1.controller和service
只是做一个简单的测试,controller和service中能制造异常就行了。
package com.wzk.demo.controller;
import com.wzk.demo.common.Result;
import com.wzk.demo.pojo.User;
import com.wzk.demo.service.MyExceptionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
/**
* @author WangZhiKai
* @date 2019/9/12 11:27
*/
@RestController
@RequestMapping("/exp")
public class MyExceptionController {
@Autowired
private MyExceptionService myExceptionService;
/**
* 登录
*/
@RequestMapping("/login")
public Result testExp(@RequestBody @Valid User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return Result.error(bindingResult.getFieldErrors().get(0).getDefaultMessage());
}
return myExceptionService.login(user);
}
/**
* 注册
*/
@RequestMapping("/registry")
public Result registry(@RequestBody @Valid User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return Result.error(bindingResult.getFieldErrors().get(0).getDefaultMessage());
}
return myExceptionService.registry(user);
}
}
package com.wzk.demo.service.impl;
import com.wzk.demo.common.Result;
import com.wzk.demo.common.ResultEnum;
import com.wzk.demo.exception.MyException;
import com.wzk.demo.pojo.User;
import com.wzk.demo.service.MyExceptionService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
/**
* @author WangZhiKai
* @date 2019/9/12 14:18
*/
@Service
public class MyExceptionServiceImpl implements MyExceptionService {
@Override
public Result login(User user) {
if (!"wangzk".equals(user.getUsername())) {
throw new MyException(ResultEnum.EXCEPTION_PARAM_ERROR);
}else if (!"66666".equals(user.getPassword())) {
throw new MyException(ResultEnum.EXCEPTION_DB_ERROR);
}else {
return new Result(ResultEnum.OK);
}
}
@Override
public Result registry(User user) {
if (!StringUtils.hasLength(user.getTelephone())) {
throw new MyException(ResultEnum.EXCEPTION_PHONE_MISS);
}else if (!"18100000000".equals(user.getTelephone())) {
throw new MyException(ResultEnum.EXCEPTION_PHONE_ERROR);
}else {
return new Result(ResultEnum.OK);
}
}
}
1.controller方法参数里面有一个 BindingResult 参数,这个是spring检查被@valid注解修饰的对象入参合法性的,可以先忽略, 如果感兴趣可以在网上搜@valid+BindingResult实现参数校验的文章,这里不细述;
2.Result是我自定义的返回对象,可以看一下:
@Data
public class Result {
private String code;
private String msg;
private Object data;
public Result() {
}
public Result(ResultEnum resultEnum) {
this.code = resultEnum.getCode();
this.msg = resultEnum.getMsg();
}
public static Result error() {
return error("0", "未知异常!");
}
public static Result error(String msg) {
return error("1",msg);
}
private static Result error(String code, String msg) {
Result result = new Result();
result.code = code;
result.msg = msg;
return result;
}
}
3.Result里面有一个构造方法传入ResultEnum枚举对象,这个是为了简化传参,code和msg放在枚举中就不用分别设值,例如:
return new Result(ResultEnum.OK);
顺便看一下ResultEnum:
package com.wzk.demo.common;
/**
* @author WangZhiKai
* @date 2019/9/12 11:35
*/
public enum ResultEnum {
OK("0","操作成功!"),
EXCEPTION_PARAM_ERROR("PARAM-1001","参数错误,操作失败!"),
EXCEPTION_DB_ERROR("PARAM-1002","数据库操作异常,操作失败!"),
EXCEPTION_PHONE_MISS("PARAM-1003","参数缺失,操作失败!电话号不能为空!"),
EXCEPTION_PHONE_ERROR("PARAM-1004","参数错误,操作失败!电话号错误!");
private String code;
private String msg;
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
ResultEnum(String code, String msg) {
this.code = code;
this.msg = msg;
}
}
2.pojo类
package com.wzk.demo.pojo;
import lombok.Data;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
/**
* @author WangZhiKai
* @date 2019/9/12 11:30
*/
@Data
public class User {
public static final String REGEXP_PHONE =
"^((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}$";
@NotNull(message = "用户名不能为空!")
private String username;
@NotNull(message = "密码不能为空!")
@Size(min = 1, max = 12, message = "密码长度不能超过12位字符")
private String password;
@Max(value = 150,message = "年龄不在合法范围!")
@Min(value = 0,message = "年龄不在合法范围!")
private Integer age;
@Pattern(regexp = REGEXP_PHONE, message = "手机号码格式错误!")
private String telephone;
}
参数上面的注解都是为了避免在service层进行繁琐的参数校验,service只需要关注业务逻辑即可,具体的使用可以在网上看看 其他的文章;
3.自定义异常类MyException
package com.wzk.demo.exception;
import com.wzk.demo.common.ResultEnum;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author WangZhiKai
* @date 2019/9/12 15:08
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class MyException extends RuntimeException{
private String code;
private String msg;
public MyException(String msg) {
this.code = "0";
this.msg = msg;
}
public MyException(ResultEnum resultEnum) {
super(resultEnum.getMsg());
this.code = resultEnum.getCode();
this.msg = resultEnum.getMsg();
}
}
这里同样有一个传入ResultEnum的构造方法,方便对异常信息做统一处理,例如:
throw new MyException(ResultEnum.EXCEPTION_PARAM_ERROR);
4.全局异常处理器 MyExceptionHandler
package com.wzk.demo.exception;
import com.wzk.demo.common.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import javax.servlet.http.HttpServletRequest;
/**
* @author WangZhiKai
* @date 2019/9/12 16:40
*/
@RestControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler {
private static Logger logger = LoggerFactory.getLogger(MyExceptionHandler.class);
@ExceptionHandler(value = MyException.class)
public ResponseEntity<Result> handleMyException(MyException e, HttpServletRequest request) {
logger.error(e.getMsg(),e);
Result result = new Result();
result.setCode(e.getCode());
result.setMsg(e.getMsg());
result.setData(false);
return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
}
}
1.@RestControllerAdvice注解是标明它只会捕获被@RestController修饰的类中的异常,而且不能在里面做try catch,否则不会被捕获,要注意的是,在service中的异常也是可以捕获到的,但是也不能进行try catch。如果controller层使用的是@Controller注解,这里的@RestControllerAdvice也要改成@ControllerAdvice,否则无效;
2.@ExceptionHandler(value = MyException.class)标明需要捕获的异常类型,这里我是只捕获自定义异常,这里也可以改成其他的异常,可以写多个方法分别处理不同的异常