zoukankan      html  css  js  c++  java
  • 07 REST客户端

      在Spring Cloud集群中,各个角色的通信基于REST服务,因此在调用服务时,就不可避免地需要使用REST服务器请求的客户端。我们知道Spring可以用自带的RestTemplate使用HttpClient发送请求,此处将介绍另一个REST客户端:Feign。Feign框架已经被集成到Spring Cloud的Netflix项目中,使用该框架可以在Spring Cloud的集群中更加简单地调用REST服务。

      在学习Feign前,我们先了解一下REST客户端。本文将简单地讲述Apache CXF与Restlet这两款Web Service框架。并使用者两个框架来编写REST客户端,最后在编写一个Feign的Hello World例子。通过此过程,我们可以对Feign有一个初步的印象。

      创建3个maven项目,用于本节内容的测试,目录结构如下

    1.创建服务项目restserver并发布服务

      创建一个maven项目并引入依赖

      pom.xml代码清单

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.triheart</groupId>
        <artifactId>restserver</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>1.5.4.RELEASE</version>
            </dependency>
            <!--
                服务器端要加上这个依赖,否则客户端在请求时会报以下异常:
                Exception in thread "main" feign.FeignException: status 415 reading PersonClient#createPersonXML(Person); content:
                {"timestamp":1502705981406,"status":415,"error":"Unsupported Media Type","exception":"org.springframework.web.HttpMediaTypeNotSupportedException","message":"Content type 'application/xml;charset=UTF-8' not supported","path":"/person/createXML"}
    
             -->
            <dependency>
                <groupId>com.fasterxml.jackson.jaxrs</groupId>
                <artifactId>jackson-jaxrs-xml-provider</artifactId>
                <version>2.9.0</version>
            </dependency>
        </dependencies>
        
    </project>

      编写启动类以及控制器发布服务

      启动类代码清单RestServerApp.java

    package com.triheart.restserver;
    
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    
    /**
     * @author 阿遠
     * Date: 2018/8/28
     * Time: 17:07
     */
    @SpringBootApplication
    public class RestServerApp {
    
        public static void main(String[] args) {
            new SpringApplicationBuilder(RestServerApp.class).run(args);
        }
    }

      控制器代码清单

      MyController.java

    package com.triheart.restserver;
    
    import org.springframework.http.MediaType;
    import org.springframework.web.bind.annotation.*;
    
    import javax.servlet.http.HttpServletRequest;
    
    /**
     * @author 阿遠
     * Date: 2018/8/28
     * Time: 17:10
     */
    @RestController
        public class MyController {
    
        /**
         * 查询方法,参数为Person的id
         */
        @RequestMapping(value = "/person/{personId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
        public Person findPerson(@PathVariable("personId") Integer personId,
                                 HttpServletRequest request) {
            Person p = new Person();
            p.setId(personId);
            p.setName("Crazyit");
            p.setAge(30);
            p.setMessage(request.getRequestURL().toString());
            return p;
        }
    
        @RequestMapping(value = "/hello", method = RequestMethod.GET)
        public String hello() {
            return "Hello World";
        }   
    } 

      Person.java

    package com.triheart.restserver;
    
    /**
     * @author 阿遠
     * Date: 2018/8/28
     * Time: 17:09
     */
    public class Person {
    
        private Integer id;
    
        private String name;
    
        private Integer age;
    
        private String message;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    View Code

      启动服务器发布服务。

    2.使用CXF调用REST服务

      CXF是目前一个较为流行的Web Service框架,是Apache的一个开源项目。使用CXF可以发布和调用使用各种协议的服务,包括SOAP协议、XML/HTTP等。当前CXF已经对REST风格的Web Service提供支持,可以发布或调用REST风格的Web Service。在restclient项目中的pom.xml文件中引入相关的依赖

      pom.xml代码清单如下

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.triheart</groupId>
        <artifactId>restclient</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <dependencies>
            <!-- CXF -->
            <dependency>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-core</artifactId>
                <version>3.1.10</version>
            </dependency>
            <dependency>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-rt-rs-client</artifactId>
                <version>3.1.10</version>
            </dependency>
       
    </project>

      编写代码请求/person/{personId}服务,代码清单如下

    package com.triheart.restclient;
    
    import java.io.InputStream;
    import javax.ws.rs.core.Response;
    import org.apache.cxf.helpers.IOUtils;
    import org.apache.cxf.jaxrs.client.WebClient;
    
    /**
     * @author 阿遠
     * Date: 2018/8/28
     * Time: 17:07
     */
    public class CxfClient {
    
        public static void main(String[] args) throws Exception {
            // 创建WebClient
            WebClient client = WebClient.create("http://localhost:8080/person/1");
            // 获取响应
            Response response = client.get();
            // 获取响应内容
            InputStream ent = (InputStream) response.getEntity();
            String content = IOUtils.readStringFromStream(ent);
            // 输出字符串
            System.out.println(content);
        }
    }

       客户端使用了WebClient类发送请求,将获取的相应读入输入流,获取服务返回的JSON字符串,并在控制台输出。测试结果如下

      可以看到,我们使用org.apache.cxf.jaxrs.client.WebClient这个类调用了服务器发布的/person/{personId}服务。

    3.使用Restlet调用REST服务

       Restlet是一个轻量级的REST服务,使用它可以发布和调用REST风格的WebService。下面我们就来看相关的实例。在restclient项目的pom.xml文件中引入如下依赖

     <!-- Restlet -->
            <dependency>
                <groupId>org.restlet.jee</groupId>
                <artifactId>org.restlet</artifactId>
                <version>2.3.10</version>
            </dependency>
            <dependency>
                <groupId>org.restlet.jee</groupId>
                <artifactId>org.restlet.ext.jackson</artifactId>
                <version>2.3.10</version>
            </dependency>
        </dependencies>
    
        <repositories>
            <repository>
                <id>maven-restlet</id>
                <name>Restlet repository</name>
                <url>http://maven.restlet.org</url>
            </repository>
        </repositories>

      由于maven仓库没有Restlet的依赖,需要额外配置仓库地址。下面编写客户端实现

    package com.triheart.restclient;
    
    import java.util.HashMap;
    import java.util.Map;
    import org.restlet.data.MediaType;
    import org.restlet.ext.jackson.JacksonRepresentation;
    import org.restlet.representation.Representation;
    import org.restlet.resource.ClientResource;
    
    /**
     * @author 阿遠
     * Date: 2018/8/28
     * Time: 17:07
     */
    public class RestletClient {
    
        public static void main(String[] args) throws Exception {
            ClientResource client = new ClientResource(
                    "http://localhost:8080/person/1");
            // 调用get方法,服务器发布的是GET
            Representation response = client.get(MediaType.APPLICATION_JSON);
            // 创建JacksonRepresentation实例,将响应转换为Map
            JacksonRepresentation jr = new JacksonRepresentation(response,
                    HashMap.class);
            // 获取转换后的Map对象
            Map result = (HashMap) jr.getObject();
            // 输出结果
            System.out.println(result.get("id") + "-" + result.get("name") + "-"
                    + result.get("age") + "-" + result.get("message"));
        }
    }

      启动这个测试,可以在控制台看到如下结果

    4.Feign介绍以及使用Feign调用服务

      4.1Feign的介绍  

       Feign是GitHub上的一个开源项目,目的是简化WebService客户端的开发。在使用Feign时,可以使用注解来访问接口,被注解修饰的接口具有访问WebService的能力。这些注解中既包括Feign自带的注解,也支持插件式的编码器和解码器,使用者可以通过该特性对请求和相应进行不同的封装和解析。Spring Cloud将Feign集成到Netflix项目中,当与Eureka、Ribbon集成时,Feign就具有负载均衡的功能。

      4.2使用Feign调用服务

      先使用Feign编写一个HelloWorld的客户端,访问服务端的/hello服务,得到返回的字符串。

      feignclient的pom.xml代码清单

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.triheart</groupId>
        <artifactId>feignclient</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <dependencies>
            <dependency>
                <groupId>io.github.openfeign</groupId>
                <artifactId>feign-core</artifactId>
                <version>9.5.0</version>
            </dependency>
            <dependency>
                <groupId>io.github.openfeign</groupId>
                <artifactId>feign-gson</artifactId>
                <version>9.5.0</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.16.18</version>
            </dependency>
        </dependencies>
        
    </project>
    View Code 

      新建接口HelloClient,代码清单如下

    package com.triheart.feignclient;
    
    import feign.RequestLine;
    
    /**
     * @author 阿遠
     * Date: 2018/8/28
     * Time: 19:24
     */
    public interface HelloClient {
        
        @RequestLine("GET /hello")
        String sayHello();
    } 

      HelloClient表示一个服务接口,在接口的sayHello方法中使用了@RequestLine注解表示使用GET方法向/hello发送请求。接下来编写客户端的运行类,代码清单如下

    package com.triheart.feignclient;
    
    import feign.Feign;
    
    /**
     * @author 阿遠
     * Date: 2018/8/28
     * Time: 19:24
     */
    public class HelloMain {
    
        public static void main(String[] args) {
            // 调用Hello接口
            HelloClient hello = Feign.builder().target(HelloClient.class,
                    "http://localhost:8080/");
            System.out.println(hello.sayHello());
        }
    }

     在运行类中,使用Feign创建HelloClient接口的实例,最后调动接口定义的方法。运行测试代码,可以在控制台看到如下结果

      可见,接口已经被调用。如果熟悉AOP我们就会明白,Feign实际上帮我们生成了动态代理类。Feign使用的是JDK的动态代理,生成的代理类会将请求的信息封装,交给feign.client接口发送请求为该接口的默认实现类最终会使用java.net.HttpURLConnection来发送HTTP请求。

      编写第二个Feign客户端,调用/person/{personId}服务,新建服务类调用接口并添加注解,代码清单如下

    package com.triheart.feignclient;
    
    import feign.Param;
    import feign.RequestLine;
    import lombok.Data;
    
    /**
     * @author 阿遠
     * Date: 2018/8/28
     * Time: 19:24
     */
    public interface PersonClient {
    
        @RequestLine("GET /person/{personId}")
        Person findById(@Param("personId") Integer personId);
        @Data           
        class Person {
            Integer id;
            String name;
            Integer age;
            String message;
        }
    } 
    package com.triheart.feignclient;
    
    import feign.Feign;
    import feign.gson.GsonDecoder;
    
    /**
     * @author 阿遠
     * Date: 2018/8/28
     * Time: 19:24
     */
    public class PersonMain {
    
        public static void main(String[] args) {
            PersonClient personService = Feign.builder()
                    .decoder(new GsonDecoder())
                    .target(PersonClient.class, "http://localhost:8080/");
            PersonClient.Person person = personService.findById(2);
            System.out.println(person.id);
            System.out.println(person.name);
            System.out.println(person.age);
            System.out.println(person.message);
        }
    }

     定义的接口名称为findById,参数为personId。需要注意的是,由于会返回Person实例,我们在接口中定义了一个Person的类;为了减少代码量,我们使用了Lombok项目,使用了项目中的@Data注解。我们启动运行类,可以看到控制台上输入如下

      很明显,接口已经被调用。注意:我们在调用Person服务的运行类中添加了解码器的配置,GsonDecder会将返回的JSON字符串转换为接口方法返回的对象。

    5.总结

      本文使用了CXF、Restlet、Feign来编写客户端,在编写客户端的过程中,我们可以看到Feign的代码更加的“面向对象”。

  • 相关阅读:
    杂记5
    杂记4
    杂记3
    杂记2
    杂记1
    也来个网页版本的五子棋
    验证码识别
    npm publish命令
    window nginx php ci框架环境搭建
    也来个网页版本的五子棋
  • 原文地址:https://www.cnblogs.com/a-yuan/p/9550824.html
Copyright © 2011-2022 走看看