SoFunction
Updated on 2025-04-15

Detailed explanation of the resource representation and link relationship of SpringHateoas hypermedia API

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

EntityModelis 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&lt;User&gt; getUser(@PathVariable Long id) {
        User user = (id)
                .orElseThrow(() -&gt; 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&lt;EntityModel&lt;User&gt;&gt; getAllUsers() {
        // Implement the logic to obtain all users        // ...
    }
}

In the above code,()Method to convert oneUserPackage the object into oneEntityModelInstance, 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

CollectionModelUsed 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

PagedModelyesCollectionModelExtensions 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());
}

PagedModelContains 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 aswait:

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&lt;Order&gt; getOrder(@PathVariable Long id) {
    Order order = (id)
            .orElseThrow(() -&gt; new OrderNotFoundException(id));
    
    EntityModel&lt;Order&gt; 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 passAcceptThe 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.