[HTTP/Spring] RestTemplate : Spring的HTTP網絡請求框架
1 概述: RestTemplate
RestTemplate 的產生背景、存在意義
-
java應用開發中,使用
http連接,訪問第三方HTTP接口,通常使用的HTTP連接請求工具為HttpClient和OKHttp。 -
這兩種HTTP連接請求工具,使用起來比較復雜,新手容易出問題。如果使用spring框架,可以使用restTemplate來進行http連接請求。
-
restTemplate默認的連接方式是java中的HttpConnection,可以使用ClientHttpRequestFactory指定不同的HTTP連接方式。
- 相比其他http網絡請求框架,RestTemplate最大的價值在于:
- 與spring無縫銜接的原生集成優勢
- 封裝/屏蔽了底層的連接框架,如 OKhttp、httpclient等
- 解決了:封裝java dto 與http響應的轉換邏輯等問題,使得java開發工程師無需關注底層的http連接問題,使其高度聚焦于業務的開發。
RestTemplate 介紹
-
org.springframework.web.client.RestTemplate是 Spring Resources 中一個訪問第三方 RESTful API 接口的網絡請求框架,用于執行HTTP請求。 -
其暴露了一系列的模板方法API,便于操作底層的HTTP客戶端庫,如JDK的HttpURLConnection、Apache HttpComponents等。
-
RestTemplate 是用來消費 REST 服務的,所以 RestTemplate 的主要方法都與 REST 的
Http協議的一些方法緊密相連
例如 HEAD、GET、POST、PUT、DELETE 和 OPTIONS 等方法。
這些方法在 RestTemplate 類對應的方法為 headForHeaders()、getForObject()、postForObject()、put() 和 delete() 等。
- RestTemplate通常作為【共享組件】使用,其配置不支持【并發修改】,因此通常在【啟動時】準備好配置。
如果需要,可以在啟動時創建多個配置不同的RestTemplate實例。
這些實例可以使用相同的底層ClientHttpRequestFactory,如果它們需要共享HTTP客戶端資源。
2 RestTemplate 使用指南
Maven 依賴
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.7</version>
</dependency>
RestTemplate的初始配置
基本配置
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
return new RestTemplate(factory);
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setReadTimeout(150000); // ms
factory.setConnectTimeout(150000); // ms
return factory;
}
}
進階配置
除了基礎配置,還可以進行更高級的配置,例如設置連接池、自定義HttpClient等。
@Configuration
public class RestTemplateConfig {
@Bean
public HttpClientConnectionManager poolingHttpClientConnectionManager() {
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
poolingHttpClientConnectionManager.setMaxTotal(500); // 最大連接數
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(100); // 同路由并發數
return poolingHttpClientConnectionManager;
}
@Bean
public HttpClient httpClient(HttpClientConnectionManager poolingHttpClientConnectionManager) {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
return httpClientBuilder.build();
}
@Bean
public ClientHttpRequestFactory clientHttpRequestFactory(HttpClient httpClient) {
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
clientHttpRequestFactory.setHttpClient(httpClient);
clientHttpRequestFactory.setConnectTimeout(5 * 1000); // 連接超時時間
clientHttpRequestFactory.setReadTimeout(10 * 1000); // 數據讀取超時時間
clientHttpRequestFactory.setConnectionRequestTimeout(10 * 1000); // 連接池獲取請求連接的超時時間
return clientHttpRequestFactory;
}
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(clientHttpRequestFactory);
return restTemplate;
}
}
最佳實踐: RestTemplateConfig
import org.apache.http.client.HttpClient;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
/**
* http連接管理器
* @return
*/
@Bean
public HttpClientConnectionManager poolingHttpClientConnectionManager() {
/*// 注冊http和https請求
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build();
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(registry);*/
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
// 最大連接數
poolingHttpClientConnectionManager.setMaxTotal(500);
// 同路由并發數(每個主機的并發)
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(100);
return poolingHttpClientConnectionManager;
}
/**
* HttpClient
* @param poolingHttpClientConnectionManager
* @return
*/
@Bean
public HttpClient httpClient(HttpClientConnectionManager poolingHttpClientConnectionManager) {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
// 設置http連接管理器
httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
/*// 設置重試次數
httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(3, true));*/
// 設置默認請求頭
/*List<Header> headers = new ArrayList<>();
headers.add(new BasicHeader("Connection", "Keep-Alive"));
httpClientBuilder.setDefaultHeaders(headers);*/
return httpClientBuilder.build();
}
/**
* 請求連接池配置
* @param httpClient
* @return
*/
@Bean
public ClientHttpRequestFactory clientHttpRequestFactory(HttpClient httpClient) {
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
// httpClient創建器
clientHttpRequestFactory.setHttpClient(httpClient);
// 連接超時時間/毫秒(連接上服務器(握手成功)的時間,超出拋出connect timeout)
clientHttpRequestFactory.setConnectTimeout(5 * 1000);
// 數據讀取超時時間(socketTimeout)/毫秒(務器返回數據(response)的時間,超過拋出read timeout)
clientHttpRequestFactory.setReadTimeout(10 * 1000);
// 連接池獲取請求連接的超時時間,不宜過長,必須設置/毫秒(超時間未拿到可用連接,會拋出org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool)
clientHttpRequestFactory.setConnectionRequestTimeout(10 * 1000);
return clientHttpRequestFactory;
}
/**
* rest模板
* @return
*/
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory) {
// boot中可使用RestTemplateBuilder.build創建
RestTemplate restTemplate = new RestTemplate();
// 配置請求工廠
restTemplate.setRequestFactory(clientHttpRequestFactory);
return restTemplate;
}
}
RestTemplate的日常使用 := 發起HTTP請求
- RestTemplate的使用非常靈活,可以支持各種HTTP方法的請求,如GET、POST、PUT、DELETE等。
以下是一些使用RestTemplate的示例:
TempUserDto result = restTemplate.getForObject("http://localhost:8080/cs-admin/rest/getUser?userName=張三&age=18", TempUserDto.class);
GET請求
- case 1 基于普通參數,映射請求參數到 Get URL Params
String url = "http://localhost:8080/api/users/{id}";
User user = restTemplate.getForObject(url, User.class, 1);
- case 2 基于Map對象,映射請求參數到 Get URL Params
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("userName", "張三");
paramMap.put("age", 18);
TempUser result = restTemplate.getForObject("http://localhost:8080/cs-admin/rest/getUser?userName={userName}&age={age}", TempUser.class, paramMap);
POST請求
普通訪問接口
String url = "http://localhost:8080/api/users";
User newUser = new User("張三", 30);
URI location = restTemplate.postForLocation(url, newUser);
TempUser param = new TempUser();
param.setUserName("張三");
param.setAge(18);
TempUser result = restTemplate.postForObject("http://localhost:8080/cs-admin/rest/getPostUser", param, TempUser.class);
帶HEAD訪問接口
// 請求頭信息
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.valueOf("application/json;charset=UTF-8"));
//headers.add("headParam1", "headParamValue");
// 請求體內容
TempUser param = new TempUser();
param.setUserName("張三");
param.setAge(18);
// 組裝請求信息
HttpEntity<TempUser> httpEntity=new HttpEntity<>(param,headers);
TempUser result = restTemplate.postForObject("http://localhost:8080/cs-admin/rest/getPostUser", httpEntity, TempUser.class);
無請求體的訪問
僅
method為post,傳參方式仍然為get的param方式
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("userName", "張三");
paramMap.put("age", 18);
TempUser result = restTemplate.postForObject("http://localhost:8080/cs-admin/rest/getPostUserNoBody?userName={userName}&age={age}", null, TempUser.class, paramMap);
System.out.println(result);
發送可攜帶文件的請求
public static void main(String[] args) {
final RestTemplate restTemplate = new RestTemplate();
MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
map.add("fileUuid","oldDocumentTrans.getFileUuid()");
map.add("sourceLanguageAbbreviation","en");
map.add("targetLanguageAbbreviation","zh");
HttpHeaders headers = new HttpHeaders();
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(map, headers);
final ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity("http://localhost:8055/documentTrans/updateLanguages",
map,
String.class
);
System.out.println(stringResponseEntity);
}
上傳文件
- 后臺接口代碼
@RequestMapping("uploadFile")
public TempUser uploadFile(HttpServletRequest request, TempUser form) {
MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) request;
//獲取文件信息
MultipartFile multipartFile = multipartHttpServletRequest.getFile("file");
TempUser tempUser = new TempUser();
if (multipartFile != null) {
tempUser.setUserName(form.getUserName()+" "+multipartFile.getOriginalFilename());
}
if(form!=null){
tempUser.setAge(form.getAge());
}
return tempUser;
}
- RestTemplate 訪問(上傳文件)
// 文件
FileSystemResource file = new FileSystemResource("D:\\Java 權威指南(中文版).pdf");
// 設置請求內容
MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
param.add("file", file);
// 其他參數
param.add("userName", "張三");
param.add("age", 18);
// 組裝請求信息
HttpEntity<MultiValueMap<String, Object>> httpEntity=new HttpEntity<>(param);
// 發送請求
TempUser result = restTemplate.postForObject("http://localhost:8080/cs-admin/rest/uploadFile", httpEntity, TempUser.class);
PUT請求
String url = "http://localhost:8080/api/users/{id}";
User updatedUser = new User("李四", 40);
restTemplate.put(url, updatedUser, 1);
DELETE請求
String url = "http://localhost:8080/api/users/{id}";
restTemplate.delete(url, 1);
獲取HTTP響應狀態
ResponseEntity<TempUser> responseEntity = restTemplate.getForEntity("http://localhost:8080/cs-admin/rest/getUser?userName=張三&age=18", TempUser.class);
// 獲取狀態對象
HttpStatus httpStatus = responseEntity.getStatusCode();
// 獲取狀態碼
int statusCodeValue = responseEntity.getStatusCodeValue();
// 獲取headers
HttpHeaders httpHeaders = responseEntity.getHeaders();
// 獲取body
TempUser result = responseEntity.getBody();
通過這些示例可以看出,
RestTemplate提供了一種簡潔的方式來消費RESTful服務。
它封裝了HTTP連接的復雜性,使得開發者可以更專注于業務邏輯的實現。
3 Spring WebClient : Spring RestTemplate 的繼承者、下一代(支持非阻塞式/支持響應式)
Spring WebClient 簡介
Spring WebClient : Spring RestTemplate 的下一代/繼承者
Spring WebClient是Spring Framework 5引入的一個非阻塞、響應式的 HTTP 客戶端,用于與RESTful服務進行交互。
它是
RestTemplate的現代替代品,支持同步和異步操作,并且能夠很好地與Spring WebFlux集成。
- Spring WebClient
Maven 依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
使用指南
配置
- 初始配置
WebClient client = WebClient.builder()
.baseUrl("http://localhost:8080")
.defaultCookie("cookieKey", "cookieValue")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
使用
- 請求
- GET Request
Mono<Employee> employeeMono = client.get()
.uri("/employees/{id}", 123)
.retrieve()
.bodyToMono(Employee.class);
- POST Request
Mono<Employee> employeeMono = client.post()
.uri("/employees")
.body(Mono.just(new Employee("John", "Doe")), Employee.class)
.retrieve()
.bodyToMono(Employee.class);
- PUT Request
Mono<Employee> employeeMono = client.put()
.uri("/employees/{id}", 123)
.body(Mono.just(new Employee("John", "Doe")), Employee.class)
.retrieve()
.bodyToMono(Employee.class);
- DELETE Request
Mono<Void> response = client.delete()
.uri("/employees/{id}", 123)
.retrieve()
.bodyToMono(Void.class);
- 使用
retrieve()
retrieve()方法是一個簡化的API,適用于希望發送HTTP請求、接收響應并以響應式方式處理這些請求和響應的常見用例。
Mono<Employee> employeeMono = client.get()
.uri("/employees/{id}", 123)
.retrieve()
.bodyToMono(Employee.class);
- 使用
exchange()
exchange()方法允許您顯式地處理請求和響應。
它返回一個ClientResponse,其中包含所有響應元素,如狀態、標頭和響應正文。
Mono<ClientResponse> responseMono = client.get()
.uri("/employees/{id}", 123)
.exchange();
responseMono.subscribe(clientResponse -> {
HttpStatus statusCode = clientResponse.statusCode();
HttpHeaders headers = clientResponse.headers();
Mono<Employee> employeeMono = clientResponse.bodyToMono(Employee.class);
// Handle the response, including error handling based on status code
});
Y 推薦文獻
- ResTemplate - Spring
- Spring WebClient
Spring Cloud 框架中,Ribbon (負載均衡器) 是如何與 Spring 的 RestTemplate 集成的?
用
@LoadBalanced標記后,調用RestTemplate的 REST 方法就會通過【負載均衡】的方式通過一定的負載策略【路由】到某個【服務實例】上。
X 參考文獻
本文鏈接: http://www.rzrgm.cn/johnnyzen
關于博文:評論和私信會在第一時間回復,或直接私信我。
版權聲明:本博客所有文章除特別聲明外,均采用 BY-NC-SA 許可協議。轉載請注明出處!
日常交流:大數據與軟件開發-QQ交流群: 774386015 【入群二維碼】參見左下角。您的支持、鼓勵是博主技術寫作的重要動力!

浙公網安備 33010602011771號