偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

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

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

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

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

HATEOAS 簡介:讓 REST API 具備“自導(dǎo)航能力”

HATEOAS 是什么?

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

?? “服務(wù)端不僅返回資源數(shù)據(jù),還提供訪問該資源相關(guān)操作的鏈接。”

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

示例:基于在線圖書系統(tǒng)的 HATEOAS 實(shí)戰(zhàn)

普通 REST API 響應(yīng):

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

HATEOAS 風(fēng)格響應(yīng):

{
  "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 中的完整開發(fā)流程

引入依賴(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>

創(chuàng)建實(shí)體類 /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;
}

控制器實(shí)現(xiàn) /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 構(gòu)建更靈活的響應(yīng)

@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"));
    // 可添加自定義狀態(tài)字段
    return model;
}

HATEOAS 的優(yōu)點(diǎn):不僅僅是“返回鏈接”這么簡單

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

那為什么現(xiàn)實(shí)中很多項(xiàng)目不采用?

前端開發(fā)者其實(shí)早就知道要請求哪個接口、用什么方法、發(fā)什么數(shù)據(jù)。 比如:

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

一旦 HATEOAS 上線,前端得讀取響應(yīng)中的 _links 字段,再動態(tài)解析后請求新的接口。復(fù)雜度上升,不劃算。

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

現(xiàn)實(shí)中,只有當(dāng)你構(gòu)建一個高度通用、自動化消費(fèi)的 API(比如客戶端不固定時),HATEOAS 才真正展現(xiàn)優(yōu)勢。 否則,對于固定結(jié)構(gòu)的系統(tǒng)來說,明確 URL 并硬編碼在客戶端會更高效。

總結(jié)一句話:

在大多數(shù)真實(shí)項(xiàng)目中,HATEOAS 并不是必選項(xiàng),但它是構(gòu)建真正 RESTful API 的“最后一公里”。

責(zé)任編輯:武曉燕 來源: 路條編程
相關(guān)推薦

2020-06-04 09:18:52

CTOif-else代碼

2020-06-15 08:12:51

try catch代碼處理器

2025-09-01 01:33:00

DockerBuild命令

2020-09-01 21:03:10

Springboot格式化方式

2023-08-25 13:34:02

JavascriptWikipediaSlack

2025-09-12 07:55:54

2025-10-29 01:45:00

toString()SpringJackson

2025-09-29 01:33:00

Spring初始化Bean

2024-12-20 18:00:00

C++折疊表達(dá)式C++17

2025-08-04 01:55:00

2025-06-04 02:10:00

2020-12-01 08:32:12

Spring Boot

2023-05-11 12:40:00

Spring控制器HTTP

2025-09-15 01:55:00

緩存代碼Easy-Cache開發(fā)

2022-04-27 08:55:01

Spring外部化配置

2023-03-27 08:28:57

spring代碼,starter

2022-05-27 21:56:55

索引存儲MySQL 存儲引擎

2025-01-15 12:31:46

2025-06-19 00:02:00

防抖節(jié)流函數(shù)

2017-04-25 10:46:57

Spring BootRESRful API權(quán)限
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號