Apache HttpClient a pit in the PUT / POST of

Conclusion:
Feign If you are using Apache HttpClient, when PUT / POST, try to use RequestBody when mass participation.
If not RequestBody, QueryString Apache HttpClient will be converted into a form submit key value, so that the data will take less than an interface side

Being given
as usual the interface definition copy service B to service A FeignClient then Postman in self-test in an interface error during [an error tone when service A service B].

Error message:

 

 

Message is not very elegant, Wuguai, because there can not happen under normal circumstances. That is, the attacker will see this prompt stop Parameter check very strict].

Production and consumption of data
services services provided by B [production]:

 

 

A service provided to the front end of the service:

 

 

A service consumer invokes the service [B]:

 

 

At that time this was written steal a lazy: direct use of QueryString, will not need a new definition of DTO data transmission. Error because they do not take the unusual way, this is something, below analysts.

BugShooting: analysis of the log


By the requested data stream, log order: service log [A] is consistent with the expectations:

 

 

Services B [log inconsistent with expectations: less QueryString]:

 

 

The problem has been positioned: A service call when the service B, the QueryString argument lost
on the importance of print log! Print log is a useful learning

 

And check code, nothing wrong with it, QueryString special mark @RequestParam also has been marked! strange

BugShooting: stand on the shoulders of giants


I want to see if someone wading through the pit, baidu, google, bing did not find the relevant case information. Only see through @Headers ( "the Content-the Type: file application / JSON" ) or @PutMapping (value =  "/ Provide / sync_strategies / syncStrategyId {}" , headers = { "the Type-the Content: file application / JSON" }) to explicitly declared Request Header approach, try the next useless.

 

BugShooting: Debug [ Nirvana ]

Will we have not used this fact, by all parameters directly can solve @RequestBody, but give yourself standing before a Flag: Feign source should re-sort again.
Debug, it is found Apache HttpClient when there is such a logical PUT or POST method:
There QueryString but not requestBody, QueryString is not placed in the URL, but the QueryString converted to the key value data form structure, the form and press of submission:

org.apache.http.client.methods.RequestBuilder # build
specify application / x-www-form- urlencoded, and writes RequestBody QueryString in:

 

 

org.apache.http.client.entity.UrlEncodedFormEntity#UrlEncodedFormEntity(java.lang.Iterable<? extends org.apache.http.NameValuePair>, java.nio.charset.Charset)

 

 

 

org.apache.http.client.utils.URLEncodedUtils # format (java.lang.Iterable <? extends org.apache.http.NameValuePair>, char, java.nio.charset.Charset) will actually convert QueryString, and to form submit the form, but also in line with Htpp agreement, but also requires the recipient to receive this way you can. See the screenshot above, the use of the service B @RequestParam, ie values from QueryString, then of course we did not get up. Simply put, just take the same courier. South Gate are usually taken, but if the courier went to the north gate, do not have to tell you this change. If you also to the south gate, is definitely out of the reach.


Two different data transmission

When an error Apache HttpClient initiated request:

 

 

 

Desired manner:

 

 

 

Found the problem, the solution becomes clear: add a RequestBody can not it
that I have sent a redundant RequestBody go:

 

 

 

 

We can already see the value of the ReqestBody

 

QueryString continue to look at whether the processing logic changes, you can see the desired different:

 

 

However, this approach increases the business does not require parameters, increase maintenance costs of the code, while other students look at the code, as this will remove the invalid parameter, then the service is not available.
Thus, the request will be packaged into a DTO parameter, and then the data can pass in the Body:

 

 

 

 

supplement


1, Apache Http Client entity initialization code entry] [RequestBody:

feign.httpclient.ApacheHttpClient#toHttpUriRequest

 

2, a step on the conditions pit: Specify as Feign use Client Apache Http Client

       <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-httpclient -->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-httpclient</artifactId>
            <version>10.8</version>
        </dependency>

Feign-httpclient earlier version of this dependency also need to add the following:

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>

 

Description: Apache HttpClient is a veteran http client, you can set the connection pool timeout time between service calls the tune right. Spring Cloud Brixtion.SR5 version from the beginning to support this alternative.
A classic HttpClient configuration:

// HttpClient 4.5.2 using classical connection pool configuration 
    Private CloseableHttpClient getHttpClient () {
         // register access protocol associated Socket factory 
        Registry <ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder. <ConnectionSocketFactory> Create () 
                .register ( "HTTP" , PlainConnectionSocketFactory. INSTANCE) 
                .register ( "HTTPS" , SSLConnectionSocketFactory.getSocketFactory ()) 
                .build (); 

        // HttpConnectionFactory: configure a write request / response processor parses 
        HttpConnectionFactory <HttpRoute, ManagedHttpClientConnection> The connectionFactory = new new ManagedHttpClientConnectionFactory (
                DefaultHttpRequestWriterFactory.INSTANCE,
                DefaultHttpResponseParserFactory.INSTANCE 
        ); 

        // the DNS resolver 
        DNSResolver DNSResolver = SystemDefaultDnsResolver.INSTANCE;
         // create a connection pool manager 
        PoolingHttpClientConnectionManager Manager = new new PoolingHttpClientConnectionManager (socketFactoryRegistry, The connectionFactory, DNSResolver);
         // set the default parameters socket 
        manager.setDefaultSocketConfig (SocketConfig. . Custom () setTcpNoDelay ( to true ) .build ()); 
        manager.setMaxTotal ( 300); // set the maximum number of connections. Above this value, a new connection request, needs to block, waiting
         // routing is the breakdown of the MaxTotal.
        //The actual maximum number of connections for each route is controlled by the default value DefaultMaxPerRoute.
        // MaxPerRoute set too small to support large concurrent: ConnectionPoolTimeoutException: Waiting for Connection Timeout from the pool 
        manager.setDefaultMaxPerRoute (200); // maximum connections per route 
        manager.setValidateAfterInactivity (5 * 1000); // from the connection when acquiring the connection pool, the connection needs to authenticate once inactive after long, default 2S 

        // configure the default request parameters 
        RequestConfig defaultRequestConfig = RequestConfig.custom () 
                .setConnectTimeout ( 2 * 1000) // connection timeout to 2S 
                . setSocketTimeout (. 5 * 1000) // wait for the data to the timeout 5S
                .setConnectionRequestTimeout (2 * 1000) //Obtaining a connection from the connection pool waiting timeout is set to 2S
 //                 .setProxy (new new the Proxy (Proxy.Type.HTTP, the InetSocketAddress new new ( "192.168.0.2", 1234))) // set up a proxy 
                .build (); 

        CloseableHttpClient closeableHttpClient = HttpClients.custom () 
                .setConnectionManager (Manager) 
                .setConnectionManagerShared ( to false ) // connection pool is not shared mode, this refers to whether sharing is shared with other httpClient 
                .evictIdleConnections (60, TimeUnit.SECONDS) // periodically recovered idle connection 
                .evictExpiredConnections () // recovering expired connected 
                .setConnectionTimeToLive (60, TimeUnit.SECONDS) // connect the survival time, if not set, then decide based on the long connection information 
                .setDefaultRequestConfig (defaultRequestConfig) // set the default request parameters 
                .setConnectionReuseStrategy (DefaultConnectionReuseStrategy.INSTANCE) // connection reuse strategy, i.e., whether the keepAlive 
                .setKeepAliveStrategy (DefaultConnectionKeepAliveStrategy.INSTANCE) // long connection configuration, i.e. how long production acquire long connection 
                .setRetryHandler ( new new DefaultHttpRequestRetryHandler (0, to false )) // set the number of retries, the default is 3 times; the current is disable off 
                .build (); 

        / ** 
         * stop or restart the JVM, connected to close the connection pool freed 
         * / 
        Runtime.getRuntime (). addShutdownHook ( new new the Thread () {
            @Override
            public void run() {
                try {
                    closeableHttpClient.close();
                    log.info("http client closed");
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
            }
        });
        return closeableHttpClient;
    }

 

https://github.com/helloworldtang/spring-boot-cookbook/blob/master/learning-demo/src/main/java/com/tangcheng/learning/web/config/RestTemplateConfig.java

 

https://mp.weixin.qq.com/s/N4zqSfMAgB6b5jnUsa1z2w

 

Guess you like

Origin www.cnblogs.com/softidea/p/12549071.html