使用 RestTemplate 進行第三方Rest服務調用

1. 前言

RestTemplateSpring 提供的一個調用 Restful 服務的抽象層,它簡化的同 Restful 服務的通訊方式,隱藏了沒必要要的一些細節,讓咱們更加優雅地在應用中調用 Restful 服務 。可是在 Spring 5.0 之後RestTemplate處於維護模式,再也不進行新特性的開發,僅僅進行一些平常維護。Spring 建議咱們使用同時支持同步、異步和 Stream 的另外一個 API —— WebClient 。可是在 Spring MVC 下目前咱們尚未更好的選擇。java

2. RestTemplate 的使用場景

咱們在項目中常常要使用第三方的 Rest API 服務,好比短信、快遞查詢、天氣預報等等。這些第三方只要提供了 Rest Api ,你均可以使用 RestTemplate 來調用它們。程序員

3. 初始化 RestTemplate

只要你的項目使用了 Spring MVC 就已經集成了RestTemplate 。可是一般狀況下該類不會自動被注入 Spring IoC容器,由於不少 Rest API 都具備特殊性,爲了更加靈活的進行定製,其構建類 RestTemplateBuilder被自動注入了 Spring IoC 容器。 咱們能夠這樣初始化它:web

package cn.felord.rest.webclient;

import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

/** * @author felord.cn * @since 14:58 **/
@Component
public class SomeWeb {

    private final RestTemplateBuilder restTemplateBuilder;

    public SomeWeb(RestTemplateBuilder restTemplateBuilder) {
        this.restTemplateBuilder = restTemplateBuilder;
    }

    public RestTemplate restTemplate() {
        // 經過 builder 定製
        return restTemplateBuilder.requestFactory(OkHttp3ClientHttpRequestFactory::new).
                build();
    }
}
複製代碼

最佳實踐:針對每個第三方服務儘可能定製對應的 RestTemplate,儘可能不公用,除非這些第三方的流程徹底一致。spring

2.1 RestTemplate 底層

默認狀況下,RestTemplate 使用 java.net.HttpURLConnection 做爲實現,一但使用它時有異常響應狀態(好比 401),就會引起異常,所以咱們通常不使用它。咱們能夠切換到 NettyApache HttpComponentsokHttp 默認實現的客戶端庫,參考 2 中的 requestFactory(ClientHttpRequestFactory factory) 接入方法,也能夠自行實現 ClientHttpRequestFactory 對接其它第三方庫進行接入。這裏我使用 okHttp 。你能夠定製這些第三方庫提供的特性豐富你的 RestTemplate,好比設置請求超時。json

3. 經常使用方法場景舉例

RestTemplate 支持全部 Restful 風格方法,你能夠根據須要進行選擇,這裏咱們只介紹一些經常使用的方法。全部方法都支持URI 模板和 URI 參數,支持下面這種寫法:api

# 相似 spring mvc 中的 @PathVariable
https://api.apiopen.top/{method}
複製代碼

3.1 {get|post}ForEntity

Get 請求後將響應映射爲 ResponseEntity<T> 響應對象,一個響應體的包裝對象。咱們使用下列代碼來隨機請求 5 條漂亮小姐姐的照片,你能夠打印進行查看:bash

@Autowired
    RestTemplate restTemplate;

    void contextLoads() {
        String url = "https://api.apiopen.top/getImages?page=0&count=5";
        ResponseEntity<String> responseEntity = restTemplate
                .getForEntity(url,String.class);
        String body = responseEntity.getBody();
        System.out.println("body = " + body);
    }
複製代碼

上面的方法改成按順序的可變參數:mvc

String url = "https://api.apiopen.top/getImages?page={page}&count={count}";
        ResponseEntity<String> responseEntity = restTemplate
                .getForEntity(url,String.class,0,5);
        String body = responseEntity.getBody();
        System.out.println("body = " + body);
複製代碼

或者使用 Map<String,Object>app

String url = "https://api.apiopen.top/getImages?page={page}&count={count}";
        HashMap<String, Object> uriParams = new HashMap<>();
        uriParams.put("page", 0);
        uriParams.put("count", 5);
        ResponseEntity<String> responseEntity = restTemplate
                .getForEntity(url, String.class, uriParams);
        String body = responseEntity.getBody();
        System.out.println("body = " + body);
複製代碼

post 請求 額外會傳入一個可能爲 null 的 VO 對象,或者 MultiValueMap 來攜帶請求體參數 ,它們最終會被封裝入異步

org.springframework.http.HttpEntity 對象,該對象可包含如下兩個部分:

  • 請求體對象,可以使用實體 VO、MultiValueMap
  • 請求頭對象, org.springframework.http.HttpHeaders
String url = "https://api.apiopen.top/getImages?page={page}&count={count}";
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<UserVO> httpEntity = new HttpEntity<>(new UserVO("userName"), headers);
        HashMap<String, Object> uriParams = new HashMap<>();
        uriParams.put("page", 0);
        uriParams.put("count", 5);
        ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, httpEntity, String.class, uriParams);
複製代碼

以上是一個調用 Post 請求並攜帶請求體和請求頭的示例。

3.2 {get|post}ForObject

咱們還能夠將響應直接映射到 POJO, 固然你須要對響應結果的結構很是瞭解,建議先映射到 String 查看一下結構。咱們給出一種示例,其餘示例參考 3.1 :

String url = "https://api.apiopen.top/getImages?page={page}&count={count}";
        HashMap<String, Object> uriParams = new HashMap<>();
        uriParams.put("page", 0);
        uriParams.put("count", 5);
        String forObject = restTemplate.getForObject(url, String.class, uriParams);
        System.out.println("forObject = " + forObject);
複製代碼

3.3 headForHeaders

該方法用於獲取全部的 URI 模板聲明資源的 Header

String url = "https://api.apiopen.top/getImages?page={page}&count={count}";
        HashMap<String, Object> uriParams = new HashMap<>();
        uriParams.put("page", 0);
        uriParams.put("count", 5);
        HttpHeaders httpHeaders = restTemplate.headForHeaders(url, uriParams);
        System.out.println(httpHeaders);
複製代碼

結果爲:

[Access-Control-Allow-Headers:"Content-Type, x-requested-with, X-Custom-Header, Authorization", Access-Control-Allow-Methods:"POST, GET, OPTIONS, DELETE", Access-Control-Allow-Origin:"*", Access-Control-Max-Age:"3600", Cache-Control:"private", Content-Length:"608", Content-Type:"application/json;charset=UTF-8", Date:"Tue, 14 Apr 2020 15:25:19 GMT", Expires:"Thu, 01 Jan 1970 00:00:00 GMT"]
複製代碼

3.4 postForLocation

Post 操做不是返回完整的資源,而是返回新建立的資源 URI 。好比上傳文件返回資源的請求路徑。

3.5 put/delete

對應 put 請求 和 delete 請求,參考前面的 api。

3.6 optionsForAllow

該方法獲取該 URI 容許的全部請求方法好比 GET、POST、PUT、DELETE 中的一個或者幾個。

3.7 exchange

該方法是通用的請求方式,支持 GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE,當上面的方式不能知足你可採用該方式定製,該方式提供了更加靈活的 API,好比你能夠定製 GET 方法的請求頭,放入 Jwt Token等操做,這是getForObject 沒法比擬的。

4. 總結

RestTemplate 是一個頗有用的請求協調器,屏蔽了調用服務的複雜度而又不失靈活。可是值得注意的是它正在退出歷史舞臺。再牛逼的程序員也有轉行的那一天不是嗎?

關注公衆號:Felordcn獲取更多資訊

我的博客:https://felord.cn

相關文章
相關標籤/搜索