簡介 RestTemplate


當你將應用程式中某些元件分離出來,成為物理上一個可獨立運行的服務,並且像 @RepositoryRestResource 提供了 REST 介面,這就可以讓需要此服務的客戶端透過 HTTP 來請求,以使用該服務。

(當然,這只是範例專案,畢竟幾乎只處理 CRUD 的服務,就粒度來說是有點小了!)

為了便於客戶端使用基於 REST 的服務,Spring 提供了 RestTemplate,不過,要能使用 RestTemplate 來運用 @RepositoryRestResource 服務,需要知道一些 HATEOAS 的概念,這會是下篇文件的主題,在這邊僅先認識一下 RestTemplate 的基本使用方式。

為了有個實際的測試對象,可以先建立一個簡單的 REST 服務。例如:

package cc.openhome;

import java.util.ArrayList;
import java.util.List;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class RestTmplApplication {
    public static void main(String[] args) {
        SpringApplication.run(RestTmplApplication.class, args);
    }

    List<Message> messages = new ArrayList<Message>() {{
        add(new Message("msg1"));
        add(new Message("msg2"));
    }};

    @GetMapping("messages")
    public List<Message> index() {
        return messages;
    }

    @GetMapping("messages/{id}")
    public Message show(@PathVariable("id") String id) {
        return messages.get(Integer.parseInt(id) - 1);
    }

    @PostMapping("messages")
    public Message create(@RequestBody Message message) {
        messages.add(message);
        return message;
    }

    @DeleteMapping("messages/{id}")
    public Message delete(@PathVariable("id") String id) {
        return messages.remove(Integer.parseInt(id) - 1);
    }
}

大部份程式碼你應該都知道作用了,唯一要注意的是 create,這個處理器接受 JSON 轉換後的 Message 實例,JSON 會是請求本體中的內容,為此必須標註 @RequestBody

首先來看看,如何透過 RestTemplate 請求並自動轉為 Message 實例,這透過 getForObject 就可以了:

...略
@SpringBootTest
public class RestTmplApplicationTests {
    private RestTemplate restTemplate = new RestTemplate();

    @Test
    public void show() {
        Message message = restTemplate.getForObject("http://localhost:8080/messages/{id}", Message.class, "1");
        assertNotNull(message.getText());
    }

基本上沒什麼好解釋的,要請求建立一個 Message 實例的話,可以使用 postForObject,因為要傳送 JSON,必須設定請求標頭,以及 POST 時的 Message 實例,postForObject 會自動將 Message 轉為 JSON 發送出去:

@Test
public void create() {
    Message message = new Message("new message");
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    HttpEntity<Message> request = new HttpEntity<>(message, headers);

    message = restTemplate.postForObject("http://localhost:8080/messages", request, Message.class);
    assertEquals(message.getText(), "new message");
}

要請求 REST 介面進行刪除,可以使用 delete

@Test
public void delete() {
    restTemplate.delete("http://localhost:8080/messages/{id}", "1");
    Message message = restTemplate.getForObject("http://localhost:8080/messages/{id}", Message.class, "1");
    assertEquals(message.getText(), "msg2");
}

至於要請求取得全部訊息,並封裝為 List<Message>,稍微多了手續,不過也可以趁機看看 exchange 方法的使用:

@Test
public void index() {
    RequestEntity<Void> request = RequestEntity
            .get(URI.create("http://localhost:8080/messages/"))
            .accept(MediaType.APPLICATION_JSON)
            .build();

    ResponseEntity<List<Message>> response = restTemplate
            .exchange(request,new ParameterizedTypeReference<List<Message>>(){});
    List<Message> messages = response.getBody();
    assertTrue(messages.size() > 0);
}

你可以在 RestTmpl 找到以上的範例專案。