Enterprise Java
Demo Project: How @BasePathAwareController Breaks Link Builders
This minimal example shows two controllers in a Spring Data REST app with base path /api:
- One using
@BasePathAwareController— link builder misses the base path. - One using
@RepositoryRestController— link builder works correctly.
1. Setup Spring Boot and Dependencies
Add to pom.xml (or build.gradle):
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
</dependencies>
2. Configure Base Path
In application.properties:
spring.data.rest.base-path=/api
This sets the base path to /api.
3. Define a Sample Entity and Repository
@Entity
public class Book {
@Id @GeneratedValue
private Long id;
private String title;
// Constructors, getters, setters
}
@RepositoryRestResource(path = "books")
public interface BookRepository extends JpaRepository&;t;Book, Long> {}
Spring Data REST exposes /api/books.
4. Controller Using @BasePathAwareController
@BasePathAwareController
@RequestMapping("/books")
public class BasePathAwareBookController {
@GetMapping("/{id}")
public ResponseEntity<Book> getBook(@PathVariable Long id) {
// For demo, just a stub Book
Book book = new Book();
book.setId(id);
book.setTitle("Sample Book");
return ResponseEntity.ok(book);
}
@GetMapping("/{id}/link")
public ResponseEntity<String> getLink(@PathVariable Long id) {
Link link = WebMvcLinkBuilder.linkTo(
WebMvcLinkBuilder.methodOn(BasePathAwareBookController.class).getBook(id))
.withSelfRel();
return ResponseEntity.ok(link.getHref());
}
}
5. Controller Using @RepositoryRestController
@RepositoryRestController
@RequestMapping("/books")
public class RepositoryRestBookController {
@GetMapping("/{id}")
public ResponseEntity<Book> getBook(@PathVariable Long id) {
Book book = new Book();
book.setId(id);
book.setTitle("RepositoryRest Book");
return ResponseEntity.ok(book);
}
@GetMapping("/{id}/link")
public ResponseEntity<String> getLink(@PathVariable Long id) {
Link link = WebMvcLinkBuilder.linkTo(
WebMvcLinkBuilder.methodOn(RepositoryRestBookController.class).getBook(id))
.withSelfRel();
return ResponseEntity.ok(link.getHref());
}
}
6. Testing the Links
Run the app and test:
GET http://localhost:8080/api/books/1/link }
- For BasePathAwareBookController, the output will be:
/books/1
Notice: Missing /api prefix! This URL is broken because the actual endpoint is /api/books/1.
- For RepositoryRestBookController, the output will be:
/api/books/1
This URL is correct.
7. Conclusion
This demo proves:
@BasePathAwareControllercausesWebMvcLinkBuilderto omit the base path/api.@RepositoryRestControllerpreserves the base path correctly in generated links.
If your app uses hypermedia links with Spring Data REST, prefer @RepositoryRestController for custom controllers to avoid broken links.
Bonus: Manual Fix for Base Path
If you must use @BasePathAwareController, add the base path manually in the link:
String basePath = "/api";
Link link = WebMvcLinkBuilder.linkTo(
WebMvcLinkBuilder.methodOn(BasePathAwareBookController.class).getBook(id))
.withSelfRel();
String fixedLink = basePath + link.getHref(); // prepend base path manually
But keep in mind this can become tedious and error-prone as your API grows.




