SpringBoot系列: RestTemplate 快速入門


====================================
相關的文章
====================================
SpringBoot系列: 與Spring Rest服務交互數據
https://www.cnblogs.com/harrychinese/p/Springboot_SpringRest.html

SpringBoot系列: Spring MVC視圖方法的補充
https://www.cnblogs.com/harrychinese/p/springboot_mvc_view_function2.html

[轉載]SpringBoot系列: SpringMVC 參數綁定注解解析
https://www.cnblogs.com/harrychinese/p/springboot_mvc_view_function.html

SpringBoot系列: RestTemplate 快速入門
https://www.cnblogs.com/harrychinese/p/springboot_resttemplate.html

 

====================================
微服務進程間通信
====================================

微服務進程之間的通訊有 http 和 rpc 兩種協議, 在 Spring Cloud 項目中一般都以 http 通信, 常用的訪問框架有:
1. JdkHttpConnection 組件
2. Apache HttpClient 組件
3. RestTemplate (Spring Framework 提供的 webclient, 缺省是基於 JdkHttpConnection 實現的, 也可以基於 Apache HttpClient 、 OkHttp 實現)
4. Feign (spring-cloud-starter-feign 項目提供的 webclient)
5. OkHttp (Square 開源的 http 客戶端)
6. AsyncHttpClient(基於 Netty 的 http 客戶端)
7. Retrofit (Square 開源的 http 客戶端, 對於 OkHttp 做了封裝)

JdkHttpConnection/Apache HttpClient 等 web 客戶端是底層客戶端, 如果直接在微服務項目中使用, 需要處理很多工作. 其他幾個客戶端都針對 Rest 服務做了很多封裝, 這包括:
1. 連接池
2. 超時設置
3. 請求和響應的編碼/解碼 (json <-> pojo)
4. 支持異步


因為我們開發的項目是基於 Spring Boot 的, 考慮到集成性和 Spring 官方的支持程度, 自然選擇 RestTemplate 或 Feign 了.
有關 http 通信經常會看到 Robin 相關資料, 該技術是 Spring Cloud Netflix 的一個項目, 是一個基於 Http 和 Tcp 的客戶端負載均衡器, 支持兩種策略 Round robin 或 weigh based. Robin 可以和 RestTemplate/Feign 搭配使用, 為 web 請求提供負載均衡特性.

 


==========================
pom.xml
==========================
RestTemplate 默認使用 jackson 完成 json 序列化和反序列化.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

 

==========================
RestTemplate 實例化
==========================
RestTemplate 實例最好是由 Spring 容器管理, 而不是在用到時候 new RestTemplate() 一個實例.
可以在 @Controller/@Service/@Configuration 類中, 聲明一個 restTemplate bean, 其他地方直接注入即可使用.

@RestController
class HelloController {
    //聲明 bean
    @Bean
    @LoadBalanced   //增加 load balance 特性. 
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    //注入
    @Autowired
    private RestTemplate restTemplate;    
    
    private void someMethod(){
       //使用 restTemplate
    }
}


或者, 先注入 RestTemplateBuilder, 然后通過該 builder 來構建 RestTemplate 實例. 使用 builder 可以為 RestTemplate 定制化東西:
builder.additionalInterceptors() 方法: 可以通過增加攔截器為每次 request 記錄 log,
builder.additionalMessageConverters() 方法: 比如增加 MessageConverter 實現特定的 json <-> pojo 的轉換,

@RestController
class Hello2Controller {    
    //注入 RestTemplateBuilder
    @Autowired
    private void initRestTemplate(RestTemplateBuilder builder){
        this.restTemplate=builder.build();
    }
       
    private RestTemplate restTemplate;    
    
    private void someMethod(){
       //使用 restTemplate
    }
}

 


==========================
RestTemplate 使用
==========================
RestTemplate 主要方法

Http 方法 | RestTemplate 方法
DELETE | delete
GET | getForObject(), getForEntity()
HEAD | headForHeaders()
OPTIONS | OptionsForAllow()
PUT | put
any | exchange(), execute()

1. delete() 方法, 在 url 資源執行 http DELETE 操作.
2. exchange() 方法, 通用的 web 請求方法, 返回一個 ResponseEntity 對象, 這個對象是從響應體映射而來. 該方法支持多種 web method, 是其他 RestTemplate 方法的基礎.
3. execute() 方法, 是 exchange() 方法的基礎.
4. getForEntity() 方法, 發送一個 GET 請求, 返回一個通用的 ResponseEntity 對象, 使用該對象可以得到 Response 字符串.
5. getForObject() 方法, 發送一個 GET 請求, 返回一個 pojo 對象.
6. headForHeaders() 方法, 發送一個 HEAD 請求, 返回包含特定資源 url 的 http 頭.
8. optionsForAllow() 方法, 發送一個 HTTP OPTIONS 請求, 返回對於特定 url 的 Allow 頭信息.
9. PostForEntity() 方法, 發送一個 Post 請求, 返回一個 ResponseEntity 對象, 這個對象是從響應體映射而來.
10. PostForObject() 方法, 發送一個 POST 請求, 返回一個特定的對象, 該對象是從響應體映射而來.
11. PostForLocation() 方法, 發送一個 POST 請求, 返回新創建資源的 URL.
12. put() 方法, 發送 PUT 請求.


--------------------------
獲取 plain json
--------------------------

ResponseEntity<String> response=restTemplate.getForEntity(url, String.class)
// 獲取包含 plain text Body 的 response
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
// 獲取 status code 
System.out.println("status code:" + response.getStatusCode());
// 使用 jackson 解析 json 字符串
// class: com.fasterxml.jackson.databind.ObjectMapper
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(response.getBody());
JsonNode value = root.path("type");

 

--------------------------
獲取 Pojo 對象
--------------------------
如果 Rest 服務返回下面的 json 格式:
{ "firstName":"John" , "lastName":"Doe" }

RestTemplate 很容易可以將 json 轉成對象:
Employee foo = restTemplate.getForObject(url, Employee.class);

如果 Rest 服務返回下面的 json 格式, json 中有一個根節點 employees, 其包含了多個 Employee 信息.

{
    "employees": [
        { "firstName":"John" , "lastName":"Doe" },
        { "firstName":"Anna" , "lastName":"Smith" },
        { "firstName":"Peter" , "lastName":"Jones" }
    ]
}

對於這種格式的 json, 我們仍然可以使用 getForObject() 方法, 只要基於 Employee 類 做個 list wrapper 類即可. 

public class EmployeeList {
    private List<Employee> employees; 
    public EmployeeList() {
        employees = new ArrayList<>();
    }
     // standard constructor and getter/setter
}
EmployeeList response = restTemplate.getForObject(
  "http://localhost:8080/employees",
  EmployeeList.class);
List<Employee> employees = response.getEmployees();

--------------------------
獲取 json 數組對象
--------------------------
雖然 restTemplate.getForObject() 能很方便地將 json 轉成 pojo, 但僅僅適合於處理單個對象的情形. 下面的 json 直接返回了一個數組, 這時使用 getForObject() 就不管用了.

[
    { "firstName":"John" , "lastName":"Doe" },
    { "firstName":"Anna" , "lastName":"Smith" },
    { "firstName":"Peter" , "lastName":"Jones" }
]


我們可以使用 exchange() 方法, 最關鍵一點是將 List<Employee> 類型傳進去, 這樣 RestTemplate 就知道如何將 json 數組轉成 object list 了.

ResponseEntity<List<Employee>> response = restTemplate.exchange(
  "http://localhost:8080/employees/",
  HttpMethod.GET,
  null,
  new ParameterizedTypeReference<List<Employee>>(){});
List<Employee> employees = response.getBody();

--------------------------
向 url 傳參
--------------------------
在 POST 和 GET 等方法, 最后一個形參往往是 url 參數變量, 比如:
getForEntity(String url,Class responseType,Object...urlVariables)
getForEntity(String url,Class responseType,Map urlVariables)

處理方式 1:
如果要使用數組或可變參數方式傳入 url param, url 的參數必須使用數字下標來占位.

String url = http://USER-SERVICE/user.do?name={1}&age={2};
String[] urlVariables=["jason",26];

 

處理方式 2:
如果要 Map 傳入 url param, url 的參數必須使用 named 方式占位

String url = http://USER-SERVICE/user.do?name={name}&age={age};
Map<String, Object> urlVariables = new HashMap<String, Object>();
urlVariables.put("name",jason);
urlVariables.put("age",26);

 

--------------------------
設置 header, Post 一個 json 串
--------------------------

String url="url";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
String body="some json body";
HttpEntity<String> requestEntity = new HttpEntity<String>(body, headers);
ResponseEntity<String> response= restTemplate.postForEntity(url, requestEntity, String.class);

HttpEntity 經常被用到, 它可以將 Headers 和要提交的數據合並成一個對象, 作為 request 對象傳參給 POST/PUT/PATCH 等很多方法. 

--------------------------
Post 一個對象 list
--------------------------
Post 操作可以直接使用 restTemplate.postForObject() 方法, 該方法即可 Post 單個對象, 也可以 Post 對象的 List.

List<Employee> newEmployees = new ArrayList<>();
newEmployees.add(new Employee(3, "Intern"));
newEmployees.add(new Employee(4, "CEO"));
 
restTemplate.postForObject(
  "http://localhost:8080/employees/",
  newEmployees,
  ResponseEntity.class);


--------------------------
使用 HEAD 獲取 headers
--------------------------

HttpHeaders httpHeaders = restTemplate.headForHeaders(fooResourceUrl);
assertTrue(httpHeaders.getContentType().includes(MediaType.APPLICATION_JSON));


--------------------------
文件上傳下載
--------------------------
參考 https://www.jianshu.com/p/bbd9848c0cfc

@Test
    public void upload() throws Exception {
        Resource resource = new FileSystemResource("/home/lake/github/wopi/build.gradle");
        MultiValueMap multiValueMap = new LinkedMultiValueMap();
        multiValueMap.add("username","lake");
        multiValueMap.add("files",resource);
        ActResult result = testRestTemplate.postForObject("/test/upload",multiValueMap,ActResult.class);
        Assert.assertEquals(result.getCode(),0);
    }

@Test
    public void download() throws Exception {
        HttpHeaders headers = new HttpHeaders();
        headers.set("token","xxxxxx");
        HttpEntity formEntity = new HttpEntity(headers);
        String[] urlVariables = new String[]{"admin"};
        ResponseEntity<byte[]> response = testRestTemplate.exchange("/test/download?username={1}",HttpMethod.GET,formEntity,byte[].class,urlVariables);
        if (response.getStatusCode() == HttpStatus.OK) {
            Files.write(response.getBody(),new File("/home/lake/github/file/test.gradle"));
        }
    }
 


--------------------------
定制化 RestTemplate
--------------------------
增加一個自定義 ErrorHandler:
restTemplate.setErrorHandler(errorHandler);

設定 httpClient 的工廠類:
restTemplate.setRequestFactory(requestFactory);
可以為 RestTemplate 設定 httpClient 的工廠類, 主要有兩個工廠類:
1. SimpleClientHttpRequestFactory 工廠類, 這是缺省的工廠類, 底層用的是 jdk 的 HttpConnection, 默認超時為-1.
2. HttpComponentsClientHttpRequestFactory 底層用的是 Apache HttpComponents HttpClient, 比 JDK 的 HttpConnection 強大, 可以配置連接池和證書等, 支持 https.
3. OkHttp3ClientHttpRequestFactory 底層使用的是 square 公司開源的 OkHttp, 該客戶端支持 https 等高級特性,  pom.xml 需要增加依賴.
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>3.8.1</version>
        </dependency>

 

====================================
參考
====================================
https://www.jianshu.com/p/bbd9848c0cfc
http://www.cnblogs.com/okong/p/springcloud-four.html
https://my.oschina.net/lifany/blog/688889
http://www.cnblogs.com/okong/p/springcloud-four.html
https://blog.csdn.net/QiaoRui_/article/details/80453799

https://spring.io/guides/gs/consuming-rest/
https://www.tutorialspoint.com/spring_boot/spring_boot_rest_template.htm
https://www.baeldung.com/rest-template
https://www.baeldung.com/spring-rest-template-list

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM