zoukankan      html  css  js  c++  java
  • SpringCloud(五) 使用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-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消费者:参考服务发现与消费

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

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

    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;
        }
    }
    
    

    下边我们在服务提供者处创建一个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:此方法有三种重载形式,分别为:

    • 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)
    注意:此方法返回的是一个包装对象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)
         *
         * 这里详细说下我遇到的坑:
         * 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请求方法如下:

    • 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写成,第一次写不知道如何将代码块中加入序号以及折叠代码功能,这可能不是一篇好文章,但是写这篇博文写了快两天,有什么好的建议欢迎评论交流,啥也不说了,如果本文对你有帮助,请帮忙点个推荐,加个关注吧!

    声明:本博客无需许可也可以转载,但烦请注明出处

  • 相关阅读:
    5.Spring高级装配(根据profile的激活状态决定使用的环境) 以及 条件化 Bean
    4.SpringJunit4ClassRunner
    3.Spring配置可选方案
    2.Spring容器bean的生命周期
    1.使用spring上下文容纳你的Bean
    maven 打包跳过 Junit test
    HTML5中meta name="viewport"
    二、JavaScript this
    VIM_插件
    VBS_DO...Loop
  • 原文地址:https://www.cnblogs.com/hellxz/p/8875452.html
Copyright © 2011-2022 走看看