Spring项目中DispatcherServlet返回截断XML无500错误的原因
问题分析:Spring XML序列化返回200但内容截断的原因
项目配置
<import resource="classpath:payment-impl-context.xml"/> <import resource="classpath:abs-payment-consumer-context.xml"/> <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/postgres"/> <security:http authentication-manager-ref="authenticationManager" create-session="stateless"> <security:intercept-url pattern="/api2/xml/**" access="permitAll" /> <security:intercept-url pattern="/**" access="isAuthenticated()" /> <security:http-basic entry-point-ref="webServiceAuthenticationEntryPoint"/> <security:csrf disabled="true"/> </security:http> <mvc:annotation-driven/> <context:component-scan base-package="com.openpayment.web.service"/> <bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"> <property name="favorPathExtension" value="false" /> </bean> <mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"> <mvc:path-matching suffix-pattern="false"/> </mvc:annotation-driven> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <list> <value>payment-messages</value> <value>web-service-messages</value> </list> </property> </bean>
控制器定义
@RestController @RequestMapping(consumes = "application/xml;charset=cp1251", produces = "application/xml;charset=cp1251")
调用栈与调试错误信息
请求调用栈
invokeAndHandle:117, ServletInvocableHandlerMethod (org.springframework.web.servlet.mvc.method.annotation) invokeHandlerMethod:854, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation) handleInternal:765, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation) handle:85, AbstractHandlerMethodAdapter (org.springframework.web.servlet.mvc.method) doDispatch:967, DispatcherServlet (org.springframework.web.servlet) doDispatch:41, LoggableDispatcherServlet (com.openpayment.web.service.init) doService:901, DispatcherServlet (org.springframework.web.servlet) processRequest:970, FrameworkServlet (org.springframework.web.servlet) doPost:872, FrameworkServlet (org.springframework.web.servlet) service:647, HttpServlet (javax.servlet.http) service:846, FrameworkServlet (org.springframework.web.servlet) service:728, HttpServlet (javax.servlet.http) internalDoFilter:305, ApplicationFilterChain (org.apache.catalina.core) doFilter:210, ApplicationFilterChain (org.apache.catalina.core) doFilter:51, WsFilter (org.apache.tomcat.websocket.server) internalDoFilter:243, ApplicationFilterChain (org.apache.catalina.core) doFilter:210, ApplicationFilterChain (org.apache.catalina.core) doFilter:317, FilterChainProxy$VirtualFilterChain (org.springframework.security.web) invoke:127, FilterSecurityInterceptor (org.springframework.security.web.access.intercept) doFilter:91, FilterSecurityInterceptor (org.springframework.security.web.access.intercept) doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web) doFilter:114, ExceptionTranslationFilter (org.springframework.security.web.access) doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web) doFilter:111, AnonymousAuthenticationFilter (org.springframework.security.web.authentication) doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web) doFilter:170, SecurityContextHolderAwareRequestFilter (org.springframework.security.web.servletapi) doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web) doFilterInternal:158, BasicAuthenticationFilter (org.springframework.security.web.authentication.www) doFilter:107, OncePerRequestFilter (org.springframework.web.filter) doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web) doFilterInternal:66, HeaderWriterFilter (org.springframework.security.web.header) doFilter:107, OncePerRequestFilter (org.springframework.web.filter) doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web) doFilterInternal:56, WebAsyncManagerIntegrationFilter (org.springframework.security.web.context.request.async) doFilter:107, OncePerRequestFilter (org.springframework.web.filter) doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web) doFilter:105, SecurityContextPersistenceFilter (org.springframework.security.web.context) doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web) doFilterInternal:214, FilterChainProxy (org.springframework.security.web) doFilter:177, FilterChainProxy (org.springframework.security.web) invokeDelegate:347, DelegatingFilterProxy (org.springframework.web.filter) doFilter:263, DelegatingFilterProxy (org.springframework.web.filter) internalDoFilter:243, ApplicationFilterChain (org.apache.catalina.core) doFilter:210, ApplicationFilterChain (org.apache.catalina.core) invoke:222, StandardWrapperValve (org.apache.catalina.core) invoke:123, StandardContextValve (org.apache.catalina.core) invoke:502, AuthenticatorBase (org.apache.catalina.authenticator) invoke:171, StandardHostValve (org.apache.catalina.core) invoke:100, ErrorReportValve (org.apache.catalina.valves) invoke:953, AccessLogValve (org.apache.catalina.valves) invoke:118, StandardEngineValve (org.apache.catalina.core) service:408, CoyoteAdapter (org.apache.catalina.connector) process:1041, AbstractHttp11Processor (org.apache.coyote.http11) process:603, AbstractProtocol$AbstractConnectionHandler (org.apache.coyote) run:310, JIoEndpoint$SocketProcessor (org.apache.tomcat.util.net) runWorker:1128, ThreadPoolExecutor (java.util.concurrent) run:628, ThreadPoolExecutor$Worker (java.util.concurrent) run:834, Thread (java.lang)
调试模式下的异常跟踪
com.fasterxml.jackson.databind.JsonMappingException: local part cannot be "null" when creating a QName (through reference chain: com.openpayment.external.pay24.model.GetUIProvidersResponse["providers"]->com.openpayment.external.pay24.model.GetUIProvidersResponse$Providers["getUIProviders"]->com.openpayment.external.pay24.model.GetUIProvidersResponse$GetUIProviders["provider"]->java.util.ArrayList[53]->com.openpayment.external.pay24.model.GetUIProvidersResponse$Provider["field"]->java.util.ArrayList[0]->com.openpayment.external.pay24.model.GetUIProvidersResponse$Field["data"]->java.util.ArrayList[0])
问题原因
HTTP响应状态码已提前提交
Spring MVC在执行序列化逻辑前,已经将200状态码写入响应头并提交了响应。HTTP协议规定,响应头一旦发送给客户端,就无法再修改状态码,所以即使后续序列化抛出异常,也只能维持200状态,同时终止响应体输出,导致XML内容截断。序列化异常发生在响应提交后
从调用栈可以看到,异常发生在ServletInvocableHandlerMethod.invokeAndHandle阶段——这是Spring MVC开始写入响应体的环节,此时响应已经被提交,无法回滚或修改状态码。缺少针对序列化阶段的异常处理
常规的@ExceptionHandler全局异常处理器无法处理这种情况,因为异常触发时响应已经提交,处理器无法再修改状态码,只能任由截断内容返回。
解决方案建议
- 修复序列化根源问题:排查
GetUIProvidersResponse$Field.data中的元素,确保生成QName时的本地名称不为null。可以通过给字段设置默认值、在序列化前过滤null元素,或者自定义Jackson序列化器处理null场景。 - 提前校验响应体:实现
ResponseBodyAdvice接口,在序列化前对响应对象进行校验,提前发现可能导致序列化失败的问题,在响应未提交时返回500状态码。 - 配置Jackson容错机制:调整Jackson XML的配置,比如关闭严格的QName校验,或者开启相关容错参数,避免因单个null值导致整个序列化失败。
内容的提问来源于stack exchange,提问作者Eljah
相关产品推荐
相关产品推荐

