探究Spring Boot中的接收引數問題與客戶端傳送請求傳遞資料
阿新 • • 發佈:2020-03-16
結合此篇參考[Spring框架學習筆記(9)——API介面設計相關知識及具體編碼實現](https://www.cnblogs.com/stars-one/p/12305429.html)
在使用Spring Boot進行接收引數的時候,發現了許多問題,之前一直都很忙,最近才稍微有空研究一下此問題。
網上的大多數文章,只講Spring Boot如何實現接受引數,卻不講如何在客戶端呼叫,**本篇使用Jsoup、okhttp3和postwoman測試工具進行截圖,講解如何在伺服器實現介面,同時在客戶端如何發起請求並傳參**
## 四種請求方式介紹
可能剛入門Web開發的大家會有疑惑?不是隻有get和post這兩種?
最近幾年好像流行restful風格的介面,裡面有多種方式,按照功能進行區分
不過,目前常用的就只有下面這四個方式**get、post、put、delete**
本質上除了get,其他凡是都是post方式衍生出來的版本,呼叫的時候只需要修改方法名即可(具體可檢視post方式中的程式碼例子)
## get方式
### 介紹
如下圖,我寫了一個介面
![](https://img2020.cnblogs.com/blog/1210268/202003/1210268-20200316172211569-1276607179.png)
由於是本地部署,ip為`localhost`,contextpath名為`requestdemo`,埠預設為`8080`,所以訪問的url就為`http://localhost:8080/requestdemo/api/user/users`
![](https://img2020.cnblogs.com/blog/1210268/202003/1210268-20200316172504331-1062121966.png)
由於我們使用了`@RequestMapping`註解,所以,不過使用什麼方式都可以訪問到資料,如下圖,切換為post方式訪問
![](https://img2020.cnblogs.com/blog/1210268/202003/1210268-20200316172537729-1301065146.png)
如果想要指定get方式,可以使用`@GetMapping`註解,GetMapping註解其實是相當於這樣的寫法`@RequestMapping("users",method = [(RequestMethod.GET)])`,註解中method屬性接收的是陣列引數
指定了get方式,使用其他的post方式、put方式等都是返回不了資料的,報405錯誤程式碼,如下圖
![](https://img2020.cnblogs.com/blog/1210268/202003/1210268-20200316173053178-1572600377.png)
### 接收資料
```
@GetMapping("select")
fun selectByPk(@RequestParam("id") ids:Int):User {
println(ids)
return User("zhangsan",19)
}
@GetMapping("select1")
fun selectByPk1(ids:Int) {
println(ids)
}
```
第一種方式客戶端應該這樣呼叫,輸入網址即可
```
http://localhost:8080/requestdemo/api/user/select?id=1
```
第二種引數沒有註解,所以spring boot預設以變數名作為引數,所以應該是這樣呼叫
```
http://localhost:8080/requestdemo/api/user/select1?ids=1
```
下面報錯的截圖也是充分說明了這一點
![](https://img2020.cnblogs.com/blog/1210268/202003/1210268-20200316190306762-139092185.png)
### 客戶端程式碼發起get請求
客戶端發起get請求用程式碼寫就比較簡單,只要我們把url拼接好即可
**Jsoup:**
Jsoup中的get方法是返回一個Document(網頁物件,之後可進行css篩選來找到指定節點,從而獲取內容)
```
//需要ignoreContentType,忽略content-type,否則會報錯,拿不到伺服器返回的資料
val doc= Jsoup.connect("http://localhost:8080/requestdemo/api/user/select?id=1")
.ignoreContentType(true)
.get()
//輸出伺服器返回json資料
println(doc.body().text())
```
**Okhttp3:**
```
val client = OkHttpClient()
val request = Request.Builder()
.url("http://localhost:8080/requestdemo/api/user/select?id=1")
.get()
.build()
val call = client.newCall(request)
call.enqueue(object :Callback{
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
println(response.body()?.string())
}
})
```
![](https://img2020.cnblogs.com/blog/1210268/202003/1210268-20200316192611280-1392580729.png)
## post方式
### @RequestParam註解
@RequestParam用來處理Content-Type: 為 application/x-www-form-urlencoded編碼的內容,提交方式GET、POST。
有如下的介面:
```
@PostMapping("update")
fun update(@RequestParam map: HashMap) {
//輸出傳入的key和value
for (mutableEntry in map) {
println(mutableEntry.key + "=" + mutableEntry.value)
}
}
```
**PS:不能省略RequestParam註解,否則伺服器後端接收不到資料**
使用postwoman測試介面:
![](https://img2020.cnblogs.com/blog/1210268/202003/1210268-20200316193732904-371653861.png)
注意,這種方式的資料格式是表單資料**application/x-www-form-urlencoded**
**Jsoup:**
```
val dataMap = hashMapOf("name" to "zhangsag","age" to 11.toString())
val doc= Jsoup.connect("http://localhost:8080/requestdemo/api/user/update")
.ignoreContentType(true)
.data(dataMap)
.post()
//輸出結果
println(doc.body().text())
```
Jsoup中的data方法可以接受Map,或者鍵值對,上面的例子可以改成下面的程式碼
```
val doc= Jsoup.connect("http://localhost:8080/requestdemo/api/user/update")
.ignoreContentType(true)
.data("name","zhangsan")
.data("age",12.toString())
.post()
//輸出結果
println(doc.body().text())
```
**Okhttp3:**
```
fun sendPostRequest(){
val url ="http://localhost:8080/requestdemo/api/user/update"
val client = OkHttpClient()
val formBodyBuilder = FormBody.Builder()
formBodyBuilder.add("names", "zhangsxx")
formBodyBuilder.add("ages", "19")
val request = Request.Builder()
.url(url)
.post(formBodyBuilder.build())
.build()
val call = client.newCall(request)
call.enqueue(object : Callback{
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
}
} )
}
```
這裡有個小問題沒搞明白,如果介面是下面這樣,客戶端應該如何傳遞資料?我試了幾種方法,都是報錯,**說是無法把String型別轉為User型別**,是不是這種方法是不能傳遞的?
有路過的大神希望可以幫忙解答一下
```
@PostMapping("update2")
fun update2(@RequestParam user: User) {
println(user.toString())
}
```
### @RequestBody註解
有時候,我們需要傳遞一個json字串,就需要用到此註解
@RequestBody接受的是一個json物件的字串,而不是Json物件
有下面的一個介面:
```
@PostMapping("update1")
fun update1(@RequestBody user:User) {
println(user.toString())
}
```
我們在客戶端傳遞了json資料,之後spring boot就會自動呼叫jackson框架,把json字串資料轉為實體類
![](https://img2020.cnblogs.com/blog/1210268/202003/1210268-20200316195539824-47648368.png)
![](https://img2020.cnblogs.com/blog/1210268/202003/1210268-20200316195512909-23406201.png)
**Jsoup:**
```
val json = "{\"name\":\"zhangs\",\"age\":18}"
val doc= Jsoup.connect("http://localhost:8080/requestdemo/api/user/update1")
.requestBody(json)
.header("content-type","application/json")
.post()
```
jsoup中需要新增請求頭來宣告傳遞json字串資料,舉一反三,傳遞其他形式只需要更改content-type的值為其他形式即可
![](https://img2020.cnblogs.com/blog/1210268/202003/1210268-20200316201802184-70317853.png)
**OkHttp3:**
```
val json = "{\"name\":\"zhangs\",\"age\":18}"
val client = OkHttpClient()
val request = Request.Builder()
.url("http://localhost:8080/requestdemo/api/user/update1")
.post(RequestBody.create(MediaType.parse("application/json"),json))
.build()
val call = client.newCall(request)
call.enqueue(object :Callback{
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
println(response.body()?.string())
}
})
```
這裡與上面的okhttp有所類似,就是post方法裡面的引數的資料不一樣
@RequestParam -> application/x-www-form-urlencoded -> FormBody
@RequestBody -> application/json -> RequestBody
舉一反三,如果是xml格式或者是二進位制格式,應該使用RequestBody來進行構建資料,具體可以自行操作一下,這裡就不再演示了
![](https://img2020.cnblogs.com/blog/1210268/202003/1210268-20200316200934685-1435365228.png)
### put、delete方式如何寫
Jsoup:
```
val json = "{\"name\":\"zhangs\",\"age\":18}"
val doc= Jsoup.connect("http://localhost:8080/requestdemo/api/user/update1")
.requestBody(json)
.header("content-type","application/json")
.method(xx)
.post()
```
在method方法新增即可,下圖是可選的數值:
![](https://img2020.cnblogs.com/blog/1210268/202003/1210268-20200316201300984-1866043043.png)
Okhttp3中,只需要把post方法更改為put或者delete方法即可
```
val json = "{\"name\":\"zhangs\",\"age\":18}"
val client = OkHttpClient()
val request = Request.Builder()
.url("http://localhost:8080/requestdemo/api/user/update1")
//put或delete
.put(RequestBody.create(MediaType.parse("application/json"),json))
.build()
val call = client.newCall(request)
call.enqueue(object :Callback{
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
println(response.body()?.string())
}
})
```
## ajax請求轉程式碼(補充)
**如有下面的ajax程式碼:**
```
var sg = 'UDZRb1loVWQFDAI9BTVcYFc6ADRTNQo8UWBQY1I5ASYBdVU_aXTEAYQdpBGEGagI2Vj4HO1Q7VmI_c';
$.ajax({
type : 'post',
url : '/ajaxm.php',
data : { 'action':'downprocess','sign':sg,'ves':1 },
dataType : 'json',
success:function(msg){
var date = msg;
...
```
**在postwoman應該這樣進行請求:**
![](https://img2020.cnblogs.com/blog/1210268/202003/1210268-20200310230525146-1580128319.png)
**Jsoup程式碼:**
```
//我這裡不加頭部,也可以獲取到資料
val postUrl = "https://www.lanzous.com/ajaxm.php"
val params = LinkedHashMap()
params["action"] = "downprocess"
params["sign"] = sign
params["ves"] = "1"
val result = Jsoup.connect(postUrl)
.data(params)
.post()
.html()
```
## 參考
[post frombody](http://jessehu.cn/2019/01/08/okhttp3/okhttp03/)
[SpringBoot 出現 Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported](https://blog.csdn.net/feiyst/article/details/88431621)
[OKHTTP3 簡單使用(三) POST方法](http://jessehu.cn/2019/01/08/okhttp3/okh