Content-Length 标头已存在

Posted

技术标签:

【中文标题】Content-Length 标头已存在【英文标题】:Content-Length header already present 【发布时间】:2010-07-26 05:21:20 【问题描述】:

我正在使用 android 中包含的 Apache HttpClient (4.1) 来执行 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: 在 org.apache.http.protocol.RequestContent.process(RequestContent.java:70) 在 org.apache.http.protocol.BasicHttpProcessor.process(BasicHttpProcessor.java:290)

有什么想法吗?

【问题讨论】:

【参考方案1】:

正如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 实例的更简洁的方法(除了编写工厂 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 类。希望这将在未来的版本中得到解决。

【讨论】:

【参考方案2】:

我自己没有使用过 HttpClient,但我怀疑问题在于 putMethod.setEntity(...) 隐式提供了内容长度,而您还通过 putMethod.addHeader(...) 调用之一显式设置它。

【讨论】:

没错。除非您使用普通的 Socket,否则您不必自己在 Java 中设置 Content-Length。 @EJP - 或 java.net URLConnection 类。 不,他们是为你做的。如果你使用这些,你只需要设置它。 很公平,但 Javadoc 并没有说它不能构建自己的 Content-Length 标头,它还说'在常见情况下,所有预连接参数和一般请求属性可以忽略:预连接参数和请求属性默认为合理值。对于这个接口的大多数客户端来说,只有两个有趣的方法……' 顺便说一句 - 我能够通过设置请求拦截器(最后)并查看修改后的请求来验证这一点。它显示已经设置的 Content-Length。【参考方案3】:

当我使用 http://docs.spring.io/spring-ws/site/apidocs/org/springframework/ws/transport/http/HttpComponentsMessageSender.html 作为 Spring WebService 消息发送器时,我会遇到这种情况。在那种情况下,像 HttpPut 或 HttpRequest 这样的东西不容易访问,所以,用 HttpClientBuilder 构建 HttpClient,我最终在罪魁祸首 RequestContent 前面插入了一个 HttpRequestInterceptor :

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

【讨论】:

【参考方案4】:

John Rix's answer 有正确的想法。以下是使用普通 java 的方法:

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

【讨论】:

你必须导入那个拦截器类import org.springframework.ws.transport.http.HttpComponentsMessageSender.RemoveSoapHeadersInterceptor;【参考方案5】:

如果您结帐http://docjar.org/docs/api/org/apache/http/protocol/RequestContent.html,您会注意到如果您自己设置了它,它会引发该异常。因此,内部工作自动设置内容长度。这也意味着,要将其设置为“0”,您需要将实体设置为 null。

【讨论】:

【参考方案6】:

@John Rix 的回答 这段代码帮助解决了问题

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

【讨论】:

以上是关于Content-Length 标头已存在的主要内容,如果未能解决你的问题,请参考以下文章

Content-Length 标头与分块编码

ERROR 验证错误:Lambda 函数结果验证失败,该函数尝试删除只读标头,标头名称:Content-Length

拒绝获取不安全的标头“Content-Length”

如何从使用 axios 发送的 POST 请求中获取 Content-Length 标头?

如何从跨域 Ajax 请求访问 Content-Length 标头?

如何在没有 Content-Length 标头的情况下流式传输 HTTP 文件上传?