eekboom
eekboom

Reputation: 5812

Can't get HAL rendering with Spring HATEOAS and Jersey

I used the Spring Initializr (https://start.spring.io/ ) to create an app with only starters "Jersey (JAX-RS)" and "HATEOAS". Then I added @EnableHypermediaSupport(type = HAL), but I just can't get links rendered correctly in HAL format.

The format I aim for:

{
    "name": "Hello",
    "count": 42,
    "_links": {
        "self": {
            "href": "http://localhost:8080/api/foo"
        }
    }
}

What I currently get instead:

{
    "name": "Hello",
    "count": 42,
    "links": [
        {
            "rel": "self",
            "href": "http://localhost:8080/api/foo"
        }
    ]
}

Besides the classes generated by the initializr, I added this configuration

@Configuration
@ApplicationPath("api")
@Component
@EnableHypermediaSupport(type = HAL)
public class JerseyConfig extends ResourceConfig {
    public JerseyConfig() {
        registerEndpoints();
    }

    private void registerEndpoints() {
        register(FooResource.class);
    }
}

this resource (endpoint):

@Component
@Path("foo")
public class FooResource {

    @GET
    @Produces(MediaTypes.HAL_JSON_VALUE)
    public Resource<Foo> getFoo() {

        final Resource<Foo> fooTo = new Resource<>(new Foo("Hello", 42));

        fooTo.add(JaxRsLinkBuilder.linkTo(FooResource.class).withRel("self"));

        return fooTo;
    }
}

and a dummy bean:

public class Foo {
    private String name;
    private int count;

    public Foo() {
    }

    public Foo(String name, int count) {
        this.name = name;
        this.count = count;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
}

I also tried adding

@EnableHypermediaSupport(type = HAL)

next to the @SpringBootApplication on the application class.

When I

GET localhost:8080/api/foo

I get a response with correct content type

application/hal+json;charset=UTF-8

but still the wrong format ("link" property as array instaed of "_links" property as object/map).

Upvotes: 3

Views: 1572

Answers (2)

Paul Samsotha
Paul Samsotha

Reputation: 209012

Just to add to the answer from @zerflagL, to register the Jackson2HalModule, you should use a ContextResolver, as mentioned here. Also on top of this module, you need to configure the HalHandlerInstantiator with the ObjectMapper. If you miss the last part, you will get exceptions about Jackson not being able to instantiate the serializers because of no default constructor.

@Provider
public class JacksonContextResolver implements ContextResolver<ObjectMapper> {

    private final ObjectMapper mapper = new ObjectMapper();

    public JacksonContextResolver() {
        mapper.registerModule(new Jackson2HalModule());
        mapper.setHandlerInstantiator(new HalHandlerInstantiator(
                new AnnotationRelProvider(), null, null));
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        return mapper;
    }
}

Then just register the context resolver with your Jersey ResourceConfig

register(new JacksonContextResolver());

Upvotes: 4

a better oliver
a better oliver

Reputation: 26828

Spring HATEOAS was developed to work with Spring MVC. Everything related to JSON serialization / deseralization is registered for Spring MVC, MappingJackson2HttpMessageConverter to be precise.

If you want the same for Jersey you would have to set up Jackson so that org.springframework.hateoas.hal.Jackson2HalModule gets registered.

Upvotes: 3

Related Questions