WebSphere中Spring Boot大Payload触发IllegalArgumentException问题排查
异常现象
将Spring Boot 2.7.18应用部署在WebSphere Application Server 9上时,处理大Payload的POST请求会抛出如下异常:
java.lang.IllegalArgumentException: null at com.ibm.wsspi.webcontainer.util.RequestUtils.parseQueryString(RequestUtils.java:288) ...
小Payload请求可正常处理,大Payload则触发错误。
现有@InitBinder配置
@InitBinder public void dataBinding(WebDataBinder binder) { binder.setAutoGrowCollectionLimit(4500); SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy"); dateFormat.setLenient(false); binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true)); }
已设置集合上限为4500,小Payload可正常运行,但Payload增大后报错,堆栈跟踪显示错误发生在com.ibm.wsspi.webcontainer.util.RequestUtils.parseQueryString(RequestUtils.java:288)。
完整堆栈跟踪
java.lang.IllegalArgumentException: null at com.ibm.wsspi.webcontainer.util.RequestUtils.parseQueryString(RequestUtils.java:288) at com.ibm.wsspi.webcontainer.util.RequestUtils.parseQueryString(RequestUtils.java:108) at com.ibm.ws.webcontainer.servlet.RequestUtils.parsePostData(RequestUtils.java:384) at com.ibm.ws.webcontainer.servlet.RequestUtils.parsePostDataLong(RequestUtils.java:396) at com.ibm.ws.webcontainer.srt.SRTServletRequest.parseParameters(SRTServletRequest.java:2398) at com.ibm.ws.webcontainer.srt.SRTServletRequest.getParameterMap(SRTServletRequest.java:3042) at javax.servlet.ServletRequestWrapper.getParameterMap(ServletRequestWrapper.java:203) at org.springframework.security.web.firewall.StrictHttpFirewall$StrictFirewalledRequest.getParameterMap(StrictHttpFirewall.java:778) at javax.servlet.ServletRequestWrapper.getParameterMap(ServletRequestWrapper.java:203) at javax.servlet.ServletRequestWrapper.getParameterMap(ServletRequestWrapper.java:203) at javax.servlet.ServletRequestWrapper.getParameterMap(ServletRequestWrapper.java:203) at com.vayana.boot.infra.web.security.filter.XSSRequestWrapper.<init>(XSSRequestWrapper.java:34) at com.vayana.boot.infra.web.security.filter.SecurityFilter.doFilter(SecurityFilter.java:84) at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:197) at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:90) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:337) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:122) at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:116) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126) at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:109) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:147) at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:125) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:223) at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:132) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:112) at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:82) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:221) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267) at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:197) at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:90) at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:197) at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:90) at org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:126) at org.springframework.boot.web.servlet.support.ErrorPageFilter.access$000(ErrorPageFilter.java:64) at org.springframework.boot.web.servlet.support.ErrorPageFilter$1.doFilterInternal(ErrorPageFilter.java:101) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:119) at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:197) at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:90) at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:96) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:197) at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:90) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:197) at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:90) at com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:979) at com.ibm.ws.webcontainer.filter.WebAppFilterManager.invokeFilters(WebAppFilterManager.java:1119) at com.ibm.ws.webcontainer.servlet.CacheServletWrapper.handleRequest(CacheServletWrapper.java:82) at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:966) at com.ibm.ws.webcontainer.WSWebContainer.handleRequest(WSWebContainer.java:1817) at com.ibm.ws.webcontainer.channel.WCChannelLink.ready(WCChannelLink.java:382) at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:465) at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleNewRequest(HttpInboundLink.java:532) at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.processRequest(HttpInboundLink.java:318) at com.ibm.ws.http.channel.inbound.impl.HttpICLReadCallback.complete(HttpICLReadCallback.java:88) at com.ibm.ws.ssl.channel.impl.SSLReadServiceContext$SSLReadCompletedCallback.complete(SSLReadServiceContext.java:1833) at com.ibm.ws.tcp.channel.impl.AioReadCompletionListener.futureCompleted(AioReadCompletionListener.java:175) at com.ibm.io.async.AbstractAsyncFuture.invokeCallback(AbstractAsyncFuture.java:217) at com.ibm.io.async.AsyncChannelFuture.fireCompletionActions(AsyncChannelFuture.java:161) at com.ibm.io.async.AsyncFuture.completed(AsyncFuture.java:138) at com.ibm.io.async.ResultHandler.complete(ResultHandler.java:204) at com.ibm.io.async.ResultHandler.runEventProcessingLoop(ResultHandler.java:775) at com.ibm.io.async.ResultHandler$2.run(ResultHandler.java:905) at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1909)
已尝试的步骤
- 增大
binder.setAutoGrowCollectionLimit值 - 测试小Payload验证功能正常
观察结果
- 小Payload(如600条数据)可正常处理
- Payload超过一定大小就触发错误
疑问
- 是否需要调整WebSphere的特定配置或上限以处理大Payload?
- 该问题是否与WebSphere的请求参数或查询字符串大小限制有关?若有关,如何修改相关配置?
- 在WebSphere或Spring Boot中还有哪些设置或方法可有效处理大Payload?
- 生产环境中的Web Application Firewall(WAF)对请求体长度有严格限制,是否会导致该问题?若会,如何配置Spring Boot以在不触发WAF限制的前提下有效处理大Payload?
补充说明:生产环境中的WAF对请求体长度有严格限制,超过限制的请求会被拒绝,影响应用功能,是否有通过Spring Boot处理该问题的简便方法?
解决方案
1. 调整WebSphere的参数解析限制
从堆栈看,错误发生在WebSphere解析POST数据为参数的阶段,这和WebSphere的请求参数最大数量限制有关,而非单纯的请求体大小。WebSphere默认限制单个请求的参数数量为1000,当大Payload包含的参数(比如表单字段、数组元素)超过这个值时,就会触发IllegalArgumentException。
修改WebSphere配置步骤:
- 登录WebSphere管理控制台,进入服务器 > 服务器类型 > WebSphere应用服务器 > 你的服务器名称
- 点击Web容器设置 > Web容器
- 展开自定义属性,添加或修改以下属性:
com.ibm.ws.webcontainer.maxParamCount:设置为足够大的值(比如5000,对应你的集合上限4500)com.ibm.ws.webcontainer.postBufferSize:如果请求体过大,可适当增大这个值(单位为字节,比如10485760即10MB)
- 保存配置并重启服务器
2. 优化Spring Boot参数绑定配置
虽然你已经设置了autoGrowCollectionLimit,但还可以补充以下配置:
- 在
application.properties中添加:
替代spring.mvc.format.date=dd-MM-yyyy spring.mvc.format.date-time=dd-MM-yyyy HH:mm:ss@InitBinder中的日期编辑器配置,避免重复设置,同时确保全局生效。 - 如果使用
@RequestBody接收JSON格式的Payload,确保启用Jackson的大集合支持:spring.jackson.deserialization.accept-single-value-as-array=true
3. 避免提前解析参数(针对自定义Filter)
从堆栈看,错误发生在XSSRequestWrapper初始化时调用getParameterMap(),这会触发WebSphere提前解析POST参数。如果你的XSS过滤不需要处理请求参数,可修改XSSRequestWrapper,延迟参数解析,或者只针对需要的参数进行处理,避免一次性解析所有参数。
4. 处理WAF请求体限制的方案
如果WAF限制了请求体大小,可通过以下方式优化:
- 分批次提交数据:将大Payload拆分为多个小请求,前端分页提交,后端批量处理。
- 使用流式处理:在Spring Boot中使用
InputStream直接读取请求体,避免一次性加载到内存,同时配合Jackson的流式解析逐行处理数据:@PostMapping("/batch") public ResponseEntity<Void> processBatch(HttpServletRequest request) throws IOException { try (JsonParser parser = new JsonFactory().createParser(request.getInputStream())) { parser.nextToken(); // 跳过数组开始标记 while (parser.nextToken() == JsonToken.START_OBJECT) { YourEntity entity = parser.readValueAs(YourEntity.class); // 处理单个实体逻辑 } } return ResponseEntity.ok().build(); } - 压缩请求体:前端对请求体进行Gzip压缩,后端启用Spring Boot的压缩支持:
同时确认WAF支持解压Gzip格式的请求。server.compression.enabled=true server.compression.mime-types=application/json server.compression.min-response-size=1024
内容的提问来源于stack exchange,提问作者Kundan Kumar

