欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

详解Spring cloud使用Ribbon进行Restful请求

程序员文章站 2023-11-24 09:31:58
写在前面 本文由markdown格式写成,为本人第一次这么写,排版可能会有点乱,还望各位海涵。  主要写的是使用ribbon进行restful请求,测试各个方...

写在前面

本文由markdown格式写成,为本人第一次这么写,排版可能会有点乱,还望各位海涵。
 主要写的是使用ribbon进行restful请求,测试各个方法的使用,代码冗余较高,比较适合初学者,介意轻喷谢谢。

前提

  1. 一个可用的eureka注册中心(文中以之前博客中双节点注册中心,不重要)
  2. 一个连接到这个注册中心的服务提供者
  3. 一个ribbon的消费者

注意:文中使用@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:此方法有三种重载形式,分别为:

  1. getforentity(string url, class<t> responsetype)
  2. getforentity(string url, class<t> responsetype, object... urivariables)
  3. getforentity(string url, class<t> responsetype, map<string, ?> urivariables)
  4. getforentity(uri url, class<t> responsetype)

注意:此方法返回的是一个包装对象responseentity<t>其中t为responsetype传入类型,想拿到返回类型需要使用这个包装类对象的getbody()方法

getforobject:此方法也有三种重载形式,这点与getforentity方法相同:

  1. getforobject(string url, class<t> responsetype)
  2. getforobject(string url, class<t> responsetype, object... urivariables)
  3. getforobject(string url, class<t> responsetype, map<string, ?> urivariables)
  4. 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;
  }
}

下边我们在服务提供者处创建一个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
 */
@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");
    }
  }
}

接下来我们在服务消费者项目中创建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
 */
@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);
  }
}

先启动注册中心,然后通过访问消费者对外提供的接口进行测试,这些都是本人实际操作过的了,这里就不写测试了

post请求

post请求和get请求都有*forentity和*forobject方法,其中参数列表有些不同,除了这两个方法外,还有一个postforlocation方法,其中postforlocation以post请求提交资源,并返回新资源的uri

postforentity:此方法有三种重载形式,分别为:

  1. postforentity(string url, object request, class<t> responsetype, object... urivariables)
  2. postforentity(string url, object request, class<t> responsetype, map<string, ?> urivariables)
  3. postforentity(uri url, object request, class<t> responsetype)

注意:此方法返回的是一个包装对象responseentity<t>其中t为responsetype传入类型,想拿到返回类型需要使用这个包装类对象的getbody()方法

postforobject:此方法也有三种重载形式,这点与postforentity方法相同:

  1. postforobject(string url, object request, class<t> responsetype, object... urivariables)
  2. postforobject(string url, object request, class<t> responsetype, map<string, ?> urivariables)
  3. postforobject(uri url, object request, class<t> responsetype)

注意:此方法返回的对象类型为responsetype传入类型

postforlocation:此方法中同样有三种重载形式,分别为:

  1. postforlocation(string url, object request, object... urivariables)
  2. postforlocation(string url, object request, map<string, ?> urivariables)
  3. postforlocation(uri url, object request)

注意:此方法返回的是新资源的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
 */
@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)
   *
   * 这里详细说下我遇到的坑:
   * 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请求方法如下:

  1. put(string url, object request, object... urivariables)
  2. put(string url, object request, map<string, ?> urivariables)
  3. put(uri url, object request)

delete请求方法如下:

  1. delete(string url, object... urivariables)
  2. delete(string url, map<string, ?> urivariables)
  3. 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写成,第一次写不知道如何将代码块中加入序号以及折叠代码功能,这可能不是一篇好文章,但是写这篇博文写了快两天,有什么好的建议欢迎评论交流,

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。