spring-projects / spring-data-rest

Simplifies building hypermedia-driven REST web services on top of Spring Data repositories
https://spring.io/projects/spring-data-rest
Apache License 2.0
906 stars 558 forks source link

Templated links are missing #2295

Closed logicg8 closed 11 months ago

logicg8 commented 11 months ago

Template Links in the form of /orders/{id} are missing. A client has no way to know that /order/12 is a valid link.

mp911de commented 11 months ago

If you would like us to spend some time helping you to diagnose the problem, please spend some time describing it and, ideally, providing a minimal sample that reproduces the problem.

logicg8 commented 11 months ago

Thank you for your response. Here is a complete sample of the problem:

build.gradle

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.1.2'
    id 'io.spring.dependency-management' version '1.1.2'
}

group = 'com.example'
version = '1.0'

java {
    sourceCompatibility = '17'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-data-rest'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    runtimeOnly 'com.h2database:h2'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
    useJUnitPlatform()
}

In com.example package:

TaversonIssueApplication.java

@SpringBootApplication
public class TaversonIssueApplication {

    public static void main(String[] args) {
        SpringApplication.run(TaversonIssueApplication.class, args);
    }

}

User.java

@Entity
@Table(name="`User`")
@Data
public class User {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    private String name;

}

UserRepository.java

public interface UserRepository extends CrudRepository<User, Long> {
}

In /src/main/resources:

INSERT INTO "user" (name) VALUES ('bob');
INSERT INTO "user" (name) VALUES ('sally');

Once application is started, a request to http://localhost:8080/ results in:

{
  "_links" : {
    "users" : {
      "href" : "http://localhost:8080/users"
    },
    "profile" : {
      "href" : "http://localhost:8080/profile"
    }
  }
}

From this response, a client has know way of knowing that http://localhost:8080/users/1 is a valid link. A Traverson HAL client cannot follow the link from http://localhost:8080/ to http://localhost:8080/users/1. This is because no templated link is provided such as `http://localhost:8080/users/{id}.

I apologize if I'm overlooking a way for Spring Data REST to provide such a templated link. If not, could Spring Data REST produce the following in response to http://localhost:8080/:

{
  "_links" : {
    "users" : {
      "href" : "http://localhost:8080/users/{id}"
    },
    "profile" : {
      "href" : "http://localhost:8080/profile"
    }
  }
}
odrotbohm commented 11 months ago

This is an invalid expectation. The root resource exposes only the links to collection URIs. The link to item resources are contained in the collection resource. In your example, following the users link will lead you to a resource that lists both user documents and each of them containing a self link that fundamentally is the identifier for the individual documents which clients should use.

Beyond that, there's no need for a client to use templates for item resources, as it's supposed to work with full URIs anyway.

logicg8 commented 11 months ago

Makes sense. I was looking at how GitHub API represents "user_url" with https://api.github.com/users/{user}. I think the difference there is that https://api.github.com/users/ is not a valid link but https://api.github.com/users/logicg8 for example is valid. In Spring Data REST, the collection URI, https://localhost/users/, is valid.

Thank you for taking time to look at my comments and responding, and many thanks for creating an incredible and awesome project.