详解Spring cloud使用Ribbon进行Restful请求
写在前面
本文由markdown格式写成,为本人第一次这么写,排版可能会有点乱,还望各位海涵。
主要写的是使用Ribbon进行Restful请求,测试各个方法的使用,代码冗余较高,比较适合初学者,介意轻喷谢谢。
前提
- 一个可用的Eureka注册中心(文中以之前博客中双节点注册中心,不重要)
- 一个连接到这个注册中心的服务提供者
- 一个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中添加
org.springframework.boot spring-boot-maven-plugin org.apache.maven.plugins maven-compiler-plugin 1.8
测试项目构建
Eureka注册中心:参考注册中心的搭建
服务提供者:参考注册服务提供者
ribbon消费者:参考服务发现与消费
项目搭建完后,记得按照这几个教程中提到的配置hosts文件
为了防止项目中的RequestMapping相同,这里就删除所有的controller类(服务提供者和消费者),接下来我会将每个restful方法都封装成一个类,方便大家查看
Get请求
getForEntity:此方法有三种重载形式,分别为:
- getForEntity(Stringurl,Class
responseType) - getForEntity(Stringurl,Class
responseType,Object...uriVariables) - getForEntity(Stringurl,Class
responseType,Map uriVariables) - getForEntity(URIurl,Class
responseType)
注意:此方法返回的是一个包装对象ResponseEntity
getForObject:此方法也有三种重载形式,这点与getForEntity方法相同:
- getForObject(Stringurl,Class
responseType) - getForObject(Stringurl,Class
responseType,Object...uriVariables) - getForObject(Stringurl,Class
responseType,Map uriVariables) - getForObject(URIurl,Class
responseType)
注意:此方法返回的对象类型为responseType传入类型
为了方便测试,这里分别在服务提供者和服务消费者中提供相同的User类,用于方便测试
packagecom.cnblogs.hellxz; /** *用于测试的pojo */ publicclassUser{ privateStringname; privateStringsex; privateStringphone; publicUser(){} publicUser(Stringname,Stringsex,Stringphone){ this.name=name; this.sex=sex; this.phone=phone; } publicStringtoString(){ return"user:{" +"name:"+name+"," +"sex:"+sex+"," +"phone:"+phone +"}"; } publicStringgetName(){ returnname; } publicvoidsetName(Stringname){ this.name=name; } publicStringgetSex(){ returnsex; } publicvoidsetSex(Stringsex){ this.sex=sex; } publicStringgetPhone(){ returnphone; } publicvoidsetPhone(Stringphone){ this.phone=phone; } }
下边我们在服务提供者处创建一个GetRequestController
packagecom.cnblogs.hellxz; importorg.apache.log4j.Logger; importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.cloud.client.ServiceInstance; importorg.springframework.cloud.client.discovery.DiscoveryClient; importorg.springframework.web.bind.annotation.*; /** *@Author:Hellxz *@Description:服务提供者 *@Date:2018/4/1811:36 */ @RestController publicclassGetRequestController{ @Autowired privateDiscoveryClientclient;//注入发现客户端 privatefinalLoggerlogger=Logger.getLogger(GetRequestController.class); /** *gostraighttest */ @GetMapping(value="/hello") publicStringhello(){ //获取服务实例,作用为之后console显示效果 ServiceInstanceserviceInstance=client.getLocalServiceInstance(); logger.info("/hellohost:"+serviceInstance.getHost()+"service_id:"+serviceInstance.getServiceId()); return"hello"; } /** *parametertest */ @GetMapping(value="/greet/{dd}") publicStringgreet(@PathVariableStringdd){ ServiceInstanceserviceInstance=client.getLocalServiceInstance(); logger.info("/hellohost:"+serviceInstance.getHost()+"service_id:"+serviceInstance.getServiceId()); return"hello"+dd; } /** *返回测试对象 */ @GetMapping("/user") publicUsergetUser(){ ServiceInstanceserviceInstance=client.getLocalServiceInstance(); logger.info("/user"+serviceInstance.getHost()+"port:"+serviceInstance.getPort()+"serviceInstanceid:"+serviceInstance.getServiceId()); returnnewUser("hellxz","male","123456789"); } /** *根据名称返回对象,这里模拟查数据库操作 */ @GetMapping("/user/{name}") publicUsergetUserSelect(@PathVariableStringname){ ServiceInstanceserviceInstance=client.getLocalServiceInstance(); logger.info("/user"+serviceInstance.getHost()+"port:"+serviceInstance.getPort()+"serviceInstanceid:"+serviceInstance.getServiceId()); if(name.isEmpty()){ returnnewUser(); }elseif(name.equals("hellxz")){ returnnewUser("hellxz","male","123456789"); }else{ returnnewUser("随机用户","male","987654321"); } } }
接下来我们在服务消费者项目中创建GetRequestController
packagecom.cnblogs.hellxz; importorg.apache.log4j.Logger; importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.http.ResponseEntity; importorg.springframework.web.bind.annotation.*; importorg.springframework.web.client.RestTemplate; importorg.springframework.web.util.UriComponents; importorg.springframework.web.util.UriComponentsBuilder; importjava.net.URI; importjava.util.HashMap; importjava.util.Map; /** *@Author:Hellxz *@Description:ribbon消费者应用Controller,get请求 *@Date:2018/4/1615:54 */ @RestController publicclassGetRequestController{ privateLoggerlogger=Logger.getLogger(GetRequestController.class); @Autowired //注入restTemplate privateRestTemplaterestTemplate; /** *ResponseEntitygetForEntity(Stringurl,Class responseType) *TgetBody()以下此方法相同 */ @GetMapping(value="/entity/noparam") publicStringnoParamGetForEntity(){ //这里注释掉,因为之前想当然使用了直链访问服务提供者的接口,这样是不会返回结果的,而且会报错 //returnrestTemplate.getForEntity("http://localhost:8080/hello",String.class).getBody(); //使用restTemplate调用微服务接口 returnrestTemplate.getForEntity("http://hello-service/hello",String.class).getBody(); } /** *ResponseEntity getForEntity(Stringurl,Class responseType,Object...uriVariables) */ @GetMapping("/entity/type") publicUsergetForEntityIdentifyByType(){ //不传参返回指定类型结果 ResponseEntity entity=restTemplate.getForEntity("http://hello-service/user",User.class); Userbody=entity.getBody(); logger.info("user:"+body); returnbody; //以上可简写为 //returnrestTemplate.getForEntity("http://hello-service/user",User.class).getBody(); } /** *ResponseEntity getForEntity(Stringurl,Class responseType,Object...uriVariables) *使用占位符对参数进行替换,内部使用String.format方法实现 */ @GetMapping(value="/entity") //如果接收的参数是使用参数没有使用?有则使用@PathVariable,否则用@RequestParam publicStringgetForEntityByQuestionMarkParam(@RequestParam("name")Stringname){ //主要测试getEntity方法,这里测试直接传参 returnrestTemplate.getForEntity("http://hello-service/greet/{1}",String.class,name).getBody(); } /** *getForEntity方法内部会提取map中,以占位符为key的值作为参数回填入url中 *ResponseEntity getForEntity(Stringurl,Class responseType,Map uriVariables) */ @GetMapping(value="/entity/map/{name}") //如果接收的参数是使用参数没有使用?有则使用@PathVariable,否则用@RequestParam publicStringgetForEntityByMap(@PathVariable("name")Stringname){ //主要测试getEntity方法,这里测试map传参 Map reqMap=newHashMap(); reqMap.put("name",name); returnrestTemplate.getForEntity("http://hello-service/greet/{name}",String.class,reqMap).getBody(); } /** *ResponseEntity getForObject(URIurl,Class responseType) */ @GetMapping("/entity/uri") publicStringgetForEntityByURI(){ //使用uri进行传参并访问 UriComponentsuriComponents=UriComponentsBuilder.fromUriString("http://hello-service/greet/{name}").build().expand("laozhang").encode(); URIuri=uriComponents.toUri(); returnrestTemplate.getForEntity(uri,String.class).getBody(); } /** *TgetForObject(Stringurl,Class responseType) */ @GetMapping("/object") publicUsergetForObjectWithNoParam(){ //相比getForEntity方法,获取对象可以省去调用getBody returnrestTemplate.getForObject("http://hello-service/user",User.class); } /** *TgetForObject(Stringurl,Class responseType,Map uriVariables) */ @GetMapping("/object/map") publicUsergetForObjectByMap(){ //使用map传参 Map paramMap=newHashMap<>(); paramMap.put("name","hellxz"); returnrestTemplate.getForObject("http://hello-service/user",User.class,paramMap); } /** *TgetForObject(Stringurl,Class responseType,Object...uriVariables) */ @GetMapping("/object/param/{name}") publicUsergetForObjectByParam(@PathVariableStringname){ returnrestTemplate.getForObject("http://hello-service/user/{name}",User.class,name); } /** *TgetForObject(URIurl,Class responseType) */ @GetMapping("/object/uri/{name}") publicUsergetForObjectByURI(@PathVariableStringname){ UriComponentsuriComponents=UriComponentsBuilder.fromUriString("http://hello-service/user/{name}") .build().expand(name).encode(); URIuri=uriComponents.toUri(); returnrestTemplate.getForObject(uri,User.class); } }
先启动注册中心,然后通过访问消费者对外提供的接口进行测试,这些都是本人实际操作过的了,这里就不写测试了
Post请求
post请求和get请求都有*ForEntity和*ForObject方法,其中参数列表有些不同,除了这两个方法外,还有一个postForLocation方法,其中postForLocation以post请求提交资源,并返回新资源的URI
postForEntity:此方法有三种重载形式,分别为:
- postForEntity(Stringurl,Objectrequest,Class
responseType,Object...uriVariables) - postForEntity(Stringurl,Objectrequest,Class
responseType,Map uriVariables) - postForEntity(URIurl,Objectrequest,Class
responseType)
注意:此方法返回的是一个包装对象ResponseEntity
postForObject:此方法也有三种重载形式,这点与postForEntity方法相同:
- postForObject(Stringurl,Objectrequest,Class
responseType,Object...uriVariables) - postForObject(Stringurl,Objectrequest,Class
responseType,Map uriVariables) - postForObject(URIurl,Objectrequest,Class
responseType)
注意:此方法返回的对象类型为responseType传入类型
postForLocation:此方法中同样有三种重载形式,分别为:
- postForLocation(Stringurl,Objectrequest,Object...uriVariables)
- postForLocation(Stringurl,Objectrequest,Map
uriVariables) - postForLocation(URIurl,Objectrequest)
注意:此方法返回的是新资源的URI,相比getForEntity、getForObject、postForEntity、postForObject方法不同的是这个方法中无需指定返回类型,因为返回类型就是URI,通过Object...uriVariables、Map
按照之前的方式,我们分别在提供服务者和消费者的项目中分别创建PostRequestController
如下服务者PostRequestController代码如下:
packagecom.shunneng.springcloudhelloworld; importorg.apache.log4j.Logger; importorg.springframework.web.bind.annotation.*; importorg.springframework.web.util.UriComponents; importorg.springframework.web.util.UriComponentsBuilder; importjava.net.URI; /** *@Author:Hellxz *@Description: *@Date:2018/4/1810:21 */ @RestController publicclassPostRequestController{ privateLoggerlogger=Logger.getLogger(PostRequestController.class); /** *接收一个对象再返回回去,postForEntity/postForObject方法通用 */ @PostMapping("/user") publicUserreturnUserByPost(@RequestBodyUseruser){ logger.info("/use接口"+user); if(user==null)returnnewUser("这是一个空对象","",""); returnuser; } /** *测试PostForEntity方法的参数,可以直接看输出判断结果了 */ @PostMapping("/user/{str}") publicUserreturnUserByPost(@PathVariableStringstr,@RequestBodyUseruser){ logger.info("/user/someparam接口传参name:"+str+""+user); if(user==null)returnnewUser("这是一个空对象","",""); returnuser; } /** *为postForLocation方法返回URI */ @PostMapping("/location") publicURIreturnURI(@RequestBodyUseruser){ //这里模拟一个url,真实资源位置不一定是这里 UriComponentsuriComponents=UriComponentsBuilder.fromUriString("http://hello-service/location") .build().expand(user).encode(); URItoUri=uriComponents.toUri(); //这里不知道是什么问题,明明生成uri了,返回之后好像并没有被获取到 logger.info("/locationuri:"+toUri); returntoUri; } }
消费端PostRequestController代码:
packagecom.cnblogs.hellxz; importorg.apache.log4j.Logger; importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.http.ResponseEntity; importorg.springframework.web.bind.annotation.PostMapping; importorg.springframework.web.bind.annotation.RestController; importorg.springframework.web.client.RestTemplate; importorg.springframework.web.util.UriComponents; importorg.springframework.web.util.UriComponentsBuilder; importjava.net.URI; /** *@Author:Hellxz *@Description:Ribbon消费者post请求controller *@Date:2018/4/189:47 */ @RestController publicclassPostRequestController{ privateLoggerlogger=Logger.getLogger(PostRequestController.class); @Autowired privateRestTemplaterestTemplate; /** *ResponseEntitypostForEntity(Stringurl,Objectrequest,Class responseType) *其中参数url不多说,Objectrequest如果是不是一个HttpEntity对象,会自动转换为HttpEntity对象,视作完整的body来处理; *如果是HttpEntity对象,那么会被直接当做body处理并且包含header内容。 *以下对于重写的方法就不多说了,使用方法大体同getForEntity,如果仅是简单post对象,那么使用不带Object...variables或Mapvariables的方法即可。 *postForEntity(Stringurl,Objectrequest,Class responseType,Object...uriVariables) *postForEntity(Stringurl,Objectrequest,Class responseType,Map uriVariables) * *这里详细说下我遇到的坑: *1、其他几个重载方法的最后边的Object...variables和Mapvariables都是对之前的url进行操作的, *也就是说,在post请求的url中使用占位符进行传参,而如果在url中没有使用占位符,那么这些最后传的参数是无效的! *2、方法中Objectrequest这个对象如果和服务提供者的接收参数类型相同,那么服务提供者仅需使用@RequestBody接收参数即可。 *3、如果二者都使用了,这就比较有趣了,需要一边通过@PathVariable注解接收uri中的参数,一边还需要@RequestBody接收对象或RequestParam按字段接收参数! *4、如果报错了,请仔细看看我上边写的三条,并注意服务提供者的参数接收注解的使用等。 */ @PostMapping("/entity") publicUserpostForEntity(){ Useruser=newUser("hellxz1","1","678912345"); ResponseEntity entity=restTemplate.postForEntity("http://hello-service/user/{str}",user,User.class,"测试参数"); Userbody=entity.getBody();//所有restTemplate.*ForEntity方法都是包装类,body为返回类型对象 returnbody; } /** *使用URI传参,测试结果会显示在服务提供者的终端中 *ResponseEntity postForEntity(URIurl,Objectrequest,Class responseType) */ @PostMapping("/entity/uri") publicUserpostForEntityByURI(){ Useruser=newUser("老张","1","678912345"); //这里只是将url转成URI,并没有添加参数 UriComponentsuriComponents=UriComponentsBuilder.fromUriString("http://hello-service/user") .build().encode(); URItoUri=uriComponents.toUri(); //使用user传参 Userobject=restTemplate.postForObject(toUri,user,User.class); returnobject; } /** *这里测试postForObject方法,需要注意的参数如上述方法的描述,区别只是不需要getBody了,这里就不再累述了 *postForObject(Stringurl,Objectrequest,Class responseType,Object...uriVariables) *postForObject(Stringurl,Objectrequest,Class responseType,Map uriVariables) */ @PostMapping("/object") publicUserpostForObject(){ Useruser=newUser("hellxz2","1","123654987"); //这里url传1是为了调用服务者项目中的一个接口 UserresponseBody=restTemplate.postForObject("http://hello-service/user/1",user,User.class); returnresponseBody; } /** *post请求还有一种:postForLocation,这里也同样有三种重载,除了无需指定返回类型外,用法相同,返回类型均为URI,也就不累述了 *postForLocation(Stringurl,Objectrequest,Object...uriVariables) *postForLocation(Stringurl,Objectrequest,Map uriVariables) *postForLocation(URIurl,Objectrequest) */ @PostMapping("/location") publicURIpostForLocation(){ Useruser=newUser("hellxz3","1","987654321"); URIuri=restTemplate.postForLocation("http://hello-service/location",user); //不知道为什么返回来是空,这个方法仅供参考吧,如果知道是什么情况,我会回来改的 logger.info("/locationuri:"+uri); returnuri; } }
Put请求&&Delete请求
put请求相对于get和post请求方法来的更为简单,其中无需指定put请求的返回类型,当然也没有返回值,也是三种重载,和之前写的基本一致,这里就不想多说了,delete请求和put请求都是没有返回值的,这里再特地重复写也没什么意思,这里先分别列出这两个请求的方法,代码写在一个类中了
put请求方法如下:
- put(Stringurl,Objectrequest,Object...uriVariables)
- put(Stringurl,Objectrequest,Map
uriVariables) - put(URIurl,Objectrequest)
delete请求方法如下:
- delete(Stringurl,Object...uriVariables)
- delete(Stringurl,Map
uriVariables) - delete(URIurl)
在提供服务者项目中添加PutAndDeleteRequestController,代码如下
packagecom.cnblogs.hellxz; importorg.apache.log4j.Logger; importorg.springframework.web.bind.annotation.*; /** *@Author:Hellxz *@Description:服务提供者put&delete请求controller *@Date:2018/4/1914:11 */ @RestController publicclassPutAndDeleteRequestController{ privateLoggerlogger=Logger.getLogger(PutAndDeleteRequestController.class); @PutMapping("/put") publicvoidput(@RequestBodyUseruser){ logger.info("/put"+user); } @DeleteMapping("/delete/{id}") publicvoiddelete(@PathVariableLongid){ logger.info("/deleteid:"+id); } }
在提供服务者项目中添加PutAndDeleteRequestController,代码如下
packagecom.cnblogs.hellxz; importorg.apache.log4j.Logger; importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.web.bind.annotation.*; importorg.springframework.web.client.RestTemplate; /** *@Author:Hellxz *@Description:put请求、delete请求,重载的参数与上述demo基本相同,不予列出 *@Date:2018/4/1913:43 */ @RestController publicclassPutRequestController{ privateLoggerlogger=Logger.getLogger(PostRequestController.class); @Autowired privateRestTemplaterestTemplate; /** *put请求示例,一般put请求多用作修改 */ @PutMapping("/put") publicvoidput(@RequestBodyUseruser){ restTemplate.put("http://hello-service/put",user); } /** *delete请求示例 */ @DeleteMapping("/del/{id}") publicvoiddelete(@PathVariableLongid){ restTemplate.delete("http://hello-service/delete/{1}",id); } }
结语
这篇博文使用markdown写成,第一次写不知道如何将代码块中加入序号以及折叠代码功能,这可能不是一篇好文章,但是写这篇博文写了快两天,有什么好的建议欢迎评论交流,
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。