Springboot全局异常处理
一、Spring Boot默认的异常处理机制
默认情况下,springboot提供了两种相应方式:① 浏览器请求头为Accept: text/html,springboot会默认相应一个html文档内容,为“Whitelabel Error Page”;② 会返回Json格式字符串信息。
原理:
/**
* springboot 默认提供了程序出错的结果映射路径/error,
* 这个/error请求会在BasicErrorController中处理。
* 其内部是通过判断请求头中的Accept的内容是否为text/html来区分请求是来自
* 客户端浏览器(浏览器通常默认自动发送请求头内容Accept:text/html)
* 还是客户端接口的调用,以此来决定返回页面视图还是 JSON 消息内容。
* public static final String TEXT_HTML_VALUE = "text/html";
*/
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections
.unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
HttpStatus status = getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return new ResponseEntity<>(status);
}
Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
return new ResponseEntity<>(body, status);
}
二、如何自定义错误页面
三、通过@ControllerAdvice注解来处理异常
两种情况:
局部异常处理 @Controller + @ExceptionHandler
全局异常处理 @ControllerAdvice + @ExceptionHandler
1. 局部异常处理 @Controller + @ExceptionHandler
局部异常主要用到的是@ExceptionHandler注解,此注解注解到类的方法上,当此注解里定义的异常抛出时,此方法会被执行。如果@ExceptionHandler所在的类是@Controller,则此方法只作用在此类。如果@ExceptionHandler所在的类带有@ControllerAdvice注解,则此方法会作用在全局。
@Controller
public class BaseErrorController extends AbstractController{
private Logger logger = LoggerFactory.getLogger(this.getClass());
@RequestMapping(value="/ex")
@ResponseBody
public String error(){
int i=5/0;
return "ex";
}
//局部异常处理
@ExceptionHandler(Exception.class)
@ResponseBody
public String exHandler(Exception e){
// 判断发生异常的类型是除0异常则做出响应
if(e instanceof ArithmeticException){
return "发生了除0异常";
}
// 未知的异常做出响应
return "发生了未知异常";
}
}
该注解用于标注处理方法处理那些特定的异常。被该注解标注的方法可以有以下任意顺序的参数类型:
-
Throwable、Exception 等异常对象;
-
ServletRequest、HttpServletRequest、ServletResponse、HttpServletResponse;
-
HttpSession 等会话对象;
-
org.springframework.web.context.request.WebRequest;
-
java.util.Locale;
-
java.io.InputStream、java.io.Reader;
-
java.io.OutputStream、java.io.Writer;
-
org.springframework.ui.Model;
并且被该注解标注的方法可以有以下的返回值类型可选:
-
ModelAndView;
-
org.springframework.ui.Model;
-
java.util.Map;
-
org.springframework.web.servlet.View;
-
@ResponseBody 注解标注的任意对象;
-
HttpEntity<?> or ResponseEntity<?>;
-
void;
以上罗列的不完全,更加详细的信息可参考:Spring ExceptionHandler。
2. 全局异常处理 @ControllerAdvice + @ExceptionHandler
在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler、@InitBinder、@ModelAttribute,并应用到所有@RequestMapping中。
简单的说,进入Controller层的错误才会由@ControllerAdvice处理,拦截器抛出的错误以及访问错误地址的情况@ControllerAdvice处理不了,由SpringBoot默认的异常处理机制处理。
四、自定义
1. 自定义返回结果
package com.kuen.language.common.to;
import org.springframework.http.HttpStatus;
import java.util.HashMap;
import java.util.Map;
/**
* @program: Language
* @description: 返回结果对象
* @author: Kuen.Hou
* @create: 2022-03-05 09:59
**/
public class ResultObject extends HashMap<String, Object> {
private static final long serialVersionUID = -2227292363737068440L;
public ResultObject() {
put("code", 0);
}
@Override
public ResultObject put(String key, Object value) {
super.put(key, value);
return this;
}
public ResultObject data(Object value) {
super.put("data", value);
return this;
}
public static ResultObject error() {
return error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "未知异常,请联系管理员");
}
public static ResultObject error(String msg) {
return error(HttpStatus.INTERNAL_SERVER_ERROR.value(), msg);
}
public static ResultObject error(int code, String msg) {
ResultObject r = new ResultObject();
r.put("code", code);
r.put("msg", msg);
return r;
}
public static ResultObject ok() {
return new ResultObject();
}
public static ResultObject ok(String msg) {
ResultObject r = new ResultObject();
r.put("msg", msg);
return r;
}
public static ResultObject ok(Map<String, Object> map) {
ResultObject r = new ResultObject();
r.putAll(map);
return r;
}
}
2. 自定义异常
package com.kuen.language.multiLanguage.exception;
/**
* @program: Language
* @description: 自定义异常
* @author: Kuen.Hou
* @create: 2022-03-05 10:31
**/
public class CustomException extends RuntimeException {
private static final long serialVersionUID = -173110604484282583L;
private final String message;
private int code = 500;
public CustomException(String message) {
super(message);
this.message = message;
}
public CustomException(String message, Throwable e) {
super(message, e);
this.message = message;
}
public CustomException(String message, int code) {
super(message);
this.message = message;
this.code = code;
}
public CustomException(String message, int code, Throwable e) {
super(message, e);
this.message = message;
this.code = code;
}
}
3. 自定义全局异常处理器
package com.kuen.language.common.handler;
import com.kuen.language.common.to.ResultObject;
import com.kuen.language.multiLanguage.exception.CustomException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
/**
* @program: Language
* @description: 自定义异常处理器
* @author: Kuen.Hou
* @create: 2022-03-05 10:56
**/
@RestControllerAdvice
@Slf4j
public class CustomExceptionHandler {
/**
* 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
* @param binder
*/
@InitBinder
public void initBinder(WebDataBinder binder) {
// System.out.println("请求有参数才进来");
}
/**
* 把值绑定到Model中,使全局@RequestMapping可以获取到该值
* @param model
*/
@ModelAttribute
public void addAttributes(Model model) {
// model.addAttribute("author", "Kuen.Hou");
}
@ExceptionHandler(Exception.class)
public Object handleException(Exception e, HttpServletRequest req){
ResultObject r = new ResultObject();
//业务异常
if(e instanceof CustomException){
r.put("code", ((CustomException) e).getCode());
r.put("msg", e.getMessage());
}else{//系统异常
r.put("code","500");
r.put("msg","未知异常,请联系管理员");
}
//使用HttpServletRequest中的header检测请求是否为ajax, 如果是ajax则返回json, 如果为非ajax则返回view(即ModelAndView)
String contentTypeHeader = req.getHeader("Content-Type");
String acceptHeader = req.getHeader("Accept");
String xRequestedWith = req.getHeader("X-Requested-With");
if ((contentTypeHeader != null && contentTypeHeader.contains("application/json"))
|| (acceptHeader != null && acceptHeader.contains("application/json"))
|| "XMLHttpRequest".equalsIgnoreCase(xRequestedWith)) {
return r;
} else {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", e.getMessage());
modelAndView.addObject("url", req.getRequestURL());
modelAndView.addObject("stackTrace", e.getStackTrace());
modelAndView.setViewName("error");
return modelAndView;
}
}
}