Disable Hypertext Application Language (HAL) in JSON?
Using Spring Data REST with JPA in version 2.0.2.RELEASE.
How can I disable Hypertext Application Language (HAL) in the JSON ? http://stateless.co/hal_specification.html
I have tried many things already, but to no avail. For example, I have set Accept and Content-type headers to "application/json" instead of "application/hal+json" but I still receive the JSON content with hyper links.
For example, I'd like to get something like:
{
"name" : "Foo",
"street" : "street Bar",
"streetNumber" : 2,
"streetLetter" : "b",
"postCode" : "D-1253",
"town" : "Munchen",
"country" : "Germany",
"phone" : "+34 4410122000",
"vat" : "000000001",
"employees" : 225,
"sector" : {
"description" : "Marketing",
"average profit": 545656665,
"average employees": 75,
"average profit per employee": 4556
}
}
Instead of:
{
"name" : "Foo",
"street" : "street Bar",
"streetNumber" : 2,
"streetLetter" : "b",
"postCode" : "D-1253",
"town" : "Munchen",
"country" : "Germany",
"phone" : "+34 4410122000",
"vat" : "000000001",
"employees" : 225,
"_links" : {
"self" : {
"href" : "http://localhost:8080/app/companies/1"
},
"sector" : {
"href" : "http://localhost:8080/app/companies/1/sector"
}
}
}
Thanks for your help.
(Hyper)media types
The default settings for Spring Data REST use HAL as the default hypermedia representation format, so the server will return the following for the given Accept
headers:
- No header ->
application/hal+json
-> HAL -
application/hal+json
->application/hal+json
-> HAL -
application/json
->application/json
-> HAL (this is what the default configures) -
application/x-spring-data-verbose+json
->application/x-spring-data-verbose+json
-> a Spring Data specific format (usinglinks
for the links container andcontent
as wrapper for the collection items.
If you configure RepositoryRestConfiguration.setDefaultMediaType(…)
to a non-HAL format, the server will return the Spring Data specific JSON format unless you explicitly ask for application/hal+json
. Admittedly the configuration option is probably a bit misleading, so I filed DATAREST-294 to improve this. The issue was resolved in 2.1 RC1 (Dijkstra) 2014.
Note that we effectively need a hypermedia format in place to be able to express relations between managed resources and enable discoverability of the server. So there's no way you'll be able to get rid of it completely. This is mostly due to the fact that you could easily crash the server if you expose entities that have bidirectional relationships or make up an enormous object graph.
Inlining related entities
If you never want to have sectors linked to and always inline them, one option is to simply exclude the SectorRepository
from being exported as a REST resource in the first place. You can achieve this by annotating the repository interface with @RepositoryRestResource(exported = false)
.
To get a representation returned as you posted in your lower example have a look at the projections feature introduced in Spring Data REST 2.1 M1. It basically allow you to craft optional views on a resource that can differ from the default one via a simple interface.
You'd basically define an interface:
@Projection(name = "foo", types = YourDomainClass.class)
interface Inlined {
// list all other properties
Sector getSector();
}
If you either put this interface into a (sub)package of your domain class or manually register it via RepositoryRestConfiguration.projectionConfiguration()
the resources exposing YourDomainClass
will accept a request parameter projection
so that passing in foo
in this example would render the inlined representation as you want it.
This commit has more info on the feature in general, this commit has an example projection defined.
So you want 2 things:
1) get rid of _links
field
2) include the related sector
field
Possible solution (works for me :D)
1) get rid of _links
For this create the class below:
[... package declaration, imports ...]
public class MyRepositoryRestMvcConfiguration extends RepositoryRestMvcConfiguration {
public MyRepositoryRestMvcConfiguration(ApplicationContext context, ObjectFactory<ConversionService> conversionService) {
super(context, conversionService);
}
@Bean
protected LinkCollector linkCollector() {
return new LinkCollector(persistentEntities(), selfLinkProvider(), associationLinks()) {
public Links getLinksFor(Object object, List<Link> existingLinks) {
return new Links();
}
};
}
}
and use it e.g.:
[... package declaration, imports ...]
@SpringBootApplication
@Import({MyRepositoryRestMvcConfiguration.class})
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
I'm pretty sure (99%, but not tested) that you won't need this class for removing the _links
for the related entity/entities included the way next point (2) is showing.
2) include the related sector
field
For this you could use Excerpts (especially made for this scenario). Because the Spring example is so eloquent and it's silly to just copy it here I'll just point it: https://docs.spring.io/spring-data/rest/docs/3.1.x/reference/html/#projections-excerpts.excerpting-commonly-accessed-data.
But just for the record and your convenience I'll paste the main parts of the spring example:
@Projection(name = "inlineAddress", types = { Person.class })
interface InlineAddress {
String getFirstName();
String getLastName();
Address getAddress();
}
see at Projection javadoc that types
means The type the projection type is bound to.
The excerpt could be used this way:
@RepositoryRestResource(excerptProjection = InlineAddress.class)
interface PersonRepository extends CrudRepository<Person, Long> {}
in order to get this (when also using MyRepositoryRestMvcConfiguration):
{
"firstName" : "Frodo",
"lastName" : "Baggins",
"address" : {
"street": "Bag End",
"state": "The Shire",
"country": "Middle Earth"
}
}
For you the sector
is the equivalent of address
.
Final notes
When returning arrays the _links
field won't be removed (it's too intrusive to do it); in the end you'll have something like this:
{
"_embedded" : {
"persons" : [ {person1}, {person2}, ..., {personN} ]
},
"_links" : {
e.g. first, next, last, self, profile
},
"page" : {
"size" : 1,
"totalElements" : 10,
"totalPages" : 10,
"number" : 0
}
}
As you can see even if we'd have _links
removed that still won't be enough; one would probably also want _embedded
replaced by persons
which would lead to less maintainable code (too much spring intrusive overrides). But if one really wants these too he should start checking RepositoryRestMvcConfiguration
and RepositoryEntityController.getCollectionResource
.
Spring is evolving so I feel the need to point that this works with at least:
spring-data-rest-webmvc 3.1.3.RELEASE
or, if you prefeer spring boot version:
spring-boot-starter-parent 2.1.1.RELEASE