spring3升级到spring4通用异常处理返回jsonp多了/**/的解决办法
问题描述
在spring3中定义了通用的异常处理,具体代码如下:
public class CommonExceptionHandler implements HandlerExceptionResolver { private static final Logger logger = LoggerFactory.getLogger(CommonExceptionHandler.class); @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { ModelAndView mv = new ModelAndView(); Map<String,Object> model=new HashMap(); mv.addObject("code", 0); model.put("errorCode", 0); model.put("errorMessage", "系统异常"); logger.info("未捕获处理异常日志开始:"); logger.info(ex.toString()); logger.error("System error",ex); mv.addObject("model",model); return mv; } }
<bean id="exceptionResolver" class="com.******.exception.CommonExceptionHandler" />
因为项目前后端分离,前端使用jsonp读取api数据,spring升级后发现如果出现异常前端js没有弹出提示,调试发现返回的数据中多了/**/。
/**/jQuery172038147174217261703_1533279228074({******});
分析问题
跟踪源代码调试进入DispatcherServlet:
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) { this.logger.debug("ModelAndViewDefiningException encountered", exception); mv = ((ModelAndViewDefiningException)exception).getModelAndView(); } else { Object handler = mappedHandler != null ? mappedHandler.getHandler() : null; mv = this.processHandlerException(request, response, handler, exception); errorView = mv != null; } } if (mv != null && !mv.wasCleared()) { this.render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else if (this.logger.isDebugEnabled()) { this.logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + this.getServletName() + "': assuming HandlerAdapter completed request handling"); } if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { if (mappedHandler != null) { mappedHandler.triggerAfterCompletion(request, response, (Exception)null); } } }
继续跟踪进入render方法:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { Locale locale = this.localeResolver.resolveLocale(request); response.setLocale(locale); View view; if (mv.isReference()) { view = this.resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request); if (view == null) { throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + this.getServletName() + "'"); } } else { view = mv.getView(); if (view == null) { throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a View object in servlet with name '" + this.getServletName() + "'"); } } if (this.logger.isDebugEnabled()) { this.logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + this.getServletName() + "'"); } try { if (mv.getStatus() != null) { response.setStatus(mv.getStatus().value()); } view.render(mv.getModelInternal(), request, response); } catch (Exception var7) { if (this.logger.isDebugEnabled()) { this.logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" + this.getServletName() + "'", var7); } throw var7; } }
然后进入AbstractView里面的render方法:
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { if (this.logger.isTraceEnabled()) { this.logger.trace("Rendering view with name '" + this.beanName + "' with model " + model + " and static attributes " + this.staticAttributes); } Map<String, Object> mergedModel = this.createMergedOutputModel(model, request, response); this.prepareResponse(request, response); this.renderMergedOutputModel(mergedModel, this.getRequestToExpose(request), response); }
this.renderMergedOutputModel发现是一个abstract方法,继续追踪实现类AbstractJackson2View:
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { OutputStream stream = this.updateContentLength ? this.createTemporaryOutputStream() : response.getOutputStream(); Object value = this.filterAndWrapModel(model, request); this.writeContent((OutputStream)stream, value); if (this.updateContentLength) { this.writeToResponse(response, (ByteArrayOutputStream)stream); } }
继续跟踪 this.writeContent:
protected void writeContent(OutputStream stream, Object object) throws IOException { JsonGenerator generator = this.objectMapper.getFactory().createGenerator(stream, this.encoding); this.writePrefix(generator, object); Class<?> serializationView = null; FilterProvider filters = null; Object value = object; if (object instanceof MappingJacksonValue) { MappingJacksonValue container = (MappingJacksonValue)object; value = container.getValue(); serializationView = container.getSerializationView(); filters = container.getFilters(); } if (serializationView != null) { this.objectMapper.writerWithView(serializationView).writeValue(generator, value); } else if (filters != null) { this.objectMapper.writer(filters).writeValue(generator, value); } else { this.objectMapper.writeValue(generator, value); } this.writeSuffix(generator, object); generator.flush(); }
继续跟踪 this.writePrefix,发现是一个未实现的方法,子类具有重写,继续跟踪 MappingJackson2JsonView :
protected void writePrefix(JsonGenerator generator, Object object) throws IOException { if (this.jsonPrefix != null) { generator.writeRaw(this.jsonPrefix); } String jsonpFunction = null; if (object instanceof MappingJacksonValue) { jsonpFunction = ((MappingJacksonValue)object).getJsonpFunction(); } if (jsonpFunction != null) { generator.writeRaw("/**/"); generator.writeRaw(jsonpFunction + "("); } }
原来问题是出现在这里
解决思路
既然是序列化过程中出现问题,那就不走序列化,直接输出json。
@ExceptionHandler(Exception.class) public void handlingException(HttpServletRequest request, HttpServletResponse response, Exception exception){ Map<String,Object> model=new HashMap(); model.put("result", "FAULT"); model.put("errorCode", 0); model.put("errorMessage", "系统异常"); logger.info("未捕获处理异常日志开始:"); logger.info(exception.toString()); logger.error("System error",exception); String callBack = request.getParameter("callback"); response.setStatus(HttpStatus.OK.value()); response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setCharacterEncoding("UTF-8"); response.setHeader("Cache-Control", "no-cache, must-revalidate"); try { String json = JSON.toJSONString(model); if(StringUtils.isNotBlank(callBack)){ response.getWriter().write(callBack+"("+json+");"); }else{ response.getWriter().write(json); } } catch (IOException e) { logger.error("与客户端通讯异常:"+ e.getMessage(), e); } }