跳至主要内容

Link API

Link 模組提供 HATEOAS 連結建構功能。

套件: io.leandev.appfuse.web.link

核心類別

LinkBuilder

HATEOAS 連結建構器。

public class LinkBuilder {
static LinkBuilder linkTo(Class<?> controller)
LinkBuilder(Class<?> controller)
LinkBuilder(URIBuilder uriBuilder)
}

主要方法

方法說明
withContext(String)設定 Context 前綴(如 API 版本)
slash(Object)附加路徑片段
toUri()輸出 URI
toHref()輸出 href 字串
withRel(String)建立帶 rel 的 Link
withSelfRel()建立 self rel Link

使用範例

基本用法

String href = LinkBuilder.linkTo(UserController.class)
.slash("users")
.slash(123)
.toHref();
// 結果: "users/123"

使用 Context 前綴

String href = LinkBuilder.linkTo(UserController.class)
.withContext("api/v1")
.slash("users")
.slash(123)
.toHref();
// 結果: "api/v1/users/123"
Link selfLink = LinkBuilder.linkTo(UserController.class)
.withContext("api/v1")
.slash("users")
.slash(user.getId())
.withSelfRel();
// 結果: Link with href="api/v1/users/123" rel="self"

Link ordersLink = LinkBuilder.linkTo(OrderController.class)
.withContext("api/v1")
.slash("users")
.slash(user.getId())
.slash("orders")
.withRel("orders");
// 結果: Link with href="api/v1/users/123/orders" rel="orders"

在 Controller 中使用

@RestController
@RequestMapping("/api/v1/users")
public class UserController {

@GetMapping("/{id}")
public EntityModel<User> getUser(@PathVariable Long id) {
User user = userService.findById(id);

EntityModel<User> model = EntityModel.of(user);

model.add(LinkBuilder.linkTo(UserController.class)
.withContext("api/v1")
.slash("users")
.slash(id)
.withSelfRel());

model.add(LinkBuilder.linkTo(OrderController.class)
.withContext("api/v1")
.slash("users")
.slash(id)
.slash("orders")
.withRel("orders"));

return model;
}
}

集合資源連結

@GetMapping
public CollectionModel<EntityModel<User>> getUsers() {
List<EntityModel<User>> users = userService.findAll().stream()
.map(user -> {
EntityModel<User> model = EntityModel.of(user);
model.add(LinkBuilder.linkTo(UserController.class)
.withContext("api/v1")
.slash("users")
.slash(user.getId())
.withSelfRel());
return model;
})
.collect(Collectors.toList());

CollectionModel<EntityModel<User>> collection = CollectionModel.of(users);

collection.add(LinkBuilder.linkTo(UserController.class)
.withContext("api/v1")
.slash("users")
.withSelfRel());

return collection;
}

設計考量

相對路徑

LinkBuilder 產生相對路徑(移除前導斜線),適合 Reverse Proxy 架構:

// 產生相對路徑
LinkBuilder.linkTo(Controller.class)
.withContext("api/v1")
.slash("users")
.toHref();
// 結果: "api/v1/users"(無前導斜線)

與 Spring HATEOAS 整合

LinkBuilder 產生的 Link 物件與 Spring HATEOAS 完全相容:

// 可直接用於 EntityModel、CollectionModel
EntityModel<User> model = EntityModel.of(user);
model.add(LinkBuilder.linkTo(...)
.withSelfRel());

輸出格式

JSON 輸出範例

{
"id": 123,
"name": "John Doe",
"email": "john@example.com",
"_links": {
"self": {
"href": "api/v1/users/123"
},
"orders": {
"href": "api/v1/users/123/orders"
}
}
}

最佳實踐

  1. 統一 Context - 使用統一的 API 版本前綴
  2. 語意化 Rel - 使用有意義的 rel 名稱
  3. Self Link - 每個資源都應包含 self link
  4. 相關資源 - 提供相關資源的連結

相關連結