FastJSON serialization HttpServletResponse error has already been called for this response and Stream not in async mode

overview

This article documents a problem encountered when using FastJSON. Two different applications, the same code snippet, but inconsistent error logs.

getOutputStream() has already been called for this response

In the production environment, the EFK log query platform records the following detailed error information:

write javaBean error, fastjson version 1.2.68, class org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryResponseWrapper, fieldName : 2, getOutputStream() has already been called for this response
at com.aba.open.merchant.aop.ControllerLogAop.doBefore(ControllerLogAop.java:45)
at com.aba.open.merchant.aop.ControllerLogAop.doAround(ControllerLogAop.java:68)
at com.aba.open.merchant.controller.MerchantLoginController$$EnhancerBySpringCGLIB$$420d319e.login(<generated>)

Now that the specific number of lines of the code that reported the error has been printed out, let's first look at the code:

@Aspect
@Component
@Slf4j
public class ControllerLogAop {
    
    

    @Pointcut("execution(public * com.aba.open.merchant.controller..*.*(..))")
    public void webLog() {
    
    
    }

    /**
     * 在切点之前织入
     */
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
    
    
        log.info("===== Start =====");
        // 打印请求入参
        Object[] args = joinPoint.getArgs();
        Object[] arguments = new Object[args.length];
        for (int i = 0; i < args.length; i++) {
    
    
            if (args[i] instanceof ServletRequest || args[0] instanceof ServletResponse || args[0] instanceof MultipartFile) {
    
    
                continue;
            }
            arguments[i] = args[i];
        }
        log.info("类{}方法{},请求参数= {}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), JSON.toJSONString(arguments));
    }

    /**
     * 在切点之后织入
     */
    @After("webLog()")
    public void doAfter() throws Throwable {
    
    
        log.info("===== End =====");
    }

    /**
     * 环绕
     */
    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    
    
        long startTime = System.currentTimeMillis();
        Object result = proceedingJoinPoint.proceed();
        // 打印出参    
        log.info("返回参数= {}", JSON.toJSONString(result));
        // 执行耗时    
        log.info("耗时{} ms", System.currentTimeMillis() - startTime);
        return result;
    }
}

Code analysis: very common AOP programming, the purpose is to record the request body and response body of the controller interface, to facilitate troubleshooting.

The reason for the error [should] be caused by an error in FastJSON serializing a certain request body or response body parameter.

At the same time, according to the omitted log, it can be known that the error message printed when requesting the following interface:

@PostMapping("/login")
@ApiOperation(value = "手机验证码登录", produces = "application/json", consumes="application/json")
public Response<String> login(@RequestBody @Valid MerchantLoginDTO loginDTO, HttpServletRequest request, HttpServletResponse response) {
    
    
}

The above error is reported in the production environment. Although there are lines of code that report errors, the root cause of the error may not be immediately analyzed.

It is best to reproduce the problem locally, then solve it locally, submit the code, release it online, and return to verify.

If you already know which interface is reporting the error, then use postman to debug locally and try to reproduce the problem.

Fortunately, the problem can be reproduced:
insert image description here
combined with the error code line, it is not difficult to know the cause of the error: FastJSON failed to serialize HttpServletResponse, which caused this error.

The solution to the error report is naturally very simple: modify the conditional judgment statement in forthe loop , which is obviously a small error when typing or copying the code , it should be .ifargs[0]args[i]

In fact, after this modification, the error disappears.

UT010034: Stream not in async mode

It is worth mentioning that after repairing merchantthe error log of the above application. After a while, another service useralso broke the FastJSON serialization problem. The specific error message is as follows:

write javaBean error, fastjson version 1.2.83, class io.undertow.servlet.spec.HttpServletResponseImpl, fieldName : 1,
write javaBean error, fastjson version 1.2.83, class io.undertow.server.HttpServerExchange, 
fieldName : exchange, UT010034: Stream not in async mode
at com.aba.enduser.aop.ControllerLogAop.doBefore(ControllerLogAop.java:45)

Specific error code snippet:

@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
    
    
    // 打印请求相关参数
    log.info("===== Start =====");
    // 打印请求入参
    Object[] args = joinPoint.getArgs();
    Object[] arguments = new Object[args.length];
    for (int i = 0; i < args.length; i++) {
    
    
        if (args[i] instanceof ServletRequest || args[0] instanceof ServletResponse || args[0] instanceof MultipartFile) {
    
    
            continue;
        }
        arguments[i] = args[i];
    }
    // 45行
    log.info("类{}方法{},请求参数= {}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), JSON.toJSONString(arguments));
}

Two services, AOP configuration class is copy in the past! !

At this point we are familiar with the road, let's take a look at the controller interface layer code:

@GetMapping(value = "/excelUser")
public void excelUser(String channel, HttpServletResponse response, String startTime, String endTime) {
    
    
}

There is also a HttpServletResponse.

Solution: Still replace the AOP programming class and the conditional judgment statement in forthe loop with .ifargs[0]args[i]

The problem is solved, why?

analyze

According to the above description, both applications have the same ControllerLogAop, that is, AOP programming, the purpose is self-evident, naturally to record the requestBody and responseBody of the controller interface.

Secondly, the controller layer interfaces of the two applications are very similar, and do not care about other String type or POJO entity type request parameters. Both interfaces have HttpServletResponse.

Why are the error logs different for the same code fragment in different applications?

Considering that merchantthe FastJSON version used by the service is 1.2.68, is it related to the FastJSON version?

Then upgrade merchantthe FastJSON version used by the service to 1.2.83, but still report an error:

aba-merchant-provider | [http-nio-8849-exec-2] |  | ERROR | o.a.c.c.C.[Tomcat].[localhost].[/].[dispatcherServlet] | log | 175 | - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.alibaba.fastjson.JSONException: write javaBean error, fastjson version 1.2.83, class org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryResponseWrapper, fieldName : 2, getOutputStream() has already been called for this response] with root cause
java.lang.IllegalStateException: getOutputStream() has already been called for this response

It can be seen that different error logs have nothing to do with the FastJSON version.

Stream not in async mode

Error stack log:

at io.undertow.servlet.spec.ServletOutputStreamImpl.isReady(ServletOutputStreamImpl.java:757)

The source code is as follows:

@Override
public boolean isReady() {
    
    
    if (listener == null) {
    
    
        // TODO: is this the correct behaviour?
        throw UndertowServletMessages.MESSAGES.streamNotInAsyncMode();
    }
    if (!anyAreSet(state, FLAG_READY)) {
    
    
        if (channel != null) {
    
    
            channel.resumeWrites();
        }
        return false;
    }
    return true;
}

reference

https://www.cnblogs.com/qingmuchuanqi48/p/12079415.html
https://blog.csdn.net/hq091117/article/details/124618238

Guess you like

Origin blog.csdn.net/lonelymanontheway/article/details/130475795