Getting started with SpringBoot RestTemplate

The RestTemplate class provided by the spring framework can be used to call rest services in applications. It simplifies the communication with the http service, unifies the RESTful standards, and encapsulates the http link. We only need to pass in the url and return value types. Compared with the HttpClient, RESTClient is a more elegant way to call RESTful services.

RestTemplate relies on the JDK to provide http connection (HttpURLConnection) by default. If necessary, it can also be replaced by other HTTP libraries such as Apache HttpComponents, Netty or OkHttp through the setRequestFactory method.

This article describes how to use RestTemplate, as well as configuration and injection in Spring Boot.

Implementation logic

RestTemplate contains the following parts:

  • HttpMessageConverter object converter
  • ClientHttpRequestFactory defaults to JDK's HttpURLConnection
  • ResponseErrorHandler exception handling
  • ClientHttpRequestInterceptor request interceptor

A picture can be intuitively understood:

The direct use method is very simple:

public class RestTemplateTest {
    public static void main(String[] args) {
        RestTemplate restT = new RestTemplate();
        //通过Jackson JSON processing library直接将返回值绑定到对象
        Quote quote = restT.getForObject("http://gturnquist-quoters.cfapps.io/api/random", Quote.class);
        String quoteString = restT.getForObject("http://gturnquist-quoters.cfapps.io/api/random", String.class);
        System.out.println(quoteString);
    }
}

Send a GET request 

// 1-getForObject()
User user1 = this.restTemplate.getForObject(uri, User.class);

// 2-getForEntity()
ResponseEntity<User> responseEntity1 = this.restTemplate.getForEntity(uri, User.class);
HttpStatus statusCode = responseEntity1.getStatusCode();
HttpHeaders header = responseEntity1.getHeaders();
User user2 = responseEntity1.getBody();
  
// 3-exchange()
RequestEntity requestEntity = RequestEntity.get(new URI(uri)).build();
ResponseEntity<User> responseEntity2 = this.restTemplate.exchange(requestEntity, User.class);
User user3 = responseEntity2.getBody();

Send POST request 

// 1-postForObject()
User user1 = this.restTemplate.postForObject(uri, user, User.class);

// 2-postForEntity()
ResponseEntity<User> responseEntity1 = this.restTemplate.postForEntity(uri, user, User.class);

// 3-exchange()
RequestEntity<User> requestEntity = RequestEntity.post(new URI(uri)).body(user);
ResponseEntity<User> responseEntity2 = this.restTemplate.exchange(requestEntity, User.class);

Set HTTP Header 

// 1-Content-Type
RequestEntity<User> requestEntity = RequestEntity
        .post(new URI(uri))
        .contentType(MediaType.APPLICATION_JSON)
        .body(user);

// 2-Accept
RequestEntity<User> requestEntity = RequestEntity
        .post(new URI(uri))
        .accept(MediaType.APPLICATION_JSON)
        .body(user);

// 3-Other
RequestEntity<User> requestEntity = RequestEntity
        .post(new URI(uri))
        .header("Authorization", "Basic " + base64Credentials)
        .body(user);

Catch exception

captureHttpServerErrorException

try {
    responseEntity = restTemplate.exchange(requestEntity, String.class);
} catch (HttpServerErrorException e) {
    // log error
}

Custom exception handler 

public class CustomErrorHandler extends DefaultResponseErrorHandler {
    
    public void handleError(ClientHttpResponse response) throws IOException {
        // todo
    }
}

Then set the exception handler: 

@Configuration
public class RestClientConfig {
    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setErrorHandler(new CustomErrorHandler());
        return restTemplate;
    }
}

Configuration class

Create HttpClientConfigclasses, set connection pool size, timeout period, retry mechanism, etc. The configuration is as follows:

 

/**
 * - Supports both HTTP and HTTPS
 * - Uses a connection pool to re-use connections and save overhead of creating connections.
 * - Has a custom connection keep-alive strategy (to apply a default keep-alive if one isn't specified)
 * - Starts an idle connection monitor to continuously clean up stale connections.
 *
 * @author XiongNeng
 * @version 1.0
 * @since 2018/7/5
 */

@Configuration
@EnableScheduling
public class HttpClientConfig {

    private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientConfig.class);

    @Resource
    private HttpClientProperties p;

    @Bean
    public PoolingHttpClientConnectionManager poolingConnectionManager() {
        SSLContextBuilder builder = new SSLContextBuilder();
        try {
            builder.loadTrustMaterial(null, new TrustStrategy() {
                public boolean isTrusted(X509Certificate[] arg0, String arg1) {
                    return true;
                }
            });
        } catch (NoSuchAlgorithmException | KeyStoreException e) {
            LOGGER.error("Pooling Connection Manager Initialisation failure because of " + e.getMessage(), e);
        }

        SSLConnectionSocketFactory sslsf = null;
        try {
            sslsf = new SSLConnectionSocketFactory(builder.build());
        } catch (KeyManagementException | NoSuchAlgorithmException e) {
            LOGGER.error("Pooling Connection Manager Initialisation failure because of " + e.getMessage(), e);
        }

        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
                .<ConnectionSocketFactory>create()
                .register("https", sslsf)
                .register("http", new PlainConnectionSocketFactory())
                .build();

        PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        poolingConnectionManager.setMaxTotal(p.getMaxTotalConnections());  //最大连接数
        poolingConnectionManager.setDefaultMaxPerRoute(p.getDefaultMaxPerRoute());  //同路由并发数
        return poolingConnectionManager;
    }

    @Bean
    public ConnectionKeepAliveStrategy connectionKeepAliveStrategy() {
        return new ConnectionKeepAliveStrategy() {
            
            public long getKeepAliveDuration(HttpResponse response, HttpContext httpContext) {
                HeaderElementIterator it = new BasicHeaderElementIterator
                        (response.headerIterator(HTTP.CONN_KEEP_ALIVE));
                while (it.hasNext()) {
                    HeaderElement he = it.nextElement();
                    String param = he.getName();
                    String value = he.getValue();
                    if (value != null && param.equalsIgnoreCase("timeout")) {
                        return Long.parseLong(value) * 1000;
                    }
                }
                return p.getDefaultKeepAliveTimeMillis();
            }
        };
    }

    @Bean
    public CloseableHttpClient httpClient() {
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(p.getRequestTimeout())
                .setConnectTimeout(p.getConnectTimeout())
                .setSocketTimeout(p.getSocketTimeout()).build();

        return HttpClients.custom()
                .setDefaultRequestConfig(requestConfig)
                .setConnectionManager(poolingConnectionManager())
                .setKeepAliveStrategy(connectionKeepAliveStrategy())
                .setRetryHandler(new DefaultHttpRequestRetryHandler(3, true))  // 重试次数
                .build();
    }

    @Bean
    public Runnable idleConnectionMonitor(final PoolingHttpClientConnectionManager connectionManager) {
        return new Runnable() {
            
            @Scheduled(fixedDelay = 10000)
            public void run() {
                try {
                    if (connectionManager != null) {
                        LOGGER.trace("run IdleConnectionMonitor - Closing expired and idle connections...");
                        connectionManager.closeExpiredConnections();
                        connectionManager.closeIdleConnections(p.getCloseIdleConnectionWaitTimeSecs(), TimeUnit.SECONDS);
                    } else {
                        LOGGER.trace("run IdleConnectionMonitor - Http Client Connection manager is not initialised");
                    }
                } catch (Exception e) {
                    LOGGER.error("run IdleConnectionMonitor - Exception occurred. msg={}, e={}", e.getMessage(), e);
                }
            }
        };
    }

    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setThreadNamePrefix("poolScheduler");
        scheduler.setPoolSize(50);
        return scheduler;
    }
}

Then configure the RestTemplateConfig class, use the previously configured CloseableHttpClient class injection, and configure some default message converters: 

/**
 * RestTemplate客户端连接池配置
 *
 * @author XiongNeng
 * @version 1.0
 * @since 2018/1/24
 */
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class RestTemplateConfig {

    @Resource
    private CloseableHttpClient httpClient;

    @Bean
    public RestTemplate restTemplate(MappingJackson2HttpMessageConverter jackson2HttpMessageConverter) {
        RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory());

        List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
        StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(Charset.forName("utf-8"));
        messageConverters.add(stringHttpMessageConverter);
        messageConverters.add(jackson2HttpMessageConverter);
        restTemplate.setMessageConverters(messageConverters);

        return restTemplate;
    }

    @Bean
    public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory() {
        HttpComponentsClientHttpRequestFactory rf = new HttpComponentsClientHttpRequestFactory();
        rf.setHttpClient(httpClient);
        return rf;
    }

}

Note that if there is no HttpClient class of apache, you need to add in the pom file:  

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

send files 

MultiValueMap<String, Object> multiPartBody = new LinkedMultiValueMap<>();
multiPartBody.add("file", new ClassPathResource("/tmp/user.txt"));
RequestEntity<MultiValueMap<String, Object>> requestEntity = RequestEntity
        .post(uri)
        .contentType(MediaType.MULTIPART_FORM_DATA)
        .body(multiPartBody);

download file 

// 小文件
RequestEntity requestEntity = RequestEntity.get(uri).build();
ResponseEntity<byte[]> responseEntity = restTemplate.exchange(requestEntity, byte[].class);
byte[] downloadContent = responseEntity.getBody();
  
// 大文件  
ResponseExtractor<ResponseEntity<File>> responseExtractor = new ResponseExtractor<ResponseEntity<File>>() {
    
    public ResponseEntity<File> extractData(ClientHttpResponse response) throws IOException {
        File rcvFile = File.createTempFile("rcvFile", "zip");
        FileCopyUtils.copy(response.getBody(), new FileOutputStream(rcvFile));
        return ResponseEntity.status(response.getStatusCode()).headers(response.getHeaders()).body(rcvFile);
    }
};
File getFile = this.restTemplate.execute(targetUri, HttpMethod.GET, null, responseExtractor);

Service injection 

@Service
public class DeviceService {
    private static final Logger logger = LoggerFactory.getLogger(DeviceService.class);

    @Resource
    private RestTemplate restTemplate;
}

Practical use examples 

// 开始推送消息
logger.info("解绑成功后推送消息给对应的POS机");
LoginParam loginParam = new LoginParam();
loginParam.setUsername(managerInfo.getUsername());
loginParam.setPassword(managerInfo.getPassword());
HttpBaseResponse r = restTemplate.postForObject(
        p.getPosapiUrlPrefix() + "/notifyLogin", loginParam, HttpBaseResponse.class);
if (r.isSuccess()) {
    logger.info("推送消息登录认证成功");
    String token = (String) r.getData();
    UnbindParam unbindParam = new UnbindParam();
    unbindParam.setImei(pos.getImei());
    unbindParam.setLocation(location);
    // 设置HTTP Header信息
    URI uri;
    try {
        uri = new URI(p.getPosapiUrlPrefix() + "/notify/unbind");
    } catch (URISyntaxException e) {
        logger.error("URI构建失败", e);
        return 1;
    }
    RequestEntity<UnbindParam> requestEntity = RequestEntity
            .post(uri)
            .contentType(MediaType.APPLICATION_JSON)
            .accept(MediaType.APPLICATION_JSON)
            .header("Authorization", token)
            .body(unbindParam);
    ResponseEntity<HttpBaseResponse> responseEntity = restTemplate.exchange(requestEntity, HttpBaseResponse.class);
    HttpBaseResponse r2 = responseEntity.getBody();
    if (r2.isSuccess()) {
        logger.info("推送消息解绑网点成功");
    } else {
        logger.error("推送消息解绑网点失败,errmsg = " + r2.getMsg());
    }
} else {
    logger.error("推送消息登录认证失败");
}

GitHub source

springboat rest template

203 original articles published · Liked 6 · Visits 4474

Guess you like

Origin blog.csdn.net/weixin_42073629/article/details/105609348