【发布时间】:2011-03-11 07:11:57
【问题描述】:
使用 Spring-WS 时如何在客户端动态设置自定义 HTTP 标头(不是 SOAP 标头)?
【问题讨论】:
标签: java spring http header spring-ws
使用 Spring-WS 时如何在客户端动态设置自定义 HTTP 标头(不是 SOAP 标头)?
【问题讨论】:
标签: java spring http header spring-ws
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;
}
}
配置:
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
...
<property name="interceptors">
<list>
<bean class="com.blah.AddHttpHeaderInterceptor" />
</list>
</property>
</bean>
【讨论】:
context.getConnection() 返回MockSenderConnection。我正在使用MockWebServiceServer 进行单元测试。
ClientInterceptorAdapter
connection 是否是HeadersAwareSenderWebServiceConnection 的实例,以防使用不同的传输方式。
ClientInterceptor 非常适合静态标头值。但是当每个请求都应该应用不同的值时,就不可能使用它。在那种情况下WebServiceMessageCallback 很有帮助:
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 );
}
}
【讨论】:
org.springframework.ws.transport.http.CommonsHttpConnection 已被弃用,取而代之的是 org.springframework.ws.transport.http.HttpComponentsConnection。
context.getConnection() 返回MockSenderConnection。我正在使用MockWebServiceServer 进行单元测试。
以下片段已使用 Spring 4.0 进行了测试。它将WebServiceMessageCallback 附加到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);
【讨论】:
Spring 的 webServiceTemplate.marshalSendAndReceive(request) 方法内部使用 HttpComponentsMessageSender 通过网络发送 SOAP 消息,这进一步使用 WebServiceConnection 与服务器建立 http 连接。您所要做的就是编写自己的自定义 HttpComponentsMessageSender 并在 postMethod 中设置 cookie。
客户发件人代码:
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;
}
}
弹簧配置:
<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>
在此之后,我只需获取 bean webServiceTemplate 并调用 marshalSendAndReceive 方法。因此,在进行 HTTP 调用之前,每个请求都会设置其自定义 cookie。
【讨论】:
当使用spring integration 3和spring integration-ws时,可以使用以下代码来处理请求:
public boolean handleRequest(MessageContext messageContext)
throws WebServiceClientException {
TransportContext context = TransportContextHolder.getTransportContext();
HttpUrlConnection connection = (HttpUrlConnection) context
.getConnection();
connection.getConnection().addRequestProperty("HEADERNAME",
"HEADERVALUE");
return true;
}
拦截器可以通过以下方式连接到出站网关:
<ws:outbound-gateway ...
interceptor="addPasswordHeaderInterceptor" >
</ws:outbound-gateway>
<bean id="addPasswordHeaderInterceptor class="com.yourfirm.YourHttpInterceptor" />
【讨论】:
其实它是@Tomasz的答案的更新版本,但是提供了新的Spring-WS API、Java 8快捷方式,并且关心用单独的方法创建WebServiceMessageCallback实例。
我相信它更明显和自给自足。
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
}
});
}
}
【讨论】:
使用 java 1.8 的示例方法:如何添加 HTTP 标头:
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);
}
});
}
解释: 使用 getWebServiceTemplate().marshalSendAndReceive,例如此处所述:https://spring.io/guides/gs/consuming-web-service/
第一个参数是URI,第二个参数是请求发送的对象。作为第三个参数,您可以添加为函数
new WebServiceMessageCallback()
覆盖public void doWithMessage 的位置。此方法在发送请求之前被调用。您可以在其中访问消息并通过
TransportContext context = TransportContextHolder.getTransportContext();
HttpUrlConnection connection = (HttpUrlConnection) context.getConnection();
connection.addRequestHeader("ID", id);
【讨论】: