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.

Info: This example only covers the validation part and extends from our existing Produce Spring WS contract-first soap service tutorial. We cover how to configure Spring using Java Configuration how you can validate the incoming and/or outgoing messages.

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 validate request response with xsd

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("http://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="http://memorynotfound.com">
                    cvc-datatype-valid.1.2.1: 'invalid-id' is not a valid value for 'integer'.
                </my:error>
                <my:error xmlns:my="http://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>
spring ws validate request response with xsd cloacking masking validation errors

References

Download

You may also like...