1
votes

Supprimez le lien _embedded in HATEOAS à l'aide de spring-boot-2.2.1

J'utilise spring-boot-2.2.1 avec spring-HATEOAS. Les liens hypermédia fonctionnent bien mais Je vois l'attribut _embedded lors du renvoi des liens, veuillez trouver le code ci-dessous pour référence et le projet dans github ici ,

Endpoint:

a) Renverra CollectionModel => localhost: 8099 / api / v1 / ability / list / noembedded

et

b) Renverra List > localhost: 8099 / api / v1 / ability / list /

XXX

Controller.Java

[
  {
    "capabilityId": "sample",
    "techStack": "Java",
    "numOfDevelopers": 25,
    "numOfAvailableDevelopers": 10,
    "_links": {
      "getThisCapability": {
        "href": "http://localhost:9771/api/v1/capability/sample"
      },
      "getAllCapabilities": {
        "href": "http://localhost:9771/api/v1/capability/list"
      }
    }
  }
]

Réponse réelle

[
  {
    "id": "sample",
    "techStack": "Java",
    "numOfDevelopers": 25,
    "numOfAvailableDevelopers": 10,
    "_links": {
      "getThisCapability": {
        "href": "http://localhost:8099/api/v1/capability/sample"
      },
      "getAllCapabilities": {
        "href": "http://localhost:8099/api/v1/capability/list"
      }
    }
  }
]

Réponse attendue: p>

{
  "_embedded": {
    "capabilityList": [
      {
        "id": "sample",
        "techStack": "Java",
        "numOfDevelopers": 25,
        "numOfAvailableDevelopers": 10,
        "_links": {
          "getThisCapability": {
            "href": "http://localhost:8099/api/v1/capability/sample"
          },
          "getAllCapabilities": {
            "href": "http://localhost:8099/api/v1/capability/list"
          },
          "deleteThisCapability": {
            "href": "http://localhost:8099/api/v1/capability/sample"
          },
          "createCapability": {
            "href": "localhost:8099/api/v1/capability"
          }
        }
      }
    ]
  }
}

J'ai essayé

spring.data.rest.defaultMediaType = application / json

spring.hateoas.use-hal-as-default-json-media-type = false

Mais pas de chance, je peux toujours voir l'attribut _embedded dans la réponse. Quelqu'un pourrait-il m'aider à identifier le problème.

J'utilisais spring-boot-1.5.10 avant de pouvoir afficher les liens correctement sans _embedded code>, veuillez vous référer ici .

Cela fonctionne bien après ajouté sous l'annotation dans la classe principale et si je retourne List

@EnableHypermediaSupport (type = EnableHypermediaSupport.HypermediaType.HAL)

localhost: 9771 / api / v1 / ability / list

Cela produit le résultat ci-dessous:

@RestController
@RequestMapping(value = "/api/v1/capability")
@RequiredArgsConstructor
@CrossOrigin
public class CapabilityController {

    private final CapabilityService capabilityService;
    private final CapabilityResourceAssembler capabilityResourceAssembler;

    @GetMapping(value = "/list")
    public CollectionModel<EntityModel<Capability>> getAllCapabilities() {
       List<EntityModel<Capability>> capabilities = capabilityService.listCapabilities().stream()
               .map(capability -> new EntityModel<>(capability,
                       linkTo(methodOn(CapabilityController.class).getCapabilityById(capability.getId())).withRel("getThisCapability"),
                       linkTo(methodOn(CapabilityController.class).getAllCapabilities()).withRel("getAllCapabilities")
               )).collect(Collectors.toList());
        return new CollectionModel<>(capabilities);
    }
}

Malheureusement, ce n'est pas travaillant dans la dernière version. Toute aide serait vraiment appréciée.


3 commentaires

L'exemple de code ne semble pas reproduire le comportement de votre question. L'exécution de SpringBootUnittestApplication et l'accès à : 8099 / api / v1 / ability / list renvoie un objet JSON vide. Je suppose que le magasin de données est vide? Pouvez-vous mettre à jour les exemples pour les rendre aussi minimaux que possible, par exemple en supprimant la dépendance à Mongo et en codant en dur certaines données?


@AndyWilkinson Merci beaucoup pour votre réponse. Bien sûr, laissez-moi mettre à jour le Github.


@AndyWilkinson Poussé les modifications dans le référentiel github en désactivant MongoDB. Veuillez vérifier cet hôte local de point de terminaison: / api / v1 / ability / list / noembedded pour les deux projets. Dans spring-boot-unittest, les liens de repo ne seront pas rendus correctement qui utilise spring-boot-2.x et dans spring-hypermedia les liens sont rendus correctement qui utilise spring-boot.15.x. votre aide sera vraiment appréciée.


3 Réponses :


2
votes

Si je comprends correctement la spécification HAL , cela être invalide HAL, c'est pourquoi Spring HATEOAS ne produira pas ce résultat tant que vous retournerez un CollectionModel dans votre requête. Notez qu'il est possible que la collection ait également des liens, qui seraient à côté de la propriété _embedded dans une propriété _links comme illustré dans cet exemple de document .

Au cas où vous voudriez vraiment vous débarrasser de la propriété _embedded et produire une liste de EntityModel s, alors cela devrait fonctionner si vous modifiez votre code pour renvoyer un List > . Vous perdrez alors les propriétés _embedded et _links qui sont produites par Spring HATEOAS. Voici votre extrait de code modifié:

@RestController
@RequestMapping(value = "/api/v1/capability")
@RequiredArgsConstructor
@CrossOrigin
public class CapabilityController {

    private final CapabilityService capabilityService;
    private final CapabilityResourceAssembler capabilityResourceAssembler;

    @GetMapping(value = "/list")
    public List<EntityModel<Capability>> getAllCapabilities() {
       List<EntityModel<Capability>> capabilities = capabilityService.listCapabilities().stream()
               .map(capability -> new EntityModel<>(capability,
                       linkTo(methodOn(CapabilityController.class).getCapabilityById(capability.getId())).withRel("getThisCapability"),
                       linkTo(methodOn(CapabilityController.class).getAllCapabilities()).withRel("getAllCapabilities")
               )).collect(Collectors.toList());
        return capabilities;
    }
}

Je vous déconseille fortement cela car vous perdez les avantages de Spring HATEOAS.

De plus, je recommande de faire utilisez votre CapabilityResourceAssembler et créez les instances EntityModel en utilisant abilityResourceAssembler.toModel (...) pour ne pas devez répéter le code que vous avez implémenté dans votre fonction .map (...) .


9 commentaires

Je connais cette solution. Je l'utilise déjà pour l'une de mes API. Mais si nous utilisons EntityModel pour List, nous perdons des _links aussi bien que vous l'avez mentionné. Je souhaite conserver _links.


Actuellement, cela ne semble pas possible dans Spring HATEOAS. Cela entrerait également en conflit avec toute classe que vous retournez (peut-être ne pas être List ) qui a une propriété _links .


Pourriez-vous s'il vous plaît vérifier ma question à nouveau. Je l'ai mis à jour. Cela fonctionnait bien avec l'ancienne version.


@VelNaga En regardant votre question mise à jour et le code que vous avez fourni, j'ai immédiatement remarqué que vous renvoyez List ( github.com/VelDeveloper/spring-hypermedia/blob/master/src/ma‌ dans /… ), pas Resources qui serait la bonne façon de renvoyer une collection de ressources. Par conséquent, dans l'ancien projet, vous avez fait la même chose que je l'ai proposé ci-dessus - vous n'auriez pas dû être en mesure de voir les liens liés à la collection dans le code que vous avez fourni via GitHub. Pouvez-vous vérifier cela?


Capability étend RecourseSupport donc List signifie la liste des ressources puisque je ne veux pas de _embedded dans la réponse. Si je renvoie Resources alors ma réponse contiendra _embedded . La principale chose à noter dans l'ancienne API est que je peux voir _links dans la réponse et que les liens en réponse sont rendus correctement, ce qui ne fonctionne pas dans la nouvelle API.


Lorsque vous êtes passé à la nouvelle API, vous avez changé le type de retour de votre méthode d'une collection de ressources à une ressource qui contient plusieurs ressources ( CollectionModel ). Pour obtenir exactement la même chose qu'avant, renvoyez List> . Dans l'ancienne API, vous n'auriez dû voir aucun lien à côté du tableau. Dans la nouvelle API, vous pouvez, car il s'agit d'une ressource ( CollectionModel ). Regardez exactement où la propriété _links que vous mentionnez se trouve dans la hiérarchie, elle appartient à un seul objet Capability - pas à la collection. Et renvoyer des EntityModel s le conservera.


continuons cette discussion dans le chat .


Poussé des modifications dans le référentiel github en désactivant MongoDB. Veuillez vérifier cet hôte local de point de terminaison: / api / v1 / ability / list / noembedded pour les deux projets. Dans spring-boot-unittest, les liens de repo ne seront pas rendus correctement qui utilise spring-boot-2.x et dans spring-hypermedia les liens sont rendus correctement qui utilise spring-boot.15.x. votre aide sera vraiment appréciée.


J'ai donc vérifié à nouveau votre code. C'est fondamentalement la même chose, sauf pour le EntityModel qui n'est pas correctement sérialisé en 2.2.1. Je vous suggère de découvrir comment sérialiser des instances de EntityModel contenues dans une List puis d'utiliser ma solution proposée détaillée dans ma réponse. Mais je vous encourage toujours à utiliser le CollectionModel et à utiliser correctement HAL afin que vous ayez des liens au niveau de la collection.



2
votes

Lors de l'utilisation de Spring Boot 1.5, vous vous reposiez sur une limitation de Spring HATEOAS qui a abouti à l'utilisation du ObjectMapper Jackson qui est destiné à être spécifique à HAL comme ObjectMapper à l'échelle de l'application . Cette pollution signifiait que le formatage HAL était appliqué aux réponses alors qu'il n'aurait pas dû l'être.

La limitation a été corrigée dans Spring HATEOAS 1.0 et son ObjectMapper spécifique à HAL ne pollue plus l'ensemble de l'application. Si vous souhaitez que l'application principale ObjectMapper applique une sérialisation de style HAL, vous pouvez la réactiver en la personnalisant:

@Bean
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder,
                                 HypermediaMappingInformation mappingInformation) {
    ObjectMapper objectMapper = builder.build();
    mappingInformation.configureObjectMapper(objectMapper);
    return objectMapper;
}

Bien que je pense que ce qui précède va travail, je ferai écho à la préoccupation que Daniel a soulevée dans sa réponse concernant le format de la réponse et la conformité avec la spécification HAL. p>


1 commentaires

Cela fonctionne très bien !!! Merci beaucoup pour vos messages.



0
votes

Je recommande d'étendre CollectionModel avec un champ de type collection annoté @JsonValue, puis dans votre assembleur, remplacez toCollectionModel pour renvoyer le CollectionModel personnalisé

  @Override
public CustomCollectionModel toCollectionModel(Iterable<? extends T> entities) {
    List<EntityModel<T>> resourceList = new ArrayList<>();

    for (T entity : entities) {
        resourceList.add(toModel(entity));
    }
    return new CustomCollectionModel(resourceList);
}

puis dans votre assembleur

XXX


0 commentaires