Spring Web Services处理空BODY的SOAP PING请求失败的问题排查与解决方案咨询
针对你遇到的空SOAP Body的PING请求无法处理的问题,我整理了几个可行的解决方案,结合你的场景(已有正常业务端点、WSDL定义了无part的PingRequest/Response),推荐优先尝试默认端点配置或者直接定义匹配空Body的端点:
方案1:配置默认端点(推荐,适配"未找到端点时兜底处理"场景)
你提到XML配置可以设置defaultEndpoint,用注解方式实现的话,可以通过自定义EndpointMapping并指定默认端点来实现:
步骤1:创建处理空请求的端点类
这个端点负责处理所有无法匹配到其他端点的请求(包括空Body的PING),直接返回原请求的SOAP消息:
@Component public class PingDefaultEndpoint implements WebServiceMessageReceiver { private static final Logger LOGGER = LoggerFactory.getLogger(PingDefaultEndpoint.class); @Override public void receive(MessageContext messageContext) throws Exception { SaajSoapMessage request = (SaajSoapMessage) messageContext.getRequest(); SaajSoapMessage response = (SaajSoapMessage) messageContext.getResponse(); // 复制请求的Envelope到响应(实现"返回与请求完全相同的消息"的需求) SOAPEnvelope requestEnvelope = request.getSaajMessage().getSOAPPart().getEnvelope(); SOAPEnvelope responseEnvelope = response.getSaajMessage().getSOAPPart().getEnvelope(); // 复制Header(如果有) if (requestEnvelope.getHeader() != null) { responseEnvelope.addHeader(requestEnvelope.getHeader()); } // 空Body直接保留 LOGGER.info("处理空Body的PING请求,返回原消息"); } }
步骤2:在配置类中设置默认端点
在你的WebServiceConfiguration类中,定义PayloadRootAnnotationMethodEndpointMapping的Bean,并设置默认端点:
@Configuration @EnableWs public class WebServiceConfiguration extends WsConfigurerAdapter { @Autowired private PingDefaultEndpoint pingDefaultEndpoint; @Bean public PayloadRootAnnotationMethodEndpointMapping payloadRootAnnotationMethodEndpointMapping() { PayloadRootAnnotationMethodEndpointMapping endpointMapping = new PayloadRootAnnotationMethodEndpointMapping(); // 设置默认端点,当找不到匹配的@PayloadRoot端点时触发 endpointMapping.setDefaultEndpoint(pingDefaultEndpoint); return endpointMapping; } // 其他原有配置(比如WSDL定义、拦截器等)... }
这样,当空Body的请求进来时,因为没有匹配的@PayloadRoot端点,就会触发这个默认端点,直接返回原请求消息。
方案2:直接定义匹配空Body的PING端点
如果你的WSDL里明确定义了PingRequest(无part的message),可以通过@SoapAction或者直接处理原始SOAP消息来定义端点:
方式A:使用@SoapAction匹配(如果请求有SOAPAction头)
如果PING请求带有SOAPAction: "Ping"这样的头,可以直接用@SoapAction注解来匹配:
@Endpoint public class PingEndpoint { private static final Logger LOGGER = LoggerFactory.getLogger(PingEndpoint.class); @SoapAction("Ping") // 替换为你的PING请求实际的SOAPAction值 @ResponsePayload public void handlePing(@RequestPayload SoapMessage requestMessage, MessageContext messageContext) throws Exception { // 直接复制请求到响应 SoapMessage responseMessage = messageContext.getResponse(); requestMessage.writeTo(responseMessage.getOutputStream()); LOGGER.info("处理PING请求(通过SOAPAction匹配)"); } }
方式B:匹配空Body的通用端点
如果没有SOAPAction头,可以通过@PayloadRoot结合自定义的空类型,或者直接处理SoapMessage:
@Endpoint public class EmptyBodyEndpoint { private static final Logger LOGGER = LoggerFactory.getLogger(EmptyBodyEndpoint.class); @PayloadRoot(namespace = "http://schemas.xmlsoap.org/soap/envelope/", localPart = "Body") @ResponsePayload public void handleEmptyBody(@RequestPayload SoapBody soapBody, MessageContext messageContext) throws Exception { // 判断Body是否为空 if (soapBody.getChildNodes().getLength() == 0) { SaajSoapMessage request = (SaajSoapMessage) messageContext.getRequest(); SaajSoapMessage response = (SaajSoapMessage) messageContext.getResponse(); request.writeTo(response.getOutputStream()); LOGGER.info("处理空Body请求"); } } }
为什么之前的拦截器不生效?
你提到的EndpointInterceptor不触发的问题,确实是因为Spring WS的MessageDispatcher在找不到端点映射时,不会执行拦截器链(正如你看到的源码注释:No triggering of interceptors if no endpoint is found)。所以必须通过端点映射的方式(默认端点或者匹配空Body的端点)来处理这类请求,而不是拦截器。
内容的提问来源于stack exchange,提问作者Dusko

