一、簡介
常見的http客戶端請求工具:
以上 工具雖然常用乏盐,但對于 RESTful
操作相對不是太友好。所以晌柬,從 Spring3.0
開始支持的一個(gè) HTTP 請求工具RestTemplate
,提供了常見的 REST
請求方案的模板。
RestTemplate
只是提供了 Http
請求模板,其底層默認(rèn)是使用HttpURLConnection
作為真正的請求工具懂讯。它可以通過構(gòu)造方法替換底層的執(zhí)行引擎,常見的引擎又HttpClient
台颠、Netty
褐望、OkHttp
勒庄。
二、簡單的請求
1瘫里、GET請求
1. 直接拼接參數(shù)
String params = "http://localhost:10011/user/params?name=諸葛亮&age=100";
String result = template.getForObject(HOST + params, String.class);
LOGGER.info("請求返回結(jié)果:{}", result);
2. 占位符參數(shù)
// 1实蔽、可以直接在方法中指定參數(shù), 此種方式還可以使用下標(biāo)的形式。
// 請求路徑還可以為:name={0}&age={1}
String url = "http://localhost:10011/user/params?name={name}&age={age}";
String result = template.getForObject(url , String.class, "李四", 20);
LOGGER.info("請求返回結(jié)果:{}", result);
// 2谨读、 通過 Map 傳遞參數(shù)局装,此種方式必須使用占位符,并且 map 中的 key 值劳殖,要與請求
// 路徑中的 占位符一致
Map<String, Object> map = new HashMap<>();
map.put("name", "王五");
map.put("age", 30);
ResponseEntity<String> forEntity = template.getForEntity(HOST + url , String.class, map);
2铐尚、POST請求
接口實(shí)例
如下的接口實(shí)例中兩個(gè)方法的唯一區(qū)別為 一個(gè)參數(shù)中加了注解@RequestBody
,一個(gè)沒有加哆姻,兩者在 POST
請求中傳值方式不同宣增。
// 接口 1
@RequestMapping("/obj")
@ResponseBody
public User getObj(User user){
return user;
}
// 接口 2
@RequestMapping("/body")
@ResponseBody
public User getBody(@RequestBody User user){
return user;
}
1、帶有 @RequestBody
注解的請求
此種情況下矛缨,請求參數(shù)可以為 設(shè)置User
對象爹脾,也可以說使用Map
String url = "http://localhost:10011/user/body";
User user = new User().setName("張三")
.setAge(40);
String result = template.postForObject(url, user, String.class);
LOGGER.info("請求返回結(jié)果:{}", result);
Map<String, Object> map = new HashMap<>();
map.put("name", "王五");
map.put("age", 30);
ResponseEntity<String> forEntity = template.postForEntity(url, map, String.class);
LOGGER.info("請求返回結(jié)果:{}", forEntity.getBody());
2、不帶@RequestBody
注解的請求
此種情況下箕昭,傳的參數(shù)需要使用MultiValueMap
MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
map.add("name", "王五");
map.add("age", 30);
ResponseEntity<String> forEntity = template.postForEntity(url, map, String.class);
LOGGER.info("請求返回結(jié)果:{}", forEntity.getBody());
三灵妨、帶請求頭的請求
1、GET 請求
GET
請求中的getForObject
和 getForEntity
方法不支持傳遞頭信息盟广,需要使用 通用方法exchange
闷串,如下所示:
String url = "http://localhost:10011/user/params?name={name}&age={age}";
// 定義頭信息
HttpHeaders headers = new HttpHeaders();
headers.add("token", UUID.randomUUID().toString());
// 請求參數(shù)
Map<String, Object> map = new HashMap<>();
map.put("name", "王五");
map.put("age", 30);
// 使用 HttpEntity 對 頭信息進(jìn)行封裝
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(headers);
// 請求方法 exchange
ResponseEntity<String> response = template.exchange(url, HttpMethod.GET, entity, String.class, map);
LOGGER.info("請求返回結(jié)果:{}", response.getBody());
2、POST請求
POST
請求添加頭信息比較簡單筋量,只需要使用 HttpEntity
對請求對象進(jìn)行一次封裝即可烹吵。
// 定義頭信息
HttpHeaders headers = new HttpHeaders();
headers.add("token", UUID.randomUUID().toString());
User user = new User().setName("張三")
.setAge(40);
// 使用 HttpEntity 把請求對象user 和 header 進(jìn)行組裝
HttpEntity<User> userHttpEntity = new HttpEntity<>(user, headers);
// 把請求參數(shù)改為 httpEntity 對象即可
String result = template.postForObject(url, userHttpEntity, String.class);
LOGGER.info("請求返回結(jié)果:{}", result);
四、form 表單請求
form
表單的提交只需要需要把頭信息ContentType
設(shè)置為MediaType.APPLICATION_FORM_URLENCODED
桨武,同時(shí)參數(shù)需要使用 MultiValueMap
封裝肋拔。如下所示:
String url = "http://localhost:10011/user/obj";
HttpHeaders headers = new HttpHeaders();
headers.add("token", UUID.randomUUID().toString());
// 設(shè)置請求類型
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// 組裝參數(shù)
MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
multiValueMap.add("name", "王五");
multiValueMap.add("age", 30);
// 發(fā)送請求
HttpEntity< MultiValueMap<String, Object>> request = new HttpEntity<>(multiValueMap, headers);
ResponseEntity<String> exchange = template.exchange(url, HttpMethod.POST, request, String.class);
LOGGER.info("請求返回結(jié)果:{}", exchange.getBody());
五、上傳下載
1呀酸、上傳
String url = "http://localhost:10011/user/upload";
HttpHeaders headers = new HttpHeaders();
// 模擬讀取文件
File file = new File("F:/run.bat");
//從File句柄創(chuàng)建一個(gè)新的FileSystemResource
FileSystemResource resource = new FileSystemResource(file);
MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
multiValueMap.add("name", "update.bat");
multiValueMap.add("file", resource);
HttpEntity< MultiValueMap<String, Object>> request = new HttpEntity<>(multiValueMap, headers);
ResponseEntity<String> exchange = template.exchange(url, HttpMethod.POST, request, String.class);
LOGGER.info("請求返回結(jié)果:{}", exchange.getBody());
2凉蜂、下載
1. 小文件下載
小文件下載比較簡單,只需要把請求響應(yīng)類型設(shè)置為byte[]
即可性誉,如下所示:
String url = "http://localhost:10011/user/downLoad";
// 請求下載
ResponseEntity<byte[]> entity = template.getForEntity(url, byte[].class);
// 寫文件
byte[] body = entity.getBody();
Files.write(Paths.get("D:/download.jpg"), body);
這種下載方法實(shí)際上是將下載文件一次性加載到客戶端本地內(nèi)存窿吩,然后從內(nèi)存將文件寫入磁盤。這種方式對于小文件的下載還比較適合错览,如果文件比較大或者文件下載并發(fā)量比較大纫雁,容易造成內(nèi)存的大量占用,從而降低應(yīng)用的運(yùn)行效率倾哺。
2. 大文件下載
這種下載方式的區(qū)別在于
- 設(shè)置了請求頭APPLICATION_OCTET_STREAM轧邪,表示以流的形式進(jìn)行數(shù)據(jù)加載
- RequestCallback 結(jié)合File.copy保證了接收到一部分文件內(nèi)容刽脖,就向磁盤寫入一部分內(nèi)容。而不是全部加載到內(nèi)存忌愚,最后再寫入磁盤文件曲管。
String url = "http://localhost:10011/user/downLoad";
String targetPath = "D:/download.jpg";
// 定義請求頭的接收類型
RequestCallback requestCallback = request -> request.getHeaders()
.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
//對響應(yīng)進(jìn)行流式處理而不是將其全部加載到內(nèi)存中
template.execute(url, HttpMethod.GET, requestCallback, clientHttpResponse -> {
Files.copy(clientHttpResponse.getBody(), Paths.get(targetPath));
return null;
});
六、更換底層請求執(zhí)行引擎
Spring
中已經(jīng)提供了大部分的執(zhí)行引擎硕糊,比如 HttpClient
和OkHttp3
等院水,要想使用這些底層引擎只需要加入響應(yīng)的依賴,然后再初始化類似简十,在構(gòu)造方法中指定響應(yīng)的執(zhí)行引擎即可衙耕。
// 1、使用 HttpClient 引擎勺远。
RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
// 2、使用OkHttp3 引擎
RestTemplate restTemplate = new RestTemplate(new OkHttp3ClientHttpRequestFactory());
七时鸵、攔截器
當(dāng)所有的請求需要設(shè)置公共信息時(shí)胶逢,就可以使用攔截器,為每個(gè)請求設(shè)置饰潜。設(shè)置方法如下:
RestTemplate template = new RestTemplate();
// 此處設(shè)置了一個(gè)公共請求頭 AUTHORIZATION
ClientHttpRequestInterceptor interceptor = new ClientHttpRequestInterceptor() {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpHeaders headers = request.getHeaders();
headers.set(HttpHeaders.AUTHORIZATION, "636213dc4ac6e9220a0f5107");
return execution.execute(request, body);
}
};
template.setInterceptors(Arrays.asList(interceptor));