Spring Boot HATEOAS入门

x33g5p2x  于2022-10-06 转载在 Spring  
字(4.7k)|赞(0)|评价(0)|浏览(387)

什么是 HATEOAS ?超媒体作为应用程序状态引擎是 REST 应用程序架构的约束之一。

HATEOAS 将显示给定资源的相关链接的概念引入了 RESTful 服务。当我们返回特定资源的详细信息时,我们还会返回指向可以对该资源执行的操作的链接,以及指向相关资源的链接。如果服务消费者可以使用响应中的链接来执行事务,那么它就不需要对所有链接进行硬编码。

让我们在实践中看看如何做到这一点。使用“web”和“hateoas”启动器构建一个新项目:

spring init -dweb,hateoas demo-hateoas

将包括以下依赖项:

<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> 		
	<!-- Test --> 
	<dependency> 			
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId> 
		<scope>test</scope>
	</dependency> 	
</dependencies>

为简单起见,我们将创建一个 In-Memory 存储库以使用 hatoas 进行查询,如本教程所述:如何在 Spring Boot 中定义模拟存储库

让我们从模型开始:

package com.example.demohateoas.model;

public class Customer {
  private final Long id;
  private final String firstName;
  private final String lastName;

  public Customer(Long id, String firstName, String lastName) {
    this.id = id;
    this.firstName = firstName;
    this.lastName = lastName;
  }

  public Long getId() {
    return this.id;
  }

  public String getFirstName() {
    return this.firstName;
  }

  public String getLastName() {
    return this.lastName;
  }
}

我们将添加一个简单的界面:

package com.example.demohateoas;

import java.util.List;

public interface CustomerRepository {
  List<Customer> findAll();

  Customer findCustomer(Long id);
}

现在我们将定义一个使用@Repository 标记的接口的实现:

package com.example.demohateoas;

import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Repository;
import org.springframework.util.ObjectUtils;

@Repository
public class MockCustomerRepository implements CustomerRepository {
  private final List<Customer> customers = new ArrayList<>();

  public MockCustomerRepository() {
    this.customers.add(new Customer(1L, "John", "Smith"));
    this.customers.add(new Customer(2L, "Mark", "Spencer"));
    this.customers.add(new Customer(2L, "Andy", "Doyle"));
  }

  @Override
  public List<Customer> findAll() {
    return this.customers;
  }

  @Override
  public Customer findOne(Long id) {
    for (Customer customer : this.customers) {
      if (ObjectUtils.nullSafeEquals(customer.getId(), id)) {
        return customer;
      }
    }
    return null;
  }
}

最有趣的部分来了。我们将添加一个控制器来利用响应中的链接:

package com.example.demohateoas.controller;

import org.springframework.hateoas.EntityLinks;
import org.springframework.hateoas.ExposesResourceFor;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.Resources;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import com.example.demohateoas.model.Customer;
import com.example.demohateoas.model.CustomerRepository;

@Controller
@RequestMapping("/customers")
@ExposesResourceFor(Customer.class)
public class CustomerController {
  private final CustomerRepository repository;
  private final EntityLinks entityLinks;

  public CustomerController(CustomerRepository repository, EntityLinks entityLinks) {
    this.repository = repository;
    this.entityLinks = entityLinks;
  }

  @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
  HttpEntity<Resources<Customer>> showCustomers() {
    Resources<Customer> resources = new Resources<>(this.repository.findAll());
    resources.add(this.entityLinks.linkToCollectionResource(Customer.class));
    return new ResponseEntity<>(resources, HttpStatus.OK);
  }

  @GetMapping(path = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
  HttpEntity<Resource<Customer>> showCustomer(@PathVariable Long id) {
    Resource<Customer> resource = new Resource<>(this.repository.findOne(id));
    resource.add(this.entityLinks.linkToSingleResource(Customer.class, id));
    return new ResponseEntity<>(resource, HttpStatus.OK);
  }
}

如您所见,从 GET 请求返回的关键类是 org.springframework.hateoas.Resource 和 org.springframework.hateoas.Resources,它们是简单的 Resources 包装了能够向其添加链接的域对象。如果必须使用链接丰富单个资源,则使用方法 linkToSingleResource。另一方面,对于集合,则使用linkToCollectionResource

Main 应用程序类完成了应用程序:

package com.example.demohateoas;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

如果您构建并运行应用程序,您将看到在 Root Web 上下文和 /{id} 路径中都提供了一个链接,以启用导航:

总结

在本教程中,我们了解了 HATEOAS 如何通过在响应中包含超媒体链接来提供信息以动态导航站点的 REST 接口。然而,这种能力不同于基于 SOA 的系统和 WSDL 驱动的接口,服务器和客户端通常必须访问一个固定的规范,该规范可能在网站的其他地方、另一个网站上进行。

相关文章

微信公众号

最新文章

更多