leftso 2149 0 2018-09-02 16:26:34

文章位置:左搜> 编程技术> 正文

前言

在这个Spring HATEOAS示例中,我们将学习如何将HATEOAS链接添加到在spring boot项目中创建的现有REST API。我们将使用类
org.springframework.hateoas.ResourceSupport 、
org.springframework.hateoas.mvc.ControllerLinkBuilder
org.springframework.hateoas.Link Spring HATEOAS模块提供的类。

为了演示链接的创建,我们将首先创建一些REST API并查看它们的输出。然后,我们将HATEOAS链接应用到REST资源,然后我们将比较有无链接的输出。
HATEOAS(The Hypermedia As The Engine Of Application Statue)
 

项目结构

Spring HATEOAS示例 - 项目结构

创建REST API

在这个例子中,我创建了三个带有端点的REST API,如下所示:
  • /employees
  • /employees/{id}
  • /employees/{id}/report

REST资源模型

EmployeeListVO:
@XmlRootElement (name="employees")
public class EmployeeListVO implements Serializable
{
    private static final long serialVersionUID = 1L;
      
    private List<EmployeeVO> employees = new ArrayList<EmployeeVO>();
  
    public List<EmployeeVO> getEmployees() {
        return employees;
    }
  
    public void setEmployees(List<EmployeeVO> employees) {
        this.employees = employees;
    }
}
EmployeeListVO:
@XmlRootElement(name = "employee")
@XmlAccessorType(XmlAccessType.NONE)
public class EmployeeVO implements Serializable
{
    private static final long serialVersionUID = 1L;
     
    public EmployeeVO(Integer id, String firstName, String lastName, String email) {
        super();
        this.employeeId = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
    }
 
    public EmployeeVO() {
 
    }
 
    @XmlAttribute
    private Integer employeeId;
 
    @XmlElement
    private String firstName;
 
    @XmlElement
    private String lastName;
 
    @XmlElement
    private String email;
 
    //removed getters and setters for readability
 
    @Override
    public String toString() {
        return "EmployeeVO [id=" + employeeId + ", firstName=" + firstName + ", lastName=" + lastName + ", email=" + email
                + "]";
    }
}
EmployeeReport:
@XmlRootElement(name="employee-report")
public class EmployeeReport implements Serializable {
 
    private static final long serialVersionUID = 1L;
 
    //根据需要添加其他信息
}

REST Controller

EmployeeRESTController:
@RestController
public class EmployeeRESTController {
     
    @RequestMapping(value = "/employees")
    public EmployeeListVO getAllEmployees()
    {
        EmployeeListVO employeesList  = new EmployeeListVO();
 
        for (EmployeeVO employee : EmployeeDB.getEmployeeList())
        {
            employeesList.getEmployees().add(employee);
        }
 
        return employeesList;
    }
      
    @RequestMapping(value = "/employees/{id}")
    public ResponseEntity<EmployeeVO> getEmployeeById (@PathVariable("id") int id)
    {
        if (id <= 3) {
            EmployeeVO employee = EmployeeDB.getEmployeeList().get(id-1);
            return new ResponseEntity<EmployeeVO>(employee, HttpStatus.OK);
        }
        return new ResponseEntity<EmployeeVO>(HttpStatus.NOT_FOUND);
    }
     
    @RequestMapping(value = "/employees/{id}/report")
    public ResponseEntity<EmployeeReport> getReportByEmployeeById (@PathVariable("id") int id)
    {
        //Do some operation and return report
        return null;
    }
}

DAO层

我已经创建了EmployeeDB类来模拟DAO层。在实际应用中,从数据源获取数据将是更复杂的代码。

EmployeeDB
public class EmployeeDB {
 
    public static List<EmployeeVO> getEmployeeList()
    {
        List<EmployeeVO> list = new ArrayList<>();
 
        EmployeeVO empOne = new EmployeeVO(1, "Lokesh", "Gupta", "demo@gmail.com");
        EmployeeVO empTwo = new EmployeeVO(2, "Amit", "Singhal", "asinghal@yahoo.com");
        EmployeeVO empThree = new EmployeeVO(3, "Kirti", "Mishra", "kmishra@gmail.com");
 
        list.add(empOne);
        list.add(empTwo);
        list.add(empThree);
 
        return list;
    }
}
 

启动类:

@SpringBootApplication
public class Application
{
    public static void main(String[] args)
    {
        ApplicationContext ctx = SpringApplication.run(Application.class, args);
    }
}


Maven - pom.xml

我们来看看用于这个项目的pom.xml文件。

<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.demo</groupId>
    <artifactId>springbootdemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
 
    <name>springbootdemo</name>
    <url>http://maven.apache.org</url>
 
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
    </parent>
 
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-hateoas</artifactId>
        </dependency>
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
 
    <repositories>
        <repository>
            <id>repository.spring.release</id>
            <name>Spring GA Repository</name>
            <url>http://repo.spring.io/release</url>
        </repository>
    </repositories>
</project>
 

API输出

/employees
{
    "employees": [
        {
            "employeeId": 1,
            "firstName": "Lokesh",
            "lastName": "Gupta",
            "email": "demo@gmail.com"
        },
        {
            "employeeId": 2,
            "firstName": "Amit",
            "lastName": "Singhal",
            "email": "asinghal@yahoo.com"
        },
        {
            "employeeId": 3,
            "firstName": "Kirti",
            "lastName": "Mishra",
            "email": "kmishra@gmail.com"
        }
    ]
}
/employees/{id}
{
	"employeeId": 1,
	"firstName": "Lokesh",
	"lastName": "Gupta",
	"email": "demo@gmail.com"
}


如何将HATEOAS链接添加到REST资源

步骤1)使用ResourceSupport类扩展资源模型

扩展您想添加HATEOAS链接的所有模型类 - 包括org.springframework.hateoas.ResourceSupport类。

@XmlRootElement(name = "employee")
@XmlAccessorType(XmlAccessType.NONE)
public class EmployeeVO extends ResourceSupport implements Serializable
{
    //rest all code is same
}
 
//...
 
@XmlRootElement (name="employees")
public class EmployeeListVO extends ResourceSupport implements Serializable
{
    //rest all code is same
}
 
//...
 
@XmlRootElement(name="employee-report")
public class EmployeeReport extends ResourceSupport implements Serializable {
 
    //rest all code is same
}
 

步骤2)在REST控制器中构建和添加链接

要添加链接,您需要ControllerLinkBuilderLink类。Link是要在REST资源中添加的链接的最终表示形式。而ControllerLinkBuilder有助于使用基于构建器模式的各种方法构建链接。

添加链接到收集资源

这里我们将添加两种类型的链接。在第一个链接中,收集资源将指向自己。在第二种类型的链接中,集合中的每个资源都将指向它的URI位置,其中可以使用完整的表示形式。另外,每个资源都会有方法链接,可以根据这些链接对个别资源执行一些操作。
 

@RequestMapping(value = "/employees")
public EmployeeListVO getAllEmployees()
{
    EmployeeListVO employeesList  = new EmployeeListVO();
 
    for (EmployeeVO employee : EmployeeDB.getEmployeeList())
    {
        //Adding self link employee 'singular' resource
        Link link = ControllerLinkBuilder
                .linkTo(EmployeeRESTController.class)
                .slash(employee.getEmployeeId())
                .withSelfRel();
 
        //Add link to singular resource
        employee.add(link);
         
      //Adding method link employee 'singular' resource
        ResponseEntity<EmployeeReport> methodLinkBuilder = ControllerLinkBuilder
                .methodOn(EmployeeRESTController.class).getReportByEmployeeById(employee.getEmployeeId());
        Link reportLink = ControllerLinkBuilder
                .linkTo(methodLinkBuilder)
                .withRel("employee-report");
 
        //Add link to singular resource
        employee.add(reportLink);
   
        employeesList.getEmployees().add(employee);
    }
     
    //Adding self link employee collection resource
    Link selfLink = ControllerLinkBuilder
            .linkTo(ControllerLinkBuilder
            .methodOn(EmployeeRESTController.class).getAllEmployees())
            .withSelfRel();
 
    //Add link to collection resource
    employeesList.add(selfLink);
      
    return employeesList;
}
输出:
{
    "employees": [
        {
            "employeeId": 1,
            "firstName": "Lokesh",
            "lastName": "Gupta",
            "email": "howtodoinjava@gmail.com",
            "_links": {
                "self": {
                    "href": "http://localhost:8080/1"
                },
                "employee-report": {
                    "href": "http://localhost:8080/employees/1/report"
                }
            }
        },
        {
            "employeeId": 2,
            "firstName": "Amit",
            "lastName": "Singhal",
            "email": "asinghal@yahoo.com",
            "_links": {
                "self": {
                    "href": "http://localhost:8080/2"
                },
                "employee-report": {
                    "href": "http://localhost:8080/employees/2/report"
                }
            }
        },
        {
            "employeeId": 3,
            "firstName": "Kirti",
            "lastName": "Mishra",
            "email": "kmishra@gmail.com",
            "_links": {
                "self": {
                    "href": "http://localhost:8080/3"
                },
                "employee-report": {
                    "href": "http://localhost:8080/employees/3/report"
                }
            }
        }
    ],
    "_links": {
        "self": {
            "href": "http://localhost:8080/employees"
        }
    }
}
 

添加链接到单个资源

添加单数资源的链接与我们在前面部分看到的完全相同。单数资源表示通常具有更多信息/字段以及附加链接。

@RequestMapping(value = "/employees/{id}")
public ResponseEntity<EmployeeVO> getEmployeeById (@PathVariable("id") int id)
{
    if (id <= 3) {
        EmployeeVO employee = EmployeeDB.getEmployeeList().get(id-1);
         
        //Self link
        Link selfLink = ControllerLinkBuilder
                .linkTo(EmployeeRESTController.class)
                .slash(employee.getEmployeeId())
                .withSelfRel();
         
        //Method link
        Link reportLink = ControllerLinkBuilder
                .linkTo(ControllerLinkBuilder.methodOn(EmployeeRESTController.class)
                .getReportByEmployeeById(employee.getEmployeeId()))
                .withRel("report");
         
        employee.add(selfLink);
        employee.add(reportLink);
        return new ResponseEntity<EmployeeVO>(employee, HttpStatus.OK);
    }
    return new ResponseEntity<EmployeeVO>(HttpStatus.NOT_FOUND);
}
输出:
{
    "employeeId": 1,
    "firstName": "Lokesh",
    "lastName": "Gupta",
    "email": "howtodoinjava@gmail.com",
    "_links": {
        "self": {
            "href": "http://localhost:8080/1"
        },
        "report": {
            "href": "http://localhost:8080/employees/1/report"
        }
    }
}

总结

正如你看到上面的演示,使用spring hashoas模块添加HATEOAS链接非常容易和一分钟时间搞定。这将大大增加API的可发现性和实用性。
项目源码下载

评论区域

暂无评论,快来抢首发吧!!!