小白一看就會(huì)的Spring的RestTemplate的使用

寫在前面

您好,我是碼農(nóng)飛哥,感謝您閱讀此文。作為一名Java開發(fā)者,我們?cè)趺炊祭@不開調(diào)用外部接口的場(chǎng)景,調(diào)用的方式要么是通過(guò)Http協(xié)議來(lái)調(diào)用,要么是通過(guò)RPC協(xié)議來(lái)調(diào)用,通過(guò)Http協(xié)議調(diào)用的話我們就需要用到Http的Api。比較常用的有Apache的HttpClient和原生的HttpURLConnection。這些Api都比較好用,但是我們今天要介紹一種更加好用API,Spring自帶的RestTemplate,能力更強(qiáng),使用更方便。

目錄

    寫在前面
    怎么用?
        SpringBoot項(xiàng)目
        設(shè)置超時(shí)時(shí)間
    GET請(qǐng)求
        返回業(yè)務(wù)對(duì)象類getForObject方法
    POST 請(qǐng)求
    postForLocation
    exchange 方法的使用
    總結(jié)

怎么用?
SpringBoot項(xiàng)目

SpringBoot項(xiàng)目中,只需要引入spring-boot-starter-web依賴就可以了,其實(shí)spring-boot-starter-web依賴也是SpringBoot項(xiàng)目必備的一個(gè)依賴。

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

設(shè)置超時(shí)時(shí)間

引入依賴之后,就來(lái)開始使用吧,任何一個(gè)Http的Api我們都可以設(shè)置請(qǐng)求的連接超時(shí)時(shí)間,請(qǐng)求超時(shí)時(shí)間,如果不設(shè)置的話,就可能會(huì)導(dǎo)致連接得不到釋放,造成內(nèi)存溢出。這個(gè)是我們需要重點(diǎn)注意的點(diǎn),下面就來(lái)看看RestTemplate如何來(lái)設(shè)置超時(shí)時(shí)間呢?我們可以在SimpleClientHttpRequestFactory類中設(shè)置這兩個(gè)時(shí)間,然后將factory傳給RestTemplate實(shí)例,設(shè)置如下:

@Configuration
public class RestTemplateConfig {
/**
* 服務(wù)器返回?cái)?shù)據(jù)(response)的時(shí)間
/
private static final Integer READ_TIME_OUT = 6000;
/
*
* 連接上服務(wù)器(握手成功)的時(shí)間
*/
private static final Integer CONNECT_TIME_OUT = 6000;

@Bean
public RestTemplate restTemplate(){
    ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient());
    return new RestTemplate(requestFactory);
}

@Bean
public HttpClient httpClient(){
   //默認(rèn)證書有效
    SSLConnectionSocketFactory sslConnectionSocketFactory = SSLConnectionSocketFactory.getSocketFactory();
    SSLContext sslContext = null;
    try {
        //信任所有的SSL證書
        sslContext = SSLContextBuilder.create().setProtocol(SSLConnectionSocketFactory.SSL)
                .loadTrustMaterial((x, y) -> true).build();
    } catch (Exception e) {
        e.printStackTrace();
    }
    if (sslContext != null) {
        sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext);
    }
    // 支持HTTP、HTTPS
    Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
            .register("http", PlainConnectionSocketFactory.getSocketFactory())
            .register("https", sslConnectionSocketFactory)
            .build();
    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
    connectionManager.setMaxTotal(200);
    connectionManager.setDefaultMaxPerRoute(100);
    connectionManager.setValidateAfterInactivity(2000);
    RequestConfig requestConfig = RequestConfig.custom()
            // 服務(wù)器返回?cái)?shù)據(jù)(response)的時(shí)間,超時(shí)拋出read timeout
            .setSocketTimeout(READ_TIME_OUT)
            // 連接上服務(wù)器(握手成功)的時(shí)間,超時(shí)拋出connect timeout
            .setConnectTimeout(CONNECT_TIME_OUT)
            // 從連接池中獲取連接的超時(shí)時(shí)間,超時(shí)拋出ConnectionPoolTimeoutException
            .setConnectionRequestTimeout(1000)
            .build();
    return HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).setConnectionManager(connectionManager).build();
}

}

說(shuō)完了RestTemplate的相關(guān)設(shè)置,下面就來(lái)看看平時(shí)我們用的最多兩種請(qǐng)求方法:get方法和post方法吧。
GET請(qǐng)求

RestTemplate中提供的get請(qǐng)求的方法主要分為兩類,一類是只返回請(qǐng)求體,一類是返回ResponseEntity對(duì)象,這個(gè)對(duì)象主要是包裝了Http請(qǐng)求的響應(yīng)狀態(tài)status,響應(yīng)頭headers,和響應(yīng)體body。后面我們會(huì)詳細(xì)介紹。首先來(lái)看看getForObject方法。
返回業(yè)務(wù)對(duì)象類getForObject方法

getForObject方法的重載方法有如下三個(gè):

/**
方法一,直接將參數(shù)添加到url上面。
* Retrieve a representation by doing a GET on the specified URL.
* The response (if any) is converted and returned.
*

URI Template variables are expanded using the given URI variables, if any.
* @param url the URL 請(qǐng)求地址
* @param responseType the type of the return value 響應(yīng)體的類型
* @param uriVariables the variables to expand the template 傳入的參數(shù)
* @return the converted object
/
@Nullable
T getForObject(String url, Class responseType, Object… uriVariables) throws RestClientException;
/
*
方法二,通過(guò)Map來(lái)提交參數(shù)。
* Retrieve a representation by doing a GET on the URI template.
* The response (if any) is converted and returned.
*

URI Template variables are expanded using the given map.
* @param url the URL
* @param responseType the type of the return value
* @param uriVariables the map containing variables for the URI template
* @return the converted object
*/
@Nullable
T getForObject(String url, Class responseType, Map<String, ?> uriVariables) throws RestClientException;

/**
   方法三,用URI來(lái)請(qǐng)求。
 * Retrieve a representation by doing a GET on the URL .
 * The response (if any) is converted and returned.
 * @param url the URL
 * @param responseType the type of the return value
 * @return the converted object
 */
@Nullable
<T> T getForObject(URI url, Class<T> responseType) throws RestClientException;

下面定義了一個(gè)接口,用來(lái)測(cè)試上面三個(gè)方法的使用,這個(gè)接口有兩個(gè)參數(shù),分別是userId和userName。
根據(jù)傳入的userId和userName來(lái)查詢用戶,如果可以查詢的到的話,則返回查詢到的用戶,如果查詢不到的話,則返回找不到數(shù)據(jù)。
響應(yīng)體是JSON格式的。

/**
* get請(qǐng)求獲取用戶
*
* @param userName 用戶名
* @param userId 用戶id
* @return
*/
@ResponseBody
@RequestMapping(value = “/getUser.do”)
public ResultData getUserByName(
@RequestParam(name = “userId”,required = false) Integer userId,
@RequestParam(name = “userName”,required = false) String userName) {
if (StringUtils.isAnyBlank(userName)) {
return new ResultData<>(HttpStatus.BAD_REQUEST.value(), null, “參數(shù)不能為空”);
}
List userList = new ArrayList<>();
for (int i = 1; i <= 2; i++) {
User user = new User();
user.setUserId(i);
user.setUserName(“張三” + i);
user.setAge(20 + i);
userList.add(user);
}
for (User user : userList) {
if (userName.equals(user.getUserName()) && userId.equals(user.getUserId())) {
return new ResultData(HttpStatus.OK.value(), user, “成功”);
}
}
return new ResultData<>(HttpStatus.INTERNAL_SERVER_ERROR.value(), null, “找不到數(shù)據(jù)”);
}

下面我們就分別用那三個(gè)方法請(qǐng)求/getUser.do接口進(jìn)行測(cè)試:

@Test
public void getForObjectTest() {
String baseUrl = “http://localhost:8081/testRestTemplateApp/getUser.do”;
//方法一: 直接拼接參數(shù),推薦使用
String url =baseUrl+"?userName=張三1&userId=1";
ResultData resultData = restTemplate.getForObject(url, ResultData.class);
System.out.println("*****GET直接拼接參數(shù)查詢返回結(jié)果={}" + JSON.toJSONString(resultData));
//方法一:傳參替換,推薦使用
url = baseUrl+"?userName={?}&userId={?}";
resultData = restTemplate.getForObject(url, ResultData.class, “張三2”,2);
System.out.println("*****GET傳參替換查詢返回結(jié)果={}" + JSON.toJSONString(resultData));
//方法一:傳參替換,使用String.format,推薦使用
url = baseUrl + String.format("?userName=%s&userId=%s", “張三2”,2);
resultData = restTemplate.getForObject(url, ResultData.class);
System.out.println("******GET使用String.format查詢返回結(jié)果={}" + JSON.toJSONString(resultData));
//方法二:使用Map,不推薦使用
url = baseUrl + “?userName={userName}&userId={userId}”;
Map<String, Object> paramMap = new HashMap<>();
paramMap.put(“userName”, “張三1”);
paramMap.put(“userId”,1);
resultData = restTemplate.getForObject(url, ResultData.class, paramMap);
System.out.println("******GET使用Map查詢返回結(jié)果={}" + JSON.toJSONString(resultData));
//方法三:使用URI,不推薦使用
URI uri = URI.create(baseUrl+"?userName=%E5%BC%A0%E4%B8%891&userId=1");
ResultData resultData1 = restTemplate.getForObject(uri, ResultData.class);
System.out.println("******GET使用URI查詢返回結(jié)果={}" + JSON.toJSONString(resultData1));
}

運(yùn)行結(jié)果如下:
在這里插入圖片描述

需要注意的是:

傳參替換使用{?}來(lái)表示坑位,根據(jù)實(shí)際的傳參順序來(lái)填充,如下:

url = baseUrl+"?userName={?}&userId={?}";
resultData = restTemplate.getForObject(url, ResultData.class, “張三2”,2);

使用{xx}來(lái)傳遞參數(shù)時(shí),這個(gè)xx對(duì)應(yīng)的就是map中的key

   url = baseUrl + "?userName={userName}&userId={userId}";
    Map<String, Object> paramMap = new HashMap<>();
    paramMap.put("userName", "張三1");
    paramMap.put("userId",1);


當(dāng)響應(yīng)頭是application/json;charset=UTF-8格式的時(shí)候,返回的數(shù)據(jù)類型可以直接寫String.class,如下

    String url ="http://localhost:8081/testRestTemplateApp/getUser.do?userName=張三1&userId=1";
    String resultData = restTemplate.getForObject(url, String.class);


不推薦直接使用方法三傳入U(xiǎn)RI,原因主要有如下兩點(diǎn): 1. 傳入的參數(shù)包含中文時(shí)必須要轉(zhuǎn)碼,直接傳中文會(huì)報(bào)400的錯(cuò)誤,2. 響應(yīng)的結(jié)果必須要跟接口的返回值保持一致,不然回報(bào)406的錯(cuò)誤。

  //userName不能直接傳入張三1,不然會(huì)報(bào)400的錯(cuò)誤
   URI uri = URI.create(baseUrl+"?userName=%E5%BC%A0%E4%B8%891&userId=1");
   //responseType不能傳入String.class,不然會(huì)報(bào)406的錯(cuò)誤
    ResultData resultData1 = restTemplate.getForObject(uri, ResultData.class);

說(shuō)完了getForObject,下面來(lái)看看getForEntity的方法,這三個(gè)方法跟上面的getForObject三個(gè)方法分別對(duì)應(yīng),只是返回值不同。

<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
		throws RestClientException;

<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables)
		throws RestClientException;

<T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException;

這里只列舉一個(gè)參數(shù)拼接的方式來(lái)舉例說(shuō)明:

String baseUrl = “http://localhost:8081/testRestTemplateApp/getUser.do”;
//參數(shù)拼接的方式
String url =baseUrl+"?userName=張三1&userId=1";
ResponseEntity entity = restTemplate.getForEntity(url, ResultData.class);
System.out.println("*****參數(shù)拼接查詢返回結(jié)果={}" + JSON.toJSONString(entity));

運(yùn)行后的結(jié)果如下:有響應(yīng)頭heads,有響應(yīng)體body,有響應(yīng)狀態(tài)statusCodeValue等。

{“body”:{“busCode”:200,“data”:{“userId”:1,“userName”:“張三1”,“age”:21},“msg”:“成功”},“headers”:{“Content-Type”:[“application/json;charset=UTF-8”],“Transfer-Encoding”:[“chunked”],“Date”:[“Fri, 06 Mar 2020 05:42:08 GMT”],“Keep-Alive”:[“timeout=60”],“Connection”:[“keep-alive”]},“statusCode”:“OK”,“statusCodeValue”:200}

POST 請(qǐng)求

說(shuō)完了get請(qǐng)求相關(guān)的方法之后,接下來(lái)我們來(lái)看看post請(qǐng)求相關(guān)的方法,首先還是來(lái)看postForObject的三個(gè)重載方法。

/**
 * @param url the URL   請(qǐng)求地址
 * @param request the Object to be POSTed (may be {@code null})  請(qǐng)求體,可以傳入一個(gè)Bean對(duì)象,也可以傳入HttpEntity對(duì)象,包裝請(qǐng)求頭
 * @param responseType the type of the return value  響應(yīng)對(duì)象的類型
 * @param uriVariables the variables to expand the template 傳入的參數(shù)
 * @return the converted object 
 * @see HttpEntity
 */
@Nullable
<T> T postForObject(String url, @Nullable Object request, Class<T> responseType,
		Object... uriVariables) throws RestClientException;

/**
* @param url the URL 請(qǐng)求地址
* @param request the Object to be POSTed (may be {@code null}) 請(qǐng)求體,可以傳入一個(gè)Bean對(duì)象,也可以傳入HttpEntity對(duì)象,包裝請(qǐng)求頭
* @param responseType the type of the return value 響應(yīng)對(duì)象的類型
* @param uriVariables the variables to expand the template 傳入的map
* @return the converted object
* @see HttpEntity
*/
@Nullable
T postForObject(String url, @Nullable Object request, Class responseType,
Map<String, ?> uriVariables) throws RestClientException;

/**
 * @param url the URL
 * @param request the Object to be POSTed (may be {@code null})
 * @param responseType the type of the return value
 * @return the converted object
 * @see HttpEntity
 */
@Nullable
<T> T postForObject(URI url, @Nullable Object request, Class<T> responseType) throws RestClientException;

還是用上面的/getUser.do接口進(jìn)行測(cè)試。

@Test
public void testPostForObjectForForm() {
    String baseUrl = "http://localhost:8081/testRestTemplateApp/getUser.do";

    //方法一:表單提交
    MultiValueMap<String, Object> request = new LinkedMultiValueMap<>();
    request.set("userName","張三1");
    request.set("userId",1);
    ResultData resultData = restTemplate.postForObject(baseUrl,request, ResultData.class);
    System.out.println("*****POST表單提交使用URI查詢返回結(jié)果={}" + JSON.toJSONString(resultData));
    //方法二:使用URI
    URI uri = URI.create(baseUrl);
    resultData = restTemplate.postForObject(uri,request, ResultData.class);
    System.out.println("******POST使用URI查詢返回結(jié)果={}" + JSON.toJSONString(resultData));
}

運(yùn)行結(jié)果如下:
在這里插入圖片描述

從運(yùn)行結(jié)果我們可以看出,
如果傳入的參數(shù)是MultiValueMap類型的對(duì)象是,Spring會(huì)通過(guò)AllEncompassingFormHttpMessageConverter轉(zhuǎn)換器來(lái)將參數(shù)通過(guò)表單提交。
如果直接傳入一個(gè)Map對(duì)象,則會(huì)通過(guò)MappingJackson2HttpMessageConverter轉(zhuǎn)換器對(duì)參數(shù)進(jìn)行轉(zhuǎn)換。
說(shuō)完了表單提交,下面我們看看另外一種場(chǎng)景,如下,這個(gè)接口是一個(gè)保存用戶數(shù)據(jù)的接口,參數(shù)需要格式化后放在請(qǐng)求體中。

@ResponseBody
@PostMapping("/addUserJSON.do")
public ResultData<Boolean> addUserJSON(@RequestBody User user) {
    if (user == null) {
        return new ResultData<>(HttpStatus.BAD_REQUEST.value(), null, "參數(shù)不能為空");
    }
    return new ResultData<>(HttpStatus.OK.value(),true,"保存成功");
}

當(dāng)我們需要調(diào)用接口是通過(guò)@RequestBody來(lái)接受參數(shù)時(shí),也就是需要傳入一個(gè)JSON對(duì)象,我們?cè)撊绾握?qǐng)求呢?我們調(diào)用可以postForObject可以直接傳入U(xiǎn)ser對(duì)象, 也可以將請(qǐng)求頭設(shè)置成application/json,然后將User對(duì)象序列化,代碼如下所示:

@Test
public void testPostForObject() {
    String baseUrl = "http://localhost:8081/testRestTemplateApp/addUserJSON.do";
    User user = new User();
    user.setUserName("李四");
    user.setAge(23);
    //第一種方式:不傳入JSON的參數(shù),不設(shè)置請(qǐng)求頭
    ResultData resultData = restTemplate.postForObject(baseUrl, user, ResultData.class);
    System.out.println("*********不序列化傳入?yún)?shù)請(qǐng)求結(jié)果={}" + JSON.toJSONString(resultData));
    //第二種方式:傳入JSON類型的參數(shù),設(shè)置請(qǐng)求頭
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    HttpEntity httpEntity = new HttpEntity(JSON.toJSONString(user),headers);
    resultData = restTemplate.postForObject(baseUrl, httpEntity, ResultData.class);
    System.out.println("*********序列化參數(shù)請(qǐng)求結(jié)果={}" + JSON.toJSONString(resultData));
}

第一種方式是由于Spring內(nèi)部的MappingJackson2HttpMessageConverter會(huì)將參數(shù)進(jìn)行序列化并請(qǐng)求接口
第二種方式是直接設(shè)置好請(qǐng)求頭為application/json,并將參數(shù)序列化。所以就不需要通過(guò)MappingJackson2HttpMessageConverter進(jìn)行轉(zhuǎn)換。比較推薦
運(yùn)行結(jié)果如下:
在這里插入圖片描述

postForEntity方法在此就不在贅述了。
說(shuō)完了,get請(qǐng)求的相關(guān)方法和post請(qǐng)求的相關(guān)方法,接下來(lái)我們來(lái)看看另外一類方法
postForLocation

postForLocation的定義是POST 數(shù)據(jù)到一個(gè)URL,返回新創(chuàng)建資源的URL,就是重定向或者頁(yè)面跳轉(zhuǎn)。
同樣提供了三個(gè)方法,分別如下,需要注意的是返回結(jié)果為URI對(duì)象,即網(wǎng)絡(luò)資源

public URI postForLocation(String url, @Nullable Object request, Object… uriVariables)
throws RestClientException ;

public URI postForLocation(String url, @Nullable Object request, Map<String, ?> uriVariables)
throws RestClientException ;

public URI postForLocation(URI url, @Nullable Object request) throws RestClientException ;

這類接口主要應(yīng)用在需要跳轉(zhuǎn)頁(yè)面的請(qǐng)求,比如,登錄,注冊(cè),支付等post請(qǐng)求,請(qǐng)求成功之后需要跳轉(zhuǎn)到成功的頁(yè)面。這種場(chǎng)景下我們可以使用postForLocation了,提交數(shù)據(jù),并獲取放回的URI,一個(gè)測(cè)試如下:
首先mock一個(gè)接口

@ResponseBody
@RequestMapping(path = "loginSuccess")
public String loginSuccess(String userName, String password) {
    return "welcome " + userName;
}

/**
 * @param userName
 * @param password
 * @return
 */
@RequestMapping(path = "login", method = {RequestMethod.GET, RequestMethod.OPTIONS, RequestMethod.POST}
        ,produces = "charset/utf8")
public String login(@RequestParam(value = "userName", required = false) String userName,
                   @RequestParam(value = "password", required = false) String password) {
    return "redirect:/loginSuccess?userName=" + userName + "&password=" + password + "&status=success";
}

測(cè)試請(qǐng)求是:

@Test
public void testPostLocation() {
    String url = "http://localhost:8081/testRestTemplateApp/login";
    MultiValueMap<String, String> paramMap = new LinkedMultiValueMap<>();
    paramMap.add("userName", "bob");
    paramMap.add("password", "1212");
    URI location = restTemplate.postForLocation(url, paramMap);
    System.out.println("*******返回的數(shù)據(jù)=" + location);
}

運(yùn)行結(jié)果如下:
在這里插入圖片描述

exchange 方法的使用

前面詳細(xì)介紹了getForObject和postForObject這兩個(gè)常用的方法,現(xiàn)在再介紹一個(gè)更通用的方法exchange方法,其實(shí)getForObject和postForObject方法內(nèi)部也是調(diào)用的exchange方法。該方法的特點(diǎn)是ResponseEntity對(duì)象,該對(duì)象包括了整個(gè)響應(yīng)體,包括status,heads等等。下面展示其使用

public T toPostEntity(String url, HttpEntity httpEntity, Class responseType) {
ResponseEntity responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, responseType);
logger.info(“請(qǐng)求地址是={},響應(yīng)結(jié)果是={}”, url, new Gson().toJson(responseEntity));
//接受請(qǐng)求失敗,拋出異常
if (HttpStatus.OK.value() != responseEntity.getStatusCodeValue() || responseEntity.getStatusCode().isError()) {
throw new BusinessException(ErrorCode.RESULT_CODE_ERROR);
}
//接受請(qǐng)求成功
return responseEntity.getBody();
}

需要注意的是當(dāng)請(qǐng)求體的contentType與響應(yīng)體的contentType不相同是,傳入的responseType需要指定為String.class。不然,可能會(huì)報(bào)如下錯(cuò)誤。

Could not extract response: no suitable HttpMessageConverter found for response type [class java.lang.Object] and content type [text/html;charset=utf-8]

調(diào)用示例

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
Map<String, Object> paramMap = new HashMap<>();
paramMap.put(“areaName”, “安徽省”);
HttpEntity httpEntity = new HttpEntity(paramMap, headers);
requestHandler.toPostEntity(“http://localhost:8080/v1/data/push”, httpEntity, String.class);

調(diào)用結(jié)果是:

響應(yīng)結(jié)果是={“status”:200,“headers”:{“Aiedu-Time”:[“1616722693”],“Content-Type”:[“application/json;charset\u003dUTF-8”],“Transfer-Encoding”:[“chunked”],“Date”:[“Fri, 26 Mar 2021 01:38:13 GMT”]},“body”:"{“code”:0,“msg”:“請(qǐng)求成功”,“data”:null}"}

介紹完了restTemplate的常用方法,但是,我們或許會(huì)感覺到restTemplate的方法太多了,調(diào)用起來(lái)不太方便,為了使用方便,我們就對(duì)restTemplate做一個(gè)封裝。代碼如下所示:主要封裝成了四個(gè)方法,一個(gè)是通過(guò)get請(qǐng)求的方法,一個(gè)是通過(guò)表單提交的post請(qǐng)求方法,一個(gè)是通過(guò)json提交的post請(qǐng)求方法,最后就是上傳圖片的方法。

@Component
public class RestTemplateProxy {
@Autowired
private RestTemplate restTemplate;

/**
 *
 * @param url 請(qǐng)求地址
 *        參數(shù)可以通過(guò) http://localhost:8888/juheServer/juhe/info/queryCustomer.do?taxNo=92330424MA29G7GY5W
 *            或者 http://localhost:8888/juheServer/juhe/info/queryCustomer.do+String.format("?taxNo=%s&order=%s", "92330424MA29G7GY5W","1212121212");
 * @param responseType 返回值的類型
 * @return
 * @author xiagwei
 * @date 2020/3/5 5:28 PM
 *
 */
public <T> T getForObject(String url, Class<T> responseType) {
    return restTemplate.getForObject(url, responseType);
}

/**
 * 通過(guò)json的方式請(qǐng)求服務(wù),不需要將數(shù)據(jù)格式化,直接將請(qǐng)求對(duì)象傳入即可
 * 可以是map,可以是一個(gè)bean
 * @param url 請(qǐng)求接口
 * @param requestParam 請(qǐng)求實(shí)體
 * @param responseType 返回對(duì)象的clazz
 * @return 
 * @author xiagwei
 * @date 2020/3/5 5:36 PM
 */ 
public <T> T postForObjectJSON(String url, Object requestParam,Class<T> responseType) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    HttpEntity httpEntity = new HttpEntity(requestParam, headers);
    return restTemplate.postForObject(url, httpEntity, responseType);
}   

/**
 * 通過(guò)Form表單的方式提交
 * @param url 請(qǐng)求接口
 * @param requestParam 請(qǐng)求實(shí)體,可以是一個(gè)實(shí)體,也可以一個(gè)map
 * @param responseType 返回對(duì)象的clazz
 * @return 
 * @author xiagwei
 * @date 2020/3/5 5:42 PM
 */ 
public <T> T postForObjectForm(String url, @NotNull Object requestParam, Class<T> responseType) {
    MultiValueMap<String, Object> valueRequestMap = createValueMap(requestParam);
    return restTemplate.postForObject(url, valueRequestMap, responseType);
}

   /**
 * 最通用的請(qǐng)求方法
 *
 * @param url          請(qǐng)求的URL
 * @param requestParam 請(qǐng)求參數(shù)
 * @param headers      請(qǐng)求頭
 * @param response     響應(yīng)結(jié)果的類型
 * @return
 * @date 2021/3/10  14:21
 */
public <T> T postForEntityHeader(String url, Object requestParam, HttpHeaders headers, Class<T> response) {
    MultiValueMap<String, Object> requestEntity = createValueMap(requestParam);
    HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(requestEntity,headers);
    return restTemplate.postForObject(url, httpEntity, response);
}
/**
 * 圖片上傳
 *
 * @param url  請(qǐng)求地址
 * @param body 請(qǐng)求體
 *           MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
             body.add("uploadFile", new FileSystemResource(ImageUtil.downloadImgByUrl(url)));
 * @param responseType 返回結(jié)果的clazz對(duì)象
 * @return
 * @author xiagwei
 * @date 2020/3/5 6:05 PM
 */
public <T> T uploadImg(@NotNull String url, @NotNull  MultiValueMap<String, Object> body,Class<T> responseType) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.MULTIPART_FORM_DATA);
    HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body,headers);
    return restTemplate.postForObject(url,requestEntity,responseType);
}
 /**
 * 基礎(chǔ)的請(qǐng)求方法
 *
 * @param url
 * @param httpEntity
 * @param responseType
 * @return
 * @Author weixiang
 * @date 2020/3/5 6:05 PM
 */
 public  <T> T toPostEntity(String url, HttpEntity httpEntity, Class<T> responseType) {
    ResponseEntity<T> responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, responseType);
    logger.info("請(qǐng)求地址是={},響應(yīng)結(jié)果是={}", url, new Gson().toJson(responseEntity));
    //接受請(qǐng)求失敗,拋出異常
    if (HttpStatus.OK.value() != responseEntity.getStatusCodeValue() || responseEntity.getStatusCode().isError()) {
        throw new BusinessException(ErrorCode.RESULT_CODE_ERROR);
    }
	//接受請(qǐng)求成功
    return responseEntity.getBody();
}

private MultiValueMap createValueMap(Object requestParam) {
    MultiValueMap<String, Object> valueRequestMap = new LinkedMultiValueMap<>();
    Map<String, Object> param = null;
    if (requestParam instanceof Map) {
        param = (Map<String, Object>) requestParam;
    } else {
        param = BeanUtil.beanToMap(requestParam);
    }
    for (String key : param.keySet()) {
        valueRequestMap.add(key, param.get(key));
    }
    return valueRequestMap;
}

}

這里需要重點(diǎn)說(shuō)下,圖片上傳的方法,上傳圖片的話,我們一定要把請(qǐng)求頭設(shè)置成multipart/form-data,然后其余的參數(shù)通過(guò)MultiValueMap來(lái)設(shè)置。

public <T> T uploadImg(@NotNull String url, @NotNull  MultiValueMap<String, Object> body,Class<T> responseType) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.MULTIPART_FORM_DATA);
    HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body,headers);
    return restTemplate.postForObject(url,requestEntity,responseType);
}

總結(jié)

本文主要介紹了restTemplate類的使用,首先介紹了需要引入的依賴,然后介紹了如何設(shè)置超時(shí)時(shí)間,接著就是介紹了restTemplate中g(shù)et請(qǐng)求相關(guān)的方法和post請(qǐng)求相關(guān)的方法,以及這些方法如何調(diào)用。最后就是對(duì)常用的請(qǐng)求方法做了一個(gè)封裝。希望對(duì)讀者朋友們有所幫助。





作者:碼農(nóng)飛哥
微信公眾號(hào):碼農(nóng)飛哥