我编写了一个与第三方Web服务一起使用的Web服务客户端(使用Java Spring和JAXB Marshaller)。当我发送有效请求时,一切正常。当我发送无效请求时,Web服务服务器会响应SOAP Fault。客户端应用程序失败并出现UnmarshallingFailureException
org.springframework.oxm.UnmarshallingFailureException: JAXB unmarshalling
exception; nested exception is javax.xml.bind.UnmarshalException:
unexpected element (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Fault").
对我来说,我的ws客户端无法破译Web服务返回的SOAP错误。我编写了一个自定义的FaultMessageResolver,但它没有被调用(我在那里设置了一个断点,但它没有被击中。在添加Wss4jSecurityInterceptor进行签名,加密/解密之前,FaultMessageResolver工作正常。)这是代码:
public class VehicleServiceClientExceptionResolver implements FaultMessageResolver {
@Override
public void resolveFault(WebServiceMessage message) throws IOException {
SoapMessage soapMessage = (SoapMessage) message;
try {
JAXBContext context = JAXBContext.newInstance(ErrorMessages.class);
Unmarshaller unMarshaller = context.createUnmarshaller();
ErrorMessages errorMessages = (ErrorMessages)unMarshaller.unmarshal(soapMessage.getSoapBody().getFault().getFaultDetail().getDetailEntries().next().getSource());
if (errorMessages.getErrorMessage().size() > 0) {
throw new VehicleServiceClientException(errorMessages);
}
} catch (JAXBException e) {
LOGGER.debug(e.getMessage());
}
}
}
此自定义soap故障解析器将注入客户端Web服务模板,如下所示:
<bean id="vehicleQuotationWebServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="interceptors">
<list>
<ref bean="wsSecurityInterceptor"/>
</list>
</property>
<property name="marshaller" ref="vehicleQuotationMarshaller" />
<property name="unmarshaller" ref="vehicleQuotationMarshaller" />
<property name="messageSender" ref="urlMessageSender"/>
<property name="faultMessageResolver" ref="vehicleServiceClientFaultMessageResolver" />
<property name="defaultUri" value="https://*********/*********Service"/>
</bean>
最奇怪的是,虽然我得到了unmarshall异常,但是当我将日志级别从INFO更改为DEBUG时,我确实看到加密的服务器响应在我的eclipse控制台中被解密,我不知道这个DigesterOutputStream来自哪里,但是我认为这可能是解决这个问题的关键。
任何人都有任何想法?谢谢!
DEBUG p.xml.dsig.internal.DigesterOutputStream:
<soapenv:Body xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Id-af090516-9e00-4590-b481-c78e59d6b2fc"><soapenv:Fault><faultcode>soapenv:Client.Validation</faultcode><faultstring</faultstring><detail><em:ErrorMessages xmlns:em="urn:ford/errormessage/v1.0"><em:ErrorMessage><em:ErrorCode>GLSE903100</em:ErrorCode><em:ErrorDescription> CTT System Quote Id already exists ('1041')</em:ErrorDescription><em:ErrorTime>2014-05-16T15:13:20</em:ErrorTime></em:ErrorMessage></em:ErrorMessages></detail></soapenv:Fault></soapenv:Body>
答案 0 :(得分:2)
我找到了解决方案here:将WebServiceMessageExtractor<Object>
添加到:
WebServiceTemplate.sendAndReceive(
new WebServiceMessageCallback(),
new WebServiceMessageExtractor<Object>())
诀窍。
答案 1 :(得分:1)
另一种解决方案:
public class ExampleInterceptor implements ClientInterceptor {
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
var resp = (SoapMessage) messageContext.getResponse();
Optional.of(resp)
.filter(res -> !hasFault(res))
.orElseThrow(() -> new SoapFaultClientException(resp));
return true;
}
private boolean hasFault(final WebServiceMessage response) {
return Optional.ofNullable(response)
.filter(resp -> resp instanceof FaultAwareWebServiceMessage)
.map(resp -> (FaultAwareWebServiceMessage) resp)
.map(FaultAwareWebServiceMessage::hasFault)
.orElse(false);
}
}
@Configuration
public class ExampleConnectorConfig extends WSConnectorConfig
@Bean
public WSConnector soapConnector(Jaxb2Marshaller marshaller) {
WSConnector client = new WSConnector(messageFactory());
client.setInterceptors(new ClientInterceptor[]{new ExampleInterceptor()});
client.setDefaultUri(proxy);
return client;
}
//Example
@Bean
public SaajSoapMessageFactory messageFactory() {
SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory();
messageFactory.afterPropertiesSet();
return messageFactory;
}
}