首頁 > 軟體

解讀@RequestBody與post請求的關係

2022-12-29 14:01:40

@RequestBody與post請求的關係

@RequestBody主要用來接收前端傳遞給後端的json字串中的資料的(請求體中的資料的);

GET方式無請求體,所以使用@RequestBody接收資料時,前端不能使用GET方式提交資料,而是用POST方式進行提交。

在後端的同一個接收方法裡,@RequestBody與@RequestParam()可以同時使用,@RequestBody最多隻能有一個,而@RequestParam()可以有多個。

  • 注:一個請求,只有一個RequestBody;一個請求,可以有多個RequestParam。
  • 注:當同時使用@RequestParam()和@RequestBody時,@RequestParam()指定的引數可以是普通元素、

陣列、集合、物件等等(即:當,@RequestBody 與@RequestParam()可以同時使用時,原SpringMVC接收引數的機制不變,只不過RequestBody 接收的是請求體裡面的資料;而RequestParam接收的是key-value裡面的引數,所以它會被切面進行處理從而可以用普通元素、陣列、集合、物件等接收)。

即:如果引數是放在請求體中,傳入後臺的話,那麼後臺要用@RequestBody才能接收到;如果不是放在請求體中的話,那麼後臺接收前臺傳過來的引數時,要用@RequestParam來接收,或者形參前什麼也不寫也能接收。

  • 注:如果引數前寫了@RequestParam(xxx),那麼前端必須有對應的xxx名字才行(不管其是否有值,當然可以通過設定該註解的required屬性來調節是否必須傳),如果沒有xxx名的話,那麼請求會出錯,報400。
  • 注:如果引數前不寫@RequestParam(xxx)的話,那麼就前端可以有可以沒有對應的xxx名字才行,如果有xxx名的話,那麼就會自動匹配;沒有的話,請求也能正確傳送。
  • 追注:這裡與feign消費服務時不同;feign消費服務時,如果引數前什麼也不寫,那麼會被預設是@RequestBody的。

如果後端引數是一個物件,且該引數前是以@RequestBody修飾的,那麼前端傳遞json引數時,必須滿足以下要求:

後端@RequestBody註解對應的類在將HTTP的輸入流(含請求體)裝配到目標類(即:@RequestBody後面的類)時,會根據json字串中的key來匹配對應實體類的屬性,如果匹配一致且json中的該key對應的值符合(或可轉換為),這一條我會在下面詳細分析,其他的都可簡單略過,但是本文末的核心邏輯程式碼以及幾個結論一定要看! 實體類的對應屬性的型別要求時,會呼叫實體類的setter方法將值賦給該屬性。

json字串中,如果value為"“的話,後端對應屬性如果是String型別的,那麼接受到的就是”",如果是後端屬性的型別是Integer、Double等型別,那麼接收到的就是null。

json字串中,如果value為null的話,後端對應收到的就是null。

問題描述

由於專案是前後端分離,因此後臺使用的是spring boot,做成微服務,只暴露介面。介面設計風格為restful的風格,在get請求下,後臺接收引數的註解為RequestBody時會報錯;在post請求下,後臺接收引數的註解為RequestParam時也會報錯。

問題原因

由於spring的RequestParam註解接收的引數是來自於requestHeader中,即請求頭,也就是在url中,格式為xxx?username=123&password=456,而RequestBody註解接收的引數則是來自於requestBody中,即請求體中。

解決方法

因此綜上所述,如果為get請求時,後臺接收引數的註解應該為RequestParam,如果為post請求時,則後臺接收引數的註解就是為RequestBody。附上兩個例子,截圖如下:

  • get請求

  • post請求

另外,還有一種應用場景,介面規範為resultful風格時,舉個例子:如果要獲取某個id下此條問題答案的查詢次數的話,則後臺就需要動態獲取引數,其註解為@PathVariable,並且requestMapping中的value應為value="/{id}/queryNum",截圖如下:

Post請求中@RequestParam和@RequestBody混合使用

如何在一個@RestController方法中混合使用@RequestParam@RequestBody呢?就像這樣的程式碼:

@PostMapping("/scanRecord")
public Response scanRecord(@RequestParam("t") int type, @RequestBody ScanRecord scanRecord) {
    System.out.println("type="+type);
    System.out.println(JSON.toJSONString(scanRecord));
    return Response.ok();
}

@RequestBody用來接收http post請求的body,前端傳入序列化好的json資料,後端可以解析為json物件(Content-Type需要指定為 application/json)。

@RequestParam用來接收請求url?後面的引數,或者Content-Type為multipart/form-data、application/x-www-form-urlencoded時的http body資料

所以,如果想要在@RestController的一個方法中,同時接收@RequestParam@RequestBody資料,前端應該這樣用:

application/json的方式傳入@RequestBody接收的物件,並且在url?後帶上@RequestParam需要接收的引數。

前端js使用axios提交的話,是這樣的:

function post() {
    var data = {
        id: 1000,
        pn:'華為手機'
    };
    axios.post('http://localhost:8080/api/scanRecord?t=1', data).then(function (res) {
        
    }).catch(function (error) {

    });
}

最後,附上前端通過axios post提交multipart/form-dataapplication/x-www-form-urlencoded資料的範例程式碼:

//form-data方式
function post() {
    var data = new FormData();
    data.append("id", 1000);
    data.append("pn", '華為手機');
    axios.post('http://localhost:8080/api/scanRecord?t=1', data).then(function (res) {
    }).catch(function (error) {
    });
}

//x-www-form-urlencoded方式
function encodeData(data) {
    var args = [];
    for(var attr in data) {
        args.push(attr+"=" + data[attr]);
    }
    return encodeURI(args.join("&"));
}
function post() {
    var data = {
        id: 1000,
        pn:'華為手機'
    };
    axios.post('http://localhost:8080/api/scanRecord?t=1', encodeData(data)).then(function (res) {

    }).catch(function (error) {

    });
}

總結

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com