SpringCloud(五) 使用Ribbon進行Restful請求

寫在前面

本文由markdown格式寫成,爲本人第一次這麼寫,排版可能會有點亂,還望各位海涵。
主要寫的是使用Ribbon進行Restful請求,測試各個方法的使用,代碼冗餘較高,比較適合初學者,介意輕噴謝謝。html

前提

  • 一個可用的Eureka註冊中心(文中以以前博客中雙節點註冊中心,不重要)
  • 一個鏈接到這個註冊中心的服務提供者
  • 一個ribbon的消費者
注意:文中使用@GetMapping@PostMapping@PutMapping@DeleteMapping等註解須要升級 spring-boot-starter-parent版本到1.5.9.REALEASE以上(1.3.7.RELEASE版本沒有這些註解)
建議:每一個微服務應用都有本身的spring-boot-maven-pluginmaven-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消費者:參考服務發現與消費java

項目搭建完後,記得按照這幾個教程中提到的配置hosts文件web

爲了防止項目中的RequestMapping相同,這裏就刪除全部的controller類(服務提供者和消費者),接下來我會將每一個restful方法都封裝成一個類,方便你們查看spring

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>其中TresponseType傳入類型,想拿到返回類型須要使用這個包裝類對象的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傳入類型

爲了方便測試,這裏分別在服務提供者和服務消費者中提供相同的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;
    }
}

下邊咱們在服務提供者處建立一個GetRequestControllerapache

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
 */
@RestController
public 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");
        }
    }
}

接下來咱們在服務消費者項目中建立GetRequestControllerrestful

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
 */
@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(){
        //這裏註釋掉,由於以前想固然使用了直鏈訪問服務提供者的接口,這樣是不會返回結果的,並且會報錯
        //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);
    }
}

先啓動註冊中心,而後經過訪問消費者對外提供的接口進行測試,這些都是本人實際操做過的了,這裏就不寫測試了markdown

Post請求

post請求和get請求都有*ForEntity*ForObject方法,其中參數列表有些不一樣,除了這兩個方法外,還有一個postForLocation方法,其中postForLocation以post請求提交資源,並返回新資源的URIapp

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)maven

    注意:此方法返回的是一個包裝對象ResponseEntity<T>其中TresponseType傳入類型,想拿到返回類型須要使用這個包裝類對象的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,相比getForEntitygetForObjectpostForEntitypostForObject方法不一樣的是這個方法中無需指定返回類型,由於返回類型就是URI,經過Object... uriVariablesMap<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
 */
@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方法的參數,能夠直接看輸出判斷結果了
     */
    @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)
     * 其中參數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)
     *
     * 這裏詳細說下我遇到的坑:
     * 一、其餘幾個重載方法的最後邊的Object...variables和Map variables都是對以前的url進行操做的,
     *     也就是說,在post請求的url中使用佔位符進行傳參,而若是在url中沒有使用佔位符,那麼這些最後傳的參數是無效的!
     * 二、方法中Object request這個對象若是和服務提供者的接收參數類型相同,那麼服務提供者僅需使用@RequestBody接收參數便可。
     * 三、若是兩者都使用了,這就比較有趣了,須要一邊經過@PathVariable註解接收uri中的參數,一邊還須要@RequestBody接收對象或RequestParam按字段接收參數!
     * 四、若是報錯了,請仔細看看我上邊寫的三條,並注意服務提供者的參數接收註解的使用等。
     */
    @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請求方法以下:

  • 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)

在提供服務者項目中添加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
 */
@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);
    }
}

在提供服務者項目中添加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
 */
@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);
    }
}

結語

這篇博文使用markdown寫成,第一次寫不知道如何將代碼塊中加入序號以及摺疊代碼功能,這可能不是一篇好文章,可是寫這篇博文寫了快兩天,有什麼好的建議歡迎評論交流,啥也不說了,若是本文對你有幫助,請幫忙點個推薦,加個關注吧!

聲明:本博客無需許可也能夠轉載,但煩請註明出處

相關文章
相關標籤/搜索