Search code examples
arraysjsonspring-hateoas

Spring HATEOAS links return in HAL format for single objects, and the spec format for an array of objects


I am using Spring HATEOAS 0.20 (also tried 0.23) and there seems to be very strange behavior when returning a single object vs. an array of objects. When returning a single object, the resulting JSON returns HATEOAS links that are formatted via the HAL spec ("_links", which I do not want):

{
  "_Id": 161,
  ...
  "_links": {
    "self": {
      "href": "http://localhost:8080/library/161"
    }
  }
}

When returning an array of objects, the resulting JSON returns HATEOAS links in the spec format ("links", which I do want):

[
  {
    "_Id": 277,
    ...
    "links": [
      {
        "rel": "self",
        "href": "http://localhost:8080/library/277"
      }
    ]
  }
]

I have not specified @EnableHypermediaSupport, so HAL should not be supported. But, yet I still get HAL format when returning a single object.

Does anyone know why this would be happening? What need to be done to return a single object with HATEOAS links via the HATEOAS spec?

===== UPDATE TO POST BASED ON REQUEST FOR MORE INFO =====

If I return the JSON load using the following code, I get the HAL links:

//
//------------------- Get Single Membership As Single Object ----------------------------------------------
//
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping(value = REQUEST_GET_MEMBERSHIP, method = RequestMethod.GET,  produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<MembershipResource> getMembership(@PathVariable("membership-id") Integer membershipId) {
    Membership membership = membershipRepository.findOne(membershipId);

    if (membership == null) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("error", "Membership with ID of " + membershipId + " does not exist.");
        return new ResponseEntity<MembershipResource>(headers, HttpStatus.NOT_FOUND);
    }

    MembershipResourceAssembler assm = new MembershipResourceAssembler();
    MembershipResource membershipResource = assm.toResource(membership);

    System.out.println("membership id: " + membership.getId());
    return new ResponseEntity<MembershipResource>(membershipResource, HttpStatus.OK);
}

The above will produce HAL-specification style links:

{
  "_Id": 277,
  "member": {
    "_Id": 112,
    "title": "Mr.",
    "firstName": "ABC",
    "middleName": "",
    "lastName": "XYZ",
    "active": true,
    "_links": {
      "self": {
        "href": "http://localhost:8080/members/112"
      }
    }
  },
  "membershipTermType": "2016",
  "card": {
    "_Id": 27,
    "name": "27",
    "graphicUrl": "",
    "number": 27,
  },
  "active": false,
  "paid": true,
  "renewal": true,
  "termsAndConditionsAcknowledged": true,
  "_links": {
    "self": {
      "href": "http://localhost:8080/memberships/277"
    }
  }
}

If I modify the method to return a List of the single member, the returned JSON load is with HATEOAS-specification links:

//
//------------------- Get Single Membership As List --------------------------------------------------------
//
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping(value = REQUEST_GET_MEMBERSHIP, method = RequestMethod.GET,  produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<List<MembershipResource>> getMembership(@PathVariable("membership-id") Integer membershipId) {
    Membership membership = membershipRepository.findOne(membershipId);

    if (membership == null) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("error", "Membership with ID of " + membershipId + " does not exist.");
        return new ResponseEntity<List<MembershipResource>>(headers, HttpStatus.NOT_FOUND);
    }

    MembershipResourceAssembler assm = new MembershipResourceAssembler();
        MembershipResource membershipResource = assm.toResource(membership);

    ArrayList<MembershipResource> list = new ArrayList<MembershipResource>();
    list.add(membershipResource);

    System.out.println("membership id: " + membership.getId());
    return new ResponseEntity<List<MembershipResource>>(list, HttpStatus.OK);
}

The above will produce HATEOAS-specification style links:

[
  {
    "_Id": 277,
    "member": {
      "_Id": 112,
      "title": "Mr.",
      "firstName": "ABC",
      "middleName": "",
      "lastName": "XYZ",
      "active": true,
      "links": [
        {
          "rel": "self",
          "href": "http://localhost:8080/members/112"
        }
      ]
    },
    "membershipTermType": "2016",
    "card": {
      "_Id": 27,
      "name": "27",
      "graphicUrl": "",
      "number": 27,
      "links": []
    },
    "active": false,
    "renewal": true,
    "paid": true,
    "termsAndConditionsAcknowledged": true,
    "links": [
      {
        "rel": "self",
        "href": "http://localhost:8080/memberships/277"
      }
    ]
  }
]

Does anyone know why this would be happening? What need to be done to return a single object with HATEOAS links via the HATEOAS spec?


Solution

  • Being that I am using Spring Boot as my application server, @zeroflagL suggested that I try adding @EnableAutoConfiguration(exclude = HypermediaAutoConfiguration.class) in your application or spring.hateoas.use-hal-as-default-json-media-type=false in your properties file.

    Adding the config annotation of @EnableAutoConfiguration(exclude = HypermediaAutoConfiguration.class) to my application class fixed the issue.