Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for HATEOAS-Links in Json-Views #387

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

tinne
Copy link

@tinne tinne commented Sep 8, 2015

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.)

Karsten Tinnefeld and others added 8 commits September 6, 2015 00:01
when filtered by providing a tooling interface, now complete.

Signed-off-by: Karsten Tinnefeld <k.tinnefeld@googlemail.com>
Signed-off-by: Karsten Tinnefeld <k.tinnefeld@googlemail.com>
Signed-off-by: Karsten Tinnefeld <k.tinnefeld@googlemail.com>
Signed-off-by: Karsten Tinnefeld <k.tinnefeld@googlemail.com>
Signed-off-by: Karsten Tinnefeld <k.tinnefeld@googlemail.com>
Signed-off-by: Karsten Tinnefeld <k.tinnefeld@gmail.com>
Signed-off-by: Karsten Tinnefeld <k.tinnefeld@googlemail.com>
Signed-off-by: Karsten Tinnefeld <k.tinnefeld@gmail.com>
@sergiorc
Copy link

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

@tinne
Copy link
Author

tinne commented Nov 29, 2015

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.

@sergiorc
Copy link

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!

@odrotbohm
Copy link
Member

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.

@tinne
Copy link
Author

tinne commented Jan 13, 2016

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.

@odrotbohm
Copy link
Member

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.

@sergiorc
Copy link

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...)

@gregturn
Copy link
Contributor

gregturn commented Jan 14, 2016

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.

@sergiorc
Copy link

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.

@tinne
Copy link
Author

tinne commented Jan 14, 2016

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.

@srinivaspr
Copy link

@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 ?

@tinne
Copy link
Author

tinne commented Feb 20, 2016

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

@cesartl
Copy link

cesartl commented May 31, 2016

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.

@sergiorc
Copy link

Hi @olivergierke , have you made a decision?

@odrotbohm odrotbohm force-pushed the master branch 2 times, most recently from 4ebc1be to 266ad50 Compare July 25, 2016 18:32
@tinne
Copy link
Author

tinne commented Aug 31, 2016

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

@tachocarpintero
Copy link

@olivergierke , have you made a decission?

@pivotal-issuemaster
Copy link

@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
Copy link

@tinne Thank you for signing the Contributor License Agreement!

@forbode
Copy link

forbode commented Apr 13, 2017

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.

@jkromski
Copy link

jkromski commented Jan 9, 2019

Is it possible to have this feature?

@Puspendert
Copy link

Any planned date/release for this feature?

@gregturn gregturn changed the base branch from master to main April 7, 2021 18:21
@ajitkumargiri
Copy link

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

@a-tarasiuk
Copy link

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.