Ответ 1
Я сам не использовал HttpClient, но я подозреваю, что проблема заключается в том, что putMethod.setEntity(...)
неявно снабжает длину содержимого, и вы также явно устанавливаете его через один из вызовов putMethod.addHeader(...)
.
Я использую Apache HttpClient (4.1), включенную в Android, для выполнения HttpPut. Я проверил, что у меня есть только 1 заголовок длины содержимого. Тем не менее, каждый раз, когда я отправляю запрос, я получаю исключение протокола о уже объявленном заголовке Content-Length.
HttpClient client = new DefaultHttpClient();
putMethod = new HttpPut(url + encodedFileName);
putMethod.addHeader(..) //<-once for each header
putMethod.setEntity(new ByteArrayEntity(data));
client.execute(putMethod); //throws Exception
Вызывает: org.apache.http.ProtocolException: заголовок Content-Length уже присутствует на org.apache.http.protocol.RequestContent.process(RequestContent.java:70) на org.apache.http.protocol.BasicHttpProcessor.process(BasicHttpProcessor.java:290)
Любые идеи?
Я сам не использовал HttpClient, но я подозреваю, что проблема заключается в том, что putMethod.setEntity(...)
неявно снабжает длину содержимого, и вы также явно устанавливаете его через один из вызовов putMethod.addHeader(...)
.
Как указано igor.zh, эта проблема может возникнуть при использовании класса Spring HttpComponentsMessageSender. Чтобы быть более точным, это проблема только в том случае, если вы передаете свой собственный экземпляр HttpClient в конструктор HttpComponentsMessageSender - проблема обрабатывается автоматически в противном случае.
Как и в случае с spring -ws 2.1.4, подкласс HttpComponentsMessageSender.RemoveSoapHeadersInterceptor, который используется в конструкторе по умолчанию, был опубликован для решения этой проблемы (см. https://jira.spring.io/browse/SWS-835) и поэтому могут использоваться в ваших собственных экземплярах HttpClient, вместо того чтобы писать собственный класс для этого. Он также очищает заголовок HTTP.TRANSFER_ENCODING.
Используйте метод HttpClientBuilder.addInterceptorFirst, чтобы ввести этот перехватчик в свой собственный экземпляр HttpClient. Пример ниже с использованием проводки XML bean. Если кто-нибудь знает более сжатый способ построения экземпляра HttpClient (помимо написания класса factory bean), я все уши!
<bean id="httpClientBuilder" class="org.apache.http.impl.client.HttpClientBuilder" factory-method="create"/>
<bean id="interceptedHttpClientBuilder" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="httpClientBuilder" />
<property name="targetMethod" value="addInterceptorFirst"> </property>
<property name="arguments">
<list>
<bean class="org.springframework.ws.transport.http.HttpComponentsMessageSender.RemoveSoapHeadersInterceptor"/>
</list>
</property>
</bean>
<bean id="httpClient" factory-bean="interceptedHttpClientBuilder" factory-method="build" />
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="messageSender">
<bean class="org.springframework.ws.transport.http.HttpComponentsMessageSender">
<property name="httpClient" ref="httpClient"/>
</bean>
</property>
</bean>
В качестве альтернативы, если вы можете, просто позвольте HttpComponentsMessageSender построить собственный экземпляр HttpClient, а не передать ему. Незначительное примечание к этому: из spring -ws 2.2.0-RELEASE конструктор по умолчанию для HttpComponentsMessageSender продолжает использовать класс DefaultHttpClient, который теперь устарел. Надеюсь, это будет рассмотрено в следующем выпуске.
Это происходит со мной, когда я использовал http://docs.spring.io/spring-ws/site/apidocs/org/springframework/ws/transport/http/HttpComponentsMessageSender.html в качестве отправителя сообщений WebService Spring. В этом случае такие вещи, как HttpPut или HttpRequest, недоступны, поэтому, построив HttpClient с помощью HttpClientBuilder, я закончил тем, что вставил HttpRequestInterceptor перед виновником RequestContent:
private static class ContentLengthHeaderRemover implements HttpRequestInterceptor{
@Override
public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
request.removeHeaders(HTTP.CONTENT_LEN);// fighting org.apache.http.protocol.RequestContent ProtocolException("Content-Length header already present");
}
}
...
HttpClientBuilder httpClientBuilder = HttpClients.custom();
httpClientBuilder.addInterceptorFirst(new CcontentLengthHeaderRemover());
Если вы закажете http://docjar.org/docs/api/org/apache/http/protocol/RequestContent.html, вы заметите, что оно выбрасывает это исключение, если вы его установили сами. Поэтому внутренняя работа автоматически устанавливает длину содержимого. Это также означает, что для установки значения "0" вам нужно установить сущность в значение null.
Ответ Джона Рикса имеет правильную идею. Вот как вы это делаете с простой java:
HttpClient client = HttpClients.custom()
.addInterceptorFirst(new RemoveSoapHeadersInterceptor())
.build();