40

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

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
user366735
  • 2,183
  • 3
  • 21
  • 20

7 Answers7

34
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>
Asqan
  • 4,319
  • 11
  • 61
  • 100
user366735
  • 2,183
  • 3
  • 21
  • 20
  • 6
    Good answer, for users in the future, use HttpComponentsConnection instead of CommonsHttpConnection, as it has been deprecated. – dardo Jul 02 '12 at 20:13
  • 2
    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. – Gooseman Dec 31 '15 at 00:21
  • 1
    Better to extend `ClientInterceptorAdapter` – Muhammad Hewedy Aug 29 '18 at 08:39
  • Even better, check if `connection` is an instance of `HeadersAwareSenderWebServiceConnection`, in the case that a different transport is being used. – Adriano Machado May 24 '19 at 22:38
29

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 );
        }
}
William Brendel
  • 31,712
  • 14
  • 72
  • 77
Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • 1
    This solution is more flexible than using the client interceptor. IMHO, it should be the preferred one. – Clint Eastwood Sep 14 '13 at 10:27
  • 1
    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 – Saurabh Sharma May 03 '14 at 07:19
  • 5
    FYI, `org.springframework.ws.transport.http.CommonsHttpConnection` has been deprecated in favor of `org.springframework.ws.transport.http.HttpComponentsConnection`. – ZeroOne May 20 '15 at 14:18
  • 2
    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 Dec 31 '15 at 00:17
  • 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 – Strinder Jan 02 '17 at 15:38
  • I personally found this more useful more my use case. Thanks sir. – alegria Sep 04 '21 at 15:19
12

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" />
ruhsuzbaykus
  • 13,240
  • 2
  • 20
  • 21
4

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
            }
        });
    }

}
Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
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);
Simon
  • 81
  • 5
1

Spring's webServiceTemplate.marshalSendAndReceive(request) method internally uses HttpComponentsMessageSender to send the SOAP message over the network and this further uses WebServiceConnection to make http connection with the server. All you have to do is to write your own custom HttpComponentsMessageSender and set the cookie inside postMethod.

Custome sender code:

    package com.swap.ws.sender;

import java.io.IOException;
import java.net.URI;

import javax.annotation.Resource;

import org.apache.http.client.methods.HttpPost;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.ws.transport.WebServiceConnect ion;
import org.springframework.ws.transport.http.HttpComponen tsConnection;

/**
* 
* @author swapnil Z
*/
@Service("urlMessageSender")
public class CustomHttpComponentsMessageSender extends
org.springframework.ws.transport.http.HttpComponen tsMessageSender {
private static Logger _logger = Logger.getLogger("");


@Override
public WebServiceConnection createConnection(URI uri) throws IOException {
String cookie = null;
HttpComponentsConnection conn = (HttpComponentsConnection) super
.createConnection(uri);
HttpPost postMethod = conn.getHttpPost();
cookie = "<Your Custom Cookie>";

postMethod.addHeader("Cookie", cookie);

return conn;
}
}

Spring Configuration :

<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMe ssageFactory" />

<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshalle r">
<property name="contextPath" value="com.swap.provision" />
</bean>

<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServi ceTemplate">
<constructor-arg ref="messageFactory" />
<property name="marshaller" ref="marshaller"></property>
<property name="unmarshaller" ref="marshaller"></property>
<property name="messageSender" ref="urlMessageSender"/>
<property name="defaultUri" value=<Server URL> />
</bean>

After this I simply get bean webServiceTemplate and call marshalSendAndReceive method. So every request will have its custom cookie set before making HTTP call.

-1

The following fragment has been tested with Spring 4.0. It appends a WebServiceMessageCallback to a org.springframework.ws.client.core.WebServiceTemplate

final String DYNAMICVALUE = "myDynamo";

WebServiceMessageCallback wsCallback = new WebServiceMessageCallback() {           
       public void doWithMessage(WebServiceMessage message) {
            try {
                        SoapMessage soapMessage = (SoapMessage)message;
                        SoapHeader header = soapMessage.getSoapHeader();
                        header.addAttribute(new QName("myHeaderElement"), DYNAMICVALUE);                        
            } catch (Exception e) {
                        e.printStackTrace();
            }
       }
};

JAXBElement<MyWsResponse> response = (JAXBElement<MyWsResponse>)
        wsTemplate.marshalSendAndReceive(MyWsOP, wsCallback);
m.piunti
  • 340
  • 2
  • 8
  • 4
    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 May 20 '15 at 13:44