寫在前面
本文由markdown格式寫成,為本人第一次這麼寫,排版可能會有點亂,還望各位海涵。
主要寫的是使用Ribbon進行Restful請求,測試各個方法的使用,代碼冗餘較高,比較適合初學者,介意輕噴謝謝。
前提
注意:文中使用@GetMapping、@PostMapping、@PutMapping、@DeleteMapping等註解需要升級spring-boot-starter-parent版本到1.5.9.REALEASE以上(1.3.7.RELEASE版本沒有這些註解)
建議:每個微服務應用都有自己的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>
測試項目構建
Eureka註冊中心:參考註冊中心的搭建服務提供者:參考註冊服務提供者
ribbon消費者:參考服務發現與消費
項目搭建完後,記得按照這幾個教程中提到的配置hosts文件
為了防止項目中的RequestMapping相同,這裡就刪除所有的controller類(服務提供者和消費者),接下來我會將每個restful方法都封裝成一個類,方便大家查看
Get請求
getForEntity:此方法有三種重載形式,分別為:
注意:此方法返回的是一個包裝對象ResponseEntity<T>其中T為responseType傳入類型,想拿到返回類型需要使用這個包裝類對象的getBody()方法
getForObject:此方法也有三種重載形式,這點與getForEntity方法相同:
注意:此方法返回的對像類型為responseType傳入類型
為了方便測試,這里分別在服務提供者和服務消費者中提供相同的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; }}下邊我們在服務提供者處創建一個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: 服務提供者* @Date : 2018/4/18 11:36 */@RestControllerpublic class GetRequestController { @Autowired private DiscoveryClient client; //注入發現客戶端private final Logger logger = Logger.getLogger(GetRequestController.class); /** * go straight test */ @GetMapping(value = "/hello") public String hello(){ //獲取服務實例,作用為之後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"); } /** * 根據名稱返回對象,這裡模擬查數據庫操作*/ @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"); } }}接下來我們在服務消費者項目中創建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消費者應用Controller,get請求* @Date : 2018/4/16 15:54 */@RestControllerpublic 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(){ //這裡註釋掉,因為之前想當然使用了直鏈訪問服務提供者的接口,這樣是不會返回結果的,而且會報錯//return restTemplate.getForEntity("http://localhost:8080/hello",String.class).getBody(); //使用restTemplate調用微服務接口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(){ //不傳參返回指定類型結果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) * 使用佔位符對參數進行替換,內部使用String.format方法實現*/ @GetMapping(value="/entity") //如果接收的參數是使用參數沒有使用?有則使用@PathVariable,否則用@RequestParam public String getForEntityByQuestionMarkParam(@RequestParam("name") String name){ //主要測試getEntity方法,這裡測試直接傳參return restTemplate.getForEntity("http://hello-service/greet/{1}", String.class, name).getBody(); } /** * getForEntity方法內部會提取map中,以佔位符為key的值作為參數回填入url中* ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables) */ @GetMapping(value="/entity/map/{name}") //如果接收的參數是使用參數沒有使用?有則使用@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進行傳參並訪問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方法,獲取對象可以省去調用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); }}先啟動註冊中心,然後通過訪問消費者對外提供的接口進行測試,這些都是本人實際操作過的了,這裡就不寫測試了
Post請求
post請求和get請求都有*ForEntity和*ForObject方法,其中參數列表有些不同,除了這兩個方法外,還有一個postForLocation方法,其中postForLocation以post請求提交資源,並返回新資源的URI
postForEntity:此方法有三種重載形式,分別為:
注意:此方法返回的是一個包裝對象ResponseEntity<T>其中T為responseType傳入類型,想拿到返回類型需要使用這個包裝類對象的getBody()方法
postForObject:此方法也有三種重載形式,這點與postForEntity方法相同:
注意:此方法返回的對像類型為responseType傳入類型
postForLocation:此方法中同樣有三種重載形式,分別為:
注意:此方法返回的是新資源的URI,相比getForEntity、getForObject、postForEntity、postForObject方法不同的是這個方法中無需指定返回類型,因為返回類型就是URI,通過Object... uriVariables、Map<String, ?> uriVariables進行傳參依舊需要佔位符,參看postForEntity部分代碼
按照之前的方式,我們分別在提供服務者和消費者的項目中分別創建PostRequestController
如下服務者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 */@RestControllerpublic 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方法的參數,可以直接看輸出判斷結果了*/ @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 */@RestControllerpublic class PostRequestController { private Logger logger = Logger.getLogger(PostRequestController.class); @Autowired private RestTemplate restTemplate; /** * ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType) * 其中參數url不多說,Object request如果是不是一個HttpEntity對象,會自動轉換為HttpEntity對象,視作完整的body來處理; * 如果是HttpEntity對象,那麼會被直接當做body處理並且包含header內容。 * 以下對於重寫的方法就不多說了,使用方法大體同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) * * 這裡詳細說下我遇到的坑: * 1、其他幾個重載方法的最後邊的Object...variables和Map variables都是對之前的url進行操作的, * 也就是說,在post請求的url中使用佔位符進行傳參,而如果在url中沒有使用佔位符,那麼這些最後傳的參數是無效的! * 2、方法中Object request這個對像如果和服務提供者的接收參數類型相同,那麼服務提供者僅需使用@RequestBody接收參數即可。 * 3、如果二者都使用了,這就比較有趣了,需要一邊通過@PathVariable註解接收uri中的參數,一邊還需要@RequestBody接收對像或RequestParam按字段接收參數! * 4、如果報錯了,請仔細看看我上邊寫的三條,並註意服務提供者的參數接收註解的使用等。 */ @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, "測試參數"); User body = entity.getBody(); //所有restTemplate.*ForEntity方法都是包裝類,body為返回類型對象return body; } /** * 使用URI傳參,測試結果會顯示在服務提供者的終端中* ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType) */ @PostMapping("/entity/uri") public User postForEntityByURI(){ User user = new User("老張","1","678912345"); //這裡只是將url轉成URI,並沒有添加參數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方法,需要注意的參數如上述方法的描述,區別只是不需要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是為了調用服務者項目中的一個接口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請求的返回類型,當然也沒有返回值,也是三種重載,和之前寫的基本一致,這裡就不想多說了,delete請求和put請求都是沒有返回值的,這裡再特地重複寫也沒什麼意思,這裡先分別列出這兩個請求的方法,代碼寫在一個類中了
put請求方法如下:
delete請求方法如下:
在提供服務者項目中添加PutAndDeleteRequestController,代碼如下
package com.cnblogs.hellxz;import org.apache.log4j.Logger;import org.springframework.web.bind.annotation.*;/** * @Author : Hellxz * @Description: 服務提供者put&delete請求controller * @Date : 2018/4/19 14:11 */@RestControllerpublic 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); }}在提供服務者項目中添加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請求,重載的參數與上述demo基本相同,不予列出* @Date : 2018/4/19 13:43 */@RestControllerpublic 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); }}結語
這篇博文使用markdown寫成,第一次寫不知道如何將代碼塊中加入序號以及折疊代碼功能,這可能不是一篇好文章,但是寫這篇博文寫了快兩天,有什麼好的建議歡迎評論交流,
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。