詳解Spring cloud使用Ribbon進(jìn)行Restful請求
寫在前面
本文由markdown格式寫成,為本人第一次這么寫,排版可能會有點亂,還望各位海涵。
主要寫的是使用Ribbon進(jìn)行Restful請求,測試各個方法的使用,代碼冗余較高,比較適合初學(xué)者,介意輕噴謝謝。
前提
- 一個可用的Eureka注冊中心(文中以之前博客中雙節(jié)點注冊中心,不重要)
- 一個連接到這個注冊中心的服務(wù)提供者
- 一個ribbon的消費者
注意:文中使用@GetMapping、@PostMapping、@PutMapping、@DeleteMapping等注解需要升級 spring-boot-starter-parent版本到1.5.9.REALEASE以上(1.3.7.RELEASE版本沒有這些注解)
建議:每個微服務(wù)應(yīng)用都有自己的spring-boot-maven-plugin和maven-compiler-plugin并指定jdk編譯版本為1.8 ,指定方式如下,pom.xml中添加
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build>
測試項目構(gòu)建
Eureka注冊中心:參考注冊中心的搭建
服務(wù)提供者:參考注冊服務(wù)提供者
ribbon消費者:參考服務(wù)發(fā)現(xiàn)與消費
項目搭建完后,記得按照這幾個教程中提到的配置hosts文件
為了防止項目中的RequestMapping相同,這里就刪除所有的controller類(服務(wù)提供者和消費者),接下來我會將每個restful方法都封裝成一個類,方便大家查看
Get請求
getForEntity:此方法有三種重載形式,分別為:
- getForEntity(String url, Class<T> responseType)
- getForEntity(String url, Class<T> responseType, Object... uriVariables)
- getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables)
- getForEntity(URI url, Class<T> responseType)
注意:此方法返回的是一個包裝對象ResponseEntity<T>其中T為responseType傳入類型,想拿到返回類型需要使用這個包裝類對象的getBody()方法
getForObject:此方法也有三種重載形式,這點與getForEntity方法相同:
- getForObject(String url, Class<T> responseType)
- getForObject(String url, Class<T> responseType, Object... uriVariables)
- getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)
- getForObject(URI url, Class<T> responseType)
注意:此方法返回的對象類型為responseType傳入類型
為了方便測試,這里分別在服務(wù)提供者和服務(wù)消費者中提供相同的User類,用于方便測試
package com.cnblogs.hellxz; /** * 用于測試的pojo */ public class User { private String name; private String sex; private String phone; public User(){} public User(String name, String sex, String phone) { this.name = name; this.sex = sex; this.phone = phone; } public String toString(){ return "user:{" +"name: " + name + ", " +"sex: " + sex + ", " +"phone: " + phone +" }"; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } }
下邊我們在服務(wù)提供者處創(chuàng)建一個GetRequestController
package com.cnblogs.hellxz; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.*; /** * @Author : Hellxz * @Description: 服務(wù)提供者 * @Date : 2018/4/18 11:36 */ @RestController public class GetRequestController { @Autowired private DiscoveryClient client; //注入發(fā)現(xiàn)客戶端 private final Logger logger = Logger.getLogger(GetRequestController.class); /** * go straight test */ @GetMapping(value = "/hello") public String hello(){ //獲取服務(wù)實例,作用為之后console顯示效果 ServiceInstance serviceInstance = client.getLocalServiceInstance(); logger.info("/hello host:"+serviceInstance.getHost()+" service_id:" +serviceInstance.getServiceId()); return "hello"; } /** * parameter test */ @GetMapping(value = "/greet/{dd}") public String greet(@PathVariable String dd){ ServiceInstance serviceInstance = client.getLocalServiceInstance(); logger.info("/hello host:"+serviceInstance.getHost()+" service_id:" +serviceInstance.getServiceId()); return "hello "+dd; } /** * 返回測試對象 */ @GetMapping("/user") public User getUser(){ ServiceInstance serviceInstance = client.getLocalServiceInstance(); logger.info("/user "+serviceInstance.getHost()+" port:"+serviceInstance.getPort()+" serviceInstanceid:"+serviceInstance.getServiceId()); return new User("hellxz","male", "123456789"); } /** * 根據(jù)名稱返回對象,這里模擬查數(shù)據(jù)庫操作 */ @GetMapping("/user/{name}") public User getUserSelect(@PathVariable String name){ ServiceInstance serviceInstance = client.getLocalServiceInstance(); logger.info("/user "+serviceInstance.getHost()+" port:"+serviceInstance.getPort()+" serviceInstanceid:"+serviceInstance.getServiceId()); if(name.isEmpty()){ return new User(); }else if(name.equals("hellxz")){ return new User("hellxz","male", "123456789"); }else{ return new User("隨機用戶","male", "987654321"); } } }
接下來我們在服務(wù)消費者項目中創(chuàng)建GetRequestController
package com.cnblogs.hellxz; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import java.net.URI; import java.util.HashMap; import java.util.Map; /** * @Author : Hellxz * @Description: ribbon消費者應(yīng)用Controller,get請求 * @Date : 2018/4/16 15:54 */ @RestController public class GetRequestController { private Logger logger = Logger.getLogger(GetRequestController.class); @Autowired //注入restTemplate private RestTemplate restTemplate; /** * ResponseEntity<T> getForEntity(String url, Class<T> responseType) * T getBody() 以下此方法相同 */ @GetMapping(value="/entity/noparam") public String noParamGetForEntity(){ //這里注釋掉,因為之前想當(dāng)然使用了直鏈訪問服務(wù)提供者的接口,這樣是不會返回結(jié)果的,而且會報錯 //return restTemplate.getForEntity("http://localhost:8080/hello",String.class).getBody(); //使用restTemplate調(diào)用微服務(wù)接口 return restTemplate.getForEntity("http://hello-service/hello", String.class).getBody(); } /** * ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) */ @GetMapping("/entity/type") public User getForEntityIdentifyByType(){ //不傳參返回指定類型結(jié)果 ResponseEntity<User> entity = restTemplate.getForEntity("http://hello-service/user", User.class); User body = entity.getBody(); logger.info("user:"+body); return body; //以上可簡寫為 // return restTemplate.getForEntity("http://hello-service/user", User.class).getBody(); } /** * ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) * 使用占位符對參數(shù)進(jìn)行替換,內(nèi)部使用String.format方法實現(xiàn) */ @GetMapping(value="/entity") //如果接收的參數(shù)是使用參數(shù)沒有使用?有則使用@PathVariable,否則用@RequestParam public String getForEntityByQuestionMarkParam(@RequestParam("name") String name){ //主要測試getEntity方法,這里測試直接傳參 return restTemplate.getForEntity("http://hello-service/greet/{1}", String.class, name).getBody(); } /** * getForEntity方法內(nèi)部會提取map中,以占位符為key的值作為參數(shù)回填入url中 * ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables) */ @GetMapping(value="/entity/map/{name}") //如果接收的參數(shù)是使用參數(shù)沒有使用?有則使用@PathVariable,否則用@RequestParam public String getForEntityByMap(@PathVariable("name") String name){ //主要測試getEntity方法,這里測試map傳參 Map<String, String> reqMap = new HashMap(); reqMap.put("name",name); return restTemplate.getForEntity("http://hello-service/greet/{name}", String.class,reqMap).getBody(); } /** * ResponseEntity<T> getForObject(URI url, Class<T> responseType) */ @GetMapping("/entity/uri") public String getForEntityByURI(){ //使用uri進(jìn)行傳參并訪問 UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://hello-service/greet/{name}").build().expand("laozhang").encode(); URI uri = uriComponents.toUri(); return restTemplate.getForEntity(uri, String.class).getBody(); } /** * T getForObject(String url, Class<T> responseType) */ @GetMapping("/object") public User getForObjectWithNoParam(){ //相比getForEntity方法,獲取對象可以省去調(diào)用getBody return restTemplate.getForObject("http://hello-service/user", User.class); } /** * T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) */ @GetMapping("/object/map") public User getForObjectByMap(){ //使用map傳參 Map<String, String> paramMap = new HashMap<>(); paramMap.put("name","hellxz"); return restTemplate.getForObject("http://hello-service/user", User.class, paramMap); } /** * T getForObject(String url, Class<T> responseType, Object... uriVariables) */ @GetMapping("/object/param/{name}") public User getForObjectByParam(@PathVariable String name){ return restTemplate.getForObject("http://hello-service/user/{name}",User.class, name); } /** * T getForObject(URI url, Class<T> responseType) */ @GetMapping("/object/uri/{name}") public User getForObjectByURI(@PathVariable String name){ UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://hello-service/user/{name}") .build().expand(name).encode(); URI uri = uriComponents.toUri(); return restTemplate.getForObject(uri,User.class); } }
先啟動注冊中心,然后通過訪問消費者對外提供的接口進(jìn)行測試,這些都是本人實際操作過的了,這里就不寫測試了
Post請求
post請求和get請求都有*ForEntity和*ForObject方法,其中參數(shù)列表有些不同,除了這兩個方法外,還有一個postForLocation方法,其中postForLocation以post請求提交資源,并返回新資源的URI
postForEntity:此方法有三種重載形式,分別為:
- postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
- postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
- postForEntity(URI url, Object request, Class<T> responseType)
注意:此方法返回的是一個包裝對象ResponseEntity<T>其中T為responseType傳入類型,想拿到返回類型需要使用這個包裝類對象的getBody()方法
postForObject:此方法也有三種重載形式,這點與postForEntity方法相同:
- postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
- postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
- postForObject(URI url, Object request, Class<T> responseType)
注意:此方法返回的對象類型為responseType傳入類型
postForLocation:此方法中同樣有三種重載形式,分別為:
- postForLocation(String url, Object request, Object... uriVariables)
- postForLocation(String url, Object request, Map<String, ?> uriVariables)
- postForLocation(URI url, Object request)
注意:此方法返回的是新資源的URI,相比getForEntity、getForObject、postForEntity、postForObject方法不同的是這個方法中無需指定返回類型,因為返回類型就是URI,通過Object... uriVariables、Map<String, ?> uriVariables進(jìn)行傳參依舊需要占位符,參看postForEntity部分代碼
按照之前的方式,我們分別在提供服務(wù)者和消費者的項目中分別創(chuàng)建PostRequestController
如下服務(wù)者PostRequestController代碼如下:
package com.shunneng.springcloudhelloworld; import org.apache.log4j.Logger; import org.springframework.web.bind.annotation.*; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import java.net.URI; /** * @Author : Hellxz * @Description: * @Date : 2018/4/18 10:21 */ @RestController public class PostRequestController { private Logger logger = Logger.getLogger(PostRequestController.class); /** * 接收一個對象再返回回去,postForEntity/postForObject方法通用 */ @PostMapping("/user") public User returnUserByPost(@RequestBody User user){ logger.info("/use接口 "+user); if(user == null) return new User("這是一個空對象","",""); return user; } /** * 測試PostForEntity方法的參數(shù),可以直接看輸出判斷結(jié)果了 */ @PostMapping("/user/{str}") public User returnUserByPost(@PathVariable String str, @RequestBody User user){ logger.info("/user/someparam 接口傳參 name:"+str +" "+user); if(user == null) return new User("這是一個空對象","",""); return user; } /** * 為postForLocation方法返回URI */ @PostMapping("/location") public URI returnURI(@RequestBody User user){ //這里模擬一個url,真實資源位置不一定是這里 UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://hello-service/location") .build().expand(user).encode(); URI toUri = uriComponents.toUri(); //這里不知道是什么問題,明明生成uri了,返回之后好像并沒有被獲取到 logger.info("/location uri:"+toUri); return toUri; } }
消費端PostRequestController代碼:
package com.cnblogs.hellxz; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import java.net.URI; /** * @Author : Hellxz * @Description: Ribbon消費者post請求controller * @Date : 2018/4/18 9:47 */ @RestController public class PostRequestController { private Logger logger = Logger.getLogger(PostRequestController.class); @Autowired private RestTemplate restTemplate; /** * ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType) * 其中參數(shù)url不多說,Object request如果是不是一個HttpEntity對象,會自動轉(zhuǎn)換為HttpEntity對象,視作完整的body來處理; * 如果是HttpEntity對象,那么會被直接當(dāng)做body處理并且包含header內(nèi)容。 * 以下對于重寫的方法就不多說了,使用方法大體同getForEntity,如果僅是簡單post對象,那么使用不帶Object...variables或Map variables的方法即可。 * postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables) * postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables) * * 這里詳細(xì)說下我遇到的坑: * 1、其他幾個重載方法的最后邊的Object...variables和Map variables都是對之前的url進(jìn)行操作的, * 也就是說,在post請求的url中使用占位符進(jìn)行傳參,而如果在url中沒有使用占位符,那么這些最后傳的參數(shù)是無效的! * 2、方法中Object request這個對象如果和服務(wù)提供者的接收參數(shù)類型相同,那么服務(wù)提供者僅需使用@RequestBody接收參數(shù)即可。 * 3、如果二者都使用了,這就比較有趣了,需要一邊通過@PathVariable注解接收uri中的參數(shù),一邊還需要@RequestBody接收對象或RequestParam按字段接收參數(shù)! * 4、如果報錯了,請仔細(xì)看看我上邊寫的三條,并注意服務(wù)提供者的參數(shù)接收注解的使用等。 */ @PostMapping("/entity") public User postForEntity(){ User user = new User("hellxz1","1","678912345"); ResponseEntity<User> entity = restTemplate.postForEntity("http://hello-service/user/{str}", user, User.class, "測試參數(shù)"); User body = entity.getBody(); //所有restTemplate.*ForEntity方法都是包裝類,body為返回類型對象 return body; } /** * 使用URI傳參,測試結(jié)果會顯示在服務(wù)提供者的終端中 * ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType) */ @PostMapping("/entity/uri") public User postForEntityByURI(){ User user = new User("老張","1","678912345"); //這里只是將url轉(zhuǎn)成URI,并沒有添加參數(shù) UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://hello-service/user") .build().encode(); URI toUri = uriComponents.toUri(); //使用user傳參 User object = restTemplate.postForObject(toUri, user, User.class); return object; } /** * 這里測試postForObject方法,需要注意的參數(shù)如上述方法的描述,區(qū)別只是不需要getBody了,這里就不再累述了 * postForObject(String url, Object request, Class<T> responseType, Object... uriVariables) * postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables) */ @PostMapping("/object") public User postForObject(){ User user = new User("hellxz2","1","123654987"); //這里url傳1是為了調(diào)用服務(wù)者項目中的一個接口 User responseBody = restTemplate.postForObject("http://hello-service/user/1", user, User.class); return responseBody; } /** * post請求還有一種:postForLocation,這里也同樣有三種重載,除了無需指定返回類型外,用法相同,返回類型均為URI,也就不累述了 * postForLocation(String url, Object request, Object... uriVariables) * postForLocation(String url, Object request, Map<String, ?> uriVariables) * postForLocation(URI url, Object request) */ @PostMapping("/location") public URI postForLocation(){ User user = new User("hellxz3","1","987654321"); URI uri = restTemplate.postForLocation("http://hello-service/location", user); //不知道為什么返回來是空,這個方法僅供參考吧,如果知道是什么情況,我會回來改的 logger.info("/location uri:"+uri); return uri; } }
Put請求&&Delete請求
put請求相對于get和post請求方法來的更為簡單,其中無需指定put請求的返回類型,當(dāng)然也沒有返回值,也是三種重載,和之前寫的基本一致,這里就不想多說了,delete請求和put請求都是沒有返回值的,這里再特地重復(fù)寫也沒什么意思,這里先分別列出這兩個請求的方法,代碼寫在一個類中了
put請求方法如下:
- put(String url, Object request, Object... uriVariables)
- put(String url, Object request, Map<String, ?> uriVariables)
- put(URI url, Object request)
delete請求方法如下:
- delete(String url, Object... uriVariables)
- delete(String url, Map<String, ?> uriVariables)
- delete(URI url)
在提供服務(wù)者項目中添加PutAndDeleteRequestController,代碼如下
package com.cnblogs.hellxz; import org.apache.log4j.Logger; import org.springframework.web.bind.annotation.*; /** * @Author : Hellxz * @Description: 服務(wù)提供者 put&delete請求controller * @Date : 2018/4/19 14:11 */ @RestController public class PutAndDeleteRequestController { private Logger logger = Logger.getLogger(PutAndDeleteRequestController.class); @PutMapping("/put") public void put(@RequestBody User user){ logger.info("/put "+user); } @DeleteMapping("/delete/{id}") public void delete(@PathVariable Long id){ logger.info("/delete id:"+id); } }
在提供服務(wù)者項目中添加PutAndDeleteRequestController,代碼如下
package com.cnblogs.hellxz; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; /** * @Author : Hellxz * @Description: put請求、delete請求,重載的參數(shù)與上述demo基本相同,不予列出 * @Date : 2018/4/19 13:43 */ @RestController public class PutRequestController { private Logger logger = Logger.getLogger(PostRequestController.class); @Autowired private RestTemplate restTemplate; /** * put請求示例,一般put請求多用作修改 */ @PutMapping("/put") public void put(@RequestBody User user){ restTemplate.put("http://hello-service/put",user); } /** * delete請求示例 */ @DeleteMapping("/del/{id}") public void delete(@PathVariable Long id){ restTemplate.delete("http://hello-service/delete/{1}", id); } }
結(jié)語
這篇博文使用markdown寫成,第一次寫不知道如何將代碼塊中加入序號以及折疊代碼功能,這可能不是一篇好文章,但是寫這篇博文寫了快兩天,有什么好的建議歡迎評論交流,
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Spring Boot實現(xiàn)郵件服務(wù)(附:常見郵箱的配置)
這篇文章主要給大家介紹了關(guān)于Spring Boot實現(xiàn)郵件服務(wù)的相關(guān)資料,文中還附上了常見郵箱的配置,通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-12-12全面解析JPA?倉庫repository中的findAll()方法
這篇文章主要介紹了全面解析JPA?倉庫repository中的findAll()方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02Spring中的Eureka服務(wù)過期詳細(xì)解析
這篇文章主要介紹了Spring中的Eureka服務(wù)過期詳細(xì)解析,如果有一些服務(wù)過期了,或者宕機了,就不會調(diào)用shutdown()方法,也不會去發(fā)送請求下線服務(wù)實例,需要的朋友可以參考下2023-11-11淺談web服務(wù)器項目中request請求和response的相關(guān)響應(yīng)處理
這篇文章主要介紹了淺談web服務(wù)器項目中request請求和response的相關(guān)響應(yīng)處理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07