spring-hateoas
spring-hateoas copied to clipboard
Support for HATEOAS-Links in Json-Views
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.)
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.
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.
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!
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.
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 Resource
s.
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.
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.
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...)
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.
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.
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 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, this is up to @olivergierke to decide. Please convince him by explaining what you would like to do with the feature.
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.
Hi @olivergierke , have you made a decision?
Not sure which feedback this is still waiting for. KIndly elaborate on the labeling.
@olivergierke , have you made a decission?
@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.
@tinne Thank you for signing the Contributor License Agreement!
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.
Is it possible to have this feature?
Any planned date/release for this feature?
Hi @tinne and @odrotbohm , Is there any way in spring boot to filter json response with hateoas links?
@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.