- 使用工具Postman-win64-5.5.2-Setup.exe 模拟除浏览器外的其他客户端的错误处理机制
- 自定义一个异常类
public class UserNotExitException extends RuntimeException {
public UserNotExitException() {
super("用户不存在!");
}
}
- 自定义错误的Json数据
1.自定义异常处理&定制返回的json数据格式:
- 使用 @ControllerAdvice 实现全局异常处理,只需要定义类,添加该注解即可
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(UserNotExitException.class)
//不能在参数中声明Map,否则springmvc异常处理不起作用
//需要返回json格式给客户端显示
@ResponseBody
public ModelAndView handleExceptin(Exception e) {
ModelAndView mv = new ModelAndView();
mv.addObject("code", "user.notExist");
mv.addObject("message", e.getMessage());
return mv;
}
}
解释:
(1)@ControllerAdvice ,很多可能没有用过这个注解,这是一个增强的 Controller,是SpringMVC的功能,可以实现全局异常处理。
(2)@ExceptionHandler 注解用来指明异常的处理类型,表明处理
UserNotExitException.class
即如果这里指定为 NullpointerException,就只处理空指针的异常类,数组越界等异常就不会进到这个方法中来。
当然,也可以指明为Exception.class,处理所有异常。
-
存在问题抛出:上面的Controller层代码经过测试,页面并不能显示我们自定义的异常信息,而是之前定义的版本:
原因:出在ModelAndView的使用上, 我们没有跳转页面,所有ModelAndView没有起作用。因此换成Map
@ControllerAdvice
public class MyExceptionHandler {
//不能在参数中声明Map,否则springmvc异常处理不起作用
//需要返回json格式给客户端显示
@ExceptionHandler(UserNotExitException.class)
@ResponseBody
public Map<String,Object> handleExceptin(Exception e) {
Map<String,Object> map = new HashMap<>();
map.put("code", "user.notExist");
map.put("message", e.getMessage());
return map;
}
}
页面效果:
(1)浏览器
(2)模拟其他客户端访问:
-
存在问题:我们发现浏览器和客户端返回的都是json,没有自适应效果
- SpringBoot源码剖析:
- ErrorMvcAutoConfiguration.java中
BasicErrorController类:是一个自适应的类,会进行自适应处理
@Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes,
ObjectProvider<ErrorViewResolver> errorViewResolvers) {
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
表现在:浏览器访问返回“text/html”,客户端访问返回json
@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, isIncludeStackTrace(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, isIncludeStackTrace(request, MediaType.ALL));
return new ResponseEntity<>(body, status);
}
因此,自适应解决方法:转发到/error进行自适应响应效果处理
"forward:/error"
@ExceptionHandler(UserNotExitException.class)
public String handleExceptin(Exception e) {
Map<String,Object> map = new HashMap<>();
map.put("code", "user.notExist");
map.put("message", e.getMessage());
return "forward:/error";
}
效果展示:
(1)浏览器
(2)客户端
-
来到这一步,我们发现,自适应有了,不过浏览器页面状态码 status=200,显然不对。
-
源码一顿分析,如下:
-
查看源码给我们提供了解决方法:传入我们自己的错误状态码 4xx 5xx,否则就不会进入定制错误页面的解析流程
传入自己的错误状态码 如:4xx,5xx
-
@ExceptionHandler(UserNotExitException.class) public String handleExceptin(Exception e, HttpServletRequest request) { Map<String,Object> map = new HashMap<>(); //传入自己的错误状态码 如:4xx,5xx //Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code"); request.setAttribute("javax.servlet.error.status_code", 500); map.put("code", "user.notExist"); map.put("message", e.getMessage()); return "forward:/error"; }
- 效果展示:
(1)浏览器:
(2)客户端