Spring REST模板接受标头

时间:2014-11-05 02:13:46

标签: java spring rest spring-mvc resttemplate

在我们的一个REST服务的负载测试期间,当负载增加时,我们开始看到Spring的REST模板的这些日志:

在并发加载和3-4小时后,http请求的Accept标头变为

DEBUG: org.springframework.web.client.RestTemplate - Setting request Accept header to [text/plain, application/json, application/*+json, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain, text/plain,<and so on>, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, */*, <and so on>]

最终,使用RestTemplate对此服务的所有调用都会因400错误(错误请求)而失败

被调用的REST服务接受String作为输入,并具有以下签名

@RequestMapping(value = "/findRecordById", method = {RequestMethod.POST, RequestMethod.GET })
@ResponseBody
public String findRecordById(@RequestBody String id) {//method body}

我们正在向此服务发送POST类型的请求,其请求内容为“someId”,例如。 “123”

在轻负载下,调用服务没有问题。

令人费解的是, text / plain,* / * 会不断添加到REST模板的接受标头列表中。为什么会这样?

REST模板bean声明如下:

<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
        <constructor-arg>
            <bean class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
                <property name="readTimeout">
                    <value>90000</value>
                 </property>
                <property name="httpClient" ref="restHttpClient" />
            </bean>
        </constructor-arg>
    </bean>

    <bean id="restHttpClient"  class="org.apache.http.impl.client.DefaultHttpClient">
          <constructor-arg> 
            <bean class="org.apache.http.impl.conn.PoolingClientConnectionManager">
                <property name="defaultMaxPerRoute">
                    <value>100000</value>
                 </property>
                <property name="maxTotal">
                    <value>100000</value>
                 </property>                 

            </bean>
          </constructor-arg>
    </bean>

如何创建请求:

String postParams = "\"" + id + "\"";

String postResp = restTemplate.postForObject("findRecordById",postParams, String.class);

3 个答案:

答案 0 :(得分:1)

如果由于海报有重复的文字/普通接受标题问题而有人来到这里,我经历了同样的事情,这里发生了什么: 我们在servlet-context.xml中为rest模板设置了常用的bean定义,我们为application / json指定了一个消息转换器(这是针对spring-beans 4.0):

<beans:bean id="myRestTemplate" class="com.mypackage.MyClass">
        <beans:property name="requestFactoryNonSSL" ref="restTemplateNonSSLRequestFactory"/>
        <beans:property name="requestFactorySSL" ref="restTemplateNonSSLRequestFactory"/>
        <beans:property name="messageConverters">
            <beans:list>
                <beans:bean class="org.springframework.http.converter.StringHttpMessageConverter">
                    <beans:property name="supportedMediaTypes">
                        <beans:list>
                            <beans:value>application/json;charset=UTF-8</beans:value>
                        </beans:list>
                    </beans:property>
                </beans:bean>           
            </beans:list>
        </beans:property>
    </beans:bean>

但是在源代码中我们还使用以下方法显式添加StringHttpMessageConverter:

restTemplate.getMessageConverters().add(new StringHttpMessageConverter());

然而,这个messageConverter列表只是在每次请求时都添加了一个新的StringHttpMessageConverter实例。对于每个请求,Spring会遍历该消息转换器列表并添加相应的Accept标头(text / plain)。在这么多请求之后,这会导致标头长度变得如此之大,以至于它将被您正在调用的服务器容器拒绝。解决此问题的最简单方法是将text / plain指定为servlet-context.xml中的supportedMediaTypes,并删除代码中的上一行。 如果你不能这样做,你需要检查代码,以确保不会反复将StringHttpMessageConverter添加到restTemplate实例。

这是添加了text / plain supportedMediaType的servlet-context.xml:

<beans:bean id="myRestTemplate" class="com.mypackage.MyClass">
        <beans:property name="requestFactoryNonSSL" ref="restTemplateNonSSLRequestFactory"/>
        <beans:property name="requestFactorySSL" ref="restTemplateNonSSLRequestFactory"/>
        <beans:property name="messageConverters">
            <beans:list>
                <beans:bean class="org.springframework.http.converter.StringHttpMessageConverter">
                    <beans:property name="supportedMediaTypes">
                        <beans:list>
                            <beans:value>application/json;charset=UTF-8</beans:value>
                            <beans:value>text/plain</beans:value>
                        </beans:list>
                    </beans:property>
                </beans:bean>           
            </beans:list>
        </beans:property>
    </beans:bean>

答案 1 :(得分:0)

请你试试这个:

restTemplate.getMessageConverters().add(new StringHttpMessageConverter());

String postParams = "\"" + id + "\"";

String postResp = restTemplate.postForObject("findRecordById",postParams, String.class);

答案 2 :(得分:0)

添加了text / plain,因为您尝试读取String和RestTemplate,发现StringHttpMessageConverter是您的请求的转换器,StringHttpMessageConverter支持的媒体类型是text / plain。

正如你在RestTemplate的这个方法中看到的那样。

public void doWithRequest(ClientHttpRequest request) throws IOException {
            if (responseType != null) {
                List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
                for (HttpMessageConverter<?> messageConverter : getMessageConverters()) {
                    if (messageConverter.canRead(responseType, null)) {
                        List<MediaType> supportedMediaTypes = messageConverter.getSupportedMediaTypes();
                        for (MediaType supportedMediaType : supportedMediaTypes) {
                            if (supportedMediaType.getCharSet() != null) {
                                supportedMediaType =
                                        new MediaType(supportedMediaType.getType(), supportedMediaType.getSubtype());
                            }
                            allSupportedMediaTypes.add(supportedMediaType);
                        }
                    }
                }
                if (!allSupportedMediaTypes.isEmpty()) {
                    MediaType.sortBySpecificity(allSupportedMediaTypes);
                    if (logger.isDebugEnabled()) {
                        logger.debug("Setting request Accept header to " + allSupportedMediaTypes);
                    }
                    request.getHeaders().setAccept(allSupportedMediaTypes);
                }
            }
        }
    }