Spring WS validate Soap Request Response with XSD
This tutorial shows you how to use Spring WS to validate incoming or outgoing soap requests/responses using an XSD schema. This is one of the benefits of working contract-first. Spring WS uses a PayloadValidatingInterceptor
. You must add a reference of one or more XSD schema(s) in order for it to validate the request or response or both.
Spring WS Validate Soap Request Response
We can use the PayloadValidatingInterceptor
to validate the request and/or response. We register this interceptor by overriding the addInterceptors()
method of the WsConfigurerAdapter
from which our configuration file extends. We create a new PayloadValidatingInterceptor
and assign a reference to the XSD schema using the setXsdSchema() method. We can also configure which message to validate. It can either be only the request, only the response or both. We will not cover the rest of the configuration which we explained in an earlier tutorial, you can find the link in the previous paragraph.
package com.memorynotfound.server;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.server.EndpointInterceptor;
import org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;
import java.util.List;
@EnableWs
@Configuration
public class SoapServerConfig extends WsConfigurerAdapter {
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
PayloadValidatingInterceptor validatingInterceptor = new PayloadValidatingInterceptor();
validatingInterceptor.setValidateRequest(true);
validatingInterceptor.setValidateResponse(true);
validatingInterceptor.setXsdSchema(beersSchema());
interceptors.add(validatingInterceptor);
}
@Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext appContext){
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(appContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/ws/*");
}
@Bean(name = "beers")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema schema){
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("BeersPort");
wsdl11Definition.setLocationUri("/ws");
wsdl11Definition.setTargetNamespace(BeerEndpoint.NAMESPACE_URI);
wsdl11Definition.setSchema(schema);
return wsdl11Definition;
}
@Bean
public XsdSchema beersSchema(){
return new SimpleXsdSchema(new ClassPathResource("xsd/beers.xsd"));
}
}
Testing with Soap UI
We test the application using Soap UI. We make a getBeerRequest
and provide a string value where we expect an int. Resulting in the following error:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Client</faultcode>
<faultstring xml:lang="en">Validation error</faultstring>
<detail>
<spring-ws:ValidationError xmlns:spring-ws="http://springframework.org/spring-ws">
cvc-datatype-valid.1.2.1: 'invalid-id' is not a valid value for 'integer'.
</spring-ws:ValidationError>
<spring-ws:ValidationError xmlns:spring-ws="http://springframework.org/spring-ws">
cvc-type.3.1.3: The value 'invalid-id' of element 'beer:id' is not valid.
</spring-ws:ValidationError>
</detail>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
By default spring uses their own namespace: http://springframework.org/spring-ws localPart: ValidationError and prefix: spring-ws. For internal development this is probably ok. But when exposing your services this is most of the time not so good because we don’t want to expose to much information about our internal workings. In the next section we show you how to mask or cloak this.
Spring WS Masking/Cloaking Validation Errors
Now that the validation is working correct we can try to pimp our validation errors by changing the namespace, localPart and prefix. We can do this by extending the PayloadValidatingInterceptor
and overriding the getDetailElementName()
method which is responsible for getting the QName
.
package com.memorynotfound.server;
import org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor;
import javax.xml.namespace.QName;
public class CustomValidationInterceptor extends PayloadValidatingInterceptor {
@Override
public QName getDetailElementName() {
return new QName("https://memorynotfound.com", "error", "my");
}
}
So that’s almost it. We have created our custom CustomValidationInterceptor
so now we need to register it so Spring WS uses our new interceptor. We can do this by changing the Spring Java Configuration to the following:
package com.memorynotfound.server;
...
@EnableWs
@Configuration
public class SoapServerConfig extends WsConfigurerAdapter {
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
CustomValidationInterceptor validatingInterceptor = new CustomValidationInterceptor();
validatingInterceptor.setValidateRequest(true);
validatingInterceptor.setValidateResponse(true);
validatingInterceptor.setXsdSchema(beersSchema());
interceptors.add(validatingInterceptor);
}
...
}
Testing with Soap UI
When we redeployed our application with the new configuration to change the namespace, localPart and prefix we now have a masked response from our server:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Client</faultcode>
<faultstring xml:lang="en">Validation error</faultstring>
<detail>
<my:error xmlns:my="https://memorynotfound.com">
cvc-datatype-valid.1.2.1: 'invalid-id' is not a valid value for 'integer'.
</my:error>
<my:error xmlns:my="https://memorynotfound.com">
cvc-type.3.1.3: The value 'invalid-id' of element 'beer:id' is not valid.
</my:error>
</detail>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
References
- PayloadValidatingInterceptor JavaDoc
- WsConfigurerAdapter#addInterceptors() JavaDoc
- Spring WS API
- Spring WS Reference Documentation