Setting a custom HTTP header dynamically with Spring-WS client

47,692

Solution 1

public class AddHttpHeaderInterceptor implements ClientInterceptor {

public boolean handleFault(MessageContext messageContext)
        throws WebServiceClientException {
    return true;
}

public boolean handleRequest(MessageContext messageContext)
        throws WebServiceClientException {
     TransportContext context = TransportContextHolder.getTransportContext();
     HttpComponentsConnection connection =(HttpComponentsConnection) context.getConnection();
     connection.addRequestHeader("name", "suman");

    return true;
}

public boolean handleResponse(MessageContext messageContext)
        throws WebServiceClientException {
    return true;
}

}

config:

    <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
    ...
    <property name="interceptors">
        <list>
            <bean class="com.blah.AddHttpHeaderInterceptor" />
        </list>
    </property>
</bean>

Solution 2

ClientInterceptor works great for static header value. But it is not possible to use it when a different value should be applied per each request. In that case WebServiceMessageCallback is helpful:

final String dynamicParameter = //...

webServiceOperations.marshalSendAndReceive(request, 
    new WebServiceMessageCallback() {
        void doWithMessage(WebServiceMessage message) {
            TransportContext context = TransportContextHolder.getTransportContext();
            CommonsHttpConnection connection = (CommonsHttpConnection) context.getConnection();
            PostMethod postMethod = connection.getPostMethod();
            postMethod.addRequestHeader( "fsreqid", dynamicParameter );
        }
}

Solution 3

When using spring integration 3 and spring integration-ws, the following code can be used for handling the request:

public boolean handleRequest(MessageContext messageContext)
        throws WebServiceClientException {
    TransportContext context = TransportContextHolder.getTransportContext();
    HttpUrlConnection connection = (HttpUrlConnection) context
    .getConnection();
    connection.getConnection().addRequestProperty("HEADERNAME",
    "HEADERVALUE");

    return true;
}

The Interceptor can be connected to the outbound gateway in the following way:

<ws:outbound-gateway ...            
        interceptor="addPasswordHeaderInterceptor" >
</ws:outbound-gateway>

<bean id="addPasswordHeaderInterceptor class="com.yourfirm.YourHttpInterceptor" />

Solution 4

Example Method with java 1.8: How to add a HTTP header:

public void executeObjectWebservice(String id) {
        ExecuteObject request = new ExecuteObject();
        getWebServiceTemplate().marshalSendAndReceive("http://url/webservice-test/uc4ws",
                new ObjectFactory().createExecuteObject(request), new WebServiceMessageCallback() {
                    public void doWithMessage(WebServiceMessage message) throws IOException {
                        TransportContext context = TransportContextHolder.getTransportContext();
                        HttpUrlConnection connection = (HttpUrlConnection) context.getConnection();
                        connection.addRequestHeader("ID", id);
                    }
                });    
        }

Explanation: Use the getWebServiceTemplate().marshalSendAndReceive as described for example here: https://spring.io/guides/gs/consuming-web-service/

First parameter is the URI, second is the object which shall be send with the request. As third Parameter you can add as function

new WebServiceMessageCallback()

where you override the public void doWithMessage. This method gets called before the request is sent. Within you can access the message and add a request Header through

TransportContext context = TransportContextHolder.getTransportContext();
HttpUrlConnection connection = (HttpUrlConnection) context.getConnection();
connection.addRequestHeader("ID", id);

Solution 5

Actually, it is an updated version of the @Tomasz's answer, but provides a new Spring-WS API, Java 8 shortcuts, and cares about creating a WebServiceMessageCallback instance with a separate method.

I believe it is more obvious and self-sufficient.

final class Service extends WebServiceGatewaySupport {

    /**
     * @param URL       the URI to send the message to
     * @param payload   the object to marshal into the request message payload
     * @param headers   HTTP headers to add to the request
     */
    public Object performRequestWithHeaders(String URL, Object payload, Map<String, String> headers) {
        return getWebServiceTemplate()
                .marshalSendAndReceive(URL, payload, getRequestCallback(headers));
    }

    /**
     * Returns a {@code WebServiceMessageCallback} instance with custom HTTP headers.
     */
    private WebServiceMessageCallback getRequestCallback(Map<String, String> headers) {
        return message -> {
            TransportContext context = TransportContextHolder.getTransportContext();
            HttpUrlConnection connection = (HttpUrlConnection)context.getConnection();
            addHeadersToConnection(connection, headers);
        };
    }

    /**
     * Adds all headers from the {@code headers} to the {@code connection}.
     */
    private void addHeadersToConnection(HttpUrlConnection connection, Map<String, String> headers){
        headers.forEach((name, value) -> {
            try {
                connection.addRequestHeader(name, value);
            } catch (IOException e) {
                e.printStackTrace(); // or whatever you want
            }
        });
    }

}
Share:
47,692
user366735
Author by

user366735

Updated on March 05, 2020

Comments

  • user366735
    user366735 over 4 years

    How do you set a custom HTTP header (not SOAP header) dynamically on the client side when using Spring-WS?

  • dardo
    dardo almost 12 years
    Good answer, for users in the future, use HttpComponentsConnection instead of CommonsHttpConnection, as it has been deprecated.
  • Clint Eastwood
    Clint Eastwood almost 11 years
    This solution is more flexible than using the client interceptor. IMHO, it should be the preferred one.
  • Saurabh Sharma
    Saurabh Sharma about 10 years
    I'm getting following exception java.lang.ClassCastException: on this line context.getConnection() org.springframework.ws.transport.http.HttpServletConnection cannot be cast to org.springframework.ws.transport.http.CommonsHttpConnection
  • ZeroOne
    ZeroOne about 9 years
    The question was "How do you set a custom HTTP header (not SOAP header)", but this answer actually adds a SOAP header, not an HTTP header.
  • ZeroOne
    ZeroOne about 9 years
    FYI, org.springframework.ws.transport.http.CommonsHttpConnection has been deprecated in favor of org.springframework.ws.transport.http.HttpComponentsConnecti‌​on.
  • Gooseman
    Gooseman over 8 years
    I don't think this solution is going to work when running JUnit tests because context.getConnection() returns MockSenderConnection. I am using MockWebServiceServer for Unit testing.
  • Gooseman
    Gooseman over 8 years
    Does it work when running JUnit tests? In my case it didn't because context.getConnection() returns MockSenderConnection. I am using MockWebServiceServer for Unit testing.
  • Strinder
    Strinder over 7 years
    Important: Setting a custom header (spring uses Sun HttpConnection now) must be enabled! System.setProperty("sun.net.http.allowRestrictedHeaders", "true") Or at VM startup: -Dsun.net.http.allowRestrictedHeaders=true
  • Admin
    Admin over 6 years
    How would I use this class? I have a service class generated from WSDL already.
  • Andrew Tobilko
    Andrew Tobilko over 6 years
    @Pretty, use a URL and a payload specified there, and pass them here
  • Simon
    Simon almost 6 years
    What don't you get?
  • Muhammad Hewedy
    Muhammad Hewedy almost 6 years
    Better to extend ClientInterceptorAdapter
  • Adriano Machado
    Adriano Machado about 5 years
    Even better, check if connection is an instance of HeadersAwareSenderWebServiceConnection, in the case that a different transport is being used.
  • alegria
    alegria almost 3 years
    I personally found this more useful more my use case. Thanks sir.