本节要实现的是,当我们请求的Controller接口有错误时,可以全局捕获到相应的异常,并返回给用户,这里使用的是通过ControllerAdvice和ExceptionHandler捕获异常和错误信息,向前端返回json格式的状态码及异常描述信息。
1.创建一个全局异常捕获处理类GlobalExceptionHandler
import com.qiqi.utils.ExceptionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.TypeMismatchException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 全局异常捕获处理
*/
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* http请求的方法不正确
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseBody
public String httpRequestMethodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException e) {
logger.error("http请求的方法不正确:【"+e.getMessage()+"】");
return ExceptionUtil.resultOf(ResultStatusCode.RequestMethodNotAllowed);
}
/**
* 请求参数不全
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseBody
public String missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException e) {
logger.error("请求参数不全:【"+e.getMessage()+"】");
return ExceptionUtil.resultOf(ResultStatusCode.MissingServletRequestParameter);
}
/**
* 请求参数类型不正确
*/
@ExceptionHandler(TypeMismatchException.class)
@ResponseBody
public String typeMismatchExceptionHandler(TypeMismatchException e) {
logger.error("请求参数类型不正确:【"+e.getMessage()+"】");
return ExceptionUtil.resultOf(ResultStatusCode.TypeMismatchException);
}
/**
* 数据格式不正确
*/
@ExceptionHandler(DataFormatException.class)
@ResponseBody
public String dataFormatExceptionHandler(DataFormatException e) {
logger.error("数据格式不正确:【"+e.getMessage()+"】");
return ExceptionUtil.resultOf(ResultStatusCode.DataFormatException);
}
/**
* 用户没找到
*/
@ExceptionHandler(UserNotFoundException.class)
@ResponseBody
public String userNotFoundExceptionHandler(UserNotFoundException e) {
logger.error("用户没找到:【"+e.getMessage()+"】");
return ExceptionUtil.resultOf(ResultStatusCode.UserNotExist);
}
/**
* 非法输入
*/
@ExceptionHandler(IllegalArgumentException.class)
@ResponseBody
public String illegalArgumentExceptionHandler(IllegalArgumentException e) {
logger.error("非法输入:【"+e.getMessage()+"】");
return ExceptionUtil.resultOf(ResultStatusCode.IllegalArgumentException);
}
@ExceptionHandler //处理其他异常
@ResponseBody
public String allExceptionHandler(Exception e){
logger.error("具体错误信息:【"+ExceptionUtil.getErrorMessage(e)+"】"); //会记录出错的代码行等具体信息
int count = 0; //只打印15行的错误堆栈
for (StackTraceElement stackTraceElement : e.getStackTrace()) {
logger.error(stackTraceElement.toString());
if(count++ > 13) break;
}
return ExceptionUtil.resultOf(ResultStatusCode.SystemException);
}
}
2.自定义了一些异常UserNotFoundException、DataFormatException等。
public class UserNotFoundException extends Exception {
public UserNotFoundException() {
}
public UserNotFoundException(String message) {
super(message);
}
public UserNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}
3.自定义一个异常状态码的枚举类 ResultStatusCode
public enum ResultStatusCode {
Success("0", "Success"),
UserNotExist("1", "User not exist"),
InValidParameter("2","Invalid parameter"),
DataFormatException("4", "DataFormat exception"),
DataNotExistException("5", "DataNotExistException"),
TimeFormatException("6","TimeFormat Exception"),
PictureFormatException("7","PictureFormat Exception"),
IllegalArgumentException("8","IllegalArgumentException"),
TokenInvalidOrOverdueException("9", "Token invalid or overdue exception"),
AuthorizationCodeError("10", "authorization code error"),
WrongSignatureException("11","Wrong Signature Exception"),
SystemException("50", "system Exception"),
MissingServletRequestParameter("400","Missing servletRequest parameter"),
TypeMismatchException("401","Request parameter Type not match"),
RequestMethodNotAllowed("405","Request method not Allowed"),
;
private String code;
private String msg;
private ResultStatusCode(String code,String msg){
this.code=code;
this.msg=msg;
}
public String getMsg(){
return this.msg;
}
public String getCode(){
return this.code;
}
}
4.自定义异常处理工具类
里面编写两个方法:
方法一:打印异常的详细信息
方法二:将枚举类型的状态码及描述转为json
注意:fastjson包,必须为1.2.24,否则没有configEnumAsJavaBean()方法。
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.qiqi.exception.ResultStatusCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* 返回异常的处理
*/
public class ExceptionUtil {
private static final Logger logger = LoggerFactory.getLogger(ExceptionUtil.class);
private ExceptionUtil() {
}
/**
* 获取异常信息
*
* @param e 异常
*/
public static String getErrorMessage(Exception e) {
StringWriter sw = null;
PrintWriter pw = null;
try {
sw = new StringWriter();
pw = new PrintWriter(sw);
// 将出错的栈信息输出到printWriter中
e.printStackTrace(pw);
pw.flush();
sw.flush();
} finally {
if (sw != null) {
try {
sw.close();
} catch (IOException e1) {
logger.info(e1.getMessage());
}
}
if (pw != null) {
pw.close();
}
}
return sw.toString();
}
/**
* 异常信息-->json
*
*/
public static String resultOf(ResultStatusCode resultStatusCode) {
SerializeConfig config = new SerializeConfig();
config.configEnumAsJavaBean(ResultStatusCode.class);
return JSON.toJSONString(resultStatusCode, config);
}
}
5.使用
Controller层:抛出异常让GlobalExceptionHandler捕获处理
注意:显示的throw异常后,后面的代码不再会被执行到。
/**
* 删除用户
*/
@RequestMapping(value = "/delete", method = RequestMethod.GET)
public Response deleteUser(@RequestParam(value = "user_id", required = true) String userId)
throws UserNotFoundException {
Response response = new Response();
long id = Long.parseLong(userId);
userService.deleteUser(id);
response = new Response("0","删除用户成功",null);
logger.info("删除用户:id="+ userId + " result="+response.toString());
return response;
}
Service层: 抛出异常
public void deleteUser(long userId )throws UserNotFoundException {
if (userRepository.existsById(userId) == false){
throw new UserNotFoundException();
}
userRepository.deleteById(userId);
}
6.测试结果
一:填写请求参数时,故意让String类型的user_id =“3a",在long id = Long.parseLong(userId);转换时,会抛出IllegalArgumentException异常的子类NumberFormatException。由于我们在GlobalExceptionHandler捕获处理了IllegalArgumentException这个非法输入异常,并对其定义了状态码及描述。所以前端会收到json格式的响应:
{"code":"8","msg":"IllegalArgumentException"}
而在控制台,会输出日志:2019-01-04 14:39:21.437 ERROR com.qiqi.exception.GlobalExceptionHandler Line:118 - 非法输入:【For input string: "3a"】
二:请求时发送POST请求(代码中要求是GET请求)
前端响应:{"code":"405","msg":"Request method not Allowed"}
后台日志:2019-01-04 14:41:41.963 ERROR com.qiqi.exception.GlobalExceptionHandler Line:58 - http请求的方法不正确:【Request method 'POST' not supported】
三:在程序中抛出IOException
IOException异常没有单独捕获,被捕获在allExceptionHandler方法中,在这个方法中打印出了异常的详细信息,包括出现异常的代码行号等,同时还打印了异常的堆栈信息。
前端响应:{"code":"50","msg":"system Exception"}