You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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])

问题原因

  1. HTTP响应状态码已提前提交
    Spring MVC在执行序列化逻辑前,已经将200状态码写入响应头并提交了响应。HTTP协议规定,响应头一旦发送给客户端,就无法再修改状态码,所以即使后续序列化抛出异常,也只能维持200状态,同时终止响应体输出,导致XML内容截断。

  2. 序列化异常发生在响应提交后
    从调用栈可以看到,异常发生在ServletInvocableHandlerMethod.invokeAndHandle阶段——这是Spring MVC开始写入响应体的环节,此时响应已经被提交,无法回滚或修改状态码。

  3. 缺少针对序列化阶段的异常处理
    常规的@ExceptionHandler全局异常处理器无法处理这种情况,因为异常触发时响应已经提交,处理器无法再修改状态码,只能任由截断内容返回。


解决方案建议

  • 修复序列化根源问题:排查GetUIProvidersResponse$Field.data中的元素,确保生成QName时的本地名称不为null。可以通过给字段设置默认值、在序列化前过滤null元素,或者自定义Jackson序列化器处理null场景。
  • 提前校验响应体:实现ResponseBodyAdvice接口,在序列化前对响应对象进行校验,提前发现可能导致序列化失败的问题,在响应未提交时返回500状态码。
  • 配置Jackson容错机制:调整Jackson XML的配置,比如关闭严格的QName校验,或者开启相关容错参数,避免因单个null值导致整个序列化失败。

内容的提问来源于stack exchange,提问作者Eljah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 03:19:52