web-dev-qa-db-fra.com

En-tête Content-Length déjà présent

J'utilise Apache HttpClient (4.1) inclus dans Android pour exécuter un HttpPut. J'ai vérifié que je n'ai qu'un seul en-tête de contenu. Cependant, chaque fois que j'envoie la demande, j'obtiens une exception de protocole concernant l'en-tête Content-Length déjà spécifié.

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

Causée par: org.Apache.http.ProtocolException: en-tête Content-Length déjà présent À org.Apache.http.protocol.RequestContent.process (RequestContent.Java:70) À org.Apache .http.protocol.BasicHttpProcessor.process (BasicHttpProcessor.Java:290)

Des idées?

27
AdamC

Je n'ai pas utilisé HttpClient moi-même, mais je soupçonne que le problème est que putMethod.setEntity(...) fournit implicitement une longueur de contenu et que vous le définissez également de manière explicite via l'un des appels putMethod.addHeader(...).

28
Stephen C

Comme indiqué par igor.zh, ce problème peut survenir si vous utilisez la classe HttpComponentsMessageSender de Spring. Pour être plus précis cependant, ce n'est un problème que si vous passez votre propre instance de HttpClient dans le constructeur HttpComponentsMessageSender - le problème est traité automatiquement sinon. 

A partir de spring-ws 2.1.4, la sous-classe HttpComponentsMessageSender.RemoveSoapHeadersInterceptor utilisée dans le constructeur par défaut a été rendue publique pour résoudre ce problème (voir https://jira.spring.io/browse/SWS-835 ). et peut donc être utilisé dans vos propres instances HttpClient au lieu d'écrire votre propre classe pour le faire. Il efface également l'en-tête HTTP.TRANSFER_ENCODING. 

Utilisez la méthode HttpClientBuilder.addInterceptorFirst pour injecter cet intercepteur dans votre propre instance HttpClient. Exemple ci-dessous utilisant le câblage de bean XML. Si quelqu'un connaît une manière plus concise de construire l'instance HttpClient (à part l'écriture d'une classe de bean factory), je suis tout ouïe!

<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>

Si vous le pouvez, vous pouvez également permettre à HttpComponentsMessageSender de créer sa propre instance HttpClient plutôt que de lui en transmettre une. Note mineure à ce sujet: à partir de spring-ws 2.2.0-RELEASE, le constructeur par défaut de HttpComponentsMessageSender continue à utiliser la classe DefaultHttpClient, désormais obsolète. Espérons que cela sera résolu dans une prochaine version.

28
John Rix

Cela m’arrive lorsque j’utilise http://docs.spring.io/spring-ws/site/apidocs/org/springframework/ws/transport/http/HttpComponentsMessageSender.html en tant qu’expéditeur de messages WebService Spring. Dans ce cas, les éléments tels que HttpPut ou HttpRequest ne sont pas facilement accessibles. Par conséquent, en construisant HttpClient avec HttpClientBuilder, j'ai fini par insérer un HttpRequestInterceptor devant le RequestContent coupable:

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's ProtocolException("Content-Length header already present");
    }
}
...
    HttpClientBuilder httpClientBuilder = HttpClients.custom();
    httpClientBuilder.addInterceptorFirst(new CcontentLengthHeaderRemover());
13
igor.zh

La réponse de John Rix a la bonne idée. Voici comment procéder avec Java pur:

HttpClient client = HttpClients.custom()
    .addInterceptorFirst(new RemoveSoapHeadersInterceptor())
    .build();
3
Muhd

Si vous achetez http://docjar.org/docs/api/org/Apache/http/protocol/RequestContent.html vous remarquerez qu'il lève cette exception si vous l'avez définie vous-même. Par conséquent, le travail interne définit automatiquement la longueur du contenu. Cela signifie également que, pour le définir sur "0", vous devez définir l'entité sur null.

1
Prannay

Réponse de John Rix Ce code a aidé à résoudre le problème 

    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's ProtocolException("Content-Length header already present");
            }
        }
HttpClient client = HttpClients.custom()
                .addInterceptorFirst(new ContentLengthHeaderRemover())
                .build();
0
Alok