spring-hateoas icon indicating copy to clipboard operation
spring-hateoas copied to clipboard

Support for HATEOAS-Links in Json-Views

Open tinne opened this issue 9 years ago • 23 comments

The @JsonView annotation supports filtering class trees in order to provide custom views on specific partial data, cf. eg. https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring

The main purpose of this patch is to provide a means of views including the links properties of REST models using Resource(s). See the tests in ResourceLinksVisibleTest for further discussion.

Usage: extend ResourceLinkView to denote your view properties.

(This pull request supersedes pull request #359 and solves issue #351.)

tinne avatar Sep 08 '15 20:09 tinne

Hi @tinne

I've tested your forked branch (https://github.com/tinne/spring-hateoas/tree/resources-links) and it's not working for me. Now, every hateoas link information is lost when using a JsonView annotation at controller level, probably, because changes introduced in last versions of spring-hateoas (it worked with 0.18.0.BUILD-SNAPSHOT).

Check my .diff file (spring-hateoas.txt). It includes changes to fix this issue. To summarize, I've added @JsonView to HalLink and PageMetada inner fields.

spring-hateoas.txt

sergiorc avatar Nov 18 '15 18:11 sergiorc

Hi @sergiorc , please check with my test ResourceLinksVisibleTest.pagedResourcesSerializationWithResourcesLinksVisibleSeesMetadata - according to my tests, serializedPagedResources gets the value of ..."page":{"size":3,"totalElements":3,"totalPages":1,"number":0}} due to the fact that PagedResources.getMetaData is itself annotated @JsonView(ResourcesLinksVisible.class). (Also, we should agree to add the annotation to either the fields or the getters in a uniform way.)

Regarding the patch to Jackson2HalModule, I do not currently use HAL functionality, could you kindly provide a test?

The third change regarding JacksonSerializationTest is a bug I already addressed separately in issue #385.

tinne avatar Nov 29 '15 14:11 tinne

Hi @tinne, sorry by the delay. I've been reviewing unit tests and I'm pretty sure they are not working as expected.

You can check it using your forked branch (without my changes) and removing @JsonView(NewJsonView.class) from ResourceLinksVisibleTest.BeanWithView class.

As long as I understand, this should make a lot of tests fail, but they keep working!

sergiorc avatar Jan 11 '16 12:01 sergiorc

I don't quite get the root intention of that PR. If you don't want to expose any links, simply don't return Resource instances from the controller. Assume clients requesting HAL, which a Resource can naturally be rendered to but the controller activating a JSON view that prevents those properties from being rendered, you'd return a representation that doesn't conform to the HAL spec.

odrotbohm avatar Jan 13 '16 16:01 odrotbohm

The root intention of the PR is to provide a uniform data model for overview and detail views of some model, perhaps to several layers. The need came from a set of REST services which had methods a) give me a long list of all available SomeContainer objects and b) regarding one of these SomeContainer objects, give me more detail.

The proposed solution is to have the result set contain a Resource<List<SomeContainer>> filtered to the most important attributes of SomeContainer by a JSON view, and the detail service an unfiltered returning Resource<SomeContainer>.

Problem is, when applying any JSON view, several Resource properties regarding meta data are filtered away. PR proposes to provide a base interface for JSON views which include any such meta data field, therefore providing proper (though not fully automatic) support for using JSON views in combination with Resources.

I hope this explanation makes things a bit clearer. Feel free to check again if this is not all clear. I could also provide some demo code if needed.

tinne avatar Jan 13 '16 22:01 tinne

I still don't think we can introduce the change as all of a sudden people would have to include the JSON View class you used in their setup. So we all of a sudden run into a brittle scenario that breaks the media-type compatibility.

TBH, I think JSON Views are a pretty broken concept in the first place, that's why we didn't bother supporting it so far. It might be bad news but the PR is pretty much acknowledging this. Not because you did anything wrong or bad but because of the flawed concept.

I just checked the Jackson docs again and it seems like you can just configure objectMapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false); and all not annotated properties will be included by default.

odrotbohm avatar Jan 14 '16 11:01 odrotbohm

I know JSON Views are not a very orthodox practice from REST architecture perspective, but obtaining a reduced -filtered- version of your resource is a common requirement in a lot of cases (usually in embedded collection resources). JSON View is only a tool to avoid duplication of the endpoint and the model.

That said, I think PR inclusion should not produce any impact if the behaviour is:

  • If you don't use @JsonView, nothing changes.
  • If you apply @JsonView to your model and controller method, filter applies:
    • Only annotated fields of your model are included.
    • Every HAL metadata field is included (links, pagination...)

sergiorc avatar Jan 14 '16 11:01 sergiorc

It seems to me that the level of effort to make two models, a normal and detailed, would be about the same as trying to wire up this JsonView stuff. And simpler.

I think this is an attempt to optimize too heavily for DRY, and it seems burdensome. I'd rather just manage the two POJOs and have either two end points, or one endpoint driven by a parameter that returns Object.

gregturn avatar Jan 14 '16 13:01 gregturn

Effort can be similar in small project, but think in big ones, REST API's publishing hundreds of resources with 'filtered entities' requirements.

You'll have to maintain hundreds of duplicated POJO's and/or enpoints.

I don't think this PR really introduce complexity in spring-hateoas project, and also, it improves chances of HATEOAS adoption in any project. I have in mind REST projects using Spring MVC + JSON Views looking for HATEOAS support.

Other REST implementations (e.g. Jersey), include entity filtering as a native feature.

sergiorc avatar Jan 14 '16 15:01 sergiorc

Let me emphasize on @sergiorc's argument: spring-hateoas is currently broken with regard to JSON views and the tests go some way to show this effect. This is surely a proof nobody needs in a regression test and was only included to demonstrate why the fix is added.

The PR introduces an interface noone is forced to use of even take notice of, and spring-hateoas will continue to work as it used to (no problem without views, something somehow silly happens under views). Just in case anyone needs to apply JSON views, effectively adding some few annotations referencing a trivial interface to the HATEOAS library adds this support almost effortlessly for those who can make use of it.

Also, it is not only a matter of DRY, but of defining designed-exactly-to-their-use APIs without bothering about filtering the result of a lower layer result set in a multi-layer (e.g., micro service) architecture.

tinne avatar Jan 14 '16 20:01 tinne

@tinne Hi tinne,

This is a very useful future. I want to know when this feature will be available for release. can I apply patch and use this feature by assuming this will be available for next release ?

srinivaspr avatar Feb 18 '16 03:02 srinivaspr

@srinivaspr, this is up to @olivergierke to decide. Please convince him by explaining what you would like to do with the feature.

tinne avatar Feb 20 '16 12:02 tinne

Not sure what is the status with this but I would personally enjoy this feature if it were available. I have a fairly complex data model with nested documents in Mongoldb. Depending on the type of query I would like to show different level of details. For example in a findAllType query I prefer to show the minimum amount of information. For a findById query I want to show a higher level of details. This requirement is well satisfied using JSON views. I'm not experienced enough with REST APIs to comment on whether this is philosophically correct. Jackson offers this feature and it seems a few of us are enjoying it. I also like wrapping my model classes inside a ResourceSupport object in order to get HATEOAS for free. This is also a great feature. It's just a bit of a shame that the two can't work together.

cesartl avatar May 31 '16 13:05 cesartl

Hi @olivergierke , have you made a decision?

sergiorc avatar May 31 '16 15:05 sergiorc

Not sure which feedback this is still waiting for. KIndly elaborate on the labeling.

tinne avatar Aug 31 '16 21:08 tinne

@olivergierke , have you made a decission?

tachocarpintero avatar Mar 30 '17 14:03 tachocarpintero

@tinne Please sign the Contributor License Agreement!

Click here to manually synchronize the status of this Pull Request.

See the FAQ for frequently asked questions.

pivotal-issuemaster avatar Mar 30 '17 14:03 pivotal-issuemaster

@tinne Thank you for signing the Contributor License Agreement!

pivotal-issuemaster avatar Mar 30 '17 21:03 pivotal-issuemaster

One aspect that I have not heard brought up is security. I have domain objects that I want to expose portions of based on User Roles. The solution to this is quite simple if @JsonView is enabled.

Creating two different domain models, as suggested, is a non starter. That would entail entirely different logic and domain models creating unnecessary complexity.

If this is possible via a combination of Spring Security + HATEOAS projections I am unaware.

forbode avatar Apr 13 '17 20:04 forbode

Is it possible to have this feature?

jkromski avatar Jan 09 '19 10:01 jkromski

Any planned date/release for this feature?

Puspendert avatar Jun 13 '19 19:06 Puspendert

Hi @tinne and @odrotbohm , Is there any way in spring boot to filter json response with hateoas links?

ajitkumargiri avatar Sep 09 '21 08:09 ajitkumargiri

@ajitkumargiri , need help.

I have Java object @JsonView:

public class OrderDto extends AbstractDto<OrderDto> {
    @Valid
    private UserDto user;

    @Valid
    private GiftCertificateDto giftCertificate;

    @JsonView(View.FindOrderForUser.class)
    private BigDecimal price;

    @JsonView(View.FindOrderForUser.class)
    private LocalDateTime purchaseDate;
}

Why, when returning this OrderDto object in the controller along with HATEOAS references, I just get {} in response:

@GetMapping(UrlMapping.ORDER_USER)
@JsonView(View.FindOrderForUser.class)
public EntityModel<OrderDto> findOrderForUser(@PathVariable long userId, @PathVariable long orderId) {
        OrderDto orderDto = orderService.findOrderForUser(orderId, userId);
        return EntityModel.of(orderDto);
}

Result:

{}

But if I return a simple OrderDto object, then everything works and only the fields marked with the @ JsonView annotation are displayed:

@GetMapping(UrlMapping.ORDER_USER)
@JsonView(View.FindOrderForUser.class)
public OrderDto findOrderForUser(@PathVariable long userId, @PathVariable long orderId) {
    return orderService.findOrderForUser(orderId, userId);
}

Result:

{
    "createDate": "2021-06-25T21:38:01",
    "lastUpdateDate": "2021-05-21T17:19:06"
}

But in this case, it is necessary that the HATEOAS links are present and the result is like this:

{
    "price": 1.00,
    "purchaseDate": "2021-11-15T10:38:45.193",

    "_links": {
        "self": {
            "href": "http://localhost:8080/orders/1"
        }
    }

Also in application.properties I set the property spring.jackson.mapper.default-view-inclusion = true, but the controller method would return all fields of the OrderDto object in this situation.

@ JsonIgnore is not a solution, because sometimes I need to output as JSON all fields of an OrderDto object, and in some situations like this, not all fields.

a-tarasiuk avatar Nov 16 '21 18:11 a-tarasiuk