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.