RestTemplate詳解


在SpringCloud項目中,當我們需要遠程調用一個HTTP接口時,我們經常會用到RestTemplate這個類。這個類是Spring框架提供的一個工具類。它是一個同步的Rest API客戶端,提供了多種便捷訪問遠程Http服務的方法,能夠大大提高客戶端的編寫效率。

說明:

在項目架構中,我們基本都可以通過它來進行不同服務與服務之間的調用。在SpringCloud中我們依然可以使用HttpClient進行服務與服務調用,只不過如果采用HttpClient調用的話,會有一些弊端。例如:如果同一個服務有多個負載的話,采用HttpClient調用時,沒有辦法處理負載均衡的問題。還有另一個問題就是HttpClient只是提供了核心調用的方法並沒有對調用進行封裝,所以在使用上不太方便,需要自己對HttpClient進行簡單的封裝。

在SpringCloud中為了解決服務與服務調用的問題,於是提供了兩種方式來進行調用:RestTemplate和Feign。

一、RestTemplate常用方法

上面的方法我們大致可以分為三組:

  • getForObject --- optionsForAllow分為一組,這類方法是常規的Rest API(GET、POST、DELETE等)方法調用;
  • exchange:接收一個RequestEntity 參數,可以自己設置HTTP method, URL, headers和body。返回ResponseEntity。
  • execute:通過callback 接口,可以對請求和返回做更加全面的自定義控制。

 一般情況下,我們使用第一組和第二組方法就夠了。

二、RestTemplate簡單使用

1. RestTemplate的創建

使用RestTemplate前,必須先創建實例化。

@Bean
public RestTemplate restTemplate() {
    RestTemplate restTemplate = new RestTemplate();
    return restTemplate;
}

@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
    SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
    factory.setReadTimeout(5000);
    factory.setConnectTimeout(15000);
    //設置代理
    //factory.setProxy(null);
    return factory;
}
在內部, RestTemplate默認使用 SimpleClientHttpRequestFactoryDefaultResponseErrorHandler來分別處理 HTTP的創建和錯誤,但也可以通過 setRequestFactorysetErrorHandler來覆蓋。

通過上面代碼配置后,我們直接在代碼中注入RestTemplate就可以使用了。

2. 服務間的調用

服務端Controller:

@RestController
@RequestMapping("/server")
public class Controller {

    @GetMapping("/get")
    public Object get() {
        Map<String, String> map = new HashMap<String, String>();
        map.put("code", "0");
        map.put("msg", "success");
        map.put("data", "吉林烏拉");
        return map;
    }
}

服務端配置文件:application.yml

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8761/eureka/
spring:
  application:
    name: test-server
server:
  port: 8082

客戶端Controller:

@RestController
@RequestMapping("/client")
public class Controller {

    @GetMapping("/get")
    public Object get() {
        Map<String, String> map = new HashMap<String, String>();
        map.put("code", "0");
        map.put("msg", "success");
        map.put("data", "吉林烏拉");
        return map;
    }
}

客戶端配置文件application.yml:

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8761/eureka/
spring:
  application:
    name: test-client
server:
  port: 8081

注意:需要在啟動類上添加@EnableEurekaClient注解,將兩個服務注冊到eureka中。

(1) 調用方式一

@RestController
@RequestMapping("/client")
public class Controller {

    @Autowired
    private RestTemplate template;

    @GetMapping("/get")
    public Object get() {
        String result = template.getForObject("http://127.0.0.1:8082/server/get", String.class);
        return result;
    }
}

弊端:server端的接口地址直接寫死了,這樣當服務接口變更時,是需要更改客戶端代碼的。

(2) 調用方式二:

注冊中心這時可以派上用場了。因為注冊中心知道所有服務的地址,我們通過注冊中心先知道server端的接口地址,然后再調用。

@RestController
@RequestMapping("/client")
public class Controller {

    @Autowired
    private RestTemplate template;

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @GetMapping("/get")
    public Object get() {
        ServiceInstance serviceInstance = loadBalancerClient.choose("test-server");
        String url = String.format("http://%s:%s/server/get", serviceInstance.getHost(), serviceInstance.getPort());
        String result = template.getForObject(url, String.class);
        return result;
    }
}

SpringClourd中提供了LoadBalancerClient接口。通過這個接口我們可以通過用戶中心的Application的名字(即application.yml中的spring.application.name屬性值)來獲取該服務的地址和端口。

弊端:每次調用服務時都要先通過Application的名字來獲取ServiceInstance對象,然后才可以發起接口調用。實際上在SpringCloud中為我們提供了@LoadBalanced注解,只要將該注解添加到RestTemplate中的獲取的地方就可以了。

(3) 調用方式三

在RestTemplate實例化時添加@LoadBalanced注解

@Bean
@LoadBalanced
public RestTemplate initRestTemplate() {
     return new RestTemplate();
}

客戶端Controller:

@RestController
@RequestMapping("/client")
public class Controller {

    @Autowired
    private RestTemplate template;

    @GetMapping("/get")
    public Object get() {
        //注意,應用可能存在上下文配置(server.context-path屬性值), 如果有則需要加上, 假如上下文是web-test,則url為
        //http://TEST-SERVER/web-test/server/get
        //String url = String.format("http://%s/server/get", "test-server");
        String result = template.getForObject(url, String.class);
        return result;
    }
}

三、RestTemplate請求參數傳遞

使用RestTemplate post請求的時候主要可以通過三種方式實現:

  • 調用postForObject方法
  • 使用postForEntity方法
  • 調用exchange方法

postForObject和postForEntity方法的區別主要在於可以在postForEntity方法中設置header的屬性,當需要指定header的屬性值的時候,使用postForEntity方法。exchange方法和postForEntity類似,但是更靈活,exchange還可以調用get、put、delete請求。使用這三種方法調用post請求傳遞參數,Map不能定義為以下兩種類型(url使用占位符進行參數傳遞時除外):

Map<String, Object> paramMap = new HashMap<String, Object>();
Map<String, Object> paramMap = new LinkedHashMap<String, Object>();

前人經過測試,發現以上兩種類型不能被后台接收到,當把Map類型換成LinkedMultiValueMap后,參數成功傳遞到后台:

MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<String, Object>();

正確傳遞參數如下:

RestTemplate template = new RestTemplate();
String url = "http://192.168.2.40:8081/channel/channelHourData/getHourNewUserData";
// 封裝參數,千萬不要替換為Map與HashMap,否則參數無法傳遞
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<String, Object>();
paramMap.add("dt", "20180416");

// 1、使用postForObject請求接口
//第一參數表示要調用的服務的地址 第二個參數表示請求參數 第三個參數表示返回的消息體的數據類型
String result = template.postForObject(url, paramMap, String.class); System.out.println("result1==================" + result); // 2、使用postForEntity請求接口 HttpHeaders headers = new HttpHeaders(); HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<MultiValueMap<String, Object>>(paramMap,headers); ResponseEntity<String> response2 = template.postForEntity(url, httpEntity, String.class); System.out.println("result2====================" + response2.getBody()); // 3、使用exchange請求接口 ResponseEntity<String> response3 = template.exchange(url, HttpMethod.POST, httpEntity, String.class); System.out.println("result3====================" + response3.getBody());

get方式傳參說明:如果是get請求,又想要把參數封裝到map里面進行傳遞的話,Map需要使用HashMap,且url需要使用占位符

RestTemplate restTemplate2 = new RestTemplate();
String url = "http://127.0.0.1:8081/interact/getData?dt={dt}&ht={ht}";
   
    // 封裝參數,這里是HashMap
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("dt", "20181116");
paramMap.put("ht", "10");

//1、使用getForObject請求接口
String result1 = template.getForObject(url, String.class, paramMap);
System.out.println("result1====================" + result1);

//2、使用exchange請求接口
HttpHeaders headers = new HttpHeaders();
headers.set("id", "lidy");
HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<MultiValueMap<String, Object>>(null,headers);
ResponseEntity<String> response2 = template.exchange(url, HttpMethod.GET, httpEntity, String.class,paramMap);
System.out.println("result2====================" + response2.getBody());

delete請求, 請求方式使用 HttpMethod.DELETE

StringBuffer url = new StringBuffer(baseUrl).append("/user/delete/{id}");
 
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("id", id);

ResponseEntity<String > response = restTemplate.exchange(url.toString(), HttpMethod.DELETE, null, String .class, paramMap);
String result = response.getBody();

put請求, 請求方式使用 HttpMethod.PUT

StringBuffer url = new StringBuffer(baseUrl)
                .append("/user/edit?tmp=1")
                .append("&id={id}")
                .append("&userName={userName}")
                .append("&nickName={nickName}")
                .append("&realName={realName}")
                .append("&sex={sex}")
                .append("&birthday={birthday}");
 
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("userId", userInfoDTO.getId());
paramMap.put("userName", userInfoDTO.getUserName());
paramMap.put("nickName", userInfoDTO.getNickName());
paramMap.put("realName", userInfoDTO.getRealName());
paramMap.put("sex", userInfoDTO.getSex());
paramMap.put("birthday", userInfoDTO.getBirthday());

ResponseEntity<String > response = restTemplate.exchange(url.toString(), HttpMethod.PUT, null, String .class, paramMap);
String result = response.getBody();

 


免責聲明!

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



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