詳解 RestTemplate 操作

業餘草發表於2018-09-18

作為開發人員,我們經常關注於構建偉大的軟體來解決業務問題。資料只是軟體完成工作時 
要處理的原材料。但是如果你問一下業務人員,資料和軟體誰更重要的話,他們很可能會選擇 
資料。資料是許多業務的生命之血。軟體通常是可以替換的,但是多年積累的資料是永遠不能 
替換的。

近幾年來,以資訊為中心的表述性狀態轉移(Representational State Transfer,REST)已經稱為替代傳統SOAP Web 服務的流行方案. 
SOAP關注的一般是行為和處理,而REST關注的是要處理的資料.

從Spring3.0開始,Spring為建立Rest API提供了良好的支援.

REST提供了一個更簡單的可選方案。另外,很多的現代化應用都會有移動或富JavaScript客戶端,它們都會使用執行在伺服器上REST API。

REST的基礎知識

當談論REST時,有一種常見的錯誤就是將其視為“基於URL的Web服務”——將REST作為另一 
種型別的遠端過程呼叫(remote procedure call,RPC)機制,就像SOAP一樣,只不過是通過簡單 
的HTTP URL來觸發,而不是使用SOAP大量的XML名稱空間

恰好相反,REST與RPC幾乎沒有任何關係。RPC是面向服務的,並關注於行為和動作;而REST 
是面向資源的,強調描述應用程式的事物和名詞。

更簡潔地講,REST就是將資源的狀態以最適合客戶端或服務端的形式從伺服器端轉移到客戶 
端(或者反過來)。

在REST中,資源通過URL進行識別和定位。至於RESTful URL的結構並沒有嚴格的規則,但是 
URL應該能夠識別資源,而不是簡單的發一條命令到伺服器上。再次強調,關注的核心是事 
物,而不是行為。

Spring 中如何使用Rest資源

藉助 RestTemplate,Spring應用能夠方便地使用REST資源 
Spring的 RestTemplate訪問使用了模版方法的設計模式.

模版方法將過程中與特定實現相關的部分委託給介面,而這個介面的不同實現定義了介面的不同行為.

RestTemplate定義了36個與REST資源互動的方法,其中的大多數都對應於HTTP的方法。 
其實,這裡面只有11個獨立的方法,其中有十個有三種過載形式,而第十一個則過載了六次,這樣一共形成了36個方法。

  • delete() 在特定的URL上對資源執行HTTP DELETE操作

  • exchange() 
    在URL上執行特定的HTTP方法,返回包含物件的ResponseEntity,這個物件是從響應體中 
    對映得到的

  • execute() 在URL上執行特定的HTTP方法,返回一個從響應體對映得到的物件

  • getForEntity() 傳送一個HTTP GET請求,返回的ResponseEntity包含了響應體所對映成的物件

  • getForObject() 傳送一個HTTP GET請求,返回的請求體將對映為一個物件

  • postForEntity() 
    POST 資料到一個URL,返回包含一個物件的ResponseEntity,這個物件是從響應體中對映得 
    到的

  • postForObject() POST 資料到一個URL,返回根據響應體匹配形成的物件

  • headForHeaders() 傳送HTTP HEAD請求,返回包含特定資源URL的HTTP頭

  • optionsForAllow() 傳送HTTP OPTIONS請求,返回對特定URL的Allow頭資訊

  • postForLocation() POST 資料到一個URL,返回新建立資源的URL

  • put() PUT 資源到特定的URL

實際上,由於Post 操作的非冪等性,它幾乎可以代替其他的CRUD操作.

Get請求

RestTemplate 的get方法有以上幾個,可以分為兩類: getForEntity() 和 getForObject()

首先看 getForEntity() 的返回值型別 ResponseEntity

<T> ResponseEntity<T> getForEntity()

 看一下 ResponseEntity 的文件描述:

可以看到 它繼承了HttpEntity. 封裝了返回的響應資訊,包括 響應狀態,響應頭 和 響應體.

在測試之前我們首先 建立一個Rest服務,模擬提供Rest資料,這裡給出Controller層程式碼,具體可以檢視原始碼,文章最後會給出:

/**
 * @author itguang
 * @create 2017-12-17 10:37
 **/
@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "getAll")
    public List<UserEntity> getUser() {
        List<UserEntity> list = userService.getAll();
        return list;
    }

    @RequestMapping("get/{id}")
    public UserEntity getById(@PathVariable(name = "id") String id) {

        return userService.getById(id);
    }


    @RequestMapping(value = "save")
    public String save(UserEntity userEntity) {

        return "儲存成功";
    }


    @RequestMapping(value = "saveByType/{type}")
    public String saveByType(UserEntity userEntity,@PathVariable("type")String type) {

        return "儲存成功,type="+type;
    }




}

測試: getForEntity

  1. 無引數的 getForEntity 方法
 @RequestMapping("getForEntity")
    public List<UserEntity> getAll2() {
        ResponseEntity<List> responseEntity = restTemplate.getForEntity("http://localhost/getAll", List.class);
        HttpHeaders headers = responseEntity.getHeaders();
        HttpStatus statusCode = responseEntity.getStatusCode();
        int code = statusCode.value();

        List<UserEntity> list = responseEntity.getBody();

        System.out.println(list.toString());
        return list;

    }
  1. 有引數的 getForEntity 請求,引數列表,可以使用 {} 進行url路徑佔位符
 //有引數的 getForEntity 請求,引數列表
    @RequestMapping("getForEntity/{id}")
    public UserEntity getById2(@PathVariable(name = "id") String id) {

        ResponseEntity<UserEntity> responseEntity = restTemplate.getForEntity("http://localhost/get/{id}", UserEntity.class, id);
        UserEntity userEntity = responseEntity.getBody();
        return userEntity;
    }
  1. 有引數的 get 請求,使用map封裝引數

    //有引數的 get 請求,使用map封裝引數
    @RequestMapping("getForEntity/{id}")
    public UserEntity getById4(@PathVariable(name = "id") String id) {
        HashMap<String, String> map = new HashMap<>();
        map.put("id",id);

        ResponseEntity<UserEntity> responseEntity = restTemplate.getForEntity("http://localhost/get/{id}", UserEntity.class, map);
        UserEntity userEntity = responseEntity.getBody();

        return userEntity;
    }

通過斷點除錯我們看下 返回的 responseEntity 的資訊如圖:

因此我們可以獲取Http請求的全部資訊.

但是,通常情況下我們並不想要Http請求的全部資訊,只需要相應體即可.對於這種情況,RestTemplate提供了 getForObject() 方法用來只獲取 響應體資訊. 
getForObject 和 getForEntity 用法幾乎相同,指示返回值返回的是 響應體,省去了我們 再去 getBody() .

測試: getForObject

  1. 無引數的 getForObject 請求

    //無引數的 getForObject 請求
    @RequestMapping("getAll2")
    public List<UserEntity> getAll() {
        List<UserEntity> list = restTemplate.getForObject("http://localhost/getAll", List.class);


        System.out.println(list.toString());
        return list;

    }
  1. 有引數的 getForObject 請求,使用引數列表

    //有引數的 getForObject 請求
    @RequestMapping("get2/{id}")
    public UserEntity getById(@PathVariable(name = "id") String id) {

        UserEntity userEntity = restTemplate.getForObject("http://localhost/get/{id}", UserEntity.class, id);

        return userEntity;
    }
  1. 有引數的 get 請求,使用map封裝請求引數

    //有引數的 get 請求,使用map封裝請求引數
    @RequestMapping("get3/{id}")
    public UserEntity getById3(@PathVariable(name = "id") String id) {
        HashMap<String, String> map = new HashMap<>();
        map.put("id",id);

        UserEntity userEntity = restTemplate.getForObject("http://localhost/get/{id}", UserEntity.class, map);

        return userEntity;
    }

Post請求

瞭解了get請求後,Post請求就變得很簡單了,我們可以看到post有如下方法:

測試: postForEntity

  1. post 請求,儲存 UserEntity 對像

    //post 請求,提交 UserEntity 對像

    @RequestMapping("saveUser")
    public String save(UserEntity userEntity) {

        ResponseEntity<String> responseEntity = restTemplate.postForEntity("http://localhost/save", userEntity, String.class);
        String body = responseEntity.getBody();

        return body;

    }

瀏覽器訪問: http://localhost/saveUser?username=itguang&password=123456&age=20&email=123@123.com 
我們再次斷點除錯,檢視 responseEntity 中的資訊:

  1. 有引數的 postForEntity 請求

   // 有引數的 postForEntity 請求
    @RequestMapping("saveUserByType/{type}")
    public String save2(UserEntity userEntity,@PathVariable("type")String type) {

        ResponseEntity<String> responseEntity = restTemplate.postForEntity("http://localhost/saveByType/{type}", userEntity, String.class, type);
        String body = responseEntity.getBody();

        return body;

    }

    // 有引數的 postForEntity 請求,使用map封裝
    @RequestMapping("saveUserByType2/{type}")
    public String save3(UserEntity userEntity,@PathVariable("type")String type) {
        HashMap<String, String> map = new HashMap<>();
         map.put("type", type);


        ResponseEntity<String> responseEntity = restTemplate.postForEntity("http://localhost/saveByType/{type}", userEntity, String.class,map);
        String body = responseEntity.getBody();

        return body;

    }

我們瀏覽器訪問: localhost/saveUserByType/120?username=itguang&password=123456&age=20&email=123@123.com

就會返回: 儲存成功,type=120

對與其它請求方式,由於不常使用,所以這裡就不再講述.

相關文章