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);
}
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).
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 .
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