很多人都会在平时开发过程中遇到400异常,并且也没有走到服务端controller中,就变得有些不知所措。
我们知道SpringMVC从DispatchServlet开始接收与分发请求,从入口开始debug,还能找不到问题所在么?
从DispatchServlet的doDispatch()方法开始处理请求:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { //删除一些代码 try { ModelAndView mv = null; Exception dispatchException = null; try { // 删除一些代码 try { // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); } finally { if (asyncManager.isConcurrentHandlingStarted()) { return; } } applyDefaultViewName(request, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; // 这里捕获了异常TypeMismatchException } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { } finally { // 删除一些代码 } }
其实在这儿我们就能看到exception的具体异常栈,有兴趣的可以继续看springMVC的处理方法processDispatchResult。
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception { boolean errorView = false; if (exception != null) { if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered", exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(request, response, handler, exception);// 执行这个方法 errorView = (mv != null); } } // 方便阅读,删除了其他代码 }
这个方法中对异常进行判断,发现不是“ModelAndViewDefiningException”就交给processHandlerException方法继续处理。
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // Check registered HandlerExceptionResolvers... ModelAndView exMv = null; for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) { exMv = handlerExceptionResolver.resolveException(request, response, handler, ex); if (exMv != null) { break; } } // 去掉了一些代码 throw ex; }
这里看到for循环来找一个handlerExceptionResolver来处理这个异常。handler列表有spring自带的ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver以及自定义的exceptionResolver。
这些都继承自 AbstractHandlerExceptionResolver 类,这个类是一个抽象类,它实现了HandlerExceptionResolver接口,它对HandlerExceptionResolver接口约定的方法的所实现代码如下:
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { if (shouldApplyTo(request, handler)) { logException(ex, request); prepareResponse(ex, response); return doResolveException(request, response, handler, ex); } else { return null; } }
首先判断当前异常处理器是否可以处理当前的目标handler。例如通过for循环依次发现轮到DefaultHandlerExceptionResolver才能处理,那么最终会执行该handlerExceptionResolver的doResolveException方法。
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { try { if (ex instanceof NoSuchRequestHandlingMethodException) { return handleNoSuchRequestHandlingMethod(...); } // 删除部分else if instanceof 判断 else if (ex instanceof TypeMismatchException) { // 执行到了这里 return handleTypeMismatch((TypeMismatchException) ex, request, response, handler); } // 删除部分else if instanceof 判断 else if (ex instanceof BindException) { return handleBindException((BindException) ex, request, response, handler); } } catch (Exception handlerException) { } return null; }
通过对异常类型的判断,来执行相应handleXXXException方法。而handleXXXException方法中,有很多是会抛出400错误的!
举个几个栗子:
protected ModelAndView handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { response.sendError(400, ex.getMessage()); return new ModelAndView(); } protected ModelAndView handleServletRequestBindingException(ServletRequestBindingException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { response.sendError(400, ex.getMessage()); return new ModelAndView(); } protected ModelAndView handleTypeMismatch(TypeMismatchException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { response.sendError(400); return new ModelAndView(); } protected ModelAndView handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { response.sendError(400); return new ModelAndView(); } protected ModelAndView handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { response.sendError(400); return new ModelAndView(); } protected ModelAndView handleMissingServletRequestPartException(MissingServletRequestPartException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { response.sendError(400, ex.getMessage()); return new ModelAndView(); } protected ModelAndView handleBindException(BindException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { response.sendError(400); return new ModelAndView(); }
那么抛出400错误的时候该怎么解决呢?
从服务端角度出发,可以定义完善的全局异常处理器exceptionHandler,把易抛出400的错误例如TypeMismatchException、BindException都给处理掉,返回能看得懂的信息。
从客户端请求过程中来看,可以自定义handlerExceptionResolver,只需实现HandlerExceptionResolver接口即可,例如:
public class ApiHandlerExceptionResolver implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) { ModelAndView model = new ModelAndView(); // do something ... return model; } }
所以遇到400错误的时候不要慌,毕竟400它是个标准的错误码,好好debug或者查阅一下相关资料便能迎刃而解。