如何在Spring Boot应用中构建简单的Controller层统一异常处理框架。
Exception
首先是自定义Exception。作为web app,我们假设Exception针对的是Controller层面,因此需要code字段表示HTTP响应的状态码。
1 | import javax.servlet.http.HttpServletResponse; |
同时定义一个ErrorInfo类,用于将错误信息以Json格式返回给调用方。1
2
3
4
5
6
7
8
9public class ErrorInfo <T>{
public Integer code;
private T message;
private String url;
... // getters & setters
}
ExceptionHandler
通过@RestControllerAdvice
注解,我们定义一个ExceptionHandler,根据RestController抛出的特定Exception,构造HTTP响应的状态码及响应体。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class GlobalExceptionHandler {
// 只处理MyException
// 设置状态码,并将errorInfo作为响应体
(value = MyException.class)
public ErrorInfo<String> errorHandler(HttpServletRequest req, HttpServletResponse resp, MyException e) throws Exception {
ErrorInfo<String> errorInfo = new ErrorInfo<>();
errorInfo.setMessage(e.getMessage());
errorInfo.setCode(e.getCode());
errorInfo.setUrl(req.getRequestURL().toString());
resp.setStatus(e.getCode());
return errorInfo;
}
}
Controller
到了Controller层面,开发者按需抛出自定义Exception即可。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
"/api/login") (
public class LoginController {
... // 其他依赖
private static final Logger logger = LoggerFactory.getLogger(LoginController.class);
(method = RequestMethod.GET)
public LoginResponse UMLogin(@RequestParam String user,
@RequestParam String pwd,
@RequestParam(required = false) Boolean pwdEncoded,
HttpServletRequest request) throws MyException {
try {
if(null != pwdEncoded && pwdEncoded) {
pwd = new String(Base64.decodeBase64(pwd));
}
... // 其他准备工作
// 验证用户名及密码
LoginResult loginResult = PasswordUtil.checkUserAndPassword(user, pwd);
if(null != loginResult && loginResult.getRetCode() == 0) {
logger.info("用户{} 鉴权通过.", user);
... // 其他善后工作
return new LoginResponse(user, MyConstants.LOGIN_RESULT_SUCCESS);
} else {
logger.info("用户鉴权失败: {}", user, PasswordUtil.getErrorMessage(loginResult, user));
// 抛出自定义Exception
throw new MyException(HttpServletResponse.SC_FORBIDDEN, "用户" + user + "鉴权失败");
}
} catch (Exception e) {
String msg = "鉴权过程异常 ";
logger.error(msg, e);
// 转换后抛出自定义Exception
if(e instanceof MyException) {
throw (MyException) e;
} else {
throw new MyException(msg);
}
}
}
}
局限
最重要的局限在于对于Spring框架层及Interceptor层的异常无法捕获,需要单独处理。