Spring boot basic tutorial 2-1-11 RestTemplate integration HttpClient

Keywords: Spring JSON Java Apache

1 Overview

Http request is essential in server development. In this paper, RestTemplate is used as the facade and HttpClient is used as the implementation to demonstrate the basic Http request example.

2 source code analysis

2.1 add pom.xml dependency

  • RestTemplate is built in the spring web module, and spring boot is introduced automatically
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.5</version>
</dependency>
<!-- If not asynchronous( AsyncRestTemplate),We don't need this dependency -->
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.5.Final</version>
</dependency>

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

2.2 configuration file application.yml (optional)

# The priority of yml configuration is higher than java configuration. If both yml configuration and java configuration exist, the yml configuration will override java configuration
####restTemplate Of yml Configuration start####
---
spring:
  restTemplate:
    maxTotalConnect: 1000 #The maximum number of connections in the connection pool, 0 means unlimited; if 0 is taken, the consequence of system crash caused by connection leakage needs to be considered
    maxConnectPerRoute: 200
    connectTimeout: 3000
    readTimeout: 5000
    charset: UTF-8
####restTemplate Of yml Configuration start####

2.3 write RestTemplate configuration (required)

// necessary
@Configuration
@ConfigurationProperties(prefix = "spring.restTemplate")
@ConditionalOnClass(value = {RestTemplate.class, CloseableHttpClient.class})
public class RestTemplateConfig {

    // The priority of java configuration is lower than yml configuration; if yml configuration does not exist, java configuration will be used
    // ####Start java configuration of restTemplate####
    private int maxTotalConnection = 500; //Maximum number of connections to the connection pool

    private int maxConnectionPerRoute = 100; //Concurrent number of the same route

    private int connectionTimeout = 2 * 1000; //Connection timeout, default 2s

    private int readTimeout = 30 * 1000; //Read timeout, default 30s

    private String charset = "UTF-8";

    public void setMaxTotalConnection(int maxTotalConnection) {
        this.maxTotalConnection = maxTotalConnection;
    }

    public void setMaxConnectionPerRoute(int maxConnectionPerRoute) {
        this.maxConnectionPerRoute = maxConnectionPerRoute;
    }

    public void setConnectionTimeout(int connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
    }

    public void setReadTimeout(int readTimeout) {
        this.readTimeout = readTimeout;
    }

    public void setCharset(String charset) {
        this.charset = charset;
    }

    //Create HTTP client factory
    @Bean(name = "clientHttpRequestFactory")
    public ClientHttpRequestFactory clientHttpRequestFactory() {
        return createClientHttpRequestFactory(this.connectionTimeout, this.readTimeout);
    }

    //Initialize RestTemplate and join spring's Bean factory for spring's unified management
    @Bean(name = "restTemplate")
    @ConditionalOnMissingBean(RestTemplate.class)
    public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
        return createRestTemplate(factory);
    }

    //Initialize the RestTemplate that supports asynchrony, and join the Bean factory of spring, which is under the unified management of spring
    //If you don't use asynchrony, you don't have to create the object
    @Bean(name = "asyncRestTemplate")
    @ConditionalOnMissingBean(AsyncRestTemplate.class)
    public AsyncRestTemplate asyncRestTemplate(RestTemplate restTemplate) {
        final Netty4ClientHttpRequestFactory factory = new Netty4ClientHttpRequestFactory();
        factory.setConnectTimeout(this.connectionTimeout);
        factory.setReadTimeout(this.readTimeout);
        return new AsyncRestTemplate(factory, restTemplate);
    }

    private ClientHttpRequestFactory createClientHttpRequestFactory(int connectionTimeout, int readTimeout) {
        //maxTotalConnection and maxConnectionPerRoute must be equipped with
        if (this.maxTotalConnection <= 0) {
            throw new IllegalArgumentException("invalid maxTotalConnection: " + maxTotalConnection);
        }
        if (this.maxConnectionPerRoute <= 0) {
            throw new IllegalArgumentException("invalid maxConnectionPerRoute: " + maxTotalConnection);
        }

        //Global default header configuration
        List<Header> headers = new LinkedList<>();
        headers.add(new BasicHeader("Accept-Encoding", "gzip,deflate"));
        headers.add(new BasicHeader("Accept-Language", "zh-CN,zh;q=0.8,en;q=0.6"));

        //Disable automatic retry. If you need to retry, please control yourself
        HttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler(0, false);

        //Create an httpClient instance that actually processes http requests
        CloseableHttpClient httpClient = HttpClients.custom()
                .setDefaultHeaders(headers)
                .setRetryHandler(retryHandler)
                .build();

        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(
                httpClient);
        factory.setConnectTimeout(connectionTimeout);
        factory.setReadTimeout(readTimeout);
        return factory;
    }

    private RestTemplate createRestTemplate(ClientHttpRequestFactory factory) {
        RestTemplate restTemplate = new RestTemplate(factory);

        //We use the message converter inside the RestTemplate
        //Reset the StringHttpMessageConverter character set to solve the problem of Chinese scrambling
        modifyDefaultCharset(restTemplate);

        //Set error handler
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler());

        return restTemplate;
    }

    private void modifyDefaultCharset(RestTemplate restTemplate) {
        List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters();
        HttpMessageConverter<?> converterTarget = null;
        for (HttpMessageConverter<?> item : converterList) {
            if (StringHttpMessageConverter.class == item.getClass()) {
                converterTarget = item;
                break;
            }
        }
        if (null != converterTarget) {
            converterList.remove(converterTarget);
        }
        Charset defaultCharset = Charset.forName(charset);
        converterList.add(1, new StringHttpMessageConverter(defaultCharset));
    }
}
  • After the above configuration, the available RestTemplate instances are generated

2.4 Get request demonstration

@Slf4j
@RestController
public class GetTestController {

    @Resource
    private RestTemplate restTemplate;

    //The simplest get operation
    @GetMapping("/baidu1/{key}")
    public String get1(@PathVariable String key) throws UnsupportedEncodingException {
        String encodeKey = URLEncoder.encode(key, "UTF-8");

        String url = "http://www.baidu.com/s?bdorz_come=1&ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=" + encodeKey;

        return restTemplate.getForObject(url, String.class); //Return to Baidu main station html
    }

    //get operation of header header needs to be customized
    @GetMapping("/baidu2/{key}")
    public String get2(@PathVariable String key) throws UnsupportedEncodingException {
        HttpHeaders headers = new HttpHeaders();

        headers.set("MyHeaderKey", "MyHeaderValue");

        HttpEntity entity = new HttpEntity(headers);

        String encodeKey =URLEncoder.encode(key, "UTF-8");

        String url = "http://www.baidu.com/s?bdorz_come=1&ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=" + encodeKey;

        ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);

        return  response.getBody(); //Return to Baidu main station html
    }

}

2.5 Post request demonstration

@Slf4j
@RestController
public class PostTestController {

    @Resource
    private RestTemplate restTemplate;

    //post form presentation
    @GetMapping("/postForm")
    public String testPostForm() {
        // Fill in url
        String url = "";
        MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>();

        // Fill in form
        form.add("name", "**");
        form.add("age", "**");

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        //headers.add("xx", "yy"); / / you can add a custom header header
        HttpEntity<MultiValueMap<String, String>> formEntity = new HttpEntity<>(form, headers);
        String json = restTemplate.postForObject(url, formEntity, String.class);
        return json;
    }

    @RequestMapping("/postBody")
    public String testPostBody() {
        // Fill in url
        String url = "";

        // Fill in json string
        String jsonBody = "{\n"
                + "    \"name\": \"XX\",\n"
                + "    \"age\": \"12\",\n"
                + "    \"sex\": \"man\"\n"
                + "}\n";

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        //headers.add("xx", "yy"); / / you can add a custom header header
        HttpEntity<String> bodyEntity = new HttpEntity<>(jsonBody, headers);

        //1. Take the original json string directly
        String json = restTemplate.postForObject(url, bodyEntity, String.class);

        //2. Transfer the original json to java object, and rest template can be completed automatically
        ResultVo resultVo = restTemplate.postForObject(url, bodyEntity, ResultVo.class);
        if (resultVo != null && resultVo.success()) {
            Object res = resultVo.getData();
            log.info("Processing succeeded, return data: {}", resultVo.getData());
        } else {
            log.info("Processing failed, response result: {}", resultVo);
        }

        return json;//The returned json of the subcontracting api
    }
    
}

2.6 file upload and download request demonstration

@Slf4j
@RestController
public class FileTestController {
    @Resource
    private RestTemplate restTemplate;

    // post file upload
    // Scenario Description: only suitable for small file upload (within 20MB)
    @RequestMapping("/postFile")
    public String testPostFileBody() {
        String filePath = "D:/config.png";

        //If temporary files are generated through disk file upload, please remember to delete them. Otherwise, the more temporary files are accumulated, the disk will explode
        FileSystemResource resource = new FileSystemResource(new File(filePath));

        String url = "***";//Change to your own configuration when testing
        String appId = "***";//Change to your own configuration when testing
        String secureKey = "***";//Change to your own configuration when testing
        String time = String.valueOf(System.currentTimeMillis());
        String pubStr = "1";
        String tempStr = String.format("app_id=%s&is_public=%s&time=%s&vframe=0%s", appId, pubStr, time, secureKey);
        MultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
        form.add("is_public", pubStr);
        form.add("vframe", "0");
        form.add("file", resource);
        form.add("app_id", appId);
        form.add("time", time);
        form.add("sign", DigestUtils.md5(tempStr));

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        //headers.add("xx", "yy"); / / you can add a custom header header
        HttpEntity<MultiValueMap<String, Object>> formEntity = new HttpEntity<>(form, headers);
        String json = restTemplate.postForObject(url, formEntity, String.class);
        return json;
    }

    //File download
    //Scenario Description: only suitable for small file download (within 10MB)
    @RequestMapping("/downloadFile")
    public ResponseEntity testDownloadFile() throws Exception {
        String url = "http://editor.baidu.com/editor/download/BaiduEditor(Online)_5-9-16.exe";
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_OCTET_STREAM));
        HttpEntity<String> entity = new HttpEntity<>(headers);
        ResponseEntity<byte[]> response = restTemplate.exchange(url, HttpMethod.GET, entity, byte[].class);
        byte[] bytes = response.getBody();
        long contentLength = bytes != null ? bytes.length : 0;
        headers.setContentLength((int) contentLength);
        headers.setContentDispositionFormData("baidu.exe", URLEncoder.encode("Baidu installation package.exe", "UTF-8"));
        return new ResponseEntity<>(response.getBody(), headers, HttpStatus.OK);
    }
}

3 pit record

3.1 when only @ ConfigurationProperties are configured, bean s will not be created automatically

Correct posture:

@Configuration
@ConfigurationProperties(prefix = "spring.restTemplate")
@ConditionalOnClass(value = {RestTemplate.class, CloseableHttpClient.class})
public class RestTemplateConfig {
}

Wrong posture:

@ConfigurationProperties(prefix = "spring.restTemplate")
@ConditionalOnClass(value = {RestTemplate.class, CloseableHttpClient.class})
public class RestTemplateConfig {
}

3.2 @ConfigurationProperties cannot inject a property without a setter

3.3 the default configuration of resttemplate will be garbled

Correct posture:

private RestTemplate createRestTemplate(ClientHttpRequestFactory factory) {
        RestTemplate restTemplate = new RestTemplate(factory);

        //We use the message converter inside the RestTemplate
        //Reset the StringHttpMessageConverter character set to solve the problem of Chinese scrambling
        modifyDefaultCharset(restTemplate);

        //Set error handler
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler());

        return restTemplate;
    }

    private void modifyDefaultCharset(RestTemplate restTemplate) {
        List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters();
        HttpMessageConverter<?> converterTarget = null;
        for (HttpMessageConverter<?> item : converterList) {
            if (StringHttpMessageConverter.class == item.getClass()) {
                converterTarget = item;
                break;
            }
        }
        if (null != converterTarget) {
            converterList.remove(converterTarget);
        }
        Charset defaultCharset = Charset.forName(charset);
        converterList.add(1, new StringHttpMessageConverter(defaultCharset));
    }

Wrong posture:

@Bean
public RestTemplate getRestTemplate(){
    RestTemplate rest = new RestTemplate(this.createFactory);
    return rest;
}

How to debug RestTemplate

  • You can configure a debug level logger in logback to direct the logs below org.apache.http to the console:
<logger name="org.apache.http" level="DEBUG" additivity="false">
    <appender-ref ref="STDOUT" />
</logger>

5 project directory

6 concluding remarks

What can I say? If you have any suggestions, please leave a message for discussion, Source of this article.

Welcome to pay attention to the blogger's public number and push the latest articles for the first time.

Posted by Whitestripes9805 on Mon, 30 Dec 2019 13:46:27 -0800