使用Spring RestTemplate 詳解實踐使用及拓展增強
RestTemplate 是什么?
RestTemplate 是Spring封裝的一個Rest風(fēng)格http請求框架,底層可以切換成HttpClient OkHttp 或者Netty實現(xiàn),用戶只需要關(guān)心RestTemplate怎么用而不需要關(guān)心底層框架如何操作,使用RestTemplate不需要關(guān)心如何手動轉(zhuǎn)換返回的對象和到處都是的異常處理代碼,可以讓你的代碼更簡潔更優(yōu)雅。
你可以在 spring-web 中找到它
主要類和接口
RestOperations定義Rest 操作的接口HttpAccessor抽象http help 類InterceptingHttpAccessor HttpAccess裝飾類拓展了攔截器功能RestTemplate具體實現(xiàn)類ClientHttpRequestInterceptor攔截器接口 用于攔截http請求UriTemplateHandler uri模板處理器,后面拓展會用到

基礎(chǔ)使用
put delete 等方法參考get post 的寫法
Get獲取對象或?qū)ο蠹?/h3>
獲取 Employee 集合
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<List<Employee>> response = restTemplate.exchange(
"http://localhost:8080/employees/",
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<Employee>>(){});
List<Employee> employees = response.getBody();
返回對象list用exchange方法使用 ParameterizedTypeReference 指定返回類型 ,getForEntity 也可以使用 Object[].class 或 其他數(shù)組接收再轉(zhuǎn)為List
獲取單個對象
public class EmployeeList {
private List<Employee> employees;
public EmployeeList() {
employees = new ArrayList<>();
}
// getter/setter
}
EmployeeList response = restTemplate.getForObject(
"http://localhost:8080/employees",
EmployeeList.class);
List<Employee> employees = response.getEmployees();
Post 發(fā)送對象或集合
發(fā)送集合
List<Employee> newEmployees = new ArrayList<>(); newEmployees.add(new Employee(3, "Intern")); newEmployees.add(new Employee(4, "CEO")); restTemplate.postForObject( "http://localhost:8080/employees/", newEmployees, ResponseEntity.class);
發(fā)送對象
List<Employee> newEmployees = new ArrayList<>(); newEmployees.add(new Employee(3, "Intern")); newEmployees.add(new Employee(4, "CEO")); restTemplate.postForObject( "http://localhost:8080/employees", new EmployeeList(newEmployees), ResponseEntity.class);
上傳文件
public void uploadFile(){
HttpHeaders headers = new HttpHeaders();
//設(shè)置Content-Type
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> body
= new LinkedMultiValueMap<>();
body.add("file", getTestFile());
HttpEntity<MultiValueMap<String, Object>> requestEntity
= new HttpEntity<>(body, headers);
String serverUrl = "http://localhost:8082/spring-rest/fileserver/singlefileupload/";
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate
.postForEntity(serverUrl, requestEntity, String.class);
}
public FileSystemResource getTestFile(){
return new FileSystemResource("./test.md")
}
FileSystemResource 是spring中的一個類 參考
上傳多個文件
在上傳單個文件的基礎(chǔ)上多加幾個文件
MultiValueMap<String, Object> body
= new LinkedMultiValueMap<>();
body.add("files", getTestFile());
body.add("files", getTestFile());
body.add("files", getTestFile());
HttpEntity<MultiValueMap<String, Object>> requestEntity
= new HttpEntity<>(body, headers);
String serverUrl = "http://localhost:8082/spring-rest/fileserver/multiplefileupload/";
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate
.postForEntity(serverUrl, requestEntity, String.class);
Spring RestTemplate 拓展
- 解決restTemplate get* url參數(shù)必須寫死的問題
- 解決get*方法不好添加header信息的問題
繼承RestTemplate 拓展get方法
/**
* 繼承RestTemplate 新加get* 方法 比原有的方法多了個 httpHeaders 參數(shù)
*/
public class CustomerRestTemplate extends RestTemplate {
public <T> ResponseEntity<T> getForEntity(String url, HttpHeaders httpHeaders, Class<T> responseType, Object... uriVariables) throws RestClientException {
HttpEntity<Object> requestEntity = new HttpEntity<>(httpHeaders);
RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
public <T> ResponseEntity<T> getForEntity(String url, HttpHeaders httpHeaders, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
HttpEntity<Object> requestEntity = new HttpEntity<>(httpHeaders);
RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
public <T> T getForObject(String url, HttpHeaders httpHeaders, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
HttpEntity<Object> requestEntity = new HttpEntity<>(httpHeaders);
RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
ResponseExtractor<T> responseExtractor = new HttpMessageConverterExtractor<T>(responseType, getMessageConverters());
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
public <T> T getForObject(String url, HttpHeaders httpHeaders, Class<T> responseType, Object... uriVariables) throws RestClientException {
HttpEntity<Object> requestEntity = new HttpEntity<>(httpHeaders);
RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
ResponseExtractor<T> responseExtractor = new HttpMessageConverterExtractor<T>(responseType, getMessageConverters());
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
}
拓展URI處理邏輯
/**
* 根據(jù)uriTemplate 把 uriVariables 分成兩類
* 一類是path params 一類是 query params 分開賦值
* 如 /xx/{id}/type path params 就是 id uriVariables 剩下的就是query params 用?拼接在url后面
* 如果查詢參數(shù)中有數(shù)組或集合類型的參數(shù)會轉(zhuǎn)化成 key[]=value1&key[]=value2...
*/
public class QueryParamsUrlTemplateHandler extends DefaultUriTemplateHandler {
/**
* 匹配path param
*/
private static final Pattern NAMES_PATTERN = Pattern.compile("\\{([^/]+?)\\}");
@Override
public URI expand(String uriTemplate, Map<String, ?> uriVariables) {
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(uriTemplate);
//解析uriTemplate 提取query param
Map<String, ?> queryParam = getQueryParam(uriTemplate, uriVariables);
//設(shè)置query param
queryParam.forEach((k, v) -> {
if (v instanceof Object[]) {
Object[] arrayParam = (Object[]) v;
//把數(shù)組類型的參數(shù)拼成 參數(shù)名 + [] 的形式 k[] xx&kp[]=xx&k[]=xx
String key = k + "[]";
String strArrayParam = Stream.of(arrayParam).map(String::valueOf).collect(Collectors.joining("&" + key + "="));
uriComponentsBuilder.queryParam(key, strArrayParam);
} else if (v instanceof Iterable) {
Iterable iterable = (Iterable) v;
String key = k + "[]";
String strArrayParam = Stream.of(iterable).map(String::valueOf).collect(Collectors.joining("&" + key + "="));
uriComponentsBuilder.queryParam(key, strArrayParam);
} else {
uriComponentsBuilder.queryParam(k, v);
}
});
uriTemplate = uriComponentsBuilder.build().toUriString();
//設(shè)置path param
return super.expand(uriTemplate, uriVariables);
}
/**
* 解析uriTemplate 分離 query param
*
* @param uriTemplate uri模板
* @param uriVariables 全部的模板變量
* @return 查詢變量
*/
public Map<String, ?> getQueryParam(String uriTemplate, Map<String, ?> uriVariables) {
if (uriTemplate == null) {
return null;
}
if (uriTemplate.indexOf('{') == -1) {
return uriVariables;
}
if (uriTemplate.indexOf(':') != -1) {
uriTemplate = sanitizeSource(uriTemplate);
}
Map<String, Object> pathVariables = Maps.newHashMap();
Matcher matcher = NAMES_PATTERN.matcher(uriTemplate);
while (matcher.find()) {
String matchKey = matcher.group(1);
Object value = uriVariables.get(matchKey);
if (value != null) {
pathVariables.put(matchKey, value);
}
}
//此處為了圖方便使用了 guava 工具包中的類 功能就是取差集
MapDifference<String, Object> difference = Maps.difference(uriVariables, pathVariables);
return difference.entriesOnlyOnLeft();
}
/**
* Remove nested "{}" such as in URI vars with regular expressions.
*/
private static String sanitizeSource(String source) {
int level = 0;
StringBuilder sb = new StringBuilder();
for (char c : source.toCharArray()) {
if (c == '{') {
level++;
}
if (c == '}') {
level--;
}
if (level > 1 || (level == 1 && c == '}')) {
continue;
}
sb.append(c);
}
return sb.toString();
}
}
實際使用
初始化RestTemplate
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); requestFactory.setConnectTimeout(500);// 設(shè)置超時 requestFactory.setReadTimeout(500); //new 自己定義的類 CustomerRestTemplate restTemplate = new CustomerRestTemplate(); //設(shè)置自定義的uri處理處理器 QueryParamsUrlTemplateHandler queryParamsUrlTemplateHandler = new QueryParamsUrlTemplateHandler(); //這里使用裝飾模式 添加rootUri RootUriTemplateHandler rootUriTemplateHandler = new RootUriTemplateHandler(outUrl, queryParamsUrlTemplateHandler); restTemplate.setUriTemplateHandler(rootUriTemplateHandler); restTemplate.setRequestFactory(requestFactory);
get請求示例
Map<String, Object> params = new HashMap<>();
params.put("id", "1");
params.put("param2", "2");
params.put("param", new Integer[]{1506, 1507});
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Authorization", "Basic " + "your authorization");
ResponseEntity<Map[]> forEntity = restTemplate.getForEntity("/api/test/{id}", httpHeaders, Map[].class, params);
// url 為 api/test/1?param[]=1506¶m[]=1507¶m2=2
思考進一步封裝
可以考慮使用建造者模式改造restTemplate
Employee employee = RestTemplate.build()
.get("api/xxx/{id}")
.header("xx","xx")
.headers(new Headers())
.param("xx","xx")
.params(new HashMap(){{put("bb","bb");}})
.targetClass(Employee.class)
.execute();
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
詳解使用spring aop實現(xiàn)業(yè)務(wù)層mysql 讀寫分離
本篇文章主要介紹了使用spring aop實現(xiàn)業(yè)務(wù)層mysql 讀寫分離,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-01-01
springboot集成junit編寫單元測試實戰(zhàn)
在做單元測試時,代碼覆蓋率常常被拿來作為衡量測試好壞的指標(biāo),本文主要介紹了springboot集成junit編寫單元測試實戰(zhàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-02-02
spring mvc 實現(xiàn)獲取后端傳遞的值操作示例
這篇文章主要介紹了spring mvc 實現(xiàn)獲取后端傳遞的值操作,結(jié)合實例形式詳細分析了spring mvc使用JSTL 方法獲取后端傳遞的值相關(guān)操作技巧2019-11-11
解決IDEA中多模塊下Mybatis逆向工程不生成相應(yīng)文件的情況
這篇文章主要介紹了解決IDEA中多模塊下Mybatis逆向工程不生成相應(yīng)文件的情況,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-01-01
SpringSecurity+JWT實現(xiàn)登錄流程分析
Spring Security 是一個功能強大且高度可定制的身份驗證和訪問控制框架,它是為Java應(yīng)用程序設(shè)計的,特別是那些基于Spring的應(yīng)用程序,下面給大家介紹SpringSecurity+JWT實現(xiàn)登錄流程,感興趣的朋友一起看看吧2024-12-12
java實現(xiàn)給圖片加鋪滿的網(wǎng)格式文字水印
這篇文章主要給大家介紹了關(guān)于java實現(xiàn)給圖片加鋪滿的網(wǎng)格式文字水印的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
基于Spring boot @Value 注解注入屬性值的操作方法
這篇文章主要介紹了結(jié)合SpEL使用@Value-基于配置文件或非配置的文件的值注入-Spring Boot的相關(guān)知識,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07

