quarkus資料庫篇之一:比官方demo更簡單的基礎操作

来源:https://www.cnblogs.com/bolingcavalry/archive/2023/08/15/17624437.html
-Advertisement-
Play Games

《quarkus資料庫篇》系列的開篇,編碼實戰最基礎的資料庫增刪改查,資料庫用的是PostgreSQL,在官方demo基礎上進一步精簡,極速入門quarkus資料庫操作 ...


歡迎訪問我的GitHub

這裡分類和彙總了欣宸的全部原創(含配套源碼):https://github.com/zq2599/blog_demos

關於《資料庫篇》

  • 《quarkus資料庫篇》系列是《quarkus實戰》的子系列,目標是與大家一起在quarkus框架下完成常用的資料庫操作,如配置、增刪改查、事物等

本篇概覽

  • 本篇敢號稱比官方demo更簡單,是因為官方關於操作資料庫的demo中還有web服務的代碼(如接收http請求和響應,以及web庫的依賴),而本篇不會有這些代碼和依賴,只有存粹的資料庫操作和對應的單元測試類,至於web服務?欣宸應該會出《quarkus之web篇》吧(如果時間允許)

  • 作為《資料庫篇》的開篇,為了避免長文勸退大多數人的悲劇發生,本文被死死壓制在Hello World級別,咱們用最簡單的配置和代碼完成資料庫的增刪改查操作,掌握quarkus下基本資料庫操作全掌握,然後在後續文章中逐步深入,整體上就是一次從入門到精通之旅

  • 本篇的具體內容是創建一個maven工程,此工程有內容是

  1. 一個單表的實體類
  2. 實體類對應的service類,提供單表增刪改查的API
  3. service類對應的單元測試類,一共就這些內容
  • 來看看實際的文件和位置,如下圖
image-20220514115608338
  • 沒錯,這個工程就這麼簡單,官方demo好歹還做了web介面,可以用postman做增刪改查的測試,在本篇中這些統統砍掉,只有service層及其單元測試類

環境和版本信息

  • 電腦:MacBook Pro M1,macOS Monterey
  • jdk:11.0.14.1
  • maven:3.8.5
  • quarkus:與《quarkus實戰》系列保持一致,依舊是2.7.3.Final
  • 資料庫:使用PostgreSQL,版本13.3

源碼下載

名稱 鏈接 備註
項目主頁 https://github.com/zq2599/blog_demos 該項目在GitHub上的主頁
git倉庫地址(https) https://github.com/zq2599/blog_demos.git 該項目源碼的倉庫地址,https協議
git倉庫地址(ssh) [email protected]:zq2599/blog_demos.git 該項目源碼的倉庫地址,ssh協議
  • 這個git項目中有多個文件夾,本次實戰的源碼在quarkus-tutorials文件夾下,如下圖紅框
    image-20220312091203116
  • quarkus-tutorials是個父工程,裡面有多個module,本篇實戰的module是basic-db,如下圖紅框
    image-20220504102912592

確認資料庫已就緒

  • 請確認PostgreSQL資料庫已經就緒

  • 開發階段推薦用docker部署資料庫,簡單省事兒,參考命令如下,請將/xxx換為您自己的宿主機目錄,用於保存資料庫文件

docker run \
--name quarkus_test \
-e POSTGRES_USER=quarkus \
-e POSTGRES_PASSWORD=123456 \
-e POSTGRES_DB=quarkus_test \
-p 5432:5432 \
-v /xxx:/var/lib/postgresql/data \
postgres:13.3
  • 需要在PostgreSQL提前創建名為quarkus_test的資料庫,不用建表
  • 在開發過程中可能要連上資料庫查看數據,請自行準備客戶端工具(命令行也行),我這裡用的是IDEA自帶的資料庫工具,如下圖,已連上PostgreSQL的quarkus_test資料庫,裡面空空如也

image-20220504105123321

新建maven子工程basic-db

  • 在父工程quarkus-tutorials下麵新建名為basic-db的子項目,其pom.xml內容如下,重點是JDBC、hibernate、postgresql這三個和資料庫有關的庫
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>quarkus-tutorials</artifactId>
        <groupId>com.bolingcavalry</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>basic-db</artifactId>
    <dependencies>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-arc</artifactId>
        </dependency>
        <!-- JDBC庫 -->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-agroal</artifactId>
        </dependency>
        <!-- hibernate庫 -->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-hibernate-orm</artifactId>
        </dependency>
        <!-- postgresql庫 -->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-jdbc-postgresql</artifactId>
        </dependency>
        <!-- 單元測試庫 -->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-junit5</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>${quarkus.platform.group-id}</groupId>
                <artifactId>quarkus-maven-plugin</artifactId>
                <extensions>true</extensions>
                <executions>
                    <execution>
                        <goals>
                            <goal>build</goal>
                            <goal>generate-code</goal>
                            <goal>generate-code-tests</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <compilerArgs>
                        <arg>-parameters</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <systemPropertyVariables>
                        <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
                        <maven.home>${maven.home}</maven.home>
                    </systemPropertyVariables>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

配置文件

  • 本次實戰會用到Hibernate自動重新建表的功能,此功能會先刪除庫中已存在的同名錶,因此,只有一個profile配置的時候,不要讓此應用連接到生產環境
  • 最安全的做法是使用profile功能將生產環境和測試環境的配置文件分開,測試環境的配置文件中,是測試資料庫,並且開啟了自動重新建表的的功能,而生產環境的配置文件中,自動重新建表的功能是關閉的
  • 先來看公共配置文件application.properties,此文件和profile無關,應用一定會載入,裡面是各個profile都會用到的公共配置,例如資料庫類型
quarkus.datasource.db-kind=postgresql
quarkus.hibernate-orm.log.sql=true
quarkus.datasource.jdbc.max-size=8
quarkus.datasource.jdbc.min-size=2
  • 再看application-test.properties,這是當profile等於test時才會用到的配置文件,有兩處要註意的地方稍後會提到
quarkus.datasource.username=quarkus
quarkus.datasource.password=123456
quarkus.datasource.jdbc.url=jdbc:postgresql://192.168.50.43:15432/quarkus_test
quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.sql-load-script=import.sql
  • 上述配置,有以下兩處值得重視的配置項
  1. quarkus.hibernate-orm.database.generation:有六個取值,如下表
取值 含義
none 啥也不做
create 第一次啟動會建表,之後啟動不會再改動
drop-and-create 每一次啟動應用的時候都刪表(數據也沒了),然後建表,再執行import.sql導入數據
drop 啟動應用的時候刪表,不刪庫
update 保留數據,升級表結構
validate 檢查表結構與entity是否匹配
  • 從上表可以看出,drop-and-create這個配置很適合開發和測試階段,因為每次都會整理好數據,讓測試和驗證不受歷史數據的影響
  • 由於drop-and-createupdate會改動資料庫,因此不適合生產環境使用,這一點要牢記,官方也給出了警告

image-20220504121200224

  1. quarkus.hibernate-orm.sql-load-script:指定sql文件,在配置項quarkus.hibernate-orm.database.generation等於drop-and-create的時候,就執行此sql文件,可以用來生成初始化數據
  • 配置完成了,接下來開始寫代碼,從最核心的實體類開始

SQL文件

  • 剛纔的配置文件中配合的import.sql,其放置位置與applicatin.properites文件相同,內容如下,可見是往known_fruits表寫入了三條記錄
INSERT INTO known_fruits(id, name) VALUES (1, 'Cherry');
INSERT INTO known_fruits(id, name) VALUES (2, 'Apple');
INSERT INTO known_fruits(id, name) VALUES (3, 'Banana');
  • 從前面的配置可知,profile等於test的時候,應用啟動的時候,會根據實體類的信息執行刪表和建表的操作,然後執行import.sql導入三條記錄

編碼:實體類

  • 熟悉hibernate的讀者都知道,實體類並非只有get和set方法的Pojo,它包含了大量的JPA元信息,是應用與資料庫表映射的關鍵
  • 實體類Fruit.java如下,有幾處要註意的地方稍後會提到
package com.bolingcavalry.db.entity;

import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.QueryHint;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

@Entity
@Table(name = "known_fruits")
@NamedQuery(name = "Fruits.findAll", query = "SELECT f FROM Fruit f ORDER BY f.name", hints = @QueryHint(name = "org.hibernate.cacheable", value = "true"))
@Cacheable
public class Fruit {

    @Id
    @SequenceGenerator(name = "fruitsSequence", sequenceName = "known_fruits_id_seq", allocationSize = 1, initialValue = 10)
    @GeneratedValue(generator = "fruitsSequence")
    private Integer id;

    @Column(length = 40, unique = true)
    private String name;

    public Fruit() {
    }

    public Fruit(String name) {
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
  • 上述代碼有以下幾處要註意的
  1. 註解Table確定了表名known_fruits
  2. 增加了一個自定義SQL,名為Fruits.findAll,後面會用到
  3. 註解SequenceGenerator定義了known_fruits的自增主鍵的信息,初始值是10,也就是說通過當前應用新增的第一條記錄,ID等於10
  4. known_fruits表只有兩個欄位:id和name

service層

  • 為known_fruits表的操作增加一個服務類,用於上層的調用(所謂上層是指web介面、gRPC介面、消息消費入口等)
  • 服務類名為FruitService.java,為了省事兒就直接用class,不寫interface了,代碼如下,增刪改查服務其實就是EntityManager的基本操作,這就不贅述了:
package com.bolingcavalry.db.service;

import com.bolingcavalry.db.entity.Fruit;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;
import java.util.List;

@ApplicationScoped
public class FruitService {
    @Inject
    EntityManager entityManager;

    public List<Fruit> get() {
        return entityManager.createNamedQuery("Fruits.findAll", Fruit.class)
                .getResultList();
    }

    public Fruit getSingle(Integer id) {
        return entityManager.find(Fruit.class, id);
    }

    @Transactional
    public void create(Fruit fruit) {
        entityManager.persist(fruit);
    }

    @Transactional
    public void update(Integer id, Fruit fruit) {
        Fruit entity = entityManager.find(Fruit.class, id);

        if (null!=entity) {
            entity.setName(fruit.getName());
        }
    }

    @Transactional
    public void delete(Integer id) {
        Fruit entity = entityManager.getReference(Fruit.class, id);

        if (null!=entity) {
            entityManager.remove(entity);
        }
    }
}
  • 代碼寫到這裡其實已經完成了,當前工程已經有了資料庫增刪改查的能力,至於上層如何使用(是web調用、gRPC調用、消費消息),那並非本篇的重點,您可以根據自己需要隨意添加
  • 為了驗證服務類功能正常,接下來會寫一個單元測試類 ,調用FruitService的各API並驗證數據是否符合預期

單元測試類

  • 單元測試類只有一個,位置在quarkus-tutorials/basic-db/src/test/java,這是符合maven規範的測試類位置
  • FruitServiceTest源碼如下,有幾處要註意的地方稍後會提到
package com.bolingcavalry;

import com.bolingcavalry.db.entity.Fruit;
import com.bolingcavalry.db.service.FruitService;
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.*;

import javax.inject.Inject;
import java.util.List;

@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class FruitServiceTest {

    /**
     * import.sql中導入的記錄數量,這些是應用啟動是導入的
     */
    private static final int EXIST_RECORDS_SIZE = 3;

    /**
     * import.sql中,第一條記錄的id
     */
    private static final int EXIST_FIRST_ID = 1;

    /**
     * 在Fruit.java中,id欄位的SequenceGenerator指定了initialValue等於10,
     * 表示自增ID從10開始
     */
    private static final int ID_SEQUENCE_INIT_VALUE = 10;

    @Inject
    FruitService fruitService;

    @Test
    @DisplayName("list")
    @Order(1)
    public void testGet() {
        List<Fruit> list = fruitService.get();
        // 判定非空
        Assertions.assertNotNull(list);
        // import.sql中新增3條記錄
        Assertions.assertEquals(EXIST_RECORDS_SIZE, list.size());
    }

    @Test
    @DisplayName("getSingle")
    @Order(2)
    public void testGetSingle() {
        Fruit fruit = fruitService.getSingle(EXIST_FIRST_ID);
        // 判定非空
        Assertions.assertNotNull(fruit);
        // import.sql中的第一條記錄
        Assertions.assertEquals("Cherry", fruit.getName());
    }

    @Test
    @DisplayName("update")
    @Order(3)
    public void testUpdate() {
        String newName = "ShanDongBigCherry";

        fruitService.update(EXIST_FIRST_ID, new Fruit(newName));

        Fruit fruit = fruitService.getSingle(EXIST_FIRST_ID);
        // 從資料庫取出的對象,其名稱應該等於修改的名稱
        Assertions.assertEquals(newName, fruit.getName());
    }

    @Test
    @DisplayName("create")
    @Order(4)
    public void testCreate() {
        Fruit fruit = new Fruit("Orange");
        fruitService.create(fruit);
        // 由於是第一次新增,所以ID應該等於自增ID的起始值
        Assertions.assertEquals(ID_SEQUENCE_INIT_VALUE, fruit.getId());
        // 記錄總數應該等於已有記錄數+1
        Assertions.assertEquals(EXIST_RECORDS_SIZE+1, fruitService.get().size());
    }

    @Test
    @DisplayName("delete")
    @Order(5)
    public void testDelete() {
        // 先記刪除前的總數
        int numBeforeDelete = fruitService.get().size();

        // 刪除第一條記錄
        fruitService.delete(EXIST_FIRST_ID);

        // 記錄數應該應該等於刪除前的數量減一
        Assertions.assertEquals(numBeforeDelete-1, fruitService.get().size());
    }
}
  • 上述單元測試類有以下幾處要註意
  1. 一共五個測試方法,為了給它們排序,要用註解TestMethodOrder修飾類,並制定value為MethodOrderer.OrderAnnotation.class
  2. 再在每個方法上用Order註解修飾,就可以用value執行測試順序了
  3. 測試方法有點多,為了便於觀察,用註解DisplayName為每個測試方法起了個名字,有了名字,IDEA上的測試結果效果如下
image-20220504153230297
  • 單元測試代碼寫完了,是不是可以立即開始測試了?別急,還有個小坑,有一定幾率遇到,別看坑小,要是掉進去還有點麻煩...

IDEA的小坑

  • 回顧之前的配置,資料庫信息都放在application-test.properties文件中,因此只有profile等於test時,才有資料庫配置信息,其他profile都沒有對應的配置文件
  • 一般情況下,如何執行單元測試呢?欣宸的習慣是直接點擊下圖紅框中的按鈕,在彈出的菜單上選擇第一項Run ‘FruitServiceTest’,這樣操作簡單,又能通過IDEA界面觀察測試結果
image-20220504155008286
  • 實測發現,使用上述方式,IDEA給我們設置的profile可能不是test,而是default,而default這個profile的配置文件是不存在的,因此單元測試啟動就會失敗

  • 上述問題,我這邊偶爾遇到過幾次,目前無法穩定復現,針對此問題的解決方法如下

  • 點擊圖標運行單元測試的時候,選擇下圖紅框中的選項

image-20220504121554599

  • 在彈出的配置視窗中,新增下圖紅框中的內容,這就指定了profile等於test

image-20220504121749195

  • 運行的時候,選擇上圖配置的名字FruitServiceTest(test-profile),就能確保profile是test了

運行單元測試

  • 運行單元測試,結果如下圖,不但測試全部通過,輸出的日誌內容也非常豐富,解讀他們,是溫習前面知識點的最佳手段

image-20220504161310577

  • 還有一處要註意的,就是上圖顯示getSingle方法耗時僅6ms,例外,getSingle執行的時候也沒有SQL日誌輸出,這是因為getSingleb並沒有真正的查詢資料庫,而是使用了前面list的緩存結果,驗證是否使用了緩存很簡單,將testGet和testGetSingle兩個方法的執行順序調換一下,再執行,就發現testGetSingle執行耗時也變長了,而且SQL日誌也出現了
  • 上述這種不查資料庫而走本地緩存的操作,雖然看似提升了性能,然而風險也不小,getSingle得到的結果並非資料庫中最新的,關閉緩存的方法如下圖,修改Fruit.java的配置,如下圖

image-20220504164832854

  • 至此,相比官方demo更加精簡的quarkus資料庫操作入門已完成,希望本篇能讓咱們對quarkus的資料庫操作能力和流程有基本的認識,為接下來的逐漸深入打好基礎

歡迎關註博客園:程式員欣宸

學習路上,你不孤單,欣宸原創一路相伴...


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • [TOC] ## 1. 我以為 我以為 [GoPool](https://github.com/devchat-ai/gopool) 這個項目會曇花一現,從此在 GitHub 上封塵。 > 關於 GoPool 項目誕生的故事:[《僅三天,我用 GPT-4 生成了性能全網第一的 Golang Work ...
  • 最近小組在開展讀書角活動,我們小組選的是《深入理解JVM虛擬機》,相信這本書對於各位程式猿們都不陌生,我也是之前在學校準備面試期間大致讀過一遍,emm時隔多日,對裡面的知識也就模糊了。這次開始的時候從前面的JDK發展史和JVM虛擬機家族著手,之前都是粗略讀過,這次通過查閱相關資料並收集在每一個JDK... ...
  • 作者:老鷹湯 \ 鏈接:https://juejin.cn/post/7156439842958606349 ## 線上事故回顧 前段時間新增一個特別簡單的功能,晚上上線前`review`代碼時想到公司拼搏進取的價值觀臨時加一行log日誌,覺得就一行簡單的日誌基本上沒啥問題,結果剛上完線後一堆報警, ...
  • ## 1、問題 在工作中經常需要在內網環境中安裝python第三方庫,使用從pypi上下載的whl文件來安裝又經常遇到該庫也需要依賴包,以至於並不能成功安裝。 ## 2、解決辦法 (1)查看所需第三方庫安裝是否需要依賴庫(以requests為例) ``` pip show requests ``` ...
  • ## 教程簡介 Lucene是apache軟體基金會 jakarta項目組的一個子項目,是一個開放源代碼的全文檢索引擎工具包,但它不是一個完整的全文檢索引擎,而是一個全文檢索引擎的架構,提供了完整的查詢引擎和索引引擎,部分文本分析引擎(英文與德文兩種西方語言)。Lucene的目的是為軟體開發人員提供 ...
  • Redis典型的應用場景就是數據緩存能力,用來解決業務中最容易出現的查詢性能問題,提升系統的響應效率;其次就是分散式鎖機制,用來解決分散式系統中多線程併發處理資源的安全問題; ...
  • Java Instrumentation 包 Java Instrumentation 概述 Java Instrumentation 這個技術看起來非常神秘,很少有書會詳細介紹。但是有很多工具是基於 Instrumentation 來實現的: APM 產品: pinpoint、skywalking ...
  • ## 11.1、環境搭建 > 創建名為spring_aop_annotation的新module,過程參考[9.1節](https://www.cnblogs.com/Javaer1995/p/17610379.html "9.1節") ### 11.1.1、配置打包方式和依賴 ![image](h ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...