默认异常处理
在springboot中,在发生异常时会自动跳转至/error页面展示异常信息,根据不同Content-Type展示不同格式的错误结果。
如:浏览器访问
postman访问:
显然默认的异常显示页面很不友好。
统一异常处理
!!!!我试了下,好像这个错误处理没有生效,还没找到原因,用下面给出的这种Controller方法里面校验或者用AOP方式就能成:
@GetMapping(value = "/boy")
public Boy getBoy(@Valid Boy boy, BindingResult re){
if(re.hasFieldErrors()){
//不知道为啥没生效
List<FieldError> errorList = re.getFieldErrors();
errorList.stream().forEach(item -> Assert.isTrue(false,item.getDefaultMessage()));
}
Boy boy1=new Boy();
boy1.setName("q");
boy1.setAge(3);
return boy1;
}
AOP方式:
@Component
@Aspect
public class WebExceptionAspect implements ThrowsAdvice{
public static final Logger logger = LoggerFactory.getLogger(WebExceptionAspect.class);
//拦截被GetMapping注解的方法 @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
private void webPointcut() {
}
@AfterThrowing(pointcut = "webPointcut()",throwing = "e")
public void afterThrowing(Exception e) throws Throwable {
logger.debug("exception 来了!");
if(StringUtils.isNotBlank(e.getMessage())){
writeContent(e.getMessage());
}else{
writeContent("参数错误!");
}
}
/**
* 将内容输出到浏览器
*
* @param content
* 输出内容
*/
private void writeContent(String content) {
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getResponse();
response.reset();
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Type", "text/plain;charset=UTF-8");
response.setHeader("icop-content-type", "exception");
PrintWriter writer = null;
try {
writer = response.getWriter();
writer.print((content == null) ? "" : content);
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@RestController
public class BoyController {
@GetMapping(value = "/boy")
public Boy getBoy(@Valid Boy boy, BindingResult re){
if(re.hasFieldErrors()){
validate(re);
}
Boy boy1=new Boy();
boy1.setName("q");
boy1.setAge(3);
return boy1;
}
public void validate(, BindingResult re){
//不知道为啥没生效
List<FieldError> errorList = re.getFieldErrors();
errorList.stream().forEach(item -> Assert.isTrue(false,item.getDefaultMessage()));
}
}
利用@ControllerAdvice
和@ExceptionHandler
定义一个统一异常处理类
- @ControllerAdvice:控制器增强,使@ExceptionHandler、@InitBinder、@ModelAttribute注解的方法应用到所有的 @RequestMapping注解的方法。
- @ExceptionHandler:异常处理器,此注解的作用是当出现其定义的异常时进行处理的方法
@ControllerAdvice
public class CommonExceptionHandler {
/**
* 拦截Exception类的异常
* @param e
* @return返回json格式
*/
@ExceptionHandler(Exception.class)
@ResponseBody
public Map<String,Object> exceptionHandler(Exception e){
Map<String,Object> result = new HashMap<String,Object>();
result.put("respCode", "9999");
result.put("respMsg", e.getMessage());
//正常开发中,可创建一个统一响应实体,如CommonResp
return result;
}
/**
* 拦截 CommonException 的异常
* @param ex
* @return返回json格式
*/
@ExceptionHandler(CommonException.class)
@ResponseBody
public Map<String,Object> exceptionHandler(CommonException ex){
log.info("CommonException:{}({})",ex.getMsg(), ex.getCode());
Map<String,Object> result = new HashMap<String,Object>();
result.put("respCode", ex.getCode());
result.put("respMsg", ex.getMsg());
return result;
}
/**
* 拦截 Exception 的异常
* @param ex
* @return返回页面
*/
@ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW);
return mav;
}
/**
* 拦截 参数校验异常
* @param ex
* @return返回json
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Map<String,Object> handleBindException(MethodArgumentNotValidException ex) {
FieldError fieldError = ex.getBindingResult().getFieldError();
log.info("参数校验异常:{}({})", fieldError.getDefaultMessage(),fieldError.getField());
Map<String,Object> result = new HashMap<String,Object>();
result.put("respCode", "01002");
result.put("respMsg", fieldError.getDefaultMessage());
return result;
}
}
还有很多其他类型的异常,如NoHandlerFoundException
、HttpMediaTypeNotSupportedException
、AsyncRequestTimeoutException
等等.
数据校验
java JSR303规范:
Constraint | 详细信息 |
---|---|
@Null | 被注释的元素必须为 null |
@NotNull | 被注释的元素必须不为 null |
@AssertTrue | 被注释的元素必须为 true |
@AssertFalse | 被注释的元素必须为 false |
@Min(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@Max(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@DecimalMin(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@DecimalMax(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@Size(max, min) | 被注释的元素的大小必须在指定的范围内 |
@Digits (integer, fraction) | 被注释的元素必须是一个数字,其值必须在可接受的范围内 |
@Past | 被注释的元素必须是一个过去的日期 |
@Future | 被注释的元素必须是一个将来的日期 |
@Pattern(value) | 被注释的元素必须符合指定的正则表达式 |
Hibernate-validator附加constraint:
Constraint | 详细信息 |
---|---|
@Email | 被注释的元素必须是电子邮箱地址 |
@Length | 被注释的字符串的大小必须在指定的范围内 |
@NotEmpty | 被注释的字符串的必须非空 |
@Range | 被注释的元素必须在合适的范围内 |
使用实例:
实体类变量constraint+控制类方法变量@Validate
public class DemoReq {
@NotBlank(message="code不能为空")
String code;
@Length(max=10,message="name长度不能超过10")
String name;
//省略setter/getter
}
@GetMapping("/demo/valid")
public String demoValid(@Valid DemoReq req) {
return req.getCode() + "," + req.getName();
}
自定义校验注解
创建注解接口
@Documented
//指定注解的处理类
@Constraint(validatedBy = {ConstantValidatorHandler.class })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
public @interface Constant {
String message() default "{constraint.default.const.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String value();
}
创建处理类,主要是实现ConstraintValidator类
public class ConstantValidatorHandler implements ConstraintValidator<Constant, String> {
private String constant;
@Override
public void initialize(Constant constraintAnnotation) {
//获取设置的字段值
this.constant = constraintAnnotation.value();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
//判断参数是否等于设置的字段值,返回结果
return constant.equals(value);
}
}
使用注解
@Constant(message = "verson只能为1.0",value="1.0")
String version;