springboot優雅的統一返回格式 + 全局異常處理(包括404等異常)

来源:https://www.cnblogs.com/tuuli/archive/2023/08/24/17653165.html
-Advertisement-
Play Games

[toc] ## 1.自定義枚舉類 ```java public enum ReturnCode { RC200(200, "ok"), RC400(400, "請求失敗,參數錯誤,請檢查後重試。"), RC404(404, "未找到您請求的資源。"), RC405(405, "請求方式錯誤,請檢查 ...


目錄

1.自定義枚舉類

public enum ReturnCode {
    RC200(200, "ok"),
    RC400(400, "請求失敗,參數錯誤,請檢查後重試。"),
    RC404(404, "未找到您請求的資源。"),
    RC405(405, "請求方式錯誤,請檢查後重試。"),
    RC500(500, "操作失敗,伺服器繁忙或伺服器錯誤,請稍後再試。");

    // 自定義狀態碼
    private final int code;

    // 自定義描述
    private final String msg;

    ReturnCode(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

該枚舉類為我們和前端約定好的返回狀態碼和描述信息,可根據自己的需求修改狀態碼和描述

2.自定義統一返回格式類

@Data
public class R<T> {

    private Integer code; //狀態碼

    private String msg; //提示信息

    private T data; //數據

    private long timestamp;//介面請求時間

    public R() {
        this.timestamp = System.currentTimeMillis();
    }

    public static <T> R<T> success(T data) {
        R<T> r = new R<>();
        r.setCode(ReturnCode.RC200.getCode());
        r.setMsg(ReturnCode.RC200.getMsg());
        r.setData(data);
        return r;
    }

    public static <T> R<T> error(int code, String msg) {
        R<T> r = new R<>();
        r.setCode(code);
        r.setMsg(msg);
        r.setData(null);
        return r;
    }
}

@Data註解為Lombok工具類庫中的註解,提供類的get、set、equals、hashCode、canEqual、toString方法,使用時需配置Lombok,如不配置請手動生成相關方法。

我們返回的信息至少包括code、msg、data三部分,其中code是我們後端和前端約定好的狀態碼,msg為提示信息,data為返回的具體數據,沒有返回數據則為null。除了這三部分外,你還可以定義一些其他欄位,比如請求時間timestamp。

定義了統一返回類後,controller層返回數據時統一使用R.success()方法封裝。

@RestController
@RequestMapping("/test")
public class TestController {
    @PostMapping("/test1")
    public R<List<Student>> getStudent() {
        ArrayList<Student> list = new ArrayList<>();
        Student student1 = new Student();
        student1.setId(1);
        student1.setName("name1");
        Student student2 = new Student();
        student2.setId(2);
        student2.setName("name2");
        list.add(student1);
        list.add(student2);
        return R.success(list);
    }
}

@Data
class Student {
    private Integer id;
    private String name;
}

例如在以上代碼中,我們的需求是查詢學生信息,我們調用這個test1介面就返回了以下的結果:

{
    "code": 200,
    "msg": "ok",
    "data": [
        {
            "id": 1,
            "name": "name1"
        },
        {
            "id": 2,
            "name": "name2"
        }
    ],
    "timestamp": 1692805971309
}

image-20230823235342116

到這裡我們已經基本實現了統一返回格式,但是上面這種實現方式也有一個缺點,就是每次返回數據的時候都需要調用R.success()方法,非常麻煩,我們希望能夠在controller層里直接返回我們實際的數據,即data欄位中的內容,然後自動幫我們封裝到R.success()之中,因此我們需要一種更高級的方法。

3.統一返回格式的高級實現

我們需要利用springboot的ResponseBodyAdvice類來實現這個功能,ResponseBodyAdvice的作用:攔截Controller方法的返回值,統一處理返回值/響應體

/**
 * 攔截controller返回值,封裝後統一返回格式
 */
@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> {

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        //如果Controller返回String的話,SpringBoot不會幫我們自動封裝而直接返回,因此我們需要手動轉換成json。
        if (o instanceof String) {
            return objectMapper.writeValueAsString(R.success(o));
        }
        //如果返回的結果是R對象,即已經封裝好的,直接返回即可。
        //如果不進行這個判斷,後面進行全局異常處理時會出現錯誤
        if (o instanceof R) {
            return o;
        }
        return R.success(o);
    }
}

@RestControllerAdvice@RestController註解的增強,可以實現三個方面的功能:

  1. 全局異常處理
  2. 全局數據綁定
  3. 全局數據預處理

經過上面的處理後,我們就不需要在controller層使用R.success()進行封裝了,直接返回原始數據,springboot就會幫我們自動封裝。

@RestController
@RequestMapping("/test")
public class TestController {
    @PostMapping("/test1")
    public List<Student> getStudent() {
        ArrayList<Student> list = new ArrayList<>();
        Student student1 = new Student();
        student1.setId(1);
        student1.setName("name1");
        Student student2 = new Student();
        student2.setId(2);
        student2.setName("name2");
        list.add(student1);
        list.add(student2);
        return list;
    }
}

@Data
class Student {
    private Integer id;
    private String name;
}

此時我們調用介面返回的數據依然是自定義統一返回格式的json數據

{
    "code": 200,
    "msg": "ok",
    "data": [
        {
            "id": 1,
            "name": "name1"
        },
        {
            "id": 2,
            "name": "name2"
        }
    ],
    "timestamp": 1692805971325
}

需要註意的是,即使我們controller層的介面返回類型是void,ResponseBodyAdvice類依然會幫我們自動封裝,其中data欄位為null。返回的格式如下:

{
    "code": 200,
    "msg": "ok",
    "data": null,
    "timestamp": 1692805971336
}

4.全局異常處理

如果我們不做統一異常處理,當後端出現異常時,返回的數據就變成了下麵這樣:

後端介面:

@RestController
@RequestMapping("/test")
public class TestController {
    @PostMapping("/test1")
    public String getStudent() {
        int i = 1/0;
        return "hello";
    }
}

返回json:

{
    "code": 200,
    "msg": "ok",
    "data": {
        "timestamp": "2023-08-23T16:13:57.818+00:00",
        "status": 500,
        "error": "Internal Server Error",
        "path": "/test/test1"
    },
    "timestamp": 1692807237832
}

code返回了200,又在data中顯示500錯誤,這顯然不是我們想要的結果,我們想要的結果應該時code返回500,data返回null。解決的方式有很多,你可以通過try catch的方式來捕獲,但是我們並不知道什麼時候會出現異常,而且手動寫try catch並不方便。因此我們需要進行全局異常處理 。

@Slf4j
@RestControllerAdvice
@ResponseBody
public class RestExceptionHandler {

    /**
     * 處理異常
     *
     * @param e otherException
     * @return
     */
    @ExceptionHandler(Exception.class)
    public R<String> exception(Exception e) {
        log.error("異常 exception = {}", e.getMessage(), e);
        return R.error(ReturnCode.RC500.getCode(), ReturnCode.RC500.getMsg());
    }
}

說明:

  1. @RestControllerAdvice,RestController的增強類,可用於實現全局異常處理器
  2. @ExceptionHandler,統一處理某一類異常,比如要獲取空指針異常可以@ExceptionHandler(NullPointerException.class)

除此之外,你還可以使用@ResponseStatus來指定客戶端收到的http狀態碼,如@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)則客戶端收到的http狀態碼為500。如果不指定,則預設返回200。在這裡我們並沒有指定,因此我們的請求返回的http狀態碼全部是200,當出現異常時,我們可以修改統一返回格式中code的狀態碼,來表明具體情況。

具體效果如下:

介面:

@RestController
@RequestMapping("/test")
public class TestController {
    @PostMapping("/test1")
    public void test() {
        int i = 1/0; //發生除0異常
    }
}

返回json:

{
    "code": 500,
    "msg": "操作失敗,伺服器繁忙或伺服器錯誤,請稍後再試。",
    "data": null,
    "timestamp": 1692808061062
}

基本上實現了我們的需求。

5.更優雅的全局異常處理

在上面的全局異常處理中,我們直接捕獲了Exception.class,無論什麼異常都統一處理,但實際上我們需要根據不同的異常進行不同的處理,如空指針異常可能是前端傳參錯誤,以及我們的自定義異常等。

自定義異常如下:

@Getter
@Setter
public class BusinessException extends RuntimeException {
    private int code;
    private String msg;

    public BusinessException() {
    }

    public BusinessException(ReturnCode returnCode) {
        this(returnCode.getCode(),returnCode.getMsg());
    }

    public BusinessException(int code, String msg) {
        super(msg);
        this.code = code;
        this.msg = msg;
    }
}

註:@Getter@Setter分別提供了get和set方法,同樣需要Lombok依賴。

我們在全局異常處理中可以使用@ExceptionHandler指定異常類型,分別處理不同的異常

@Slf4j
@RestControllerAdvice
@ResponseBody
public class RestExceptionHandler {

    /**
     * 處理自定義異常
     *
     * @param e BusinessException
     * @return
     */
    @ExceptionHandler(BusinessException.class)
    public R<String> businessException(BusinessException e) {
        log.error("業務異常 code={}, BusinessException = {}", e.getCode(), e.getMessage(), e);
        return R.error(e.getCode(), e.getMsg());
    }

    /**
     * 處理空指針的異常
     *
     * @param e NullPointerException
     * @return
     * @description 空指針異常定義為前端傳參錯誤,返回400
     */
    @ExceptionHandler(NullPointerException.class)
    public R<String> nullPointerException(NullPointerException e) {
        log.error("空指針異常 NullPointerException ", e);
        return R.error(ReturnCode.RC400.getCode(), ReturnCode.RC400.getMsg());
    }

    /**
     * 處理其他異常
     *
     * @param e otherException
     * @return
     */
    @ExceptionHandler(Exception.class)
    public R<String> exception(Exception e) {
        log.error("未知異常 exception = {}", e.getMessage(), e);
        return R.error(ReturnCode.RC500.getCode(), ReturnCode.RC500.getMsg());
    }
}

需要註意的是一個異常只會被捕獲一次,比如空指針異常,只會被第二個方法捕獲,處理之後不會再被最後一個方法捕獲。當上面兩個方法都沒有捕獲到指定異常時,最後一個方法指定了@ExceptionHandler(Exception.class)就可以捕獲到所有的異常,相當於if elseif else語句

分別測試自定義異常、空指針異常以及其他異常:

  1. 自定義異常

    介面:

@RestController
@RequestMapping("/test")
public class TestController {
    @PostMapping("/test1")
    public void test() {
        throw new BusinessException(ReturnCode.RC500.getCode(),"發生異常");
    }
}

​ 返回json:

{
    "code": 500,
    "msg": "發生異常",
    "data": null,
    "timestamp": 1692809118244
}
  1. 空指針異常:

    介面:

    @RestController
    @RequestMapping("/test")
    public class TestController {
        @PostMapping("/test1")
        public void test(int id, String name) {
            System.out.println(id + name);
            boolean equals = name.equals("11");
        }
    }
    

    請求:

    image-20230824005143814

    返回json:

    {
        "code": 400,
        "msg": "請求失敗,參數錯誤,請檢查後重試。",
        "data": null,
        "timestamp": 1692809456917
    }
    
  2. 其他異常:

    介面:

    @RestController
    @RequestMapping("/test")
    public class TestController {
        @PostMapping("/test1")
        public void test() {
           throw new RuntimeException("發生異常");
        }
    }
    

    返回json:

    {
        "code": 500,
        "msg": "操作失敗,伺服器繁忙或伺服器錯誤,請稍後再試。",
        "data": null,
        "timestamp": 1692809730234
    }
    

6.處理404錯誤

即使我們配置了全局異常處理,當出現404 not found等4xx錯誤時,依然會出現意外情況:

image-20230824010132052

返回json:

{
    "code": 200,
    "msg": "ok",
    "data": {
        "timestamp": "2023-08-23T17:01:15.102+00:00",
        "status": 404,
        "error": "Not Found",
        "path": "/test/nullapi"
    },
    "timestamp": 1692810075116
}

我們可以看到發生404錯誤時控制台並沒有報異常,原因是404錯誤並不屬於異常,全局異常處理自然不會去捕獲並處理。因此我們的解決方法是當出現4xx錯誤時,讓springboot直接報異常,這樣我們的全局異常處理就可以捕獲到。

application.yml配置文件增加以下配置項:

#  當HTTP狀態碼為4xx時直接拋出異常
spring:
  mvc:
    throw-exception-if-no-handler-found: true
  #  關閉預設的靜態資源路徑映射
  web:
    resources:
      add-mappings: false

現在當我們再次請求一個不存在的介面是,控制台會報NoHandlerFoundException異常,然後被全局異常處理捕獲到並統一返回

返回json:

{
    "code": 500,
    "msg": "操作失敗,伺服器繁忙或伺服器錯誤,請稍後再試。",
    "data": null,
    "timestamp": 1692810621545
}

image-20230824011215375

當發生404錯誤時,http的狀態碼依然是200,同時code返回的是500,這不利於用戶或者前端人員的理解,因此我們可以在全局異常處理中單獨對NoHandlerFoundException異常進行處理。

/**
     * 處理404異常
     *
     * @param e NoHandlerFoundException
     * @return
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)//指定http狀態碼為404
    public R<String> noHandlerFoundException(HttpServletRequest req, Exception e) {
        log.error("404異常 NoHandlerFoundException, method = {}, path = {} ", req.getMethod(), req.getServletPath(), e);
        return R.error(ReturnCode.RC404.getCode(), ReturnCode.RC404.getMsg());
    }

在上面中,我們使用@ExceptionHandler(NoHandlerFoundException.class)單獨捕獲處理404異常,同時使用@ResponseStatus(HttpStatus.NOT_FOUND)指定http返回碼為404,我們統一返回格式中code也設置為404

現在當我們再次發生404異常時,返回json如下:

{
    "code": 404,
    "msg": "未找到您請求的資源。",
    "data": null,
    "timestamp": 1692811047868
}

image-20230824011752721

控制台日誌:

image-20230824012427283

同理我們還可以為405錯誤進行配置,405錯誤對應的異常為HttpRequestMethodNotSupportedException

/**
     * 處理請求方式錯誤(405)異常
     *
     * @param e HttpRequestMethodNotSupportedException
     * @return
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)//指定http狀態碼為405
    public R<String> HttpRequestMethodNotSupportedException(HttpServletRequest req, Exception e) {
        log.error("請求方式錯誤(405)異常 HttpRequestMethodNotSupportedException, method = {}, path = {}", req.getMethod(), req.getServletPath(), e);
        return R.error(ReturnCode.RC405.getCode(), ReturnCode.RC405.getMsg());
    }

返回json:

{
    "code": 405,
    "msg": "請求方式錯誤,請檢查後重試。",
    "data": null,
    "timestamp": 1692811288226
}

image-20230824012201529

控制台日誌:

image-20230824012503918

全局異常處理RestExceptionHandler類完整代碼如下:

package com.tuuli.config;

import com.tuuli.common.BusinessException;
import com.tuuli.common.R;
import com.tuuli.common.ReturnCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;

/**
 * 全局異常處理
 */
@Slf4j
@RestControllerAdvice
@ResponseBody
public class RestExceptionHandler {

    /**
     * 處理自定義異常
     *
     * @param e BusinessException
     * @return
     */
    @ExceptionHandler(BusinessException.class)
    public R<String> businessException(BusinessException e) {
        log.error("業務異常 code={}, BusinessException = {}", e.getCode(), e.getMessage(), e);
        return R.error(e.getCode(), e.getMsg());
    }

    /**
     * 處理空指針的異常
     *
     * @param e NullPointerException
     * @return
     * @description 空指針異常定義為前端傳參錯誤,返回400
     */
    @ExceptionHandler(value = NullPointerException.class)
    public R<String> nullPointerException(NullPointerException e) {
        log.error("空指針異常 NullPointerException ", e);
        return R.error(ReturnCode.RC400.getCode(), ReturnCode.RC400.getMsg());
    }

    /**
     * 處理404異常
     *
     * @param e NoHandlerFoundException
     * @return
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public R<String> noHandlerFoundException(HttpServletRequest req, Exception e) {
        log.error("404異常 NoHandlerFoundException, method = {}, path = {} ", req.getMethod(), req.getServletPath(), e);
        return R.error(ReturnCode.RC404.getCode(), ReturnCode.RC404.getMsg());
    }

    /**
     * 處理請求方式錯誤(405)異常
     *
     * @param e HttpRequestMethodNotSupportedException
     * @return
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public R<String> HttpRequestMethodNotSupportedException(HttpServletRequest req, Exception e) {
        log.error("請求方式錯誤(405)異常 HttpRequestMethodNotSupportedException, method = {}, path = {}", req.getMethod(), req.getServletPath(), e);
        return R.error(ReturnCode.RC405.getCode(), ReturnCode.RC405.getMsg());
    }

    /**
     * 處理其他異常
     *
     * @param e otherException
     * @return
     */
    @ExceptionHandler(Exception.class)
    public R<String> exception(Exception e) {
        log.error("未知異常 exception = {}", e.getMessage(), e);
        return R.error(ReturnCode.RC500.getCode(), ReturnCode.RC500.getMsg());
    }
}

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

-Advertisement-
Play Games
更多相關文章
  • > 優雅解決方案在最下麵,小伙伴們兒可以直接前往 😊 > # 背景 在vue3+vite2項目中,我們有時候想要動態綁定資源,比如像下麵的代碼這樣: ```html ``` 實際效果是這樣: ![](https://img2023.cnblogs.com/blog/3153981/202308/3 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言 隨著JavaScript在現代軟體開發中的日益重要地位,Node.js生態系統中的npm成為了不可或缺的工具。在npm管理依賴的過程中,package-lock.json文件的作用日益凸顯。本文將深入探討為什麼要使用package- ...
  • 今天在寫一個選擇器的時候出現一個問題 這個功能需求是:通過選擇器選擇不同的選項,點擊查詢按鈕發送請求,並將響應結果放到一個div中用v-if控制是否顯示。 看似簡單的一個功能,卻出現一個很搞笑的bug。在我選擇一個選項點擊查詢,本應該顯示結果的div沒有顯示出來,而在選擇一個其他選項,不需要點擊查詢 ...
  • ##### 13 CSS 的position屬性 就像photoshop中的圖層功能會把一整張圖片分層一個個圖層一樣,網頁佈局中的每一個元素也可以看成是一個個類似圖層的層模型。層佈局模型就是把網頁中的每一個元素看成是一層一層的,然後通過定位屬性position對元素進行定位擺放,最終實現網頁的佈局。 ...
  • ##### 12 CSS 的float屬性 - 流動佈局 流動模型(Flow),即文檔流,瀏覽器打開HTML網頁時,從上往下,從左往右,逐一載入。 在正常情況下,HTML元素都會根據文檔流來分佈網頁內容的。 文檔流有2大特征: ① 塊狀元素會隨著瀏覽器讀取文檔的順序,自上而下垂直分佈,一行一個的形式 ...
  • 伴隨物流行業的迅猛發展,一體化供應鏈模式的落地,對系統吞吐、系統穩定發出巨大挑戰,庫存作為供應鏈的重中之重表現更為明顯。 ...
  • ### 歡迎訪問我的GitHub > 這裡分類和彙總了欣宸的全部原創(含配套源碼):[https://github.com/zq2599/blog_demos](https://github.com/zq2599/blog_demos) ### 本篇概覽 - 本篇是《java與es8實戰》系列的第二 ...
  • —併發包— 大型企業開發才用,我Java學得差不多以後再來學,主要是有點難,沒學泛型與集合… 練手的ConcurrentHashMap: import java.util.HashMap;import java.util.Hashtable;import java.util.concurrent.C ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...