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的代码更加的“面向对象”。

猜你喜欢

转载自www.cnblogs.com/a-yuan/p/9550824.html
今日推荐