查看: 269|回复: 0

[Java代码] 详解Spring cloud使用Ribbon进行Restful请求

发表于 2018-5-4 08:00:02

写在前面

本文由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中添加

  1. <build>
  2. <plugins>
  3. <plugin>
  4. <groupId>org.springframework.boot</groupId>
  5. <artifactId>spring-boot-maven-plugin</artifactId>
  6. </plugin>
  7. <plugin>
  8. <groupId>org.apache.maven.plugins</groupId>
  9. <artifactId>maven-compiler-plugin</artifactId>
  10. <configuration>
  11. <source>1.8</source>
  12. <target>1.8</target>
  13. </configuration>
  14. </plugin>
  15. </plugins>
  16. </build>
复制代码

测试项目构建

Eureka注册中心:参考注册中心的搭建
服务提供者:参考注册服务提供者
ribbon消费者:参考服务发现与消费

项目搭建完后,记得按照这几个教程中提到的配置hosts文件

为了防止项目中的RequestMapping相同,这里就删除所有的controller类(服务提供者和消费者),接下来我会将每个restful方法都封装成一个类,方便大家查看

Get请求

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

  1. getForEntity(String url, Class responseType)
  2. getForEntity(String url, Class responseType, Object... uriVariables)
  3. getForEntity(String url, Class responseType, Map uriVariables)
  4. getForEntity(URI url, Class responseType)

注意:此方法返回的是一个包装对象ResponseEntity其中T为responseType传入类型,想拿到返回类型需要使用这个包装类对象的getBody()方法

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

  1. getForObject(String url, Class responseType)
  2. getForObject(String url, Class responseType, Object... uriVariables)
  3. getForObject(String url, Class responseType, Map uriVariables)
  4. getForObject(URI url, Class responseType)

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

为了方便测试,这里分别在服务提供者和服务消费者中提供相同的User类,用于方便测试

  1. package com.cnblogs.hellxz;
  2. /**
  3. * 用于测试的pojo
  4. */
  5. public class User {
  6. private String name;
  7. private String sex;
  8. private String phone;
  9. public User(){}
  10. public User(String name, String sex, String phone) {
  11. this.name = name;
  12. this.sex = sex;
  13. this.phone = phone;
  14. }
  15. public String toString(){
  16. return "user:{"
  17. +"name: " + name + ", "
  18. +"sex: " + sex + ", "
  19. +"phone: " + phone
  20. +" }";
  21. }
  22. public String getName() {
  23. return name;
  24. }
  25. public void setName(String name) {
  26. this.name = name;
  27. }
  28. public String getSex() {
  29. return sex;
  30. }
  31. public void setSex(String sex) {
  32. this.sex = sex;
  33. }
  34. public String getPhone() {
  35. return phone;
  36. }
  37. public void setPhone(String phone) {
  38. this.phone = phone;
  39. }
  40. }
复制代码

下边我们在服务提供者处创建一个GetRequestController

  1. package com.cnblogs.hellxz;
  2. import org.apache.log4j.Logger;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.cloud.client.ServiceInstance;
  5. import org.springframework.cloud.client.discovery.DiscoveryClient;
  6. import org.springframework.web.bind.annotation.*;
  7. /**
  8. * @Author : Hellxz
  9. * @Description: 服务提供者
  10. * @Date : 2018/4/18 11:36
  11. */
  12. @RestController
  13. public class GetRequestController {
  14. @Autowired
  15. private DiscoveryClient client; //注入发现客户端
  16. private final Logger logger = Logger.getLogger(GetRequestController.class);
  17. /**
  18. * go straight test
  19. */
  20. @GetMapping(value = "/hello")
  21. public String hello(){
  22. //获取服务实例,作用为之后console显示效果
  23. ServiceInstance serviceInstance = client.getLocalServiceInstance();
  24. logger.info("/hello host:"+serviceInstance.getHost()+" service_id:" +serviceInstance.getServiceId());
  25. return "hello";
  26. }
  27. /**
  28. * parameter test
  29. */
  30. @GetMapping(value = "/greet/{dd}")
  31. public String greet(@PathVariable String dd){
  32. ServiceInstance serviceInstance = client.getLocalServiceInstance();
  33. logger.info("/hello host:"+serviceInstance.getHost()+" service_id:" +serviceInstance.getServiceId());
  34. return "hello "+dd;
  35. }
  36. /**
  37. * 返回测试对象
  38. */
  39. @GetMapping("/user")
  40. public User getUser(){
  41. ServiceInstance serviceInstance = client.getLocalServiceInstance();
  42. logger.info("/user "+serviceInstance.getHost()+" port:"+serviceInstance.getPort()+" serviceInstanceid:"+serviceInstance.getServiceId());
  43. return new User("hellxz","male", "123456789");
  44. }
  45. /**
  46. * 根据名称返回对象,这里模拟查数据库操作
  47. */
  48. @GetMapping("/user/{name}")
  49. public User getUserSelect(@PathVariable String name){
  50. ServiceInstance serviceInstance = client.getLocalServiceInstance();
  51. logger.info("/user "+serviceInstance.getHost()+" port:"+serviceInstance.getPort()+" serviceInstanceid:"+serviceInstance.getServiceId());
  52. if(name.isEmpty()){
  53. return new User();
  54. }else if(name.equals("hellxz")){
  55. return new User("hellxz","male", "123456789");
  56. }else{
  57. return new User("随机用户","male", "987654321");
  58. }
  59. }
  60. }
复制代码

接下来我们在服务消费者项目中创建GetRequestController

  1. package com.cnblogs.hellxz;
  2. import org.apache.log4j.Logger;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.http.ResponseEntity;
  5. import org.springframework.web.bind.annotation.*;
  6. import org.springframework.web.client.RestTemplate;
  7. import org.springframework.web.util.UriComponents;
  8. import org.springframework.web.util.UriComponentsBuilder;
  9. import java.net.URI;
  10. import java.util.HashMap;
  11. import java.util.Map;
  12. /**
  13. * @Author : Hellxz
  14. * @Description: ribbon消费者应用Controller,get请求
  15. * @Date : 2018/4/16 15:54
  16. */
  17. @RestController
  18. public class GetRequestController {
  19. private Logger logger = Logger.getLogger(GetRequestController.class);
  20. @Autowired
  21. //注入restTemplate
  22. private RestTemplate restTemplate;
  23. /**
  24. * ResponseEntity<T> getForEntity(String url, Class<T> responseType)
  25. * T getBody() 以下此方法相同
  26. */
  27. @GetMapping(value="/entity/noparam")
  28. public String noParamGetForEntity(){
  29. //这里注释掉,因为之前想当然使用了直链访问服务提供者的接口,这样是不会返回结果的,而且会报错
  30. //return restTemplate.getForEntity("http://localhost:8080/hello",String.class).getBody();
  31. //使用restTemplate调用微服务接口
  32. return restTemplate.getForEntity("http://hello-service/hello", String.class).getBody();
  33. }
  34. /**
  35. * ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
  36. */
  37. @GetMapping("/entity/type")
  38. public User getForEntityIdentifyByType(){
  39. //不传参返回指定类型结果
  40. ResponseEntity<User> entity = restTemplate.getForEntity("http://hello-service/user", User.class);
  41. User body = entity.getBody();
  42. logger.info("user:"+body);
  43. return body;
  44. //以上可简写为
  45. // return restTemplate.getForEntity("http://hello-service/user", User.class).getBody();
  46. }
  47. /**
  48. * ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
  49. * 使用占位符对参数进行替换,内部使用String.format方法实现
  50. */
  51. @GetMapping(value="/entity")
  52. //如果接收的参数是使用参数没有使用?有则使用@PathVariable,否则用@RequestParam
  53. public String getForEntityByQuestionMarkParam(@RequestParam("name") String name){
  54. //主要测试getEntity方法,这里测试直接传参
  55. return restTemplate.getForEntity("http://hello-service/greet/小贝", String.class, name).getBody();
  56. }
  57. /**
  58. * getForEntity方法内部会提取map中,以占位符为key的值作为参数回填入url中
  59. * ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables)
  60. */
  61. @GetMapping(value="/entity/map/{name}")
  62. //如果接收的参数是使用参数没有使用?有则使用@PathVariable,否则用@RequestParam
  63. public String getForEntityByMap(@PathVariable("name") String name){
  64. //主要测试getEntity方法,这里测试map传参
  65. Map<String, String> reqMap = new HashMap();
  66. reqMap.put("name",name);
  67. return restTemplate.getForEntity("http://hello-service/greet/{name}", String.class,reqMap).getBody();
  68. }
  69. /**
  70. * ResponseEntity<T> getForObject(URI url, Class<T> responseType)
  71. */
  72. @GetMapping("/entity/uri")
  73. public String getForEntityByURI(){
  74. //使用uri进行传参并访问
  75. UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://hello-service/greet/{name}").build().expand("laozhang").encode();
  76. URI uri = uriComponents.toUri();
  77. return restTemplate.getForEntity(uri, String.class).getBody();
  78. }
  79. /**
  80. * T getForObject(String url, Class<T> responseType)
  81. */
  82. @GetMapping("/object")
  83. public User getForObjectWithNoParam(){
  84. //相比getForEntity方法,获取对象可以省去调用getBody
  85. return restTemplate.getForObject("http://hello-service/user", User.class);
  86. }
  87. /**
  88. * T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)
  89. */
  90. @GetMapping("/object/map")
  91. public User getForObjectByMap(){
  92. //使用map传参
  93. Map<String, String> paramMap = new HashMap<>();
  94. paramMap.put("name","hellxz");
  95. return restTemplate.getForObject("http://hello-service/user", User.class, paramMap);
  96. }
  97. /**
  98. * T getForObject(String url, Class<T> responseType, Object... uriVariables)
  99. */
  100. @GetMapping("/object/param/{name}")
  101. public User getForObjectByParam(@PathVariable String name){
  102. return restTemplate.getForObject("http://hello-service/user/{name}",User.class, name);
  103. }
  104. /**
  105. * T getForObject(URI url, Class<T> responseType)
  106. */
  107. @GetMapping("/object/uri/{name}")
  108. public User getForObjectByURI(@PathVariable String name){
  109. UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://hello-service/user/{name}")
  110. .build().expand(name).encode();
  111. URI uri = uriComponents.toUri();
  112. return restTemplate.getForObject(uri,User.class);
  113. }
  114. }
复制代码

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

Post请求

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

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

  1. postForEntity(String url, Object request, Class responseType, Object... uriVariables)
  2. postForEntity(String url, Object request, Class responseType, Map uriVariables)
  3. postForEntity(URI url, Object request, Class responseType)

注意:此方法返回的是一个包装对象ResponseEntity其中T为responseType传入类型,想拿到返回类型需要使用这个包装类对象的getBody()方法

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

  1. postForObject(String url, Object request, Class responseType, Object... uriVariables)
  2. postForObject(String url, Object request, Class responseType, Map uriVariables)
  3. postForObject(URI url, Object request, Class responseType)

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

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

  1. postForLocation(String url, Object request, Object... uriVariables)
  2. postForLocation(String url, Object request, Map uriVariables)
  3. postForLocation(URI url, Object request)

注意:此方法返回的是新资源的URI,相比getForEntity、getForObject、postForEntity、postForObject方法不同的是这个方法中无需指定返回类型,因为返回类型就是URI,通过Object... uriVariables、Map uriVariables进行传参依旧需要占位符,参看postForEntity部分代码

按照之前的方式,我们分别在提供服务者和消费者的项目中分别创建PostRequestController

如下服务者PostRequestController代码如下:

  1. package com.shunneng.springcloudhelloworld;
  2. import org.apache.log4j.Logger;
  3. import org.springframework.web.bind.annotation.*;
  4. import org.springframework.web.util.UriComponents;
  5. import org.springframework.web.util.UriComponentsBuilder;
  6. import java.net.URI;
  7. /**
  8. * @Author : Hellxz
  9. * @Description:
  10. * @Date : 2018/4/18 10:21
  11. */
  12. @RestController
  13. public class PostRequestController {
  14. private Logger logger = Logger.getLogger(PostRequestController.class);
  15. /**
  16. * 接收一个对象再返回回去,postForEntity/postForObject方法通用
  17. */
  18. @PostMapping("/user")
  19. public User returnUserByPost(@RequestBody User user){
  20. logger.info("/use接口 "+user);
  21. if(user == null) return new User("这是一个空对象","","");
  22. return user;
  23. }
  24. /**
  25. * 测试PostForEntity方法的参数,可以直接看输出判断结果了
  26. */
  27. @PostMapping("/user/{str}")
  28. public User returnUserByPost(@PathVariable String str, @RequestBody User user){
  29. logger.info("/user/someparam 接口传参 name:"+str +" "+user);
  30. if(user == null) return new User("这是一个空对象","","");
  31. return user;
  32. }
  33. /**
  34. * 为postForLocation方法返回URI
  35. */
  36. @PostMapping("/location")
  37. public URI returnURI(@RequestBody User user){
  38. //这里模拟一个url,真实资源位置不一定是这里
  39. UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://hello-service/location")
  40. .build().expand(user).encode();
  41. URI toUri = uriComponents.toUri();
  42. //这里不知道是什么问题,明明生成uri了,返回之后好像并没有被获取到
  43. logger.info("/location uri:"+toUri);
  44. return toUri;
  45. }
  46. }
复制代码

消费端PostRequestController代码:

  1. package com.cnblogs.hellxz;
  2. import org.apache.log4j.Logger;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.http.ResponseEntity;
  5. import org.springframework.web.bind.annotation.PostMapping;
  6. import org.springframework.web.bind.annotation.RestController;
  7. import org.springframework.web.client.RestTemplate;
  8. import org.springframework.web.util.UriComponents;
  9. import org.springframework.web.util.UriComponentsBuilder;
  10. import java.net.URI;
  11. /**
  12. * @Author : Hellxz
  13. * @Description: Ribbon消费者post请求controller
  14. * @Date : 2018/4/18 9:47
  15. */
  16. @RestController
  17. public class PostRequestController {
  18. private Logger logger = Logger.getLogger(PostRequestController.class);
  19. @Autowired
  20. private RestTemplate restTemplate;
  21. /**
  22. * ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType)
  23. * 其中参数url不多说,Object request如果是不是一个HttpEntity对象,会自动转换为HttpEntity对象,视作完整的body来处理;
  24. * 如果是HttpEntity对象,那么会被直接当做body处理并且包含header内容。
  25. * 以下对于重写的方法就不多说了,使用方法大体同getForEntity,如果仅是简单post对象,那么使用不带Object...variables或Map variables的方法即可。
  26. * postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
  27. * postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
  28. *
  29. * 这里详细说下我遇到的坑:
  30. * 1、其他几个重载方法的最后边的Object...variables和Map variables都是对之前的url进行操作的,
  31. * 也就是说,在post请求的url中使用占位符进行传参,而如果在url中没有使用占位符,那么这些最后传的参数是无效的!
  32. * 2、方法中Object request这个对象如果和服务提供者的接收参数类型相同,那么服务提供者仅需使用@RequestBody接收参数即可。
  33. * 3、如果二者都使用了,这就比较有趣了,需要一边通过@PathVariable注解接收uri中的参数,一边还需要@RequestBody接收对象或RequestParam按字段接收参数!
  34. * 4、如果报错了,请仔细看看我上边写的三条,并注意服务提供者的参数接收注解的使用等。
  35. */
  36. @PostMapping("/entity")
  37. public User postForEntity(){
  38. User user = new User("hellxz1","1","678912345");
  39. ResponseEntity<User> entity = restTemplate.postForEntity("http://hello-service/user/{str}", user, User.class, "测试参数");
  40. User body = entity.getBody(); //所有restTemplate.*ForEntity方法都是包装类,body为返回类型对象
  41. return body;
  42. }
  43. /**
  44. * 使用URI传参,测试结果会显示在服务提供者的终端中
  45. * ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType)
  46. */
  47. @PostMapping("/entity/uri")
  48. public User postForEntityByURI(){
  49. User user = new User("老张","1","678912345");
  50. //这里只是将url转成URI,并没有添加参数
  51. UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://hello-service/user")
  52. .build().encode();
  53. URI toUri = uriComponents.toUri();
  54. //使用user传参
  55. User object = restTemplate.postForObject(toUri, user, User.class);
  56. return object;
  57. }
  58. /**
  59. * 这里测试postForObject方法,需要注意的参数如上述方法的描述,区别只是不需要getBody了,这里就不再累述了
  60. * postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
  61. * postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
  62. */
  63. @PostMapping("/object")
  64. public User postForObject(){
  65. User user = new User("hellxz2","1","123654987");
  66. //这里url传1是为了调用服务者项目中的一个接口
  67. User responseBody = restTemplate.postForObject("http://hello-service/user/1", user, User.class);
  68. return responseBody;
  69. }
  70. /**
  71. * post请求还有一种:postForLocation,这里也同样有三种重载,除了无需指定返回类型外,用法相同,返回类型均为URI,也就不累述了
  72. * postForLocation(String url, Object request, Object... uriVariables)
  73. * postForLocation(String url, Object request, Map<String, ?> uriVariables)
  74. * postForLocation(URI url, Object request)
  75. */
  76. @PostMapping("/location")
  77. public URI postForLocation(){
  78. User user = new User("hellxz3","1","987654321");
  79. URI uri = restTemplate.postForLocation("http://hello-service/location", user);
  80. //不知道为什么返回来是空,这个方法仅供参考吧,如果知道是什么情况,我会回来改的
  81. logger.info("/location uri:"+uri);
  82. return uri;
  83. }
  84. }
复制代码

Put请求&&Delete请求

put请求相对于get和post请求方法来的更为简单,其中无需指定put请求的返回类型,当然也没有返回值,也是三种重载,和之前写的基本一致,这里就不想多说了,delete请求和put请求都是没有返回值的,这里再特地重复写也没什么意思,这里先分别列出这两个请求的方法,代码写在一个类中了

put请求方法如下:

  1. put(String url, Object request, Object... uriVariables)
  2. put(String url, Object request, Map uriVariables)
  3. put(URI url, Object request)

delete请求方法如下:

  1. delete(String url, Object... uriVariables)
  2. delete(String url, Map uriVariables)
  3. delete(URI url)

在提供服务者项目中添加PutAndDeleteRequestController,代码如下

  1. package com.cnblogs.hellxz;
  2. import org.apache.log4j.Logger;
  3. import org.springframework.web.bind.annotation.*;
  4. /**
  5. * @Author : Hellxz
  6. * @Description: 服务提供者 put&delete请求controller
  7. * @Date : 2018/4/19 14:11
  8. */
  9. @RestController
  10. public class PutAndDeleteRequestController {
  11. private Logger logger = Logger.getLogger(PutAndDeleteRequestController.class);
  12. @PutMapping("/put")
  13. public void put(@RequestBody User user){
  14. logger.info("/put "+user);
  15. }
  16. @DeleteMapping("/delete/{id}")
  17. public void delete(@PathVariable Long id){
  18. logger.info("/delete id:"+id);
  19. }
  20. }
复制代码

在提供服务者项目中添加PutAndDeleteRequestController,代码如下

  1. package com.cnblogs.hellxz;
  2. import org.apache.log4j.Logger;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.web.bind.annotation.*;
  5. import org.springframework.web.client.RestTemplate;
  6. /**
  7. * @Author : Hellxz
  8. * @Description: put请求、delete请求,重载的参数与上述demo基本相同,不予列出
  9. * @Date : 2018/4/19 13:43
  10. */
  11. @RestController
  12. public class PutRequestController {
  13. private Logger logger = Logger.getLogger(PostRequestController.class);
  14. @Autowired
  15. private RestTemplate restTemplate;
  16. /**
  17. * put请求示例,一般put请求多用作修改
  18. */
  19. @PutMapping("/put")
  20. public void put(@RequestBody User user){
  21. restTemplate.put("http://hello-service/put",user);
  22. }
  23. /**
  24. * delete请求示例
  25. */
  26. @DeleteMapping("/del/{id}")
  27. public void delete(@PathVariable Long id){
  28. restTemplate.delete("http://hello-service/delete/小贝", id);
  29. }
  30. }
复制代码

结语

这篇博文使用markdown写成,第一次写不知道如何将代码块中加入序号以及折叠代码功能,这可能不是一篇好文章,但是写这篇博文写了快两天,有什么好的建议欢迎评论交流,

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



回复

使用道具 举报