使用拦截器(interceptor)报错:getWriter() has already been called for this response

一:编写拦截器

@Component
@Slf4j
public class ApiInterceptor extends HandlerInterceptorAdapter {



    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws IOException {
       
        PrintWriter out = response.getWriter();
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        // 如果打上了AuthToken注解则需要验证token
        if (method.getAnnotation(AuthToken.class) != null || handlerMethod.getBeanType().getAnnotation(AuthToken.class) != null) {
            String uri = request.getRequestURI();
            //从header中拿token,并进行解析
            String token = request.getHeader("token");
            if (StringUtils.isBlank(token)) {
                out.append(JSONObject.fromObject(PsBudgetResultVO
                        .builder()
                        .code(BaseConstants.STRING_ZERO)
                        .msg("请尝试登录!")
                        .build())
                        .toString());
                return false;
            }
        }

        return true;

    }
}

二:注册拦截器

@Configuration
@EnableWebMvc
@EnableWebSocket
@ServletComponentScan(basePackages = "com.cn")
public class WebConfig implements WebSocketConfigurer,WebMvcConfigurer {



    @Resource
    private ApiInterceptor apiInterceptor;


    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(apiInterceptor)
                .addPathPatterns("/api/**");

    }

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        
    }
}

三:发送请求

发送请求:http://localhost:8080/**/api/****?

报错:

四:分析报错原因

根据报错信息:

java.lang.IllegalStateException: getWriter() has already been called for this response
    at org.apache.catalina.connector.Response.getOutputStream(Response.java:550) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.apache.catalina.connector.ResponseFacade.getOutputStream(ResponseFacade.java:210) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
    at javax.servlet.ServletResponseWrapper.getOutputStream(ServletResponseWrapper.java:107) ~[javax.servlet-api-4.0.1.jar:4.0.1]
    at javax.servlet.ServletResponseWrapper.getOutputStream(ServletResponseWrapper.java:107) ~[javax.servlet-api-4.0.1.jar:4.0.1]
    at org.springframework.security.web.util.OnCommittedResponseWrapper.getOutputStream(OnCommittedResponseWrapper.java:145) ~[spring-security-web-5.3.9.RELEASE.jar:5.3.9.RELEASE]
    at javax.servlet.ServletResponseWrapper.getOutputStream(ServletResponseWrapper.java:107) ~[javax.servlet-api-4.0.1.jar:4.0.1]
    at org.springframework.security.web.util.OnCommittedResponseWrapper.getOutputStream(OnCommittedResponseWrapper.java:145) ~[spring-security-web-5.3.9.RELEASE.jar:5.3.9.RELEASE]

我们可以看到Response.java:550位置处报错。

 我们可以看到usingWriter=true,是导致异常的原因。

那么接下来我们只要知道哪一步将usingWriter=true就可以了。

(一):源码1(Response550

@Override
public ServletOutputStream getOutputStream()
    throws IOException {

     /**
       * 1:判断usingWriter是否为true,为true则直接判处异常
     */
    if (usingWriter) {
        throw new IllegalStateException
            (sm.getString("coyoteResponse.getOutputStream.ise"));
    }
     /**
       * 1:将usingOutputStream 赋值为true
     */
    usingOutputStream = true;
    if (outputStream == null) {
        outputStream = new CoyoteOutputStream(outputBuffer);
    }
    return outputStream;

}

  

(二):源码2

我么再在拦截器中使用了getWriter(),方法,方便我们返回我们的自定义错误信息。

源码getWriter()源码如下:

@Override
public PrintWriter getWriter()
    throws IOException {

    /**
      *1:首先判断 usingOutputStream,当usingOutputStream为true,直接抛出异
      *常      
    */
    if (usingOutputStream) {
        throw new IllegalStateException
            (sm.getString("coyoteResponse.getWriter.ise"));
    }

    if (ENFORCE_ENCODING_IN_GET_WRITER) {
        /*
         * If the response's character encoding has not been specified as
         * described in <code>getCharacterEncoding</code> (i.e., the method
         * just returns the default value <code>ISO-8859-1</code>),
         * <code>getWriter</code> updates it to <code>ISO-8859-1</code>
         * (with the effect that a subsequent call to getContentType() will
         * include a charset=ISO-8859-1 component which will also be
         * reflected in the Content-Type response header, thereby satisfying
         * the Servlet spec requirement that containers must communicate the
         * character encoding used for the servlet response's writer to the
         * client).
         */
        setCharacterEncoding(getCharacterEncoding());
    }

    /**
      *2:将  usingWriter  = true   
     */
    usingWriter = true;
    outputBuffer.checkConverter();
    if (writer == null) {
        writer = new CoyoteWriter(outputBuffer);
    }
    return writer;
}

看了上面我们可以知道,源码1中使我们的http请求默认使用的,源码2 是我们用来返回自定义异常的。这两处是相互冲突的,我们只能在一个http请求中使用一种返回值说明。

五:解决

问题已经明确,下面就是如何解决了。

(一):适用我自己的情况,因为我只需要在返回false的时候才会需要自定义返回值,所以我只需要在返回false去调用PrintWriter out = response.getWriter();即可。

修改后代码如下:

@Component
@Slf4j
@Component
@Slf4j
public class ApiInterceptor extends HandlerInterceptorAdapter {



    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws IOException {

   
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        // 如果打上了AuthToken注解则需要验证token
        if (method.getAnnotation(AuthToken.class) != null || handlerMethod.getBeanType().getAnnotation(AuthToken.class) != null) {
            String uri = request.getRequestURI();
            //从header中拿token,并进行解析
            String token = request.getHeader("token");
            if (StringUtils.isBlank(token)) {
                PrintWriter out = response.getWriter();
                out.append(JSONObject.fromObject(ResultVO
                        .builder()
                        .code(BaseConstants.STRING_ZERO)
                        .msg("请尝试登录!")
                        .build())
                        .toString());
                return false;
            }
        }

        return true;

    }
}

(二):我们在一个请求中只使用一种返回方式即可。

修改后如下:

@Component
@Slf4j
@Component
@Slf4j
public class ApiInterceptor extends HandlerInterceptorAdapter {



    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws IOException {


        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        // 如果打上了AuthToken注解则需要验证token
        if (method.getAnnotation(AuthToken.class) != null || handlerMethod.getBeanType().getAnnotation(AuthToken.class) != null) {
            String uri = request.getRequestURI();
            //从header中拿token,并进行解析
            String token = request.getHeader("token");
            if (StringUtils.isBlank(token)) {
                String res = JSONObject.fromObject(ResultVO
                        .builder()
                        .code(BaseConstants.STRING_ZERO)
                        .msg("请尝试登录!")
                        .build())
                        .toString();
                response.setContentType("application/json;charset=utf-8");
                ServletOutputStream outputStream = response.getOutputStream();
                outputStream.write(res.getBytes());
                return false;
            }
        }

        return true;

    }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值