Returned json unexpected, has "links" spelled as "_links" and structure different, in Spring hateoas
Asked Answered
L

7

15

As the title says, I have a resource object Product extending ResourceSupport. However, the responses I receive have the property "_links" instead of "links" and have a different structure.

{
  "productId" : 1,
  "name" : "2",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/products/1"
    }
  }
}

Based on the HATEOAS Reference, the expected is:

{
  "productId" : 1,
  "name" : "2",
  "links" : [
    {
      "rel" : "self"
      "href" : "http://localhost:8080/products/1"
    }
  ]
}

Was this intended? Is there a way to change it, or at leas the "link" if not the structure?

I added the selfLink through the following snippet:

product.add(linkTo(ProductController.class).slash(product.getProductId()).withSelfRel());

I am using spring boot with the following build file:

dependencies {
    compile ("org.springframework.boot:spring-boot-starter-data-rest") {
        exclude module: "spring-boot-starter-tomcat"
    }

    compile "org.springframework.boot:spring-boot-starter-data-jpa"
    compile "org.springframework.boot:spring-boot-starter-jetty"
    compile "org.springframework.boot:spring-boot-starter-actuator"

    runtime "org.hsqldb:hsqldb:2.3.2"

    testCompile "junit:junit"
}
Lottielotto answered 21/8, 2014 at 16:13 Comment(5)
I see at least one place on that page where the _links portion is referenced, in the CurieProvider API. Are you certain you're always supposed to receive links the way you think you are?Distrust
@Lottielotto are you specifying HAL with something like @EnableHypermediaSupport(type = HypermediaType.HAL)? HAL json format uses _links, but Spring default is simply "links".Hidden
I actually did not specify anything.Lottielotto
Some behavior changed recently, as on Spring Boot 1.2.8 and Spring HATEOAS 0.16.0 my configuration gives me "_links", but if I upgrade to Spring Boot 1.3.1 I start getting "links".Abramson
I am using Spring boot 1.5.4 and I start getting "_Link". The problem is Jersey client does not recognize the structure. Do you could solve the problem?Dodgson
H
7

Spring Boot now (version=1.3.3.RELEASE) has a property that controls the output JSON format of the PagedResources.

Just add the following config to your application.yml file:

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

if you need the output to be like (based on question):

{
  "productId" : 1,
  "name" : "2",
  "links" : [
    {
      "rel" : "self"
      "href" : "http://localhost:8080/products/1"
    }
  ]
}

Edited:

By the way, you only need @EnableSpringDataWebSupport annotation in this way.

Hen answered 15/6, 2016 at 0:47 Comment(0)
K
7

Another option would be to disable the whole hypermedia auto-configuration feature (this is how it's done in one of the spring-boot + REST examples here):

@EnableAutoConfiguration(exclude = HypermediaAutoConfiguration.class)

As far as I know, HypermediaAutoConfiguration doesn't really do much except for configuring HAL, so it should be perfectly fine to disable it.

Keeper answered 14/8, 2016 at 7:35 Comment(4)
disabling doesn't change the format of links in the json.Kolva
@Phaneendra: well, it 100% worked for me. But I guess time has passed, so I'm not sure this solution is relevant with newer versions of spring-boot.Keeper
hmm..possible.! I'm not allowed to change the vote now..oops!Kolva
This also worked for me. After an update from spring boot 2.2 to 2.5, my hateos links were added in the HAL format as _links instead of links. This trick restored the previous behavior.Doner
M
5

I have notice the problem appears at least when you trying to return an object extends ResourseSupport vs object containing object extends ResourseSupport. You may return even List or Array of object(s) extends ResourseSupport and have the same effect. See the example :

@RequestMapping(method = GET, value = "/read")
public NfcCommand statusPayOrder() {
    return generateNfcCommand();
}

have response:

{
    "field": "123",
    "_links": {
        "self": {
            "href": "http://bla_bla_bla_url"
        }
    }
}

When try to wrap as List:

@RequestMapping(method = GET, value = "/read")
public List<NfcCommand> statusPayOrder() {
    return Arrays.asList(generateNfcCommand());
}

getting:

[
    {
        "field": 123
        "links": [
            {
                "rel": "self",
                "href": "http://bla_bla_bla_url"
            }
        ]
    }
]

Changing structure of an answer is not the right decision, but we can try to think further this way.

Monteiro answered 29/9, 2017 at 11:39 Comment(1)
It took me a whole day to find this out. -.- Thanks for posting this update! :)Scarabaeid
S
4

If you have HAL available it will be selected for you by spring boot (and "_links" is what you get with HAL). You should be able to @EnableHypermediaSupport manually to override the defaults.

Shabbir answered 21/8, 2014 at 18:16 Comment(4)
I checked today and it seems @EnableHypermediaSupport requires type of HypermediaType. But the only one available is HALLottielotto
I guess the default isn't classified as hypermedia. If you @EnableHypermediaSupport and don't specify the type, what happens?Shabbir
HAL seems to be the default hypermedia type.Lottielotto
I guess you need to not enable hypermedia support then?Shabbir
H
1

I've recently had the opposite issue, expected the HAL form (i.e. _links) but always got links, although using @EnableAutoConfiguration.

The solution to my issue might help here as well. Due to copying&pasting from an example I found, I had two mediatypes in my RestController's @RequestMappingannotation

@RequestMapping(path="/example", produces = {MediaType.APPLICATION_JSON_VALUE, "application/hal+json"})

With @EnableAutoConfiguration Spring HATEOAS registers an ObjectMapper for application/hal+json only and in my application there was another one responsible for MediaType.APPLICATION_JSON_VALUE which won the selection and rendered standard JSON.

So my solution to only set produces="application/hal+json" so that the Spring HATEOAS object mapper is choosen. In your case you should try to keep MediaType.APPLICATION_JSON_VALUE

Heaven answered 13/7, 2018 at 13:46 Comment(0)
D
0

I am sure you are using Spring data with @RepositoryRestResource annotation

@RepositoryRestResource    
public interface XX extends CrudRepository<AA, String>

If you want to remove default HAL behavior you can add following annotaion param

@RepositoryRestResource(exported = false)
public interface XX extends CrudRepository<AA, String>

Above configurition Spring Data REST to only expose rest endpoints for resources in the parent project, without having to explicitly annotate every repository in the dependency project.

As per doc

Hiding certain repositories, query methods, or fields You may not want a certain repository, a query method on a repository, or a field of your entity to be exported at all. Examples include hiding fields like password on a User object or similar sensitive data. To tell the exporter to not export these items, annotate them with @RestResource and set exported = false.

For example, to skip exporting a Repository:

@RepositoryRestResource(exported = false)
interface PersonRepository extends CrudRepository<Person, Long> {}
Distant answered 13/7, 2017 at 16:27 Comment(1)
I have the same problem and I am not using @RepositoryRestResourceMonteiro
F
0

I was also getting the '_links' thing... I found that in my @RequestMapping I mistakenly had: @RequestMapping("id") when I should have had: @RequestMapping("{id}")

When I fixed that, the '_links' thing went away.

Fiance answered 28/9, 2022 at 1:4 Comment(3)
You can better format this answer, even if it is correctRaines
Needs to bebetter formatterRaines
You’re right. I can do that.Fiance

© 2022 - 2024 — McMap. All rights reserved.