springboot官网说了三种异常处理,分为机器客户端和浏览器客户端,区别为机器客户端请求返回的是头中Accept是"/"或者空,异常返回值json,浏览器客户端请求头中Accept是"text/html" 返回的是html
如下图:
机器客户端:
浏览器客户端:
下面对两种客户端的三种异常处理方式做简单分析:
一、浏览器客户端:
官网提供两种方式,一种是直接在public.error文件夹中定义静态文件 src/ +- main/ +- java/ | + <source code> +- resources/ +- public/ +- error/ | +- 404.html
第二种是利用模板引擎实现,官网的例子是freemaker(对模板引擎暂不熟悉,未深入了解)
src/ +- main/ +- java/ | + <source code> +- resources/ +- templates/ +- error/ | +- 5xx.ftl
工程实现:只需生成html即可
二、机器客户端
1、实现方式 继承BasicErrorControllor
public class MyErrorController extends BasicErrorController {
public MyErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
super(errorAttributes, errorProperties, errorViewResolvers);
}
@Override
protected Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
Map<String, Object> attributes = super.getErrorAttributes(request, includeStackTrace);
/**
此处是将默认的返回值删除,并加上项目中定义的异常
*/
attributes.remove("timestamp");
attributes.remove("error");
attributes.remove("path");
attributes.remove("status");
String status = attributes.get("message").toString();
ErrorEnum errorEnum = ErrorEnum.getByCode(status);
attributes.put("code",errorEnum.getCode());
attributes.put("message",errorEnum.getMessage());
attributes.put("canRetry",errorEnum.getCanRetry());
return attributes;
}
}
springboot中为了让自定义controller可以使用,需要注册为bean
添加一个配置文件,new MyErrorController的参数可以参考ErrorMvcAutoConfiguration来进行配置
@Configuration
public class ErrorConfig {
@Bean
public MyErrorController basicErrorController(ErrorAttributes errorAttributes,ServerProperties serverProperties, DispatcherServletPath dispatcherServletPath, ObjectProvider<List<ErrorViewResolver>> errorViewResolversProvider) {
return new MyErrorController(errorAttributes, serverProperties.getError(), errorViewResolversProvider.getIfAvailable());
}
}
加一个异常枚举类
public enum ErrorEnum {
ID_NOT_NULL("F001", "编号不能为空", false),
UNKNOWN("999", "不可知异常", false);
private String code;
private String message;
private Boolean canRetry;
ErrorEnum(String code, String message, Boolean canRetry) {
this.code = code;
this.message = message;
this.canRetry = canRetry;
}
public static ErrorEnum getByCode(String code) {
for (ErrorEnum errorEnum: ErrorEnum.values()) {
if (errorEnum.code.equals(code)) {
return errorEnum;
}
}
return UNKNOWN;
}
public String getCode() {
return code;
}
public String getMessage() {
return message;
}
public Boolean getCanRetry() {
return canRetry;
}
}
2、使用@ControllerAdvice
此处可以自定义异常,也可以捕获所有的异常,根据需要设置
a、捕获所有的异常
@ControllerAdvice(basePackages = {"com.gcl.manager.Controller"}) //此处是定义的处理异常的包,也可以指定到具体的类
public class MyControllerAdvice extends ResponseEntityExceptionHandler {
@ExceptionHandler(Exception.class) //此处是处理所有的异常
@ResponseBody
ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
//HttpStatus status = getStatus(request);此处可以不要,需要的话直接copy官网的修改
/**
* 此处可以自定义返回类型
*/
Map<String, Object> attributes = new HashMap<>();
ErrorEnum errorEnum = ErrorEnum.getByCode(status.value() + ""); //此处非必须这样操作,随意修改
attributes.put("code",errorEnum.getCode());
attributes.put("message",errorEnum.getMessage());
attributes.put("canRetry",errorEnum.getCanRetry());
// new ResponseEntity<>(new CustomErrorType(status.value(), ex.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
//此处可以定义一个类来处理返回的对象,ResponseEntity接收的是T,HttpStatus
return new ResponseEntity<>(attributes, HttpStatus.INTERNAL_SERVER_ERROR);
}
b、捕获对应的异常
与a相比,只需要自定义异常,并catch抛出异常即可
简单异常类
public class MyCustomerException extends RuntimeException {
public MyCustomerException(String message) {
super(message);
}
}
任意可能出现异常的地方抛出异常
try {
//根据实际校验
checkProduct(product);
}catch (Exception e){
throw new MyCustomerException("校验出错");
}
最终将a中的Exception.class改为自定义的MyCustomerException.class就可以实现拦截自定义异常,@ExceptionHandler参数是数组,可以捕获多个异常