Git Product home page Git Product logo

Comments (40)

stephaneeybert avatar stephaneeybert commented on May 28, 2024 7

@olivergierke I agree that HATEOAS is a nice idea, that having all URIs considered internals, shaped, issued and controlled by the server is sensible, as it prevents coupling between the client and the server. But even the best way to do something is not the only way to do it. And many of us will go for a middle way. If the framework would allow that, it would not occupy this prime real estate called id. I still stand by my first comment I made 2 years ago. Today the framework names this data for what it means, an id, and not for what it is, a URI. It is too bad that the designers ask our clients front ends to be agnostic about it, when they themselves are not.

from spring-hateoas.

odrotbohm avatar odrotbohm commented on May 28, 2024 5

In REST the id of a resource is its URI (hence Unique Resource Identifier). See a more in depth discussion over in the issue tracker of Spring Data REST. The purpose of these classes is to build representations for a REST service, so we need to stick to the rules here. You can still expose properties used as id's in backend systems if need but I'd strongly recommend against this as you essentially leak internals.

from spring-hateoas.

gazeciarz avatar gazeciarz commented on May 28, 2024 4

I come with the same problem. Is there any update on this topic?

from spring-hateoas.

rvanker avatar rvanker commented on May 28, 2024 3

ResourceSupport :: public Link getId() Why not following bean definition: public Link getLink()
Doing so makes is easy to add ResourceSupport as a superclass to existing domain objects that already have the method public Integer getId(). This is not possible now and unnecessary limits the use of spring-hateoas

from spring-hateoas.

miguelcobain avatar miguelcobain commented on May 28, 2024 3

PUT request URL is handled just like any other request or method.
I'll give an example.

First the client requests the api's root resource. And this is the only URL it knows about.

GET https://apiexample.com/api

{
  "link":[
    {
       "rel":"people",
       "href":"https://apiexample.com/api/people"
    }
  ]
}

Then you follow the people link.

GET https://apiexample.com/api/people

{
  "content":[
    {
      "name":"John Doe",
      "age":21,
      "link":[
        {
          "rel":"self",
          "href":"https://apiexample.com/api/people/1"
        }
      ]
    },
    {
      "name":"Eve",
      "age":21,
      "link":[
        {
          "rel":"self",
          "href":"https://apiexample.com/api/people/2"
        }
      ]
    }
  ],
  "link":[
    {
       "rel":"self",
       "href":"https://apiexample.com/api/people?page=0&pageSize=20"
    },
    {
       "rel":"next",
       "href":"https://apiexample.com/api/people?page=1&pageSize=20"
    }
    //other paging related links
  ]
}

This is a paginated resource.
Now, let's say you want to update (PUT) John Doe to age 22, for example. Just do a PUT to the self link.

PUT https://apiexample.com/api/people/1

{
  "name":"John Doe",
  "age":22
}

Similarly, imagine you want to create a new person. No problem, just POST to the people route.

POST https://apiexample.com/api/people

{
  "name":"John Doe",
  "age":22
}

The only thing hardcoded/known beforehand is this relationship between verbs and rels. I think this may be the next level of HATEOAS, but I think this is already a pretty decent level of independence/hypermedia driven.

In this case, internal id's are indeed in the link. But it's different. The client doesn't explicitly use the id to build an url. You could, for example, replace your id for an uuid, for example. Or even change the url scheme. The client wouldn't complain as long as you keep the rel's steps intact.
I'm assuming that you would only PUT something you already know about, right? So, you need to first retrieve the resource you want to update and only then update. When you retrieve the resource initially, then you know it's url through the self link.

Think of it like surfing a website. You first get the root, then surf through the links, and eventually you can reach a form and POST/PUT and that is also a link.

Sorry if I'm missing something or something wasn't exactly "REST compliant". Please add any corrections, suggestions or comments.

from spring-hateoas.

miguelcobain avatar miguelcobain commented on May 28, 2024 2

Rest was known for its flexibility and json for no-schema.

I think you mean HTTP APIs were known for it's flexibility.
REST always had these kind of constraints.

I think my 2 year comment still holds up pretty well: #66 (comment)

from spring-hateoas.

mcnichol avatar mcnichol commented on May 28, 2024 2

First, then you better complain to Roy (for reference). It's not that I made up the rules here.

Please cite where it says I can't have an id field in resource representations.

@elnur

There's a hyperbole used in the Introduction of his dissertation based on a Monty Python skit The Architects Sketch

In this sketch, the architect presents the design of a block of apartment flats built with the functionality of a slaughterhouse. The claims are made that this is the best slaughterhouse ever built but obviously terrible for actual tenants. It goes on with another presentation but the point is made.

Immediately after this, Roy makes the statement:

At least some of this behavior within the software industry is due to a lack of understanding of why a given set of architectural constraints is useful.

In other words, the reasoning behind good software architectures is not apparent to designers when those architectures are selected for reuse.

It's guiding your architectural style ( ResourceSupport.getId() returning the URI ) vs a coding style you would like that would alter the architecture.

The id of the ResourceSupport would no longer be the URI but your domain objects.

It sounds like it is doing its intended job and you are not aligned||understanding the architecture rules behind it.

from spring-hateoas.

stephaneeybert avatar stephaneeybert commented on May 28, 2024 1

Why naming this resource identifier for what it means and not for what it is ? It is fine that the REST meaning of it is an identifier. But why should we care more for the meaning it carries than for what it is ? Following that logic, we'd have to rename the org.springframework.hateoas.Link class org.springframework.hateoas.Id then. And if the REST identifier is the URI then why seeing as a REST identifier something that contains more than just the URI ? Is the rel property also part of the REST identifier ? Having the getLink getter would be consistent with the naming choice of the existing implementation of the Link class, it would not place any assumption on the meaning of it (as being a REST identifier), and it would not be obtrusive to us clients.

from spring-hateoas.

miguelcobain avatar miguelcobain commented on May 28, 2024 1

I assume that spring-hateoas tries to force developers to use the best practices as possible. The id "sort of" becomes the self link.
In my case I ended up not leaking the internal id's at all. Much happier. Why would the client need it? I would say that if it needs it, that's because it is building URLs itself. And if it is doing it, you shouldn't even need this project or hateoas at all.

from spring-hateoas.

jeffsheets avatar jeffsheets commented on May 28, 2024 1

I agree that the self link is the correct way to GET/PUT/POST for the object.

But there are also many legacy applications where the id field is a displayable value to the user in some form: perhaps the id is also the username, or perhaps the id is a recognizable industry value that the users like to view on the screen.

In those cases we are having to work around to display the id value on the screen since it is not easily retrievable from the json object. This is more an issue in legacy systems where we do not have the permission to modify the legacy database tables to have a system identifier.

For this reason I would like to have the id available in json along with the self link. Or at a minimum have the option to configure this behavior.

from spring-hateoas.

gregturn avatar gregturn commented on May 28, 2024 1

To work with hypermedia and links, I've discovered that it's most practical to do things like embed URIs provided by the hypermedia as data attributes in HTML elements. If you look at https://github.com/gregturn/spring-a-gram/blob/master/src/main/resources/static/app/main.js#L24, you can see where I create data-uri and store the whole URI. This way, in the future when I need to PATCH or PUT, I use the whole link. This decouples my front end from having to understand id's.

Regarding Angular, I saw someone else write an Angular + Spring demo app, but they used a lot of outdated patterns. I essentially rewrote the whole thing, throwing out hand written JDBC and controllers, and instead used Spring Data REST. It was super easy to tweak the Angular bits to work with this. See https://github.com/gregturn/task-manager-app.

from spring-hateoas.

kopax avatar kopax commented on May 28, 2024 1

Maybe there is a way to fork and build our own version with less strict standard.
I would be happy to speak about that with people interested.

from spring-hateoas.

elnur avatar elnur commented on May 28, 2024 1

In REST the id of a resource is its URI (hence Unique Resource Identifier).

The problems start here. This logic is flawed.

The URI I used to fetch a resource might be called its ID β€”Β I don't argue with that. What does this have to do with the ID field of the backing entity or its resource representation though? It's my right to assign whatever meaning I want to the field named id.

The ID might be some number displayed to a user in the UI. Say an order at some online store has an ID associated with it. If I have a problem with my order, I call their support and tell them the order ID. Not all IDs are meant to be hidden from end users. The store can change the order URI format as much as they want, but an order's ID will stay the same.

And people will and do this anyway. They just have to work around the library with ugly and unnecessary hacks.

from spring-hateoas.

miguelcobain avatar miguelcobain commented on May 28, 2024 1

@elnur for the record, your argument is sound and convinced me.

from spring-hateoas.

odrotbohm avatar odrotbohm commented on May 28, 2024 1

In REST the id of a resource is its URI (hence Unique Resource Identifier).

The problems start here. This logic is flawed.

First, then you better complain to Roy (for reference). It's not that I made up the rules here. Second, there's no logic in this statement, as it's a plain statement. So the logic, that the logic is flawed, is flawed.

The URI I used to fetch a resource might be called its ID β€” I don't argue with that. What does this have to do with the ID field of the backing entity or its resource representation though? It's my right to assign whatever meaning I want to the field named id.

Nothing. I didn't think, that I have to get even more fundamental than in my original comment, but apparently I have to :). There's a class that exposes the identifier of a resource. The identifier of a resource is its URI. Hence the class exposes that URI for the accessor of the ID. That's simple, straight forward, easy. If you opt into extending that class you express that you're willing to adhere to follow the rules and concepts. Extending it and then complaining about the rules is… weird.

The ID might be some number displayed to a user in the UI.

REST doesn't know about a UI.

Say an order at some online store has an ID associated with it.

REST doesn't know about an online store.

If I have a problem with my order, I call their support and tell them the order ID. Not all IDs are meant to be hidden from end users.

Right, as they're just part of the representation of the resource. From a resource point of view, it is not its identifier. That's all the type is implementing. You basically mix up your domain's bounded context with the bounded context of REST itself, where domain terms like resource, identifier etc. have well defined semantics. I can't simply forward you domains understanding of an ID into the REST context as β€” unless you're using URIs in your domain β€” they don't fit the rules that that context implies.

The store can change the order URI format as much as they want, but an order's ID will stay the same.

So what? You're basically trying to force a different, application specific kind of identity onto something that's got nothing to do with your application. That's a fundamentally flawed approach. Not because I say so, but because you basically start violating the architectural constraints that are in place to give you certain effects of your architecture. It's not up to me for you to decide what to do, and nobody forces you to follow the constraints, but just don't expect a library that wants to help people to conform to the constraints to actually get to the effects following those constraints to weaken the rules, just because you feel they're limiting. They in fact are, for a reason.

from spring-hateoas.

elnur avatar elnur commented on May 28, 2024 1

First, then you better complain to Roy (for reference). It's not that I made up the rules here.

Please cite where it says I can't have an id field in resource representations.

If you opt into extending that class you express that you're willing to adhere to follow the rules and concepts. Extending it and then complaining about the rules is… weird.

Basically, I extend ResourceSupport to inherit links manipulating methods and to create a resource assembler for it. I can't use the library's ResourceAssembler without extending ResourceSupport.

Now, for me, a resource is a DTO I use to decouple domain types from their representations. I can add whatever fields I want to the resource type and they will get exposed in the representation. The fields I don't add don't get exposed.

Since most of my domain types have an ID field β€” artificial or natural β€” I want to expose them in representations as well. But I can't do that when extending ResourceSupport without resorting to workarounds. Having to resort to workarounds is the whole point of this issue in the first place.

A suggested solution for that problem is extending Resource<T> instead. If I pass my domain object to the content field of Resource<T>, all fields of the domain object will get exposed if autodetection is enabled. To solve that problem, I have to add Jackson annotations to my domain type or create a Jackson mixin to either hide unnecessary fields if autodetection is enabled, or to expose necessary fields if autodetection is disabled. Or I have to create another DTO layer between my domain types and resources. And that becomes pretty meta.

And all I wanted was to easily add links to my resources. Without having to deal with all that ID field related pain.

And I bet that for most users of Spring HATEOAS adding links to resources is the first and main reason to use the library. Sure, it has nice additional features on top of that, but if I can't extend ResourceSupport and ResourceAssembler which are the backbone of the library, I might as well just not use it at all.

Maybe a solution to the problem would be adding a class above ResourseSupport that just adds support for links without messing with the ID field of resources?

from spring-hateoas.

Panthro avatar Panthro commented on May 28, 2024 1

I disabled the whole think all together and stopped using spring hateoas just because of this issue.

In my opinion is ridiculous that the framework decides how it should be used, I work for clients that I cannot argue saying "oh, but it's better if you don't expose your ID's", its their product and that is how they want, I just have to implement it.

Now the fact that I had to remove it entirely because of this issue is ridiculous.

from spring-hateoas.

ceefour avatar ceefour commented on May 28, 2024 1

"Simple things should be simple, complex things should be possible." - Alan Kay

In this case, a simple thing is made impossible.

Spring Data REST does make it possible to expose IDs with some configuration.

I (and many others) would argue there are various cases in which exposed ID is beneficial or even required. Interoperability with other systems is one. Had GeoNames API used Spring HATEOAS, we'll never be able to use GeoNames IDs, instead we always use GeoNames URIs. Using GeoNames IDs makes a lot of things possible and efficient.

GraphQL is another world where we're not restricted to resources-as-URI view. Most GraphQL endpoints use IDs, I have yet to find one that uses URIs as the "primary key" although it's definitely possible with GraphQL.

from spring-hateoas.

stephaneeybert avatar stephaneeybert commented on May 28, 2024

How to do a PUSH operation without leaking the internal id then ?

from spring-hateoas.

cl4r1ty avatar cl4r1ty commented on May 28, 2024

I tried to implement this today and immediately ran into the getId() issue.

You reference spring-data-rest but the issues seem to be wiped from that repo. On top of that looking at the objects used in spring-data-rest you find them using getId(). (https://github.com/spring-projects/spring-data-rest/blob/master/spring-data-rest-example/src/main/java/org/springframework/data/rest/example/jpa/Person.java)

I understand its REST and everything but it seems like a common use case in a lot of spring projects and would entail a huge refactor to already running apps.

What's the solution for this? Or is there one other than using UUID key? (Might have been one on the other repo but its gone now)

from spring-hateoas.

charroux avatar charroux commented on May 28, 2024

On the client side, self rel is not enough to know which kind of object we use. How can I discover this is a person in the following example (I can't deduce it from href since the server implementation can change) ?
{
"name": "Derek",
"links" : [
{ "rel" : "self", "href" : "http://myhost/people/derek" }
]
}
Something like that should be useful:
{
"name": "Derek",
"links" : [
{ "rel" : "self", "href" : "http://myhost/people/derek" }
],
"id":{ "rel" : "person", "href" : "http://myhost/people/derek" }
}

from spring-hateoas.

stephaneeybert avatar stephaneeybert commented on May 28, 2024

Hi Miguel,
I agree that leaking the internal is best avoided, but you still build your url to be used in a PUT request with the internal id ?

from spring-hateoas.

stephaneeybert avatar stephaneeybert commented on May 28, 2024

Hi Miguel,
Thanks for your answer, your example is a good showcase !

from spring-hateoas.

stephaneeybert avatar stephaneeybert commented on May 28, 2024

I'm now wrking on an AngularJS project and I create an abstract resource class holding a @JsonProperty("id") private Long id; and extend from it for all project resources.

It's easier to pass the id from a list view to an edit view. Just use the resource id in the view: ui-sref="/admins/edit({adminId: admin.id})"

I reckon some would manage to identify a resource by its REST URL in their client application and be able to pass this data around views.

If someone has some directions on doing this in AngularJS I'd be happy to give it a try.

Sorry if I'm bit off the mark with this comment.

from spring-hateoas.

ceefour avatar ceefour commented on May 28, 2024

πŸ‘ for this. Please rename ResourceSupport.getId() to (my proposal) getSelfLink()

Surely I can create @JsonProperty("id") getItemId() or something else, but Spring HATEOAS would do well to be a bit less opinionated. Please be Spring, Spring is flexible.

from spring-hateoas.

ravindranathakila avatar ravindranathakila commented on May 28, 2024

If it helps, http://jsonapi.org/format/#document-structure-resource-objects contains an "id" field which in the example is a number.

I think having a "Link" for id makes sense too since it points to a resource which is identified by this given id.

But in a traditional perspective of id's this is too restrictive.

And that it is by default @JsonIgnore makes the property basically useless in terms of jasonapi.

from spring-hateoas.

cy33hc avatar cy33hc commented on May 28, 2024

@olivergierke The reason put forward about leaking internal id is completely unsound. If anybody know rest then they will know from the URI, the internal Id anyway since you have just included it in the Link.

from spring-hateoas.

miguelcobain avatar miguelcobain commented on May 28, 2024

@cy33hc a hateoas client can't infer anything from an URL.
The reason is because the URL is something given to it by the server, for the client to follow.
The server can change the url at anytime, and the system shouldn't behave any differently.

Also, no one is suggesting to have ids in urls either. You don't have to.

Extracting an id from an url would mean some "out of bounds information" has happened, which is something incompatible with hateoas.
The self href should be unique and should suffice for those needs.

from spring-hateoas.

kopax avatar kopax commented on May 28, 2024

I am sad to have such a good framework in my hand and cannot use it has it is!

from spring-hateoas.

kopax avatar kopax commented on May 28, 2024

Can we have an update on this topic ? This has been like this for a while, it's a shame we cannot use such strong tool.

from spring-hateoas.

aardelean avatar aardelean commented on May 28, 2024

same problem here. Not all projects can be that smoothly be upgraded to use spring hateos if it means heavy refactoring on multiple systems at once.
Rest was known for its flexibility and json for no-schema. Now we are enforcing strict standards and give schema as arguments to why. Might as well go back to SOAP altogether. Pretty annoyed at the moment that I will have to do my own resource deserialisation just because of one word in ResourceSupport.

from spring-hateoas.

stephaneeybert avatar stephaneeybert commented on May 28, 2024

I stand by Oliver's view but holding that view does not mean we have to name the resource identifier with the "id" word. I haven't yet seen a counter argument on my comment of Nov 14 2013. The issue here, as I see it, is naming it "id" when it could have been named "link".

from spring-hateoas.

odrotbohm avatar odrotbohm commented on May 28, 2024

Hm, I think that's problematic as it's just using the term for a different, much broader concept in REST to describe a domain concept that has very well-defined semantics.

from spring-hateoas.

stephaneeybert avatar stephaneeybert commented on May 28, 2024

It could also have been named resourceId, restUrl, etc.. named after what it is, rather than after what it means.

from spring-hateoas.

gregturn avatar gregturn commented on May 28, 2024

I don't get the point about swapping "id" with "link". What if that shadows someone else's domain object? Change again? And why?

Now we're fiddling around with asking the framework to accommodate WHAT to call the resource's URI identifier to appease everyone. When we could have just done "id" and moved on to building hypermedia resources.

Besides, if we use Resource<Foo>, doesn't that solve most of this issue anyway? With that, the domain object and the resource object are nicely separated by a container with a contract (I've always favored that over Foo extends ResourceSupport)

from spring-hateoas.

Panthro avatar Panthro commented on May 28, 2024

I surely understand the architectural designs behind the decision that was made, I can relate to them and I don't question them.

The 1kk question is:

Why the default behaviour cannot be extended/modified?

If we are open to a more extensible approach, I'd happily create PR where this behaviour is by default enable but with a property it can be changed.

from spring-hateoas.

gregturn avatar gregturn commented on May 28, 2024

There is already an escape hatch: Resource<T>.

I don't see how to make these types any more extensible and not also cause considerable issues in the mediatype serializers and deserializers.

from spring-hateoas.

dougsaus avatar dougsaus commented on May 28, 2024

I am also having this issue and following other's suggestion I have re-named the entity class field (cannot rename the DB column). The field in the DB is "id". The field in my entity class is called "personId", is annotated as @id and is mapped to "id" via the @column attribute. The Resource/ResourceSupport is STILL not showing me this id when rendering the json. Why in the world can I not see "personId" in the json? Every other field from the entity is shown fine.

from spring-hateoas.

gregturn avatar gregturn commented on May 28, 2024

Can you show the results of JSON when you use Resource<Person>?

from spring-hateoas.

odrotbohm avatar odrotbohm commented on May 28, 2024

I guess we can close the discussion as the arguments have stayed the same for quite some time and I don't like repeating myself. However, there's a certain irony in bringing up GraphQL as your defense in a library that cares about REST ;).

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.