成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

別再寫死 URL 了!Spring Boot HATEOAS 教你打造真正自描述 API

開發 前端
有沒有一種方式,讓服務端在返回數據時,順帶告訴客戶端下一步能做什么??有!這就是 HATEOAS 的價值所在 ——?響應本身就攜帶導航信息,告別“后知后覺”的 URL 變更。

在日常開發 RESTful 接口時,你是不是經常看到前端代碼中充斥著類似 "https://yourapi.com/books/101" 這樣的寫死地址?當接口路徑變更,客戶端就像多米諾骨牌一樣全線崩潰。

有沒有一種方式,讓服務端在返回數據時,順帶告訴客戶端下一步能做什么? 有!這就是 HATEOAS 的價值所在 —— 響應本身就攜帶導航信息,告別“后知后覺”的 URL 變更。

HATEOAS 簡介:讓 REST API 具備“自導航能力”

HATEOAS 是什么?

HATEOAS(Hypermedia As The Engine Of Application State)是 REST 架構的高級階段,它的核心理念是:

?? “服務端不僅返回資源數據,還提供訪問該資源相關操作的鏈接。”

換句話說,客戶端拿到數據時,不再需要自己拼接 URL,而是通過服務端提供的鏈接,驅動接下來的請求。

示例:基于在線圖書系統的 HATEOAS 實戰

普通 REST API 響應:

{
  "bookId": 101,
  "title": "Spring Boot Mastery",
  "author": "John Doe"
}

HATEOAS 風格響應:

{
  "bookId": 101,
  "title": "Spring Boot Mastery",
  "author": "John Doe",
  "_links": {
    "self": { "href": "/books/101" },
    "all-books": { "href": "/books" },
    "buy-book": { "href": "/books/101/buy" },
    "reviews": { "href": "/books/101/reviews" }
  }
}

這樣,客戶端馬上知道下一步可以:

  • 再次獲取該圖書信息
  • 查看所有圖書
  • 購買圖書
  • 查看圖書評論

HATEOAS 在 Spring Boot 中的完整開發流程

引入依賴(pom.xml)

<dependencies>
    <!-- Web 核心 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>


    <!-- HATEOAS 支持 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-hateoas</artifactId>
    </dependency>


    <!-- Lombok(可選) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

創建實體類 /src/main/java/com/icoderoad/api/book/model/Book.java

package com.icoderoad.api.book.model;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book {
    private Long bookId;
    private String title;
    private String author;
    private double price;
}

控制器實現 /src/main/java/com/icoderoad/api/book/controller/BookController.java

package com.icoderoad.api.book.controller;


import com.icoderoad.api.book.model.Book;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;


import java.util.List;
import java.util.stream.Collectors;


import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;


@RestController
@RequestMapping("/books")
public class BookController {


    private final List<Book> books = List.of(
        new Book(101L, "Spring Boot Mastery", "John Doe", 29.99),
        new Book(102L, "HATEOAS in Action", "Jane Smith", 24.99)
    );


    @GetMapping("/{id}")
    public EntityModel<Book> getBook(@PathVariable Long id) {
        Book book = books.stream()
            .filter(b -> b.getBookId().equals(id))
            .findFirst()
            .orElseThrow(() -> new BookNotFoundException(id));


        return EntityModel.of(book,
            linkTo(methodOn(BookController.class).getBook(id)).withSelfRel(),
            linkTo(methodOn(BookController.class).getAllBooks()).withRel("all-books"),
            Link.of("/books/" + id + "/buy", "buy-book"),
            Link.of("/books/" + id + "/reviews", "reviews"));
    }


    @GetMapping
    public CollectionModel<EntityModel<Book>> getAllBooks() {
        List<EntityModel<Book>> bookModels = books.stream()
            .map(book -> EntityModel.of(book,
                linkTo(methodOn(BookController.class).getBook(book.getBookId())).withSelfRel(),
                linkTo(methodOn(BookController.class).getAllBooks()).withRel("books")))
            .collect(Collectors.toList());


        return CollectionModel.of(bookModels,
            linkTo(methodOn(BookController.class).getAllBooks()).withSelfRel());
    }


    @ExceptionHandler(BookNotFoundException.class)
    public ResponseEntity<String> handleNotFound(BookNotFoundException e) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage());
    }
}

自定義異常類 /src/main/java/com/icoderoad/api/book/controller/BookNotFoundException.java

package com.icoderoad.api.book.controller;


public class BookNotFoundException extends RuntimeException {
    public BookNotFoundException(Long id) {
        super("未找到書籍,ID: " + id);
    }
}

拓展:使用 RepresentationModel 構建更靈活的響應

@GetMapping("/{id}/status")
public RepresentationModel<?> getBookStatus(@PathVariable Long id) {
    RepresentationModel<?> model = new RepresentationModel<>();
    model.add(linkTo(methodOn(BookController.class).getBookStatus(id)).withSelfRel());
    model.add(linkTo(methodOn(BookController.class).getBook(id)).withRel("book"));
    // 可添加自定義狀態字段
    return model;
}

HATEOAS 的優點:不僅僅是“返回鏈接”這么簡單

  • 客戶端無需拼接 URL:前端直接讀取響應體中的鏈接發起請求,減少維護成本。
  • 應對接口演進更穩健:服務端 URL 改變后,客戶端無需改代碼。
  • 符合 RESTful 最佳實踐:實現 Richardson Maturity Model 的 Level 3(最高級別)

那為什么現實中很多項目不采用?

前端開發者其實早就知道要請求哪個接口、用什么方法、發什么數據。 比如:

axios.get("/books/101");
axios.post("/books/101/buy");

一旦 HATEOAS 上線,前端得讀取響應中的 _links 字段,再動態解析后請求新的接口。復雜度上升,不劃算。

結語:HATEOAS 適用于哪里?什么時候用值得深思

現實中,只有當你構建一個高度通用、自動化消費的 API(比如客戶端不固定時),HATEOAS 才真正展現優勢。 否則,對于固定結構的系統來說,明確 URL 并硬編碼在客戶端會更高效

總結一句話:

在大多數真實項目中,HATEOAS 并不是必選項,但它是構建真正 RESTful API 的“最后一公里”。

責任編輯:武曉燕 來源: 路條編程
相關推薦

2020-06-04 09:18:52

CTOif-else代碼

2020-06-15 08:12:51

try catch代碼處理器

2020-09-01 21:03:10

Springboot格式化方式

2023-08-25 13:34:02

JavascriptWikipediaSlack

2020-12-01 08:32:12

Spring Boot

2024-12-20 18:00:00

C++折疊表達式C++17

2025-06-04 02:10:00

2023-05-11 12:40:00

Spring控制器HTTP

2022-04-27 08:55:01

Spring外部化配置

2023-03-27 08:28:57

spring代碼,starter

2022-05-27 21:56:55

索引存儲MySQL 存儲引擎

2025-06-19 00:02:00

防抖節流函數

2025-01-15 12:31:46

2022-02-21 12:29:01

for循環前端

2023-10-25 12:38:58

交換機指示燈

2017-04-25 10:46:57

Spring BootRESRful API權限

2020-12-02 11:18:50

print調試代碼Python

2020-12-04 10:05:00

Pythonprint代碼

2023-04-17 23:49:09

開發代碼Java

2024-03-26 08:08:08

SpringBPMN模型
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产欧美一区二区三区视频在线观看 | 激情五月综合网 | 亚洲一区成人 | 成人午夜激情视频 | 俺去俺来也在线www色官网 | 又黄又爽网站 | 2018天天操 | 国产高潮在线 | 国产精品日韩欧美 | 欧美精品亚洲 | 国产一区二区三区四区 | 久久久久国产一区二区三区 | 午夜小视频在线观看 | 日本一区二区三区免费观看 | 91二区| 色激情网 | 国产福利av | 日韩国产一区二区 | 91精品在线免费观看 | 一区二区三区视频在线 | 中文av网站 | 狠狠躁日日躁夜夜躁2022麻豆 | 精品欧美黑人一区二区三区 | 中文字幕丰满人伦在线 | 国产寡妇亲子伦一区二区三区四区 | 久久精品欧美一区二区 | 一级黄色片免费看 | 国产一区二区在线视频 | 三级黄色在线观看 | 亚洲精品少妇 | 99色综合 | 亚洲一区亚洲二区 | 天堂一区二区三区 | 日韩av在线不卡 | 高清乱码男女免费观看 | 日韩精品福利 | 国产精品久久久久久久久久久久久久久 | 亚洲天堂男人天堂 | 亚洲视频免费看 | 久久伊人国产 | 国产一区二区三区久久 |