Integrated HttpClient of SpringBoot

Posted by petitduc on Fri, 25 Oct 2019 21:08:57 +0200

Table of Contents

1.maven add dependency

2. Add profile information

3. Create HttpClient entity class association configuration

4. Create connection thread processing class

5. Create request return result class

6. Create specific request class

1.maven add dependency

pom.xml: here, we add the gson dependency to send the JSON data POST request later, and add the lombook to simplify the entity object (if not needed, we don't need to add it).

        <!-- httpclient -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpasyncclient</artifactId>
            <version>4.1</version>
        </dependency>

        <!-- gson -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.2</version>
        </dependency>

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

2. Add profile information

Add some information about http configuration in application.yml

#http configuration service
http:
  maxTotal: 100 #maximum connection
  defaultMaxPerRoute : 20 #Concurrency number
  connectTimeout: 1000 #Maximum time to create connection
  connectionRequestTimeout: 500 #Maximum time to get a connection from the connection pool
  socketTimeout: 10000 #Maximum time of data transmission
  staleConnectionCheckEnabled: true #Test whether the connection is available before submitting the request

3. Create HttpClient entity class association configuration

@Configuration
public class HttpClient {
    @Value("${http.maxTotal}")
    private Integer maxTotal;

    @Value("${http.defaultMaxPerRoute}")
    private Integer defaultMaxPerRoute;

    @Value("${http.connectTimeout}")
    private Integer connectTimeout;

    @Value("${http.connectionRequestTimeout}")
    private Integer connectionRequestTimeout;

    @Value("${http.socketTimeout}")
    private Integer socketTimeout;

    @Value("${http.staleConnectionCheckEnabled}")
    private boolean staleConnectionCheckEnabled;

    /**
     * First, instantiate a connection pool manager, and set the maximum number of connections and concurrent connections.
     * @return
     */
    @Bean(name = "httpClientConnectionManager")
    public PoolingHttpClientConnectionManager getHttpClientConnectionManager(){
        PoolingHttpClientConnectionManager httpClientConnectionManager = new PoolingHttpClientConnectionManager();
        //maximum connection
        httpClientConnectionManager.setMaxTotal(maxTotal);
        //Concurrency number
        httpClientConnectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute);
        return httpClientConnectionManager;
    }

    /**
     * Instantiate the connection pool and set the connection pool manager.
     * The connection pool manager instantiated above needs to be injected as a parameter.
     * @param httpClientConnectionManager
     * @return
     */
    @Bean(name = "httpClientBuilder")
    public HttpClientBuilder getHttpClientBuilder(@Qualifier("httpClientConnectionManager")PoolingHttpClientConnectionManager httpClientConnectionManager){

        //The construction method in HttpClientBuilder is decorated with protected, so you can not directly use new to instantiate an HttpClientBuilder. You can use the static method create() provided by HttpClientBuilder to obtain the HttpClientBuilder object.
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();

        httpClientBuilder.setConnectionManager(httpClientConnectionManager);

        return httpClientBuilder;
    }

    /**
     * Inject connection pool to get httpClient
     * @param httpClientBuilder
     * @return
     */
    @Bean
    public CloseableHttpClient getCloseableHttpClient(@Qualifier("httpClientBuilder") HttpClientBuilder httpClientBuilder){
        return httpClientBuilder.build();
    }

    /**
     * Builder Is an internal class of RequestConfig
     * Get a Builder object through the custom method of RequestConfig
     * Set up the connection information of builder
     * You can also set proxy, cookieSpec and other properties here. It can be set here if necessary
     * @return
     */
    @Bean(name = "builder")
    public RequestConfig.Builder getBuilder(){
        RequestConfig.Builder builder = RequestConfig.custom();
        return builder.setConnectTimeout(connectTimeout)
                .setConnectionRequestTimeout(connectionRequestTimeout)
                .setSocketTimeout(socketTimeout)
                .setStaleConnectionCheckEnabled(staleConnectionCheckEnabled);
    }

    /**
     * Using builder to build a RequestConfig object
     * @param builder
     * @return
     */
    @Bean
    public RequestConfig getRequestConfig(@Qualifier("builder") RequestConfig.Builder builder){
        return builder.build();
    }
}

4. Create connection thread processing class

@Component
public class IdleConnectionEvictor extends Thread {
    @Autowired
    private HttpClientConnectionManager connMgr;

    private volatile boolean shutdown;

    public IdleConnectionEvictor() {
        super();
        super.start();
    }

    @Override
    public void run() {
        try {
            while (!shutdown) {
                synchronized (this) {
                    wait(5000);
                    // Close failed connections
                    connMgr.closeExpiredConnections();
                }
            }
        } catch (InterruptedException ex) {
            // End
        }
    }

    //Shut down thread cleaning up invalid connections
    public void shutdown() {
        shutdown = true;
        synchronized (this) {
            notifyAll();
        }
    }
}

5. Create request return result class

@Data
public class HttpResult {
    // Response code
    @NonNull
    private Integer code;

    // Response body
    @NonNull
    private String body;
}

6. Create specific request class

Add a general solution that may encounter problems when requesting Https and redirection

@Component
public class HttpAPIService {

    private static CloseableHttpClient httpClient;

    /**
     * Trust SSL certificate
     */
    static {
        try {
            SSLContext sslContext = SSLContextBuilder.create().useProtocol(SSLConnectionSocketFactory.SSL)
                    .loadTrustMaterial((x, y) -> true).build();
            RequestConfig config = RequestConfig.custom().setConnectTimeout(5000).setSocketTimeout(5000).build();
            httpClient = HttpClientBuilder.create().setDefaultRequestConfig(config).setSSLContext(sslContext)
                    .setSSLHostnameVerifier((x, y) -> true).build();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Autowired
    private RequestConfig config;

    /**
     * The get request without parameters returns body if the status code is 200 and null if it is not 200
     *
     * @param url
     * @return
     * @throws Exception
     */
    public String doGet(String url) throws Exception {
        // Declare http get request
        HttpGet httpGet = new HttpGet(url);

        // Mount configuration information
        httpGet.setConfig(config);


        // Allow redirection
        httpGet.getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS,true);

        // Initiate request
        CloseableHttpResponse response = this.httpClient.execute(httpGet);

        // Judge whether the status code is 200
        if (response.getStatusLine().getStatusCode() == 200) {
            // Returns the contents of the response body
            return EntityUtils.toString(response.getEntity(), "UTF-8");
        }
        return null;
    }

    /**
     * For get request with parameters, if the status code is 200, it returns body; if it is not 200, it returns null.
     *
     * @param url
     * @return
     * @throws Exception
     */
    public String doGet(String url, Map<String, Object> map) throws Exception {
        URIBuilder uriBuilder = new URIBuilder(url);

        if (map != null) {
            // Traversal map, splicing request parameters
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                uriBuilder.setParameter(entry.getKey(), entry.getValue().toString());
            }
        }

        // Call get request without parameters
        return this.doGet(uriBuilder.build().toString());

    }

    /**
     * @description post request with parameters
     * @param url
	 * @param map
	 * @param headers
     * @return com.mark.httpclient.HttpResult
     * @author Mario
     * @date 2019/7/15 14:39
     */
    public HttpResult doPost(String url, Map<String, Object> map , Map<String,Object> headers) throws Exception {
        // Declare httpPost request
        HttpPost httpPost = new HttpPost(url);
        // Add configuration information
        httpPost.setConfig(config);

        // Determine whether the map is empty or not. If it is not empty, traverse and encapsulate the from form object.
        if (map != null) {
            List<NameValuePair> list = new ArrayList<NameValuePair>();
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                list.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
            }
            // Construct from form object
            UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(list, "UTF-8");

            // Put the form in the post
            httpPost.setEntity(urlEncodedFormEntity);
        }

        // Set request header
        if (headers != null) {
            for (String key : headers.keySet()) {
                String value = headers.get(key).toString();
                httpPost.addHeader(key,value);
            }
        }

        // Initiate request
        CloseableHttpResponse response = this.httpClient.execute(httpPost);
        return new HttpResult(response.getStatusLine().getStatusCode(), EntityUtils.toString(
                response.getEntity(), "UTF-8"));
    }

    /**
     * post request without parameters
     *
     * @param url
     * @return
     * @throws Exception
     */
    public HttpResult doPost(String url) throws Exception {
        return this.doPost(url, null,null);
    }

    /**
     * @description post request sent in JSON with parameters
     * @param url
	 * @param jsonString
	 * @param headers
     * @return com.mark.httpclient.HttpResult
     * @author Mario
     * @date 2019/7/15 14:39
     */
    public static HttpResult doPostWithJson(String url, String jsonString, Map<String,Object> headers) throws Exception {
        // Declare httpPost request
        HttpPost httpPost = new HttpPost(url);

        // Set request header
        if (headers != null) {
            for (String key : headers.keySet()) {
                String value = headers.get(key).toString();
                httpPost.addHeader(key, value);
            }
        }

        // Set to send as Json data
        StringEntity stringEntity = new StringEntity(jsonString, "utf-8");
        stringEntity.setContentType("application/json");
        httpPost.setEntity(stringEntity);

        // Initiate request
        CloseableHttpResponse response = httpClient.execute(httpPost);

        return new HttpResult(response.getStatusLine().getStatusCode(), EntityUtils.toString(
                response.getEntity(), "UTF-8"));
    }
}

 

Topics: JSON Maven Lombok SSL