处理空的 SOAP 消息
Handling an empty SOAP message
尝试使用 Spring Web 服务处理空的 SOAP 消息但失败。
因此,我请求为某种 PING 方法提供端点。基本上我可以处理的 SOAP 消息如下所示:
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:def="http://www.something.com/edf/services/defaultservice">
<soapenv:Header/>
<soapenv:Body>
<def:ServiceReqType>
<transactionId>1111</transactionId>
<subscriberId>2222</subscriberId>
</def:ServiceReqType>
</soapenv:Body>
</soapenv:Envelope>
并且我可以处理正在处理 ServiceReqType
.
的端点
但是 PING 看起来像这样:
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body/>
</soapenv:Envelope>
我无法处理,Spring 记录 Can't handle [SaajSoapMessage]
。
我需要return的是完全相同的消息。
我知道有一个 type/class 缺失,我会提供给 @PayloadRoot
。
所以我想知道这个空主体请求的端点规范是什么?
仅供参考,这是我处理 ServiceReqType
:
的端点
@PayloadRoot(namespace = NAMESPACE_EDF, localPart = "ServiceReqType")
@ResponsePayload
public ServiceRespType serviceResponse(@RequestPayload ServiceReqType request) {
LOGGER.debug("-----> ServiceReqType:{}", request);
return reqProcessor.process(request);
}
更新 1:
所以我尝试通过以下方式实现拦截器:
public class CustomEndpointInterceptor implements EndpointInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomEndpointInterceptor.class);
@Override
public boolean handleRequest(MessageContext messageContext, Object endpoint) throws Exception {
LOGGER.info("---> Message context: {}", messageContext.toString());
LOGGER.info("---> Message endpoint: {}", endpoint.toString());
return false;
}
@Override
public boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
LOGGER.info("handleResponse");
return false;
}
@Override
public boolean handleFault(MessageContext messageContext, Object endpoint) throws Exception {
LOGGER.info("handleFault");
return false;
}
@Override
public void afterCompletion(MessageContext messageContext, Object endpoint, Exception ex) throws Exception {
LOGGER.info("afterCompletion");
}
}
然后在 WebServiceConfiguration
class 我添加了这个:
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(new CustomEndpointInterceptor());
super.addInterceptors(interceptors);
}
但是还是不行。我得到的是当 SOAP 消息有 BODY 时调用拦截器,但是当它发送 Ping
是 BODYless 时,我再次收到相同的错误消息并且没有调用拦截器。所以看来我必须找到一个更上层的拦截器...
更新 2:
这是此 Ping 的 WSDL 文件的样子...
<message name="PingRequest"/>
<message name="PingResponse"/>
里面没有任何东西......
为了比较,这是 ServiceReqType
的样子:
<message name="ServiceReqType">
<part name="body" element="tns:ServiceReqTypeDefinition"/>
</message>
然后 ServiceReqTypeDefinition
在随附的 xsd
文件中定义。
更新 3:
所以,找到了拦截器无法处理此类消息的原因:-/
以下代码来自 MessageDispatcher
第 234 行。
// No triggering of interceptors if no endpoint is found
if (endpointNotFoundLogger.isWarnEnabled()) {
endpointNotFoundLogger.warn("No endpoint mapping found for [" + messageContext.getRequest() + "]");
}
throw ex;
所以这是故意的,现在我需要找到如何处理这些不完整或不正确的 SOAP 消息...任何想法,因为重写 Dispatcher
感觉不是正确的方法。
更新 4
因此,我通过扩展 AbstractEndpointMapping
class 然后重写 getInternalEndpoint
设法拦截了有效负载,即无效负载,这让我有可能评估消息并查看如果我一直在尝试处理这个空的 Ping 请求。
我认为如果我知道如何定义一个 defaultEndpoint
,这一切都会很容易解决,因为我看到万一无法识别 SOAP 消息,因此找不到端点映射,这个 defaultEndpoint
用于处理该消息。
我注意到在使用 Beans
的 XML 规范时,有一个 属性 将端点定义为默认端点,但是在使用注释时如何做到这一点?
另一种方法是只创建一个端点,然后 return 它来自 getInternalEndpoint
所以 spring 可以处理处理,我想,但我还不知道如何创建一个端点对象...现在正在处理它。
p.s。 This 文档提到 defaultEndpoint
但没有提到如何以非 XML 定义 Bean 的方式设置它。
所以,最后我不得不创建一个自定义异常处理程序。像这样:
public class CustomEndpointNotFoundException extends Exception {
public CustomEndpointNotFoundException(String message) {
super(message);
}
}
然后:
@Component
@Order(Ordered.LOWEST_PRECEDENCE)
public class NoEndpointFoundEndpointMapping implements EndpointMapping {
@Override
public EndpointInvocationChain getEndpoint(MessageContext messageContext) throws Exception {
throw new CustomEndpointNotFoundException("");
}
}
最后:
@Component
public class CustomEndpointExceptionResolver implements EndpointExceptionResolver {
@Override
public boolean resolveException(MessageContext messageContext, Object endpoint, Exception ex) {
if (messageIsPing()) {
return true;
}
return false;
}
}
所以基本上我把它当作一个错误来处理。这不是真正理想的解决方案恕我直言,我仍然会看看我是否可以拦截端点解析,或者定义一个默认端点。
尝试使用 Spring Web 服务处理空的 SOAP 消息但失败。
因此,我请求为某种 PING 方法提供端点。基本上我可以处理的 SOAP 消息如下所示:
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:def="http://www.something.com/edf/services/defaultservice">
<soapenv:Header/>
<soapenv:Body>
<def:ServiceReqType>
<transactionId>1111</transactionId>
<subscriberId>2222</subscriberId>
</def:ServiceReqType>
</soapenv:Body>
</soapenv:Envelope>
并且我可以处理正在处理 ServiceReqType
.
但是 PING 看起来像这样:
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body/>
</soapenv:Envelope>
我无法处理,Spring 记录 Can't handle [SaajSoapMessage]
。
我需要return的是完全相同的消息。
我知道有一个 type/class 缺失,我会提供给 @PayloadRoot
。
所以我想知道这个空主体请求的端点规范是什么?
仅供参考,这是我处理 ServiceReqType
:
@PayloadRoot(namespace = NAMESPACE_EDF, localPart = "ServiceReqType")
@ResponsePayload
public ServiceRespType serviceResponse(@RequestPayload ServiceReqType request) {
LOGGER.debug("-----> ServiceReqType:{}", request);
return reqProcessor.process(request);
}
更新 1:
所以我尝试通过以下方式实现拦截器:
public class CustomEndpointInterceptor implements EndpointInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomEndpointInterceptor.class);
@Override
public boolean handleRequest(MessageContext messageContext, Object endpoint) throws Exception {
LOGGER.info("---> Message context: {}", messageContext.toString());
LOGGER.info("---> Message endpoint: {}", endpoint.toString());
return false;
}
@Override
public boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
LOGGER.info("handleResponse");
return false;
}
@Override
public boolean handleFault(MessageContext messageContext, Object endpoint) throws Exception {
LOGGER.info("handleFault");
return false;
}
@Override
public void afterCompletion(MessageContext messageContext, Object endpoint, Exception ex) throws Exception {
LOGGER.info("afterCompletion");
}
}
然后在 WebServiceConfiguration
class 我添加了这个:
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(new CustomEndpointInterceptor());
super.addInterceptors(interceptors);
}
但是还是不行。我得到的是当 SOAP 消息有 BODY 时调用拦截器,但是当它发送 Ping
是 BODYless 时,我再次收到相同的错误消息并且没有调用拦截器。所以看来我必须找到一个更上层的拦截器...
更新 2:
这是此 Ping 的 WSDL 文件的样子...
<message name="PingRequest"/>
<message name="PingResponse"/>
里面没有任何东西......
为了比较,这是 ServiceReqType
的样子:
<message name="ServiceReqType">
<part name="body" element="tns:ServiceReqTypeDefinition"/>
</message>
然后 ServiceReqTypeDefinition
在随附的 xsd
文件中定义。
更新 3:
所以,找到了拦截器无法处理此类消息的原因:-/
以下代码来自 MessageDispatcher
第 234 行。
// No triggering of interceptors if no endpoint is found
if (endpointNotFoundLogger.isWarnEnabled()) {
endpointNotFoundLogger.warn("No endpoint mapping found for [" + messageContext.getRequest() + "]");
}
throw ex;
所以这是故意的,现在我需要找到如何处理这些不完整或不正确的 SOAP 消息...任何想法,因为重写 Dispatcher
感觉不是正确的方法。
更新 4
因此,我通过扩展 AbstractEndpointMapping
class 然后重写 getInternalEndpoint
设法拦截了有效负载,即无效负载,这让我有可能评估消息并查看如果我一直在尝试处理这个空的 Ping 请求。
我认为如果我知道如何定义一个 defaultEndpoint
,这一切都会很容易解决,因为我看到万一无法识别 SOAP 消息,因此找不到端点映射,这个 defaultEndpoint
用于处理该消息。
我注意到在使用 Beans
的 XML 规范时,有一个 属性 将端点定义为默认端点,但是在使用注释时如何做到这一点?
另一种方法是只创建一个端点,然后 return 它来自 getInternalEndpoint
所以 spring 可以处理处理,我想,但我还不知道如何创建一个端点对象...现在正在处理它。
p.s。 This 文档提到 defaultEndpoint
但没有提到如何以非 XML 定义 Bean 的方式设置它。
所以,最后我不得不创建一个自定义异常处理程序。像这样:
public class CustomEndpointNotFoundException extends Exception {
public CustomEndpointNotFoundException(String message) {
super(message);
}
}
然后:
@Component
@Order(Ordered.LOWEST_PRECEDENCE)
public class NoEndpointFoundEndpointMapping implements EndpointMapping {
@Override
public EndpointInvocationChain getEndpoint(MessageContext messageContext) throws Exception {
throw new CustomEndpointNotFoundException("");
}
}
最后:
@Component
public class CustomEndpointExceptionResolver implements EndpointExceptionResolver {
@Override
public boolean resolveException(MessageContext messageContext, Object endpoint, Exception ex) {
if (messageIsPing()) {
return true;
}
return false;
}
}
所以基本上我把它当作一个错误来处理。这不是真正理想的解决方案恕我直言,我仍然会看看我是否可以拦截端点解析,或者定义一个默认端点。