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);
    }
}

  

 

posted @ 2018-08-03 15:17  程序小强  阅读(587)  评论(0编辑  收藏  举报