introduction
In the RESTful architectural style, hypermedia as the application state engine (HATEOAS - Hypermedia As The Engine Of Application State) is an important concept, which enables API clients to dynamically discover available operations through hypermedia links provided by the server. Spring HATEOAS is a project in the Spring ecosystem dedicated to helping developers build RESTful services that comply with HATEOAS constraints.
1. Basic concepts of HATEOAS
HATEOAS is a key constraint in the REST architectural style, which emphasizes that the API response should contain links to relevant resources. APIs that follow HATEOAS principles allow clients to navigate the entire API through links provided by the service without having to hardcode resource URLs. This approach improves the self-descriptiveness of the API, reduces the coupling between the client and the server, and enhances the evolution of the API.
In HATEOAS, the server response contains not only the requested data, but also the operation links related to the data. For example, when obtaining a user resource, the response may also include modifying, deleting a link to that user, and viewing a link to the user's related order. This approach allows clients to discover available operations by following the link without having to know the API structure in advance.
Spring HATEOAS provides a set of tools and abstractions that simplify the process of implementing HATEOAS in Spring MVC and Spring WebFlux applications. To use Spring HATEOAS, you need to add the corresponding dependencies:
<dependency> <groupId></groupId> <artifactId>spring-boot-starter-hateoas</artifactId> </dependency>
2. Resource model and representation
Spring HATEOAS provides a set of model classes to represent hypermedia resources. In Spring HATEOAS, a resource is represented as an object containing data and links. Core model classes include:
2.1 EntityModel
EntityModel
is a wrapper class that can wrap any domain object into a resource containing links:
import ; import ; import ; import ; import ; import static .*; @RestController public class UserController { private final UserRepository userRepository; public UserController(UserRepository userRepository) { = userRepository; } @GetMapping("/users/{id}") public EntityModel<User> getUser(@PathVariable Long id) { User user = (id) .orElseThrow(() -> new UserNotFoundException(id)); // Create an EntityModel with links return (user, linkTo(methodOn().getUser(id)).withSelfRel(), linkTo(methodOn().getAllUsers()).withRel("users"), linkTo(methodOn().getOrdersForUser(id)).withRel("orders")); } @GetMapping("/users") public CollectionModel<EntityModel<User>> getAllUsers() { // Implement the logic to obtain all users // ... } }
In the above code,()
Method to convert oneUser
Package the object into oneEntityModel
Instance, and three links have been added: self link (pointing to the current resource), users link (pointing to the list of all users) and orders link (pointing to the order of the current user).
2.2 CollectionModel
CollectionModel
Used to represent a resource collection, it can contain collection-level links:
@GetMapping("/users") public CollectionModel<EntityModel<User>> getAllUsers() { List<User> users = (); List<EntityModel<User>> userModels = () .map(user -> (user, linkTo(methodOn().getUser(())).withSelfRel(), linkTo(methodOn().getOrdersForUser(())).withRel("orders"))) .collect(()); return (userModels, linkTo(methodOn().getAllUsers()).withSelfRel()); }
In this example, we create a containing multipleEntityModel<User>
ofCollectionModel
, and add a self link to the collection itself.
2.3 PagedModel
PagedModel
yesCollectionModel
Extensions specifically used to represent paging resources:
@GetMapping("/users") public PagedModel<EntityModel<User>> getUsers(Pageable pageable) { Page<User> userPage = (pageable); List<EntityModel<User>> userModels = ().stream() .map(user -> (user, linkTo(methodOn().getUser(())).withSelfRel())) .collect(()); metadata = new ( (), (), (), ()); return (userModels, metadata, linkTo(methodOn().getUsers(pageable)).withSelfRel()); }
PagedModel
Contains paging metadata, such as the current page number, page size, total number of elements, and total number of pages, allowing the client to understand the paging structure and navigate to other pages.
3. Link construction and relationship
Spring HATEOAS provides convenient link building tools that enable developers to easily create links to controller methods. The main link building method is as follows:
3.1 Static links
uselinkTo()
Method and controller class references create static links:
Link usersLink = linkTo().slash("search").withRel("search");
3.2 Method reference link
usemethodOn()
andlinkTo()
Combination creates a link to a specific controller method:
Link userLink = linkTo(methodOn().getUser(123L)).withSelfRel();
3.3 Link relationship type
Spring HATEOAS defines common link relationship types, such as、
wait:
Link selfLink = linkTo(methodOn().getUser(123L)) .withRel();
Custom relationship types can also be used:
Link ordersLink = linkTo(methodOn().getOrdersForUser(123L)) .withRel("orders");
4. Resource Assembly
For complex applications, you can create a dedicated resource assembly (Assembler) to encapsulate resource creation logic:
import ; import ; import ; import static .*; @Component public class UserModelAssembler implements RepresentationModelAssembler<User, EntityModel<User>> { @Override public EntityModel<User> toModel(User user) { return (user, linkTo(methodOn().getUser(())).withSelfRel(), linkTo(methodOn().getAllUsers()).withRel("users"), linkTo(methodOn().getOrdersForUser(())).withRel("orders")); } }
Use the assembler in the controller:
@RestController public class UserController { private final UserRepository userRepository; private final UserModelAssembler assembler; public UserController(UserRepository userRepository, UserModelAssembler assembler) { = userRepository; = assembler; } @GetMapping("/users/{id}") public EntityModel<User> getUser(@PathVariable Long id) { User user = (id) .orElseThrow(() -> new UserNotFoundException(id)); return (user); } @GetMapping("/users") public CollectionModel<EntityModel<User>> getAllUsers() { List<User> users = (); return (users); } }
Resource assemblers improve code reusability and maintainability, especially when multiple controller methods require the creation of the same type of resource representation.
5. State transition of hypermedia drive
In the RESTful API, the state of a resource can be represented and operated through links. Spring HATEOAS can dynamically generate links based on the current state of the resource, realizing hypermedia-driven state transition:
@GetMapping("/orders/{id}") public EntityModel<Order> getOrder(@PathVariable Long id) { Order order = (id) .orElseThrow(() -> new OrderNotFoundException(id)); EntityModel<Order> orderModel = (order, linkTo(methodOn().getOrder(id)).withSelfRel(), linkTo(methodOn().getAllOrders()).withRel("orders")); // Add different operation links according to the order status if (() == Status.IN_PROGRESS) { (linkTo(methodOn() .cancelOrder(id)).withRel("cancel")); (linkTo(methodOn() .completeOrder(id)).withRel("complete")); } if (() == ) { (linkTo(methodOn() .refundOrder(id)).withRel("refund")); } return orderModel; }
In this example, depending on the current status of the order, the API response contains different action links. This way allows the client to understand the current state of the resource and the operations it can perform.
6. Negotiation of response format and content
Spring HATEOAS uses the HAL (Hypertext Application Language) format by default to represent hypermedia resources. HAL is a JSON format that defines the standard representation of resources and links:
{ "id": 123, "name": "John Doe", "email": "john@", "_links": { "self": { "href": "/api/users/123" }, "users": { "href": "/api/users" }, "orders": { "href": "/api/users/123/orders" } } }
In addition to HAL, Spring HATEOAS also supports other hypermedia types such as HAL-FORMS, Collection+JSON and UBER. Through configuration, content negotiation can be supported, allowing clients to request representations in different formats:
@Configuration public class HateoasConfig { @Bean public RepresentationModelProcessorInvoker processorInvoker() { return new RepresentationModelProcessorInvoker(( new HalFormsConfiguration())); } }
The client can passAccept
The header specifies the required media type:
Accept: application/-forms+json
Summarize
Spring HATEOAS provides developers with a powerful set of tools and abstractions, simplifying the development process of RESTful APIs that comply with HATEOAS constraints.
By wrapping the domain object into a resource representation containing links, the API response becomes more self-descriptive, enabling clients to discover and use API functionality by following the link. Resource model classes (such as EntityModel, CollectionModel, and PagedModel) and link building tools enable developers to easily create rich hypermedia APIs.
Resource assemblers further improve the reusability and maintainability of code, especially in complex applications.
The above is personal experience. I hope you can give you a reference and I hope you can support me more.