web-dev-qa-db-fra.com

Spring Hateoas ControllerLinkBuilder ajoute des champs nuls

Je suis un tutoriel sur Spring REST et j'essaie d'ajouter des liens HATEOAS aux résultats de mon contrôleur.

J'ai une classe d'utilisateur simple et un contrôleur CRUD pour cela.

class User {
    private int id;
    private String name;
    private LocalDate birthdate;
    // and getters/setters
}

Un service:

@Component
class UserService {
    private static List<User> users = new ArrayList<>();
    List<User> findAll() {
        return Collections.unmodifiableList(users);
    }
    public Optional<User> findById(int id) {
        return users.stream().filter(u -> u.getId() == id).findFirst();
    }
    // and add and delete methods of course, but not important here
}

Tout fonctionne bien sauf dans mon contrôleur, je veux ajouter des liens de la liste de tous les utilisateurs aux utilisateurs simples:

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;

@RestController
public class UserController {
    @Autowired
    private UserService userService;
    @GetMapping("/users")
    public List<Resource<User>> getAllUsers() {
        List<Resource<User>> userResources = userService.findAll().stream()
            .map(u -> new Resource<>(u, linkToSingleUser(u)))
            .collect(Collectors.toList());
        return userResources;
    }
    Link linkToSingleUser(User user) {
        return linkTo(methodOn(UserController.class)
                      .getById(user.getId()))
                      .withSelfRel();
    }

de sorte que pour chaque utilisateur de la liste des résultats, un lien vers l'utilisateur lui-même est ajouté.

Le lien lui-même est bien créé, mais il y a des entrées superflues dans le JSON résultant:

[
    {
        "id": 1,
        "name": "Adam",
        "birthdate": "2018-04-02",
        "links": [
            {
                "rel": "self",
                "href": "http://localhost:8080/users/1",
                "hreflang": null,
                "media": null,
                "title": null,
                "type": null,
                "deprecation": null
            }
        ]
    }
]

D'où viennent les champs avec une valeur nulle (hreflang, media etc.) et pourquoi sont-ils ajoutés? Y a-t-il un moyen de s'en débarrasser?

Ils n'apparaissent pas lors de la création d'un lien vers la liste de tous les utilisateurs:

@GetMapping("/users/{id}")
public Resource<User> getById(@PathVariable("id") int id) {
    final User user = userService.findById(id)
                                 .orElseThrow(() -> new UserNotFoundException(id));
    Link linkToAll = linkTo(methodOn(UserController.class)
                            .getAllUsers())
                            .withRel("all-users");
    return new Resource<User>(user, linkToAll);
}
8
daniu

Pour plus de précisions au cas où quelqu'un d’autre trébucherait dessus et j’ai compris: j’ai ajouté une entrée à application.properties, à savoir

spring.jackson.default-property-inclusion=NON_NULL

Pourquoi était-ce nécessaire pour les objets Link, mais pas pour le User je ne sais pas (et je n'ai pas plongé plus profondément).

7
daniu

Conformément à Spring HATEOAS # 493 "Votre structure de niveau supérieur n’est pas une structure HAL valide, ce qui permet de contourner les sérialiseurs HAL." 

pour résoudre le problème, utilisez:

return new Resources<>(assembler.toResources(sourceList));

Ce problème sera résolu dans les futures versions de Spring HATEOAS où toResources (Iterable <>) renvoie des ressources ou à l'aide de SimpleResourceAssembler .

2
semiintel

Comme semiintel a mentionné, le problème est que votre objet de retour List n'est pas un type HAL valide:

public List<Resource<User>> getAllUsers()

Ceci peut être facilement résolu en changeant le type de retour en ressources comme ceci:

public Resources<Resource<User>> getAllUsers()

Vous pouvez ensuite simplement envelopper votre liste de ressources <> dans un objet de ressources <> en modifiant votre déclaration de retour à partir de:

return userResources;

à:

return new Resources<>(userResources)

Ensuite, vous devriez obtenir la sérialisation des liens appropriée, comme vous le faites pour votre seul objet.

Cette méthode est une gracieuseté de cet article très utile pour résoudre ce problème:

https://www.logicbig.com/tutorials/spring-framework/spring-hateoas/multiple-link-relations.html

2
java-addict301