SpringBoot接收接口入?yún)⒌姆绞叫〗Y(jié)
引言
我們從調(diào)用方的視角去看待這個問題,對調(diào)用方來說,它在調(diào)用接口時有如下的幾種傳參方式:
Query參數(shù)。表現(xiàn)形式是調(diào)用方在調(diào)用接口時,入?yún)⑹瞧唇釉诮涌诘?code>URI后面的,如/test_get/requestparam_1?name=wangwu&age=18
。這種方式在入?yún)€數(shù)比較少
的GET
請求方式中比較常用。
Path參數(shù)。這是REST
風(fēng)格的路徑參數(shù)
,入?yún)⒅苯悠唇釉诮涌诘?code>URI里面,如/test_pathvar/test1/zhangsan/1
,其中的zhangsan
和1
就是就是參數(shù)。這種方式在REST
風(fēng)格的接口中比較常用。
Body參數(shù)。這種方式是把入?yún)⒎旁诹?code>請求體當(dāng)中!它跟前兩種入?yún)⒎绞降淖畲髤^(qū)別,就是:
1)前兩種入?yún)⒎绞剿鼈兊娜雲(yún)⒍际侵苯芋w現(xiàn)在了調(diào)用接口時候的URI
中
2)而當(dāng)前的這種Body參數(shù)
方式,它的入?yún)⑹欠旁诹?code>Body請求體內(nèi)
而且,Body參數(shù)又可以細(xì)分成如下的幾種方式:
application/json 前后端分離項目中常用的傳參方式
x-www-form-urlencoded 上傳表單數(shù)據(jù)
form-data 上傳表單數(shù)據(jù)
raw
binary
另外需要強調(diào)的是,無論是GET、POST、PUT還是DELETE請求方式,從技術(shù)上來說,它們是都支持上面提到的幾種傳參方式的!只不過在日常的開發(fā)中,我們可能習(xí)慣了GET+Query參數(shù)或者POST+Body參數(shù)(application/json)這樣的搭配使用方式
下面,將會依次對這幾種參數(shù)方式進行講解和代碼示例。
重要說明
項目的pom依賴
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.9.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--Hutool是一個小而全的Java工具類庫--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.6.3</version> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.30</version> </dependency> <!--fastjson--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.6</version> </dependency> </dependencies>
代碼的說明
- 下面所有接口的定義,使用的都是
@RequestMapping
,且都沒有指定具體的請求類型——如此故意為之就是為了使得接口從技術(shù)上
支持各種請求方式(GET
POST
PUT
DELETE
等),好方便測試在不同的請求方式下,是否支持各種不同的傳參方式
。但是大家在平日的業(yè)務(wù)代碼開發(fā)中,最好是在接口定義時指定特定的請求方式。 - 在下面所有的代碼中,我都在方法定義的形參中加上了
HttpServletRequest
,是因為我要從HttpServletRequest
中獲取請求的方法類型(request.getMethod()
)和URI(request.getRequestURI()
),從而在接口的返回結(jié)果在顯現(xiàn)出來,以方便調(diào)試。 - 大家在自己實際的業(yè)務(wù)代碼中可以根據(jù)自身需求決定是否加上這個。不加也不影響入?yún)⒌慕邮眨?/li>
- 下面所以接口的定義中,方法返回類型都是string類型,如:
method: [DELETE], uri: [/test_query/requestparam_1], param type: [Query] ---> SUCCESS! requestParam1 name = wangwu-delete, age = 18
但是看后面調(diào)用方調(diào)用接口的返回結(jié)果,卻是如下所示的json串:
{ "code": 0, "data": "method: [DELETE], uri: [/test_query/requestparam_1], param type: [Query] ---> SUCCESS! requestParam1 name = wangwu-delete, age = 18", "msg": "操作成功", "timestamp": 1704766444205 }
可以看到方法返回的String內(nèi)容則是在json串的data
這個key當(dāng)中。
這是因為在我的項目中,我結(jié)合@RestControllerAdvice
和ResponseBodyAdvice
,對接口返回結(jié)果進行了統(tǒng)一的處理。(核心的處理邏輯是:如果接口返回結(jié)果類型已經(jīng)是指定的ResultVO
,直接返回;否則將接口返回結(jié)果封裝到ResultVO
對象的data字段中,再返回。)
4. 本文旨在展示在各種請求方式下對不同傳參方式的支持情況,因此下面代碼中,都沒有對接口入?yún)⑦M行任何的校驗
——即:校驗不是本文的重點,關(guān)于接口入?yún)⒌男r灒梢匀タ次业牧硪黄┛汀?/p>
1.Query參數(shù)
Query參數(shù),表現(xiàn)形式是調(diào)用方在調(diào)用接口時,入?yún)⑹瞧唇釉诮涌诘?code>URI后面的,如/test_query/requestparam_1?name=wangwu&age=18
。
1.1接口定義方式1:在方法形參的位置,把每個參數(shù)都平鋪開來
- 定義方式: 在方法形參的位置,把每個參數(shù)都平鋪開來
- 兼容的請求方式:
GET
POST
PUT
DELETE
- 適用場景:雖然這種傳參方式,從技術(shù)上來說可用于
GET
POST
PUT
DELETE
等請求方式,但是在日常開發(fā)中一般偏向于應(yīng)用在入?yún)€數(shù)少(一般少于5個)
的GET
請求方式中 - 優(yōu)點:方便簡單
- 缺點:
- 調(diào)用時入?yún)⒅苯语@示在uri中,不太安全
- 方法定義時參數(shù)個數(shù)如果過多,方法體結(jié)構(gòu)會顯得很臃腫
- 沒有入?yún)⑿r?/li>
代碼
package com.su.demo.controller.param_type; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Date; @RestController @RequestMapping(value = "/param_type/query") public class TestQueryController { /** * <ul> * <li><b>入?yún)㈩悇e</b>:Query參數(shù) * <li><b>定義方式</b>: 在方法形參的位置,把每個參數(shù)都平鋪開來</li> * <li><b>調(diào)用方式</b>: 入?yún)⑵唇釉诮涌诘膗ri后面,如 /test_get/requestparam_1?name=wangwu&age=18</li> * <li><b>兼容的請求方式</b>:GET POST PUT DELETE</li> * <li><b>適用場景</b>:雖然這種傳參方式,適用于GET POST PUT DELETE等請求方式,但是一般偏向于應(yīng)用在 入?yún)€數(shù)少(一般少于5個)的<b>GET</b>請求方式中</li> * <li><b>優(yōu)點</b>:方便簡單</li> * <li><b>缺點</b>:1) 入?yún)⒅苯语@示在uri中,不太安全 2)參數(shù)個數(shù)如果過多,方法體結(jié)構(gòu)會顯得很臃腫; 3)沒有入?yún)⑿r?lt;/li> * </ul> * <p><b>注意</b>:根據(jù)自己的需求決定是否在形參中加上HttpServletRequest,我這里是為了從request中獲取method,所以加上了</p> */ @RequestMapping(value = "/test1") public String test1(String name, Integer age, HttpServletRequest request) { // 從request中獲取一些接口請求時的元數(shù)據(jù)信息,包括請求方式,Content-type等 String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(), request.getHeader("Content-Type")); return requestMetaInfo + " ---> SUCCESS! name = " + name + ", age = " + age; } }
調(diào)用case
注意看上面這個/param_type/query/test1
接口的定義方式,它并沒有指定請求方式,因此它支持GET
POST
PUT
DELETE
等所有的請求方式(已經(jīng)過驗證),為了節(jié)約文章篇幅,下面只以GET
請求方式的調(diào)用結(jié)果為例,看是否可以在controller
代碼中獲取到調(diào)用方傳過來的實參。
發(fā)起請求:
接口返回結(jié)果:
1.2接口定義方式2:在方法形參的位置,結(jié)合@RequestParam把每個參數(shù)都平鋪開來
- 定義方式: 在方法形參的位置,結(jié)合
@RequestParam
把每個參數(shù)都平鋪開來 - 兼容的請求方式:
GET
POST
PUT
DELETE
- 適用場景:雖然這種傳參方式,從技術(shù)上來說可用于
GET
POST
PUT
DELETE
等請求方式,但是在日常開發(fā)中一般偏向于應(yīng)用在入?yún)€數(shù)少(一般少于5個)
的GET
請求方式中 - 優(yōu)點:方便簡單,且結(jié)合
@RequestParam
,可實現(xiàn)入?yún)⒌?code>必填校驗/重命名/默認(rèn)值等簡單的校驗功能 - 缺點:
- 調(diào)用時入?yún)⒅苯语@示在uri中,不太安全
- 方法定義時如果參數(shù)個數(shù)如果過多,方法體結(jié)構(gòu)會顯得很臃腫
@RequestParam
能支持的校驗相對來說還是比較簡單
代碼
package com.su.demo.controller.param_type; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Date; @RestController @RequestMapping(value = "/param_type/query") public class TestQueryController { /** * <ul> * <li><b>入?yún)㈩悇e</b>:Query參數(shù) * <li><b>定義方式</b>: 在方法形參的位置,把每個參數(shù)都平鋪開來.并且相較于前一種定義方式,這種方式使用 {@link RequestParam}綁定請求參數(shù)到方法形參, 且需要注意該注解中的各個屬性的作用!</li> * <li><b>調(diào)用方式</b>: 入?yún)⑵唇釉诮涌诘膗ri后面,如 /test_get/requestparam_1?name=wangwu&age=18</li> * <li><b>兼容的請求方式</b>:GET POST PUT DELETE</li> * <li><b>適用場景</b>:雖然這種傳參方式,適用于GET POST PUT DELETE等請求方式,但是一般偏向于應(yīng)用在 入?yún)€數(shù)少(一般少于5個)的<b>GET</b>請求方式中</li> * <li><b>優(yōu)點</b>:方便簡單;且結(jié)合{@link RequestParam},可實現(xiàn)入?yún)⒌谋靥钚r?重命名/默認(rèn)值等簡單的功能</li> * <li><b>缺點</b>:1) 入?yún)⒅苯语@示在uri中,不太安全 * 2)參數(shù)個數(shù)如果過多,方法體結(jié)構(gòu)會顯得很臃腫(當(dāng)前這個方法有4個入?yún)?其實就已經(jīng)有點臃腫了) * 3){@link RequestParam}能支持的校驗相對來說還是比較簡單</li> * </ul> */ @RequestMapping(value = "/test2_requestparam") public String test2(@RequestParam String name, @RequestParam(name = "newAge") Integer age, @RequestParam(name = "birth", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date birth, @RequestParam(defaultValue = "true") Boolean enable, HttpServletRequest request) { // 從request中獲取一些接口請求時的元數(shù)據(jù)信息,包括請求方式,Content-type等 String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(), request.getHeader("Content-Type")); return requestMetaInfo + " ---> SUCCESS! name = " + name + ", age = " + age + ", birth = " + birth + ", enable = " + enable; } }
調(diào)用case
注意看上面這個/param_type/query/test2_requestparam
接口的定義方式,它并沒有指定請求方式,因此它支持GET
POST
PUT
DELETE
等所有的請求方式(已經(jīng)過驗證),為了節(jié)約文章篇幅,下面只以GET
請求方式的調(diào)用結(jié)果為例,看是否可以在controller
代碼中獲取到調(diào)用方傳過來的實參。
可以看到,參數(shù)都獲取到了。
1.3接口定義方式3:把入?yún)⒎庋b到一個實體中
- 定義方式: 把入?yún)⒎庋b到一個實體中
- 兼容的請求方式:
GET
POST
PUT
DELETE
- 適用場景:雖然這種傳參方式,從技術(shù)上來說可用于
GET
POST
PUT
DELETE
等請求方式,但是在日常開發(fā)中一般偏向于應(yīng)用在入?yún)€數(shù)多(一般大于5個)
的GET
請求方式中 - 優(yōu)點:因為參數(shù)都封裝在實體對象當(dāng)中了,所以對參數(shù)的個數(shù)就沒有什么的限制了,接口定義的時候方便多了
- 缺點:對于這種入?yún)⒌亩x方式來說,它是沒有什么缺點的,硬要說缺點的話,其實是針對Query這種傳參方式來說的,即:當(dāng)參數(shù)個數(shù)一多的時候,參數(shù)都放在請求uri中了,一個是不太安全,另外也容易造成uri的長度過過長 (雖然http協(xié)議中未明確對url進行長度限制,但在真正實現(xiàn)中,url的長度還是受到限制的,一是服務(wù)器端的限制,二就是游覽器端的限制)
注:再次申明,在下面代碼中,我在方法定義的形參中加上了
HttpServletRequest
,是因為我要從HttpServletRequest
中獲取請求的方法類型(request.getMethod()
)和URI(request.getRequestURI()
),從而在接口的返回結(jié)果在顯現(xiàn)出來,以方便調(diào)試。
大家在自己實際的業(yè)務(wù)代碼中可以根據(jù)自身需求決定是否加上這個。不加也不影響入?yún)⒌慕邮眨?/p>
代碼
package com.su.demo.controller.param_type; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Date; @RestController @RequestMapping(value = "/param_type/query") public class TestQueryController { /** * <ul> * <li><b>入?yún)㈩悇e</b>:Query參數(shù) * <li><b>定義方式</b>: 把入?yún)⒎庋b到一個實體中(入?yún)€數(shù)多于5個時一般用這種方式) * <li><b>調(diào)用方式</b>: 入?yún)⑵唇釉诮涌诘膗ri后面,如 /test_get/requestparam_1?name=wangwu&age=18</li> * <li><b>兼容的請求方式</b>:GET POST PUT DELETE</li> * <li><b>適用場景</b>:從技術(shù)上來說這種傳參方式,同時適用于GET POST PUT DELETE等多種請求方式,但是一般我個從偏向于在入?yún)€數(shù)大于5個的<b>GET</b>請求方式中使用</li> * <li><b>優(yōu)點</b>:因為參數(shù)都封裝在實體bean當(dāng)中了,所以對參數(shù)的個數(shù)就沒有什么的限制了,接口定義的時候方便多了 * <li><b>缺點</b>:對于這種入?yún)⒌腫定義方式]來說,它是沒有什么缺點的.硬要說缺點的話,其實是針對Query這種傳參方式來說的,即當(dāng)參數(shù)個數(shù)一多的時候,參數(shù)都放在請求uri中容易造成uri的長度過過長 * (雖然http協(xié)議中未明確對url進行長度限制,但在真正實現(xiàn)中,url的長度還是受到限制的,一是服務(wù)器端的限制,二就是游覽器端的限制)</li> * </ul> */ @RequestMapping(value = "/test3_entity") public String test3(UserDTO userDTO, HttpServletRequest request) { // 從request中獲取一些接口請求時的元數(shù)據(jù)信息,包括請求方式,Content-type等 String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(), request.getHeader("Content-Type")); return requestMetaInfo + " ---> SUCCESS! userDTO = " + userDTO; } }
其中UserDTO
的代碼如下:
package com.su.demo.bean.dto; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import java.util.Date; @Data public class UserDTO { /** * 主鍵ID */ private Long id; /** * 用戶姓名 */ private String name; /** * 用戶狀態(tài) true:啟用;false:禁用 */ private Boolean enable; /** * 用戶生日 */ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date birth; }
調(diào)用case
注意看上面這個/param_type/query/test3_entity
接口的定義方式,它并沒有指定請求方式,因此它支持GET
POST
PUT
DELETE
等所有的請求方式(已經(jīng)過驗證),為了節(jié)約文章篇幅,下面只以POST
請求方式的調(diào)用結(jié)果為例,看是否可以在controller
代碼中獲到調(diào)用方傳過來的實參。
可以看到,參數(shù)都獲取到了。
1.4接口定義方式4:用原生的HttpServletRequest接收參數(shù)
- 定義方式: 在方式形參的位置,用原生的
HttpServletRequest
接收 - 兼容的請求方式:
GET
POST
PUT
DELETE
- 適用場景:現(xiàn)在很少用這種方式去接收入?yún)⒘恕?/li>
- 優(yōu)點:
HttpServletRequest
是整個請求,可以獲取到所有的數(shù)據(jù).且HttpServletRequest
、HttpServletResponse
都是內(nèi)置對象,可以使用 - 缺點:代碼中再去從request中拿到參數(shù),比較麻煩
代碼
package com.su.demo.controller.param_type; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Date; @RestController @RequestMapping(value = "/param_type/query") public class TestQueryController { /** * <ul> * <li><b>入?yún)㈩悇e</b>:Query參數(shù) * <li><b>定義方式</b>: 在方式形參的位置,用原生的{@link HttpServletRequest}接收 * <li><b>調(diào)用方式</b>: 入?yún)⑵唇釉诮涌诘膗ri后面,如 /test_get/requestparam_1?name=wangwu&age=18</li> * <li><b>兼容的請求方式</b>:GET POST PUT DELETE</li> * <li><b>適用場景</b>:從技術(shù)上來說,這種傳參方式同時適用于GET POST PUT DELETE等請求方式,但是正常情況下比較少用這種方式來獲取參數(shù)了</li> * <li><b>優(yōu)點</b>:{@link HttpServletRequest}是整個請求,可以獲取到所有的數(shù)據(jù).且HttpServletRequest、HttpServletResponse都是內(nèi)置對象,可以使用</li> * <li><b>缺點</b>:代碼中再去從request中拿到參數(shù),比較麻煩</li> * </ul> * <p> 注意,這種方式其實也可以獲取body請求體里面的數(shù)據(jù),參考https://blog.csdn.net/qq_24850045/article/details/121927722 */ @RequestMapping(value = "/test4_request") public String test4(HttpServletRequest request) throws IOException { // 從request中獲取一些接口請求時的元數(shù)據(jù)信息,包括請求方式,Content-type等 String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(), request.getHeader("Content-Type")); String name = request.getParameter("name"); Integer age = Integer.parseInt(request.getParameter("age")); // 需要進行類型轉(zhuǎn)換 return requestMetaInfo + " ---> SUCCESS! name = " + name + ", age = " + age; } }
調(diào)用case
注意看上面這個/param_type/query/test4_request
接口的定義方式,它并沒有指定請求方式,因此它支持GET
POST
PUT
DELETE
等所有的請求方式(已經(jīng)過驗證),為了節(jié)約文章篇幅,下面只以PUT
請求方式的調(diào)用結(jié)果為例,看是否可以在controller
代碼中獲到調(diào)用方傳過來的實參。
可以看到,參數(shù)都獲取到了。
1.5接口定義方式5:用Map結(jié)合RequestParam接收參數(shù)
- 定義方式: 在方式形參的位置,用Map結(jié)合
RequestParam
接收參數(shù) - 兼容的請求方式:
GET
POST
PUT
DELETE
- 適用場景:適用于參數(shù)個數(shù)較多,但是自己又想偷懶不想專門定義一個實體來接收入?yún)⒌膱鼍啊R话悴煌扑]使用這種方式。
- 優(yōu)點:簡單方便,偷懶很開心
- 缺點:1)需要在代碼中通過指定特定的key的方式去獲取入?yún)?,比較麻煩;2)利用map去get到入?yún)⒅?,還需要手動的去做非空判斷+數(shù)據(jù)類型轉(zhuǎn)換等,很麻煩;3)而且這種接收入?yún)⒌姆绞?,沒有辦法結(jié)合
org.springframework.validation.annotation.Validated
來做入?yún)⑿r灒?/li>
代碼
package com.su.demo.controller.param_type; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Date; @RestController @RequestMapping(value = "/param_type/query") public class TestQueryController { /** * <ul> * <li><b>入?yún)㈩悇e</b>:Query參數(shù) * <li><b>定義方式</b>: 在方式形參的位置,結(jié)合{@link RequestParam}用Map接收 * <li><b>調(diào)用方式</b>: 入?yún)⑵唇釉诮涌诘膗ri后面,如 /test_get/requestparam_1?name=wangwu&age=18</li> * <li><b>兼容的請求方式</b>:GET POST PUT DELETE</li> * <li><b>適用場景</b>:適用于參數(shù)個數(shù)較多,但是自己又想偷懶不想專門定義一個實體來接收入?yún)⒌膱鼍?。一般不推薦使用這種方式。</li> * <li><b>優(yōu)點</b>:簡單方便</li> * <li><b>缺點</b>:需要在代碼中通過指定特定的key的方式去獲取入?yún)ⅲ容^麻煩;而且這種接收入?yún)⒌姆绞?,沒有辦法結(jié)合{@link org.springframework.validation.annotation.Validated}來做入?yún)⑿r?lt;/li> * </ul> */ @RequestMapping(value = "/test5_map") public String test5(@RequestParam Map<String, String> paramMap, HttpServletRequest request) throws IOException { // 從request中獲取一些接口請求時的元數(shù)據(jù)信息,包括請求方式,Content-type等 String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(), request.getHeader("Content-Type")); String name = paramMap.get("name"); // 判斷:如果paramMap.get("age")為null,則age賦默認(rèn)值0;否則進行入?yún)㈩愋偷霓D(zhuǎn)換 Integer age = null == paramMap.get("age") ? 0: Integer.parseInt(paramMap.get("age")); return requestMetaInfo + " ---> SUCCESS! name = " + name + ", age = " + age; } }
調(diào)用case
注意看上面這個/param_type/query/test5_map
接口的定義方式,它并沒有指定請求方式,因此它支持GET
POST
PUT
DELETE
等所有的請求方式(已經(jīng)過驗證),為了節(jié)約文章篇幅,下面只以DELETE
請求方式的調(diào)用結(jié)果為例,看是否可以在controller
代碼中獲到調(diào)用方傳過來的實參。
可以看到,參數(shù)都獲取到了。
2.Path參數(shù)
Path參數(shù),表現(xiàn)形式是入?yún)⒅苯悠唇釉诮涌诘?code>URI里面,如/test_pathvar/test1/zhangsan/1
,其中的zhangsan
和1
就是就是參數(shù)。這種方式在REST
風(fēng)格的接口中比較常用。
REST
(英文:Representational State Transfer
,簡稱REST
,意思:表述性狀態(tài)轉(zhuǎn)換,描述了一個架構(gòu)樣式的網(wǎng)絡(luò)系統(tǒng),比如web應(yīng)用),一定要記住==它是一種軟件架構(gòu)風(fēng)格!而不是標(biāo)準(zhǔn)!==它只是提供了一組設(shè)計原則和約束條件。它主要用于客戶端和服務(wù)器交互類的軟件。基于這個風(fēng)格設(shè)計的軟件可以更簡潔,更有層次,更易于實現(xiàn)緩存等機制。REST
指的是一組架構(gòu)(約束條件)和原則。滿足這些(約束條件)和(原則)的應(yīng)用程序或設(shè)計就是Restful
。
2.1接口定義方式:用占位符的方式把入?yún)⒎庋b到請求路徑中
- 定義方式: 用
占位符
的方式把入?yún)⒎庋b到請求路徑中,然后再通過@PathVariable
注解可以將URL中的占位符參數(shù)綁定到控制器(controller)處理方法的形參中 - 兼容的請求方式:
GET
POST
PUT
DELETE
- 適用場景:從技術(shù)上來說,這種傳參方式同時適用于
GET
POST
PUT
DELETE
等多種請求方式。 但是一般入?yún)€數(shù)較少,且你開發(fā)的是REST
風(fēng)格的接口,那可以考慮用這種方式 - 優(yōu)點和缺點:優(yōu)點和缺點,其實就是
REST
風(fēng)格的接口的優(yōu)點和缺點了,大家可以自行去查閱
代碼
package com.su.demo.controller.param_type; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; @RestController @RequestMapping(value = "/param_type/path_variable") public class TestPathVarController { /** * <ul> * <li><b>入?yún)㈩悇e</b>:PathVariable路徑參數(shù)方式 * <li><b>定義方式</b>: 用占位符的方式把入?yún)⒎庋b到請求路徑中,然后再通過@PathVariable注解可以將URL中的占位符參數(shù)綁定到控制器(controller)處理方法的形參中. </li> * <li><b>調(diào)用方式</b>: REST風(fēng)格路徑參數(shù),入?yún)⑵唇釉诮涌诘腢RI里面,如/test_pathvar/test1/zhangsan/1 </li> * <li><b>兼容的請求方式</b>:GET POST PUT DELETE</li> * <li><b>適用場景</b>:從技術(shù)上來說,這種傳參方式同時適用于GET POST PUT DELETE等多種請求方式. * 但是一般入?yún)€數(shù)小于5個,且你開發(fā)的是REST風(fēng)格的接口,那可以考慮用這種方式 * </li> * </ul> * <p>REST(英文:Representational State Transfer,簡稱REST,意思:表述性狀態(tài)轉(zhuǎn)換,描述了一個架構(gòu)樣式的網(wǎng)絡(luò)系統(tǒng),比如web應(yīng)用),是一種軟件架構(gòu)風(fēng)格不是標(biāo)準(zhǔn)哦! * 只是提供了一組設(shè)計原則和約束條件。它主要用于客戶端和服務(wù)器交互類的軟件?;谶@個風(fēng)格設(shè)計的軟件可以更簡潔,更有層次,更易于實現(xiàn)緩存等機制。 * REST 指的是一組架構(gòu)(約束條件)和原則。滿足這些(約束條件)和(原則)的應(yīng)用程序或設(shè)計就是 Restful。</p> */ @RequestMapping(value = "/test1/{name}/{age}") public String test1(@PathVariable("name") String name, @PathVariable("age") Integer myage, HttpServletRequest request) { // 從request中獲取一些接口請求時的元數(shù)據(jù)信息,包括請求方式,Content-type等 String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(), request.getHeader("Content-Type")); return requestMetaInfo + " ---> SUCCESS! name = " + name + ", myage = " + myage; } }
調(diào)用case
注意看上面這個/param_type/path_variable/test1/{name}/{age}
接口的定義方式,它并沒有指定請求方式,因此它支持GET
POST
PUT
DELETE
等所有的請求方式(已經(jīng)過驗證),為了節(jié)約文章篇幅,下面只以GET
請求方式的調(diào)用結(jié)果為例,看是否可以在controller
代碼中獲到調(diào)用方傳過來的實參。
可以看到,參數(shù)都獲取到了。
3.Body參數(shù)
Body參數(shù),這種方式是把入?yún)⒎旁诹?code>請求體當(dāng)中!它跟前兩種入?yún)⒎绞降淖畲髤^(qū)別,就是:
- 前兩種入?yún)⒎绞剿鼈兊娜雲(yún)⒍际侵苯芋w現(xiàn)在了調(diào)用接口時候的
URI
中 - 而當(dāng)前的這種Body參數(shù)方式,它的入?yún)⑹欠旁诹薶ttp請求的
請求體
內(nèi)
而且,Body參數(shù)又可以細(xì)分成如下的幾種方式:- application/json 前后端分離項目中常用的傳參方式
- form-data
- x-www-form-urlencoded
- raw
- binary
下面分別進行說明
3.1 application/json
這種入?yún)⒎绞?,入?yún)⒎旁趆ttp請求的請求體
當(dāng)中,并且Content-Type=application/json
。
3.1.1 接口定義方式1:在方式形參的位置,用原生的HttpServletRequest
接收
- 定義方式: 在方式形參的位置,用原生的
HttpServletRequest
接收 - 兼容的請求方式:
GET
POST
PUT
DELETE
- 適用場景:從技術(shù)上來說,這種傳參方式同時適用于GET POST PUT DELETE等請求方式,但是正常情況下比較少用這種方式來獲取
Content-Type=application/json
的body參數(shù)了 - 優(yōu)點:
HttpServletRequest
是整個請求,可以獲取到所有的數(shù)據(jù)。且HttpServletRequest
、HttpServletResponse
都是內(nèi)置對象,可以使用 - 缺點:碼中再去從request中拿到參數(shù),老麻煩了,可以看下面的代碼
代碼
package com.su.demo.controller.param_type; import cn.hutool.core.io.IoUtil; import com.alibaba.fastjson.JSON; import com.su.demo.bean.dto.UserDTO; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import java.io.IOException; @RestController @RequestMapping(value = "/param_type/body_json") public class TestBodyController { /** * <ul> * <li><b>入?yún)㈩悇e</b>:body參數(shù)方式 * <li><b>定義方式</b>: 在方式形參的位置,用原生的{@link HttpServletRequest}接收 * <li><b>調(diào)用方式</b>: 參數(shù)放在請求體 body 當(dāng)中 * <li><b>兼容的請求方式</b>:GET POST PUT DELETE</li> * <li><b>適用場景</b>:從技術(shù)上來說,這種傳參方式同時適用于GET POST PUT DELETE等請求方式,但是正常情況下比較少用這種方式來獲取body參數(shù)了</li> * <li><b>優(yōu)點</b>:{@link HttpServletRequest}是整個請求,可以獲取到所有的數(shù)據(jù).且HttpServletRequest、HttpServletResponse都是內(nèi)置對象,可以使用</li> * <li><b>缺點</b>:代碼中再去從request中拿到參數(shù),老麻煩,可以看下面的代碼</li> * </ul> * <p> 注意,這種方式其實也可以獲取body請求體里面的數(shù)據(jù),參考https://blog.csdn.net/qq_24850045/article/details/121927722 */ @RequestMapping(value = "/test1_request") public String test1(HttpServletRequest request) throws IOException { // 從request中獲取一些接口請求時的元數(shù)據(jù)信息,包括請求方式,Content-type等 String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(), request.getHeader("Content-Type")); ServletInputStream inputStream = request.getInputStream(); // 用hutool工具包的read方式,將inputStream讀取成string String body = IoUtil.read(inputStream, "UTF-8"); System.out.println("body = " + body); // 用fastjson將json字符串轉(zhuǎn)換成bean UserDTO userDTO = JSON.parseObject(body, UserDTO.class); return requestMetaInfo + " ---> SUCCESS! userDTO = " + userDTO; } }
其中UserDTO
的代碼如下:
package com.su.demo.bean.dto; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import java.util.Date; @Data public class UserDTO { /** * 主鍵ID */ private Long id; /** * 用戶姓名 */ private String name; /** * 用戶狀態(tài) true:啟用;false:禁用 */ private Boolean enable; /** * 用戶生日 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date birth; }
調(diào)用case
注意看上面這個/param_type/body_json/test1_request
接口的定義方式,它并沒有指定請求方式,因此它支持GET
POST
PUT
DELETE
等所有的請求方式(已經(jīng)過驗證),為了節(jié)約文章篇幅,下面只以POST
請求方式的調(diào)用結(jié)果為例,看是否可以在controller
代碼中獲取到調(diào)用方傳過來的實參。
可以看到,參數(shù)都獲取到了。
3.1.2 接口定義方式2:在方式形參的位置,用RequestBody
接收
- 定義方式: 在在方式形參的位置,用
RequestBody
接收 - 兼容的請求方式:
GET
POST
PUT
DELETE
- 適用場景:從技術(shù)上來說,這種傳參方式同時適用于
GET
POST
PUT
DELETE
等請求方式,但是正常情況下一般用在POST
或PUT
請求中 - 優(yōu)點:
- 方法形參的定義非常簡潔;
- 調(diào)用的時候參數(shù)放在body中,參數(shù)體可以很大,不像Query入?yún)⒎绞皆诋?dāng)參數(shù)過多的時候容易觸發(fā)服務(wù)器端
Request header is too large
的報錯
- 缺點:用
String
類型來接收參數(shù),接收之后再轉(zhuǎn)換成bean實體,還是稍微顯得有點麻煩
代碼
package com.su.demo.controller.param_type; import cn.hutool.core.io.IoUtil; import com.alibaba.fastjson.JSON; import com.su.demo.bean.dto.UserDTO; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import java.io.IOException; @RestController @RequestMapping(value = "/param_type/body_json") public class TestBodyController { /** * <ul> * <li><b>入?yún)㈩悇e</b>:body參數(shù)方式 * <li><b>定義方式</b>: 在方式形參的位置,用{@link RequestBody}接收 * <li><b>調(diào)用方式</b>: 參數(shù)放在請求體 body 當(dāng)中 * <li><b>兼容的請求方式</b>:GET POST PUT DELETE</li> * <li><b>適用場景</b>:從技術(shù)上來說,這種傳參方式同時適用于GET POST PUT DELETE等請求方式,但是正常情況下一般用在POST或PUT請求中</li> * <li><b>優(yōu)點</b>:1)方法形參的定義非常簡潔; * 2)調(diào)用的時候參數(shù)放在body中,參數(shù)體可以很大,不像Query入?yún)⒎绞皆诋?dāng)參數(shù)過多的時候容易觸發(fā)服務(wù)器端"Request header is too large"的報錯 * </li> * <li><b>缺點</b>:用String類型來接收參數(shù),接收之后再轉(zhuǎn)換成bean實體,還是稍微顯得有點麻煩</li> * </ul> */ @RequestMapping(value = "/test2_requestbody_string") public String test2(@RequestBody String body, HttpServletRequest request) { // 從request中獲取一些接口請求時的元數(shù)據(jù)信息,包括請求方式,Content-type等 String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(), request.getHeader("Content-Type")); System.out.println("body = " + body); // 用fastjson將json字符串轉(zhuǎn)換成bean UserDTO userDTO = JSON.parseObject(body, UserDTO.class); return requestMetaInfo + " ---> SUCCESS! userDTO = " + userDTO; } }
調(diào)用case
注意看上面這個/param_type/body_json/test2_requestbody_string
接口的定義方式,它并沒有指定請求方式,因此它支持GET
POST
PUT
DELETE
等所有的請求方式(已經(jīng)過驗證),為了節(jié)約文章篇幅,下面只以PUT
請求方式的調(diào)用結(jié)果為例,看是否可以在controller
代碼中獲取到調(diào)用方傳過來的實參。
可以看到,參數(shù)都獲取到了。
3.1.3 接口定義方式3:用一個實例來接收RequestBody入?yún)?/h4>
- 定義方式: 在方式形參的位置,用
RequestBody
接收,而且是直接用一個實例類
來接收了(spring自動調(diào)用相應(yīng)的org.springframework.http.converter.HttpMessageConverter
去做了入?yún)⒌霓D(zhuǎn)換) - 兼容的請求方式:
GET
POST
PUT
DELETE
- 適用場景:從技術(shù)上來說,這種傳參方式同時適用于
GET
POST
PUT
DELETE
等請求方式,但是正常情況下一般用在POST
或 PUT
請求中 - 優(yōu)點:
- 方法形參的定義非常簡潔;
- 調(diào)用的時候參數(shù)放在body中,參數(shù)體可以很大,不像Query入?yún)⒎绞皆诋?dāng)參數(shù)過多的時候容易觸發(fā)服務(wù)器端
Request header is too large
的報錯
- 缺點:好像沒啥缺點
RequestBody
接收,而且是直接用一個實例類
來接收了(spring自動調(diào)用相應(yīng)的org.springframework.http.converter.HttpMessageConverter
去做了入?yún)⒌霓D(zhuǎn)換)GET
POST
PUT
DELETE
GET
POST
PUT
DELETE
等請求方式,但是正常情況下一般用在POST
或 PUT
請求中- 方法形參的定義非常簡潔;
- 調(diào)用的時候參數(shù)放在body中,參數(shù)體可以很大,不像Query入?yún)⒎绞皆诋?dāng)參數(shù)過多的時候容易觸發(fā)服務(wù)器端
Request header is too large
的報錯
工作中前后端分離項目一般用這用方式比較多
代碼
package com.su.demo.controller.param_type; import cn.hutool.core.io.IoUtil; import com.alibaba.fastjson.JSON; import com.su.demo.bean.dto.UserDTO; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import java.io.IOException; @RestController @RequestMapping(value = "/param_type/body_json") public class TestBodyController { /** * <ul> * <li><b>入?yún)㈩悇e</b>:body參數(shù)方式 * <li><b>定義方式</b>: 在方式形參的位置,用{@link RequestBody}接收, * 而且是直接用一個實例類來接收了(spring自動調(diào)用相應(yīng)的{@link org.springframework.http.converter.HttpMessageConverter}去做轉(zhuǎn)換) * </li> * <li><b>調(diào)用方式</b>: 參數(shù)放在請求體 body 當(dāng)中 * <li><b>兼容的請求方式</b>:GET POST PUT DELETE</li> * <li><b>適用場景</b>:從技術(shù)上來說,這種傳參方式同時適用于GET POST PUT DELETE等請求方式,但是正常情況下一般用在POST或PUT請求中</li> * <li><b>優(yōu)點</b>:1)方法形參的定義非常簡潔; * 2)調(diào)用的時候參數(shù)放在body中,參數(shù)體可以很大,不像Query入?yún)⒎绞皆诋?dāng)參數(shù)過多的時候容易觸發(fā)服務(wù)器端"Request header is too large"的報錯 * </li> * <li><b>缺點</b>:好像沒啥缺點</li> * </ul> */ @RequestMapping(value = "/test3_requestbody_entity") public String test3(@RequestBody UserDTO userDTO, HttpServletRequest request) { // 從request中獲取一些接口請求時的元數(shù)據(jù)信息,包括請求方式,Content-type等 String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(), request.getHeader("Content-Type")); return requestMetaInfo + " ---> SUCCESS! userDTO = " + userDTO; } }
調(diào)用case
注意看上面這個/param_type/body_json/test3_requestbody_entity
接口的定義方式,它并沒有指定請求方式,因此它支持GET
POST
PUT
DELETE
等所有的請求方式(已經(jīng)過驗證),為了節(jié)約文章篇幅,下面只以DELETE
請求方式的調(diào)用結(jié)果為例,看是否可以在controller
代碼中獲取到調(diào)用方傳過來的實參。
可以看到,參數(shù)都獲取到了。
3.1.4 接口定義方式4:用Map來接收RequestBody入?yún)?/h4>
- 定義方式: 在方式形參的位置,用
RequestBody
接收,而且是直接用一個Map的實例類
來接收了 - 兼容的請求方式:
GET
POST
PUT
DELETE
- 適用場景:適用于參數(shù)個數(shù)較多,但是自己又想偷懶不想專門定義一個實體來接收入?yún)⒌膱鼍?。一般不推薦使用這種方式
- 優(yōu)點:簡單方便,偷懶很開心
- 缺點:1)需要在代碼中通過指定特定的key的方式去獲取入?yún)?,比較麻煩;2)利用map去get到入?yún)⒅?,還需要手動的去做非空判斷+數(shù)據(jù)類型轉(zhuǎn)換等,很麻煩;3)而且這種接收入?yún)⒌姆绞?,沒有辦法結(jié)合
org.springframework.validation.annotation.Validated
來做入?yún)⑿r灒?/li>
RequestBody
接收,而且是直接用一個Map的實例類
來接收了GET
POST
PUT
DELETE
org.springframework.validation.annotation.Validated
來做入?yún)⑿r灒?/li>一般不推薦使用這種方式
代碼
package com.su.demo.controller.param_type; import cn.hutool.core.io.IoUtil; import com.alibaba.fastjson.JSON; import com.su.demo.bean.dto.UserDTO; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import java.io.IOException; @RestController @RequestMapping(value = "/param_type/body_json") public class TestBodyController { /** * <ul> * <li><b>入?yún)㈩悇e</b>:body參數(shù)方式 * <li><b>定義方式</b>: 在方式形參的位置,用{@link RequestBody}接收, * 而且是直接用一個Map對象實例類來接收了(spring自動調(diào)用相應(yīng)的{@link org.springframework.http.converter.HttpMessageConverter}去做轉(zhuǎn)換) * </li> * <li><b>調(diào)用方式</b>: 參數(shù)放在請求體 body 當(dāng)中 * <li><b>兼容的請求方式</b>:GET POST PUT DELETE</li> * <li><b>適用場景</b>:從技術(shù)上來說,這種傳參方式同時適用于GET POST PUT DELETE等請求方式,一般使用在偷懶不想專門定義一個實體來接收入?yún)⒌膱鼍啊R徊惶扑]使用這種方式/li> * <li><b>優(yōu)點</b>:1)方法形參的定義非常簡潔; * 2)調(diào)用的時候參數(shù)放在body中,參數(shù)體可以很大,不像Query入?yún)⒎绞皆诋?dāng)參數(shù)過多的時候容易觸發(fā)服務(wù)器端"Request header is too large"的報錯 * </li> * <li><b>缺點</b>:好像沒啥缺點</li> * </ul> */ @RequestMapping(value = "/test4_requestbody_map") public String test4(@RequestBody Map<String, String> paramMap, HttpServletRequest request) { // 從request中獲取一些接口請求時的元數(shù)據(jù)信息,包括請求方式,Content-type等 String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(), request.getHeader("Content-Type")); String name = paramMap.get("name"); // 判斷:如果paramMap.get("age")為null,則age賦默認(rèn)值0;否則進行入?yún)㈩愋偷霓D(zhuǎn)換 Integer age = null == paramMap.get("age") ? 0: Integer.parseInt(paramMap.get("age")); return requestMetaInfo + " ---> SUCCESS! name = " + name + ", age = " + age; } }
調(diào)用case
注意看上面這個/param_type/body_json/test4_requestbody_map
接口的定義方式,它并沒有指定請求方式,因此它支持GET
POST
PUT
DELETE
等所有的請求方式(已經(jīng)過驗證),為了節(jié)約文章篇幅,下面只以POST
請求方式的調(diào)用結(jié)果為例,看是否可以在controller
代碼中獲取到調(diào)用方傳過來的實參。
可以看到,參數(shù)都獲取到了。
3.2 x-www-form-urlencoded
x-www-form-urlencoded
和form-data
,兩者都可以用來上傳前端表單
數(shù)據(jù),下面簡單將兩者的不同列舉出來
1.支持的入?yún)㈩愋筒煌?/strong>: x-www-form-urlencoded
只支持普通的文本內(nèi)容,不支持上傳File
文件!而form-data
則支持上傳File
文件(如圖片、音頻、視頻)。
2. 編碼不同: x-www-form-urlencoded
的編碼方式就隱藏在名字里urlencoded
,即使用js中encodeURI()
函數(shù);而form-data
的格式,要比 x-www-form-urlencoded
復(fù)雜的多,它會把內(nèi)容分成多個部分,每個部分都支持不同的格式
3. x-www-form-urlencoded
占用字節(jié)少,form-data
占用字節(jié)多
x-www-form-urlencoded
會將表單
內(nèi)的數(shù)據(jù)轉(zhuǎn)換為鍵值對
,比如name=lisi&age=23,并放在請求體body
中進行傳輸
3.2.1 接口定義方式1:在方法形參的位置,把每個參數(shù)都平鋪開來
- 定義方式: 方法形參的位置,把每個參數(shù)都平鋪開來,也可以使用
@RequestParam
注解 - 兼容的請求方式:
POST
PUT
DELETE
,注意,不兼容GET請示方式 - 適用場景:雖然這種傳參方式,適用于
POST
PUT
DELETE
等請求方式,但是一般偏向于應(yīng)用在 用POST
或PUT
方式去發(fā)送的表單
數(shù)據(jù),且參數(shù)個數(shù)少,且表單字段都是普通類型(即表單字段不是File文件) - 優(yōu)點:方便簡單
- 缺點:1)參數(shù)個數(shù)如果過多,方法體結(jié)構(gòu)會顯得很臃腫
代碼
package com.su.demo.controller.param_type; import cn.hutool.core.io.IoUtil; import com.su.demo.bean.dto.TeacherDTO; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.net.URLDecoder; import java.util.Map; @RestController @RequestMapping(value = "/param_type/body_form_urlencoded") public class TestFormUrlEncodeController { /** * <p>注意,如果使用GET請求方式,則后端獲取不到入?yún)ⅲ。。?lt;/p> * <ul> * <li><b>入?yún)㈩悇e</b>:表單參數(shù)方式,Content-Type=application/x-www-form-urlencoded * <li><b>定義方式</b>: 在方法形參的位置,把每個參數(shù)都平鋪開來</li> * <li><b>調(diào)用方式</b>: 入?yún)⒎旁诒韱沃?,且Content-Type=application/x-www-form-urlencoded</li> * <li><b>兼容的請求方式</b>:POST PUT DELETE</li> * <li><b>適用場景</b>:雖然這種傳參方式,適用于POST PUT DELETE等請求方式,但是一般偏向于應(yīng)用在 用<b>POST</b>或b>PUT</b>方式去發(fā)送的表單數(shù)據(jù),且參數(shù)個數(shù)少,且表單字段都是普通類型(即表單字段不是File文件),</li> * <li><b>優(yōu)點</b>:方便簡單</li> * <li><b>缺點</b>:1)參數(shù)個數(shù)如果過多,方法體結(jié)構(gòu)會顯得很臃腫</li> * </ul> */ @RequestMapping(value = "/test1_requestparam") public String test1(String name, @RequestParam(name = "age", required = false) Integer age, Boolean enable, HttpServletRequest request) { // 從request中獲取一些接口請求時的元數(shù)據(jù)信息,包括請求方式,Content-type等 String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(), request.getHeader("Content-Type")); return requestMetaInfo + " ---> SUCCESS! name = " + name + ", age = " + age + ", enable = " + enable; } }
調(diào)用case
注意看上面這個/param_type/body_form_urlencoded/test1_requestparam
接口的定義方式,它并沒有指定請求方式,因此它支持GET
POST
PUT
DELETE
等所有的請求方式(已經(jīng)過驗證)——經(jīng)過實測發(fā)現(xiàn),當(dāng)請示方式為GET
的時候,后端代碼獲取不到入?yún)?!而?dāng)請求方式為POST
PUT
DELETE
的時候,后端可以獲取到入?yún)ⅰ?br />下面是示例
GET,傳參失敗
可以看到,沒有獲取到入?yún)ⅲ?/p>
POST PUT DELETE 傳參成功
為了節(jié)約文章篇幅,下面只以POST
請求方式的調(diào)用結(jié)果為例,看是否可以在controller
代碼中獲取到調(diào)用方傳過來的實參。
可以看到,參數(shù)都獲取到了。
3.2.2 接口定義方式2:用一個實例來接收入?yún)?/h4>
- 定義方式: 把入?yún)⒎庋b到一個實體中,且實體一定不能使用
@RequestParam
或@RequestBody
注解修飾 - 兼容的請求方式:
POST
PUT
DELETE
,注意,不兼容GET請示方式 - 適用場景:雖然這種傳參方式,適用于
POST
PUT
DELETE
等請求方式,但是一般偏向于應(yīng)用在 用POST
或PUT
方式去發(fā)送的表單數(shù)據(jù),且表單字段都是普通類型(即表單字段不是File文件) - 優(yōu)點:方便簡單,不管表單字段個個數(shù)多或少,一般如果表單字段類型沒有File文件類型的,都可以使用這種方式
- 缺點:缺點的話就是針對Content-Type=application/x-www-form-urlencoded這種入?yún)⒎绞絹碚f了:不能上傳文件
@RequestParam
或@RequestBody
注解修飾POST
PUT
DELETE
,注意,不兼容GET請示方式POST
PUT
DELETE
等請求方式,但是一般偏向于應(yīng)用在 用POST
或PUT
方式去發(fā)送的表單數(shù)據(jù),且表單字段都是普通類型(即表單字段不是File文件)代碼
package com.su.demo.controller.param_type; import cn.hutool.core.io.IoUtil; import com.su.demo.bean.dto.TeacherDTO; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.net.URLDecoder; import java.util.Map; @RestController @RequestMapping(value = "/param_type/body_form_urlencoded") public class TestFormUrlEncodeController { /** * <p>注意,如果使用GET請求方式,則后端獲取不到入?yún)ⅲ。?!只支持POST PUT DELETE</p> * <p>且形參定義那里,一定不能使用{@link RequestParam}修飾</p> * <ul> * <li><b>入?yún)㈩悇e</b>:表單參數(shù)方式,Content-Type=application/x-www-form-urlencoded * <li><b>定義方式</b>: 把入?yún)⒎庋b到一個實體中(入?yún)€數(shù)多于5個時一般用這種方式)</li> * <li><b>調(diào)用方式</b>: 入?yún)⒎旁诒韱沃校褻ontent-Type=application/x-www-form-urlencoded</li> * <li><b>兼容的請求方式</b>:POST PUT DELETE</li> * <li><b>適用場景</b>:雖然這種傳參方式,適用于POST PUT DELETE等請求方式,但是一般偏向于應(yīng)用在 用<b>POST</b>或<b>PUT</b>方式去發(fā)送的表單數(shù)據(jù),且表單字段都是普通類型(即表單字段不是File文件),</li> * <li><b>優(yōu)點</b>:方便簡單,不管表單字段個個數(shù)多或少,一般如果表單字段類型沒有File文件類型的,都可以使用這種方式</li> * <li><b>缺點</b>:缺點的話就是針對Content-Type=application/x-www-form-urlencoded這種入?yún)⒎绞絹碚f了:不能上傳文件</li> * </ul> */ @RequestMapping(value = "/test2_entity") public String test2(TeacherDTO teacherDTO, HttpServletRequest request) { // 從request中獲取一些接口請求時的元數(shù)據(jù)信息,包括請求方式,Content-type等 String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(), request.getHeader("Content-Type")); return requestMetaInfo + " ---> SUCCESS! teacherDTO = " + teacherDTO; } }
調(diào)用case
注意看上面這個/param_type/body_form_urlencoded/test2_entity
接口的定義方式,它并沒有指定請求方式,因此它支持GET
POST
PUT
DELETE
等所有的請求方式(已經(jīng)過驗證)——經(jīng)過實測發(fā)現(xiàn),當(dāng)請示方式為GET
的時候,后端代碼獲取不到入?yún)ⅲ《?dāng)請求方式為POST
PUT
DELETE
的時候,后端可以獲取到入?yún)ⅰ?br />下面是示例
GET,傳參失敗
可以看到,沒有獲取到入?yún)ⅲ?/p>
POST PUT DELETE 傳參成功
為了節(jié)約文章篇幅,下面只以PUT
請求方式的調(diào)用結(jié)果為例,看是否可以在controller
代碼中獲取到調(diào)用方傳過來的實參。
可以看到,參數(shù)都獲取到了。
3.2.3 接口定義方式3:用原生的HttpServletRequest接收參數(shù)
- 定義方式: 用原生的
HttpServletRequest
接收參數(shù) - 兼容的請求方式:
POST
PUT
DELETE
,注意,不兼容GET請示方式 - 適用場景:由于當(dāng)請求的Content-Type=application/x-www-form-urlencoded時,只支持GET請求方式,并且即使是GET請求方式能獲取到入?yún)⒘?,之后的校驗和處理也很麻煩,所以一般不用這種方式來接收Content-Type=application/x-www-form-urlencoded的請求
- 優(yōu)點:方便簡單,且這接收入?yún)⒌姆绞剑苯诱{(diào)用方用GET的請求方式傳參
- 缺點:
- 1)需要自己在代碼中顯式的從request的inputStream流【獲取+轉(zhuǎn)換+解碼】數(shù)據(jù),很麻煩
- 2)如果請求方式是POST PUT DELETE等,同時Content-Type=application/x-www-form-urlencoded,則無法將入?yún)鞯胶竺娲a中!
代碼
package com.su.demo.controller.param_type; import cn.hutool.core.io.IoUtil; import com.su.demo.bean.dto.TeacherDTO; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.net.URLDecoder; import java.util.Map; @RestController @RequestMapping(value = "/param_type/body_form_urlencoded") public class TestFormUrlEncodeController { /** * <p>注意,只支持GET請求方式,如果使用POST、PUT、DELETE請求方式,則后端獲取不到入?yún)ⅲ。。?lt;/p> * <ul> * <li><b>入?yún)㈩悇e</b>:表單參數(shù)方式,Content-Type=application/x-www-form-urlencoded * <li><b>定義方式</b>: 在方法形參的位置,用{@link HttpServletRequest}來接收入?yún)?lt;/li> * <li><b>調(diào)用方式</b>: 入?yún)⒎旁诒韱沃?,且Content-Type=application/x-www-form-urlencoded</li> * <li><b>兼容的請求方式</b>:GET</li> * <li><b>適用場景</b>:由于當(dāng)請求的Content-Type=application/x-www-form-urlencoded時,只支持GET請求方式, * 并且即使是GET請求方式能獲取到入?yún)⒘耍蟮男r灪吞幚硪埠苈闊?,所以一般不用這種方式來接收Content-Type=application/x-www-form-urlencoded的請求</li> * <li><b>優(yōu)點</b>:方便簡單,且這接收入?yún)⒌姆绞剑苯诱{(diào)用方用GET的請求方式傳參</li> * <li><b>缺點</b>:1)需要自己在代碼中顯式的從request的inputStream流【獲取+轉(zhuǎn)換+解碼】數(shù)據(jù),很麻煩 * 2)如果請求方式是POST PUT DELETE等,同時Content-Type=application/x-www-form-urlencoded,則無法將入?yún)鞯胶竺娲a中! * </li> * </ul> */ @RequestMapping(value = "/test3_request") public String test3(HttpServletRequest request) throws IOException { // 從request中獲取一些接口請求時的元數(shù)據(jù)信息,包括請求方式,Content-type等 String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(), request.getHeader("Content-Type")); ServletInputStream inputStream = request.getInputStream(); String inputStr = IoUtil.read(inputStream, "UTF-8"); String decodeRes = URLDecoder.decode(inputStr, "UTF-8"); return requestMetaInfo + " ---> SUCCESS! " + inputStr + ", decodeResult = " + decodeRes; } }
調(diào)用case
注意看上面這個/param_type/body_form_urlencoded/test3_request
接口的定義方式,它并沒有指定請求方式,因此它支持GET
POST
PUT
DELETE
等所有的請求方式(已經(jīng)過驗證)——但是經(jīng)過實測發(fā)現(xiàn),只有當(dāng)請示方式為GET
的時候,后端代碼才能獲取到入?yún)?!而?dāng)請求方式為POST
PUT
DELETE
的時候,后端獲取入?yún)⑹ ?br />下面是示例
GET,傳參成功
可以看到,獲取到入?yún)ⅲ?/p>
POST PUT DELETE 傳參失敗
為了節(jié)約文章篇幅,下面只以POST
請求方式的調(diào)用結(jié)果為例,看是否可以在controller
代碼中獲取到調(diào)用方傳過來的實參。
可以看到,沒有獲取到參數(shù)!
3.2.4 (error)接口定義方式4:用Map接收參數(shù)
提前說明:這是一種錯誤的定義方式,這種方式,無論是否在map前面添加RequestParam
還是RequestBody
, 后端代碼都獲取不到 Content-Type=application/x-www-form-urlencoded類型請求的入?yún)ⅲ。。?/p>
- 定義方式: 用
Map
接收參數(shù)
代碼
package com.su.demo.controller.param_type; import cn.hutool.core.io.IoUtil; import com.su.demo.bean.dto.TeacherDTO; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.net.URLDecoder; import java.util.Map; @RestController @RequestMapping(value = "/param_type/body_form_urlencoded") public class TestFormUrlEncodeController { /** * <p>這是一種錯誤的定義方式,這種方式,無論是否在map前面添加{@link RequestParam}還是{@link RequestBody}, 代碼都獲取不到 Content-Type=application/x-www-form-urlencoded類型請求的入?yún)ⅲ。。?lt;/p> * <ul> * <li><b>入?yún)㈩悇e</b>:表單參數(shù)方式,Content-Type=application/x-www-form-urlencoded * <li><b>定義方式</b>: 在方法形參的位置,用Map來接收入?yún)?lt;/li> * <li><b>調(diào)用方式</b>: 入?yún)⒎旁诒韱沃?,且Content-Type=application/x-www-form-urlencoded</li> * </ul> */ @RequestMapping(value = "/test4_map") public String test4(Map<String, String> paramMap, HttpServletRequest request) { // 從request中獲取一些接口請求時的元數(shù)據(jù)信息,包括請求方式,Content-type等 String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(), request.getHeader("Content-Type")); String name = paramMap.get("name"); // 判斷:如果paramMap.get("age")為null,則age賦默認(rèn)值0;否則進行入?yún)㈩愋偷霓D(zhuǎn)換 Integer age = null == paramMap.get("age") ? 0: Integer.parseInt(paramMap.get("age")); return requestMetaInfo + " ---> SUCCESS! name = " + name + ", age = " + age; } }
調(diào)用case
無論哪種請求方式,后端獲取入?yún)⒍际?!下面?code>PUT請求方式作為示例
下面是示例
PUT,傳參失敗
可以看到,獲取不到入?yún)ⅲ?/p>
3.2.5 (error)接口定義方式5:每個參數(shù)都平鋪開來,并嘗試獲取File文件入?yún)?/h4>
先說結(jié)論,雖然在下面方法定義的,=形參中添加了MultipartFile
,但是如果調(diào)用接口的時候入?yún)㈩愋褪荂ontent-Type=application/x-www-form-urlencoded, 那由于Content-Type=application/x-www-form-urlencoded入?yún)㈩愋拖拗屏巳雲(yún)⒕蜔o法將【文件】傳過來,所以該方法的MultipartFile對象始終為空!
- 定義方式: 方法形參的位置,把每個參數(shù)都平鋪開來,也可以使用
@RequestParam
注解,是時嘗試使用MultipartFile
類型入?yún)?,去接收文?/li>
代碼
package com.su.demo.controller.param_type; import cn.hutool.core.io.IoUtil; import com.su.demo.bean.dto.TeacherDTO; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.net.URLDecoder; import java.util.Map; @RestController @RequestMapping(value = "/param_type/body_form_urlencoded") public class TestFormUrlEncodeController { /** * <p> * 雖然在方法定義這里,形參中添加了MultipartFile,但是如果調(diào)用接口的時候入?yún)㈩愋褪荂ontent-Type=application/x-www-form-urlencoded, * 那由于Content-Type=application/x-www-form-urlencoded入?yún)㈩愋拖拗屏巳雲(yún)⒕蜔o法將【文件】傳過來,所以該方法的MultipartFile對象始終為空! * </p> * <ul> * <li><b>入?yún)㈩悇e</b>:表單參數(shù)方式,Content-Type=application/x-www-form-urlencoded * <li><b>定義方式</b>: 在方法形參的位置,把每個參數(shù)都平鋪開來</li> * <li><b>調(diào)用方式</b>: 入?yún)⒎旁诒韱沃校褻ontent-Type=application/x-www-form-urlencoded</li> * </ul> */ @RequestMapping(value = "/test5_requestparam_file") public String test5(String name, Integer age, Boolean enable, MultipartFile myfile, HttpServletRequest request) throws IOException { // 從request中獲取一些接口請求時的元數(shù)據(jù)信息,包括請求方式,Content-type等 String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(), request.getHeader("Content-Type")); String fileName = null; if (null != myfile) { fileName = myfile.getOriginalFilename(); } return requestMetaInfo + " ---> SUCCESS! name = " + name + ", age = " + age + ", enable = " + enable + ", fileName = " + fileName; } }
調(diào)用case
實測發(fā)現(xiàn),不管是哪種請求方式,只要請求的時候Content-Type=application/x-www-form-urlencoded,那都無法將文件傳給后端!
下面是用POST
請求方式作示例:
以上就是SpringBoot接收接口入?yún)⒌姆绞叫〗Y(jié)的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot接收接口入?yún)⒌馁Y料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
利用Java實現(xiàn)word導(dǎo)入導(dǎo)出富文本(含圖片)的詳細(xì)代碼
這篇文章主要為大家詳細(xì)介紹了利用Java實現(xiàn)word導(dǎo)入導(dǎo)出富文本(含圖片),文中的示例代碼講解詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,感興趣的小伙伴可以學(xué)習(xí)一下2024-02-02Java微服務(wù)分布式調(diào)度Elastic-job環(huán)境搭建及配置
Elastic-Job在配置中提供了JobEventConfiguration,支持?jǐn)?shù)據(jù)庫方式配置,會在數(shù)據(jù)庫中自動創(chuàng)建JOB_EXECUTION_LOG和JOB_STATUS_TRACE_LOG兩張表以及若干索引,來記錄作業(yè)的相關(guān)信息2023-02-02Spring引入外部屬性文件配置數(shù)據(jù)庫連接的步驟詳解
這篇文章主要介紹了Spring引入外部屬性文件配置數(shù)據(jù)庫連接的步驟詳解,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01spring boot 1.5.4 集成shiro+cas,實現(xiàn)單點登錄和權(quán)限控制
這篇文章主要介紹了spring boot 1.5.4 集成shiro+cas,實現(xiàn)單點登錄和權(quán)限控制,需要的朋友可以參考下2017-06-06java:無法訪問org.springframework.boot.SpringApplication的解決方法
這篇文章主要給大家介紹了關(guān)于java:無法訪問org.springframework.boot.SpringApplication的解決方法,文中通過實例代碼將解決的辦法介紹的非常詳細(xì),需要的朋友可以參考下2023-01-01Springboot使用@RefreshScope注解實現(xiàn)配置文件的動態(tài)加載
本文主要介紹了Springboot使用@RefreshScope注解實現(xiàn)配置文件的動態(tài)加載,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09基于Protobuf動態(tài)解析在Java中的應(yīng)用 包含例子程序
下面小編就為大家?guī)硪黄赑rotobuf動態(tài)解析在Java中的應(yīng)用 包含例子程序。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07