Git Product home page Git Product logo

Comments (36)

ckristo avatar ckristo commented on May 28, 2024 9

I think I couldn't explain my problem well, sorry. I fully agree with you, the choice of using URIs to identify Spring HATEOAS resources is fine, especially when comparing that choice to other technologies like RDF where resources are also uniquely identified via URIs.

My problem -- and I think the origin of the issue as I understood it -- are quite different. The issue is not the Java API / the chosen ID type, but the behavior when serializing / deserializing a Resource object to JSON (using Jackson). The problem I face:

I have a DTO type that has a property id and a Long getId() getter method. When wrapping that type into a Spring HATEOS resource (so that I can add a self link to the JSON produced by my REST service) and using Jackson to serialize it, the JSON property id of the resulting JSON object is always null.

As far as I could find investigate, this is because ResourceSupport.getId() is annotated with @JsonIgnore. I don't know why this annotation affects the serialization of the resource's content (Resource.getContent() annotated with @JsonUnwrapped), but it does. I would have expected that Link ResourceSupport.getId() is ignored and not added to the JSON object, but my DTO's Long getId() is added correctly (and not present but always null).

from spring-hateoas.

faridanthony avatar faridanthony commented on May 28, 2024 9

@olivergierke
If you are repeating yourself over and over and we are still bringing this up, then I think you should consider why we are asking for this. Personally I don't think it's up to a framework to decide what I should or shouldn't be exposing in my response payloads. I get there is a "workaround" but having to list every class I want the id exposed isn't really all that efficient.

BTW: https://en.wikipedia.org/wiki/HATEOAS
As you can see here account number is returned in the example, which is the "identifier" for the account, you see this is the urls:

There are valid points on either side but a framework is meant to help accomplish real-world goals without having to reinvent the wheel. I'd urge you all to reconsider.

from spring-hateoas.

odrotbohm avatar odrotbohm commented on May 28, 2024 8

As indicated in #66, the ID of a resource is the URI, hence the method implementation. So if you need to expose a document field named id you probably need to expose a dedicated getter and map that using @JsonProperty.

from spring-hateoas.

Laures avatar Laures commented on May 28, 2024 8

actually the easiest thing to do is to annotate (or mix-in) @JsonProperty("entity-id") or something like that to rename the id property on a common id-interface of your entities (you can't do that on Identifiable).

While i understand @olivergierke i believe it is a mistake to force a certain behaviour on framework users out of principle. there is nothing gained by this specific naming of the interface method.

its not impossible to avoid id properties altogether (post and put operations can work fine without them) it just forces you to fully embrace hypermedia. which is something that is still difficult to sell as a design decision.

from spring-hateoas.

sudoapt-getclean avatar sudoapt-getclean commented on May 28, 2024 7

Just a quick share of how I "fixed this". I connect a spring boot data-jpa application (that encapsulates my database) to a webmvc web-backend via spring boot data-rest.
The problem above boils down to not being able to use the property id, in the transfer via springhateoas, so my soltion boils down to change the property name on the wire-only. In the data backend I annotate id's with @JsonProperty(value = "dataId")

    @Id
    @JsonProperty(value = "dataId")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

in the frontend I make deserialization work by just adding @JsonAlias(value = "dataId"), When the data is served by the frontent it is serialized as id just like originally intended.

    @JsonAlias(value = "dataId")
    private int id;

No magic, nothing genius, but this solution at least allows me to encapsulate the changes I didn't want to be making.
ps. this would obviously be a funnier name @JsonProperty(value = "link") :-)

from spring-hateoas.

Tristan971 avatar Tristan971 commented on May 28, 2024 4

The term entity doesn't exist in the REST world. Nothing here is about making it easy to access server side entities.

And software engineering is also (at least mostly) about solving business problems by the means of computer programming rather than by the one of perfectly fulfilling an elegant but borderline impossible to use model.
Which explains the state of SQL databases. Which also explains the very existence of the Spring Framework to start with. And which also explains why we have inelegant things such as side-effects and so on and so forth. Reality forges the concepts, not the other way around. It never has worked and unsurprisingly never will.

It is a bit like the debate of whether a "kernel bugfix" in the Linux kernel breaking user-space applications is the fault of "badly implemented" applications or of the kernel.
There's no calling out a way to do (having an "id" property) that has been valid for 30 years stupid...

Anyway, it's your library after all, and once this is worked around by subclassing ResourceSupport it's pretty damn neat, so you win.

from spring-hateoas.

sshcherbakov avatar sshcherbakov commented on May 28, 2024 2

As mentioned in this thread, there are situations when id field name cannot be changed in the JSON payload schema. I also don't see why would this fact break any of the REST principles (what if I have completely different (from id) business meaning of that field , which is likely since the "real" id is encouraged to be conveyed via Location HTTP header and/or HATEOAS URI).

I also appreciate HATEOAS approach and would be glad to use Links/URIs instead of ids, especially since there is convenient way to access them in the Feign client response (Resource.getId()).

But I don't find a way to paste/use Link as an argument for Feign declarative client (please let me know if there is one). So using HATEOAS URI is not an option (not because I don't like, but because there is no way).

I was actually thinking about overriding ResourceSupport (and thus Resource, as I understand) to overcome my current issue, but was not sure if my implementation would be picked up by the framework.
Alternatively, I thought about overridingfeign.codec.Decoder and may be Resource message converter to pull the id field out of response.

@olivergierke When you are saying

If all you're interested in is CRUD via JSON over HTTP, then both libraries are just the wrong tools

which tools do you mean and are you saying that Feign client doesn't follow the best REST-ful practices?

from spring-hateoas.

bvulaj avatar bvulaj commented on May 28, 2024 1

I know this is outdated, but the issue is still marked OPEN, and we're still feeling a bit of pain on this.

While I also understand @olivergierke, and agree on the semantics of IDs, the framework does explicitly allow for serialization of IDs via RepositoryRestConfiguration.exposeIdsFor(...). To me, I'm not sure it makes sense to expose and support this functionality on one side, but not support it on the other without the aforementioned work-around.

from spring-hateoas.

tiarebalbi avatar tiarebalbi commented on May 28, 2024 1

It's happening the same thing with Jackson and as you can see in the second image I have the header Accept defined.

I'm using Spring Boot 1.4.0-SNAPSHOT.

When it happen my LocalDateTime is parsed to a object:
localDateTime : {content: "2016-06-28T19:40:26.757"}

screen shot 2016-06-28 at 8 21 52 pm

screen shot 2016-06-28 at 8 23 15 pm

from spring-hateoas.

pcornelissen avatar pcornelissen commented on May 28, 2024 1

Yes it's a pitty. I have talked to to the spring guys about this at spring
one in Barcelona this year and they won't change it. It's a clash between
two different concepts that share the same name for it, it's a bad
situation.

In Rest the ID is the URL to the entity so from this point of view it's OK
to shadow this.
So if another system refences this entity it should use the url to fetch
it. But in systems where you might need the DB ID you're in trouble.
It would be nice if there would be a way for us in this case. Maybe a
"IdAwareResource" class that doesn't shadow this would be ok?

2016-11-02 12:47 GMT+01:00 Sergey Shcherbakov [email protected]:

+1

I have a predefined JSON schema and Java POJOs being auto-generated from
the schema.
I cannot change the id name in the schema.
There is explicit support for exposing "id" fields in the
RepositoryRestConfiguration.exposeIdsFor() to expose ID fields in the
JSON body for the MongoDB entities.
And please note, that at the moment it is not possible to change the name
of "id" property in a Spring Data MongoDB entity, so it always needs to be
"id".

I'm using Spring Feign client to access my Spring Data REST MongoDB
service and I need to have "id" field of retrieved entity to be populated
in order to be able to reference to the newly created instance.

It seems that with this issue, the above technology constellation (default
Spring libraries) cannot support the basic use case of one Spring REST
service calling CRUD operation of another Spring Data REST/MongoDB.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#67 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABH3YKXx7kJn4sUB_5IK2slc4QtaW3L7ks5q6HhEgaJpZM4AosYR
.

Mit freundlichen Grüßen,
Patrick Cornelißen
http://www.pcornelissen.de
http://www.orchit.de

from spring-hateoas.

odrotbohm avatar odrotbohm commented on May 28, 2024 1

@sshcherbakov — There are a couple of misconceptions here: The backend domain object doesn't have anything to do with the way ResourceSupport works. So as soon as your domain object exposes the identifier under a different property name, you'll get the ID exposed. I guess you could even create your own subclass of ResourceSupport and override getId() and expose it again by annotating the method with @JsonProperty. I'd still argue that we shouldn't make it easy to do the wrong thing.

Speaking of which: Feign actually falls exactly into that category as it's basically based on baking URI structures into clients which is fine if you think of JSON via HTTP, but not REST. Spring HATEOAS whole point is to be opinionated about things — as is Spring Data REST — and make sure you're not violating core REST principles when using it. Why else would you want to use the library in the first place? If all you're interested in is CRUD via JSON over HTTP, then both libraries are just the wrong tools. That's basically like walking into a restaurant and then wanting the check to cook like you're used to.

I am gonna close this for now as I find myself repeating the same arguments over and over again and all that comes up are basically the same, already answered questions.

from spring-hateoas.

odrotbohm avatar odrotbohm commented on May 28, 2024 1

It's gonna be my last comment here as we're sort of derailing this ticket beyond recognition and I've repeated myself quite a dozen times on this ticket and a lot of others in the same ballpark. HTTP as a partial implementation of REST has a well defined concept to identify resources: URIs. So if you use it as application communication protocol, you need to transfer backend related concepts (e.g. database identifiers) to HTTP appropriate ones. That means, exposing a backend (artificial) identifier is bad idea. To clients, having the id as a property in the payload is of little to no use anyway as it can't be dereferenced via the protocol anyway. What is a client supposed to do with a 5 or 4711? Also, not masking the id creates ambiguities for PUT requests: assume a PUT /foo/4711with a payload { id : 4712 }. What is that supposed to mean?

Spring Data MongoDB has nothing to do with anything here. Feign is anything but REST as it requires clients to replicate URI patterns, which is one of the most fundamental no-no's in REST. So if a library basically allowing you to implement RPC via HTTP doesn't work well with a library that takes REST constraints seriously, that's a problem of the former, not the latter. It might mean that using a library like Spring HATEOAS might not fit the problem but giving up all design constraints as a consequence would basically subvert its purpose. If you just want to serialize objects into JSON as they are, you don't need any of this. Jackson alone will do this for you.

So I think any client that would use Spring Resource type wouldn't be able to automatically get entity with populated id field.

That's correct, because that's not what a REST client does. It accesses and inspects a representation of a resource. The term entity doesn't exist in the REST world. Nothing here is about making it easy to access server side entities. That's not the goal of Spring HATEOAS or Spring Data REST.

from spring-hateoas.

gigecogary avatar gigecogary commented on May 28, 2024 1

Just a quick share of how I "fixed this". I connect a spring boot data-jpa application (that encapsulates my database) to a webmvc web-backend via spring boot data-rest.
The problem above boils down to not being able to use the property id, in the transfer via springhateoas, so my soltion boils down to change the property name on the wire-only. In the data backend I annotate id's with @JsonProperty(value = "dataId")

    @Id
    @JsonProperty(value = "dataId")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

in the frontend I make deserialization work by just adding @JsonAlias(value = "dataId"), When the data is served by the frontent it is serialized as id just like originally intended.

    @JsonAlias(value = "dataId")
    private int id;

No magic, nothing genius, but this solution at least allows me to encapsulate the changes I didn't want to be making.
ps. this would obviously be a funnier name @JsonProperty(value = "link") :-)

I don't understand how this is a workaround? The whole issue revolves around the fact that the model cannot have an id field (which contradicts the getId method signature of ResourceSupport). If you could have an id field, you could've easily serialized it using @JsonProperty in your overridden method.

On that note, does anyone here have a real workaround? My models for historic reasons all have id fields. And they are serialized as such. Should I
(1) rewrite all my models (which needs to extend ResourceSupport) to not have an id field and thus rewrite the mapping to the database or
(2) write a set of DTO that duplicates the fields from each model and have that be the return object for all REST calls?

from spring-hateoas.

pcornelissen avatar pcornelissen commented on May 28, 2024 1

As it was written earlier in this Thread you can use @JsonProperty to rename the ID field in the JSON, so it's available with another name and on the other side you can do the same to map the other name back to id, which is used in the java code.
That is a pity and my strongest pet peeve with spring hateoas. You either have to work around that or don't use the resource stuff on the client

from spring-hateoas.

stephaneeybert avatar stephaneeybert commented on May 28, 2024

I was just about to write the exact same issue as Laures did.

It could easily be named getResourceId.

I also understand Oliver's point.

But let's recognize that most of these resources will have to handle some (REST or data) domain id, if only for PUSH operations.

This is forcing us into using a @JsonProperty for most of our resources.

Tough.

from spring-hateoas.

stephaneeybert avatar stephaneeybert commented on May 28, 2024

Thanks Laures for that comment.

I had seen Oliver's suggestion about the use of the @JsonProperty but I'm not prepared to go that route as of now.

I also posted my little message on the #66 issue... Have a look :-)

from spring-hateoas.

ckristo avatar ckristo commented on May 28, 2024

I currently try to achieve that the ID of my DTO class is included in the JSON output when I wrap it into a (custom) resource class. It seems that this is not easily possible because ResourceSupport.getId() is annotated with @JsonIgnore (when I overwrite it using @JacksonIgnore(false) on a subclass, the DTO ID is rendered correctly, but also the Link ID is included) . What I find strange: it seems that Jackson ignores the ID property of the Resource's content (that gets unwrapped) just because of the fact that ResourceSupport.getId() is annotated with @JacksonIgnore. This behaviour of Jackson doesn't feel right / very natural to me. Could that be a problem of Jackson?

from spring-hateoas.

ckristo avatar ckristo commented on May 28, 2024

Any thoughts on this? I think that Jackson shouldn't assume anything about unwrapped properties (unless there are annotations on the class that is unwrapped)..

from spring-hateoas.

odrotbohm avatar odrotbohm commented on May 28, 2024

What feels least natural in the first place is to make an identifier anything else but a URI in a REST context :). See #66.

from spring-hateoas.

ckristo avatar ckristo commented on May 28, 2024

Any thoughts on that? This issue currently prevents me from returning Spring HATEOAS Resource instances (which I could use to set links). I still think that Jackson should not ignore unwrapped properties on name clashes..

from spring-hateoas.

dcerecedo avatar dcerecedo commented on May 28, 2024

What about resource support being able to parse an incoming self link according to the @ExposesResourceFor and/or the Assembler constructor argument where the Rest Controller is referenced?
I agree that the URI is the ID of the resource. But if the ResourceSupport is capable of turning internal Id's into URIs, it should be able to do the reverse: parsing an URI and extracting the internal Id.

from spring-hateoas.

gregturn avatar gregturn commented on May 28, 2024

@dcerecedo Sounds like a great idea. These ideas are best captured in tentative unit tests, so we can act on them.

For example, we definitely have coded samples of parsing a HAL document into a Resources object => acd939a

It's the perfect use case people want. The question is, is something in the library missing here? Does ResourceSupport not have the same option? Can we show it with a simple test case?

from spring-hateoas.

dcerecedo avatar dcerecedo commented on May 28, 2024

@gregturn ResourceSupport is only oriented towards transforming an Entity into a Resource and, as far as I know, there is no other support within Spring HATEOAS to do the reverse.
Up till now, I've been relying on Assemblers that inherit from ResourceSupport and implement the Converter interface and provide means to serialize and deserialize (overloading convert) resources.
The main need for properly deserializing URIs into Ids comes from update requests because I need to instantiate an Entity and set its Id before passing it to the corresponding Service for manipulation.
Controller write methods are declared with a Resource as parameter. After deserializing JSON into the Resource, I invoke the convert method on the Assembler to get the corresponding Entity, but without its Id. I have to set it relying on the @PathVariable

@RequestMapping(value="/{id}", method = RequestMethod.PUT)
 public ResponseEntity<R> update(@PathVariable("id") ID id, @Valid @RequestBody R inputResource){
    T entity= getAssembler().convert(inputResource);
    entity.setId(id);
    T updated= getService().update(entity);
    R outputResource= getAssembler().toResource(updated);
    return new ResponseEntity<>(outputResource, HttpStatus.OK);
}

I'd like to have all conversion logic in the same place, the Assembler. And I'd like the framework to provide the appropriate support to do this deserialization. But this does not seem possible.
To make this work, the deserialization logic needs the context of the Controller method (only available in the method itself) being called to access the URI template and the actual URI (available in the Resource). So this has been the most elegant solution I have come up with.

from spring-hateoas.

jar349 avatar jar349 commented on May 28, 2024

+1

My legacy domain model represents identity in the aggregate itself. If getId() were renamed to getSelfLink(), I would be able to migrate to sprint-hateos right away (leveraging other links) and then come back and refactor my domain model.

from spring-hateoas.

pcornelissen avatar pcornelissen commented on May 28, 2024

The getId is marked as jsonignore and this "shadows" any id field in the enclosed content class. But couldn't you just replace the @JsonIgnore with @JsonProperty("_IGNORE"), this should enable jackson to set the id field in the content again. Or at least I hope it would... ;-)
I have not tried that, but isn't this something that is worth pursuing?

from spring-hateoas.

robmoore avatar robmoore commented on May 28, 2024

I'm seeing the same thing since upgrading to 1.4.0. Any DateTime properties are now expanded into an object with 'content'.

firstEncounterDate: { content: "2014-01-02" }

from spring-hateoas.

pcornelissen avatar pcornelissen commented on May 28, 2024

Usually this is because you don't have the dependency for jsr310 in your
classpath:

com.fasterxml.jackson.datatype jackson-datatype-jsr310

I still have specialized mapper config in my projects, although I
think it's no longer necessary.

@bean
public MappingJackson2HttpMessageConverter
mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new
MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,
false);
objectMapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
objectMapper.registerModule(new Jdk8Module());
objectMapper.registerModule(new JavaTimeModule());
jsonConverter.setObjectMapper(objectMapper);
return jsonConverter;
}

from spring-hateoas.

robmoore avatar robmoore commented on May 28, 2024

@pcornelissen Thanks for the suggestion. I didn't have the JSR-310 package dependency declared but adding it didn't help. I should have mentioned that I'm using Joda DateTime objects rather than native Java time objects.

from spring-hateoas.

robmoore avatar robmoore commented on May 28, 2024

I've posted a question about the 'content' field in date types to SO.

from spring-hateoas.

sshcherbakov avatar sshcherbakov commented on May 28, 2024

+1

I have a predefined JSON schema and Java POJOs being auto-generated from the schema.
I cannot change the id name in the schema.
There is explicit support for exposing "id" fields in the RepositoryRestConfiguration.exposeIdsFor() to expose ID fields in the JSON body for the MongoDB entities.
And please note, that at the moment it is not possible to change the name of "id" property in a Spring Data MongoDB entity, so it always needs to be "id".

I'm using Spring Feign client to access my Spring Data REST MongoDB service and I need to have "id" field of retrieved entity to be populated in order to be able to reference the newly created instance.

It seems that with this issue, the above technology constellation (default Spring libraries) cannot support the basic use case of one Spring REST service or application calling CRUD operations of another Spring Data REST/MongoDB service.

from spring-hateoas.

gregturn avatar gregturn commented on May 28, 2024

Feign is a shortcut for writing GET /orders/123 in code via interface declaration. It has nothing to do with hypermedia or navigating links. So in a nutshell, feign has little to do with REST and everything to do with more maintainable RPC.

from spring-hateoas.

gregturn avatar gregturn commented on May 28, 2024

I'd go on to say that writing servers with Spring MVC and writing clients with Frign is quite efficient...for RPC. But neither of these technologies is built to handle dynamically changing routes or relationship based links like Spring Restbucks shows.

That requires adding links on the server side and then using B something like Traverson or rest.js to navigate by rel. it's a little more effort at first, but makes both sides much more flexible for API changes coming in the future by being less brittle.

from spring-hateoas.

sshcherbakov avatar sshcherbakov commented on May 28, 2024

Thanks @gregturn that explains some points.
Just to mention is that that's not Feign who hides the id field during serialization.
So I think any client that would use Spring Resource type wouldn't be able to automatically get entity with populated id field.
And again, the id field might be part of the pre-defined server-side schema and cannot be avoided and not necessarily have "entity instance id" meaning.

So, from one point, I understand that the Resource is part of spring-hateoas project. So its purpose is to support the "HATEOAS" way (and not necessarily others).
But the reason I use Resource on the client side is because Spring Data REST wraps entities into HAL envelope on the server side (by default or when spring-hateoas is available).

I still can't get the reason of not populating entity (not Resource!) id field during deserialization.

from spring-hateoas.

sshcherbakov avatar sshcherbakov commented on May 28, 2024

I assume the topic is closed now.
Just to avoid all misunderstandings, it is about "id" property of a class that initially represents database entity, not about "id" property of a org.springframework.hateoas.Resource class that represents REST resource.

from spring-hateoas.

ericsnx avatar ericsnx commented on May 28, 2024

I've one question about the proposed solution, extends the ResourceSupport and override the getter, but in the endI will have to return Link object on my id field instead of the UUID, String or Integer? because the ResourceSupport getId return Link.

because in the end is an useless solution for those that can change the api contract right?

from spring-hateoas.

pcornelissen avatar pcornelissen commented on May 28, 2024

Additionally:

Using the ID to store a reference to somethin in another system is highyl fragile, because servicenames/hostnames or URL patterny may evolve over time, thus leading to "update logic" where we have to notify all clients that the URL schema has changed, so they can update their database items.
The ID is supposed to be a stable identifier, which is far more likely to be a DB-Id like an int or UUID than a link, which could change at any moment.

Forcing the ID-Link to remain stable breaks the service independence. It's far better to provide a templated URL in a service document, which allows to generate the link using the stable ID that has been stored.

Also the hateoas principle has the "self" link already, so there is no need to present this well established link in two properties (id field and links array) and in the same step forcing developers to rename ID fields that are used as ID everywhere to something else just for the sake of transporting it via REST.

Just because on a concept level a rest resource is identified by its URL doesn't mean that it has to be used as field with the name id!

from spring-hateoas.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.