post get請求,SpringBoot Http getMapping、postMaping等詳細解析

 2023-11-18 阅读 27 评论 0

摘要:背景 一直以來對http各種請求controller層該如何處理,總是弄不大明白。于是決定自己寫一些測試來總結一下。 項目環境:springBoot、swagger。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starte

背景

一直以來對http各種請求controller層該如何處理,總是弄不大明白。于是決定自己寫一些測試來總結一下。
項目環境:springBoot、swagger。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>2.3.4.RELEASE</version>
</dependency>
<!--swagger-->
<dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version><exclusions><exclusion><artifactId>swagger-models</artifactId><groupId>io.swagger</groupId></exclusion></exclusions>
</dependency>
<dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>2.9.2</version>
</dependency>
<dependency><groupId>io.swagger</groupId><artifactId>swagger-models</artifactId><version>1.5.24</version><exclusions><exclusion><artifactId>swagger-annotations</artifactId><groupId>io.swagger</groupId></exclusion></exclusions>
</dependency>

總結的內容:
方法上的注解:@postMapping、@GetMapping、@PutMapping、@DeleteMapping、@PatchMapping(部分修改時使用,但基本不用這個)。
參數上的注解:@PathVariable、@RequestBody、@RequestPart。
決定從實際需求出發來說明用法。

參數上的注解

@PathVariable

中文翻譯過來就是路徑參數。@PathVariable 注解可以將 URL 中占位符參數綁定到控制器處理方法的入參中;主要用于接收http://host:port/path/{參數值}數據,實現了實現了RestFul的風格,將中文參數轉成16進制。

/*** 主要用于接收http://host:port/path/{參數值}數據* 實現了RestFul的風格* 中文參數會轉成16進制* @param name* @return*/@ApiOperation("PathVariable傳參")@GetMapping("/get/{name}")public ResponseVo<String> pathVariable(@ApiParam(value = "你的名字") @PathVariable String name ){return ResponseFactory.success(name);}

swagger中顯示:
在這里插入圖片描述

注:參數下顯示的是“path”;李錦榮轉成了%E6%9D%8E%E9%94%A6%E8%8D%A3;PathVariable的優點除了實現了restFul風格,暫時不知道有什么好處(如有知道的評論區告訴我,謝謝)

猜想:如果參數中存在特殊字符,比方/是否會影響?

參數如數“/李錦榮”,返回錯誤信息—HTTP Status 400 – Bad Request:

<!doctype html><html lang="en"><head><title>HTTP Status 400 – Bad Request</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 400 – Bad Request</h1></body></html>

證明@pathVariable不適合傳入特殊字符,如果一定要用,可以參考文章“關于@PathVariable你需要知道的事”。地址:https://www.jianshu.com/p/1a1384b9cd34

猜想:能否實現對參數的非空校驗?

1.嘗試加入@PathVariable(required = true)
controller層:

    /*** PathVariable能否實現對參數的非空校驗* 1.嘗試加入@PathVariable(required = true)* @param name* @return*/@ApiOperation("PathVariable傳參")@GetMapping("/get/{name}/1")public ResponseVo<String> pathVariable2(@ApiParam(value = "你的名字") @PathVariable(required = true) String name ){return ResponseFactory.success(name);}

swagger不輸入參數,點擊執行
在這里插入圖片描述
成功進入方法內,參數為undefined,說明@PathVariable(required = true)起不到校驗作用
在這里插入圖片描述

2.嘗試加入@NotNull

/*** PathVariable能否實現對參數的非空校驗* 2.嘗試加入@NotNull* @param name* @return*/@ApiOperation("PathVariable傳參")@GetMapping("/get/{name}/2")public ResponseVo<String> pathVariable3(@ApiParam(value = "你的名字") @PathVariable(required = true) @NotNull String name ){return ResponseFactory.success(name);}

結果。仍然是undefined:
在這里插入圖片描述
加入@NotEmpty ——無效;加入@Length(max = 3)——有效
結論:以上測試得出的結論,@PathVariable無法對參數進行非空校驗。

RequestParam

主要用于接收http://host:port/path?參數名=參數值數據(與@PathVariable格式不同)
@RequestParam注解的參數在url中傳遞。
@RequestParam適用于注解int和string,因為前端傳給后臺,最終就這兩個類型。也可以理解為只注解鍵值對的參數格式。
@RequestParam不適用注解實體(DTO等)。

/*** 主要用于接收http://host:port/path?參數名=參數值數據(與@PathVariable格式不同)* 適用于注解int和string,因為前端傳給后臺,最終就這兩個類型* 不適用注解實體(DTO等)* @param name* @return*/@ApiOperation("RequestParam傳參")@GetMapping("/get/requestParam")public ResponseVo<String> requestParam(@ApiParam(value = "你的名字") @RequestParam(required = true) String name ){return ResponseFactory.success(name);}

swagger中顯示:
在這里插入圖片描述
注:這里可以看到RequestParam顯示的是query,而且沒有特殊字符的問題。@RequestParam(required = true)默認就是true,會校驗非空。就不演示了,應該都知道的。

驗證@RequestParam不適用注解實體(DTO等)

/*** RequestParam注解DTO實體會報String無法轉為DTO的錯誤* @param dogDTO* @return*/@ApiOperation("RequestParam傳參")@GetMapping("/get/requestParam1")public ResponseVo<String> requestParam1(@ApiParam(value = "你的名字") @RequestParam DogDTO dogDTO ){return ResponseFactory.success(dogDTO.getDogName());}

在這里插入圖片描述
執行后報錯:Cannot convert value of type ‘java.lang.String’ to required type ‘com.aliyu.entity.demo.dto.DogDTO’:
注:從swagger圖中看到 參數類型是string 而不是對應的dto實體。

@RequestBody

1.參數放在body中,默認前端提交的Content-Type就是application/json;
2.Content-Type為application/x-www-form-urlencoded,正常情況不支持。當然有別的解決方式,但要需要加東西。參考地址:https://blog.csdn.net/weixin_32836221/article/details/112111893 ;
3.其他格式包括application/json, application/xml等。這些格式的數據必須使用@RequestBody來處理;
4.不能處理multipart/form-data,處理它的是@RequestPart,待會講到。
簡單來說就是,如果參數是json就用@RequestBody;如果用了@RequestBody,就規定了參數是json。

@ApiOperation("post請求-DTO實體-RequestBody傳參數")@PostMapping("/post/requestBody")public ResponseVo requestBody(@RequestBody DogDTO dogDTO) {return ResponseFactory.success(dogDTO.getDogName());}

在這里插入圖片描述

證明Content-Type為application/x-www-form-urlencoded正常情況不支持

要證明這個,涉及到一個知識點,如何限制前端傳入參數的contentType。

/*** 反例:Content-Type為application/x-www-form-urlencoded,正常情況不支持* 報錯Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported* consumes設置前端傳入的參數的contentType* produces設置返回的contentType* @param dogDTO* @return*/@ApiOperation("RequestBody接收contentType為application/x-www-form-urlencoded")@PostMapping(value="/post/requestBody2", consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE} )public ResponseVo requestBody2(@RequestBody DogDTO dogDTO) {return ResponseFactory.success(dogDTO.getDogName());}

注:postMapper中有兩個配置,一個consumes設置前端傳入的參數的contentType,produces設置返回的contentType。返回的默認就是json不需要設置。
接下來看swagger中的展示:
在這里插入圖片描述
成功設置了contentType為application/x-www-form-urlencoded,請求看看:
在這里插入圖片描述
報錯Content type ‘application/x-www-form-urlencoded;charset=UTF-8’ not supported。

證明不能處理multipart/form-data

/*** 反例子* 證明RequestBody不支持接收contentType為multipart/form-data* @param dogDTO* @return*/@ApiOperation("RequestBody接收contentType為multipart/form-data")@PostMapping(value = "/post/requestBody3",consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})public ResponseVo requestBody3(@RequestBody DogDTO dogDTO) {return ResponseFactory.success(dogDTO.getDogName());}

在這里插入圖片描述
成功設置了contentType為multipart/form-data,請求看看:
在這里插入圖片描述

@RequestPart

這個注解設定contentType為multipart/form-data, 適用于文件上傳
同時上傳文件和輸入json參數的話,也必須用這個。但是@RequestPart用于注解json參數時正常情況會報錯。
注解int或string、注解文件:

/*** 這個注解設定contentType為multipart/form-data,適用于文件上傳* @param dogName* @param file* @return*/@ApiOperation("RequestPart上傳文件,@RequestPart注解string或int參數")@PostMapping("/post/requestPart")public ResponseVo requestPart(@RequestPart String dogName,@RequestPart MultipartFile file) {return ResponseFactory.success(dogName);}

@RequestPart對應顯示的是formData:
在這里插入圖片描述
contentType為multipart/form-data:
在這里插入圖片描述
點擊執行后,后端報錯:
com.fasterxml.jackson.core.JsonParseException: Unrecognized token ‘AAA’: was expecting (JSON String, Number, Array, Object or token ‘null’, ‘true’ or ‘false’)
at [Source: (PushbackInputStream); line: 1, column: 4]
意思是無法解析此參數類型,它支持的類型是“JSON String, Number, Array, Object or token ‘null’, ‘true’ or ‘false’”。也就是controller層的@RequestPart String dogName不能用 string,還是得用DTO。
如果注解的參數是DTO加文件

/*** 注解一個dto和一個文件* @param dto* @param file* @return*/@ApiOperation("RequestPart上傳文件,@RequestPart注解string或int參數")@PostMapping("/post/requestPart2")public ResponseVo requestPart2(@RequestPart DogDTO dto,@RequestPart MultipartFile file) {return ResponseFactory.success(dto);}

在這里插入圖片描述
點擊執行,報錯:HttpMediaTypeNotSupportedException: Content type ‘application/octet-stream’ not supported
參考地址:https://blog.csdn.net/gao_grace/article/details/96431269
大概的意思是:使用swagger調用的api,查看swagger生成的curl命令發現,請求中file這個參數指定了content-type,但是dto卻并沒有。后端的找不到contentType,只好使用默認的application/octet-stream。但是又沒有對應類型的消息轉換器,所以就報不支持了。
我覺得說法上可能不準確,因為一個請求只能有一個contentType,并不是一個參數配一次。不過DTO需要設置它的dataType為json,這樣才行。但是swagger目前沒有支持的配置,實際的前端是有的(前端代碼不熟,有知道的可以提供一下示例)。
解決的方案是寫一個@component注解轉換器的類,如下:

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.stereotype.Component;import java.lang.reflect.Type;
/***@author: aliyu*@create: 2021/1/14 10:35*@description: 增加對application/octet-stream的消息轉換器* 用@RequestPart同時上傳文件和json時,傳入的contentType為null,導致使用默認的application/octet-stream* 而application/octet-stream沒有對應的消息轉換器*/
@Component
public class MultipartJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {protected MultipartJackson2HttpMessageConverter(ObjectMapper objectMapper) {super(objectMapper, MediaType.APPLICATION_OCTET_STREAM);}@Overridepublic boolean canWrite(Class<?> clazz, MediaType mediaType) {return false;}@Overridepublic boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {return false;}@Overrideprotected boolean canWrite(MediaType mediaType) {return false;}
}

重啟項目后,可以正常傳遞參數。

查詢——@GetMapping

主要用于查詢。
get請求特點:
a. 請求參數會添加到請求資源路徑的后面,只能添加少量參數(因為請求行只有一行,大約只能存放2K左右的數據)
b. 請求參數會顯示在瀏覽器地址欄,路由器會記錄請求地址 (極為的不安全)

極少參數的查詢,查詢參數不包含實體類(只是基本類型)

查詢參數不超過3個,最多的情況是通過id獲取詳情。如果超過3個,建議新建一個DTO來存放。

@ApiOperation("基本類型參數,少量參數傳參")@GetMapping("/get/getMapping")public ResponseVo<String> getMapping(@ApiParam(value = "你的名字") @RequestParam String name,@ApiParam(value = "性別") @RequestParam String sex,@ApiParam(value = "年齡") @RequestParam Integer age){return ResponseFactory.success(name);}

在這里插入圖片描述注:執行后可以正常獲取參數,至于為什么不使用@PathVariable?因為前面說到它不支持參數非空校驗,還有就是不能輸入像“/”這樣的特殊字符。如果參數不加@RequestParam 也是可以的,不過就是參數都非必輸了(因為RequestParam 默認就是必輸)。如果參數非空的話,這個注解就必須加上了。

查詢參數為DTO

業務場景上,一個模塊的查詢一般都是某一個實體類的部分字段,如果字段比較多,或者考慮到擴展性。就應該建一個DTO來傳遞。對于get請求,是不能使用@ResponseBody的。這時候這個dto其實啥注解也不加就可以了。

/*** get請求dto傳參。* @param dto* @return*/@ApiOperation("dto傳參")@GetMapping("/get/getMapping2")public ResponseVo<String> getMapping2(DogDTO dto){return ResponseFactory.success(dto.getDogName());}

在這里插入圖片描述
注:不加默認也是query傳參,至于dto參數的校驗是通過在dto實體類中為各個屬性加校驗注解的方式實現的(可以參考我的另一篇校驗的文章)

新增——@PostMapping

冪等性:是指一次和多次請求某一個資源對于資源本身應該具有同樣的結果(網絡超時等問題除外)。
也就是說,其任意多次執行對資源本身所產生的影響均與一次執行的影響相同,
http協議明確規定,put、get與delete請求都是具有冪等性的,而post為非冪等性請求。

偏向于添加時使用
a. 請求參數添加到實體內容里面,可以添加大量的參數(也解釋了為什么瀏覽器地址欄不能發送post請求,在地址欄里我們只能填寫URL,并不能進入到Http包的實體當中)
b. 相對get安全,但是,post請求不會對請求參數進行加密處理(可以使用https協議來保證數據安全)。

極少參數的新增(參數只是基本類型,不建議)

這種情況,比較少見。一般新增都會新增一條數據。表字段一般不會太少,所以新增時要錄入的參數一般也不會太少。

@ApiOperation("post請求-少量參數-RequestParam傳參")@PostMapping("/post/postMappring")public ResponseVo postMappring(@ApiParam(value = "你的名字") @RequestParam String dogName,@ApiParam(value = "年齡") @RequestParam int age,@ApiParam(value = "性別") @RequestParam String sex) {return ResponseFactory.success(dogName);}

在這里插入圖片描述
注:可以看到,這樣的參數其實是存在url中的,和get差不多了。。。所以不建議這樣寫。
另外如果一個參數建一個@RequestBody,會報錯“I/O error while reading input message; nested exception is java.io.IOException: Stream closed”。有個牛人寫了“SpringBoot Controller 中使用多個@RequestBody的正確姿勢”,地址:https://blog.csdn.net/w605283073/article/details/82119284

總結:基于以上,即使只有一兩個參數,也建議使用dto。

參數為DTO

新增所有需要的參數放入一個dto中,不包括有文件的情況。

@ApiOperation("傳參dto")@PostMapping("/post/postMappring4")public ResponseVo postMappring4(@RequestBody DogDTO dto) {return ResponseFactory.success(dto.getDogName());}

在這里插入圖片描述

參數為DTO加文件

遇到上傳文件時,只能使用@RequestPart注解文件,dto也必須使用@RequestPart注解。這是因為當同時存在@RequestPart和@RequestBody時。@RequestPart的優先級更高,會將contentType設置為multipart/form-data。但前面說過@RequestBody不支持multipart/form-data。

/*** 注解一個dto和一個文件* @param dto* @param file* @return*/@ApiOperation("RequestPart注解上傳文件和DTO")@PostMapping("/post/requestPart5")public ResponseVo postMappring5(@RequestPart DogDTO dto,@RequestPart MultipartFile file) {return ResponseFactory.success(dto);}

在這里插入圖片描述
可以正常獲取到數據:
在這里插入圖片描述

修改——@PutMapping

參數為基本類型

使用@RequestParam

@ApiOperation("put請求-單個參數RequestParam、文件上傳")
@PutMapping("/put/putMapping")
public ResponseVo putMapping(@RequestParam String dogName,@RequestPart MultipartFile file) {return ResponseFactory.success(dogName);
}

在這里插入圖片描述
在這里插入圖片描述
注:與post沒有區別,不作特殊說明

參數為DTO加文件

@ApiOperation("put請求-DTO加文件上傳")@PutMapping("/put/putMapping2")public ResponseVo putMapping2(@RequestPart DogDTO dto,@RequestPart MultipartFile file) {return ResponseFactory.success(dto.getDogName());}

注:與post沒有區別,不作特殊說明

刪除——@DeleteMapping

通過基本類型參數(少參數)

PathVariable 方式

/*** 單個參數* @param id* @return*/
@DeleteMapping("/deleteMapping/{id}")
public ResponseVo deleteMapping(
@ApiParam(required = false, value = "主鍵id") @PathVariable int id) {return ResponseFactory.success(id);
}

在這里插入圖片描述
注:getMapping時PathVariable 注解的參數是可以為null的,但是@DeleteMapping時卻不可以,此時它默認id是一定要有值的。所以如果確定id不存在特殊字符,且一定有值,可以使用這種方式。

RequestParam方式

@DeleteMapping("/deleteMapping/{id}/1")public ResponseVo deleteMapping3(@PathVariable int id,@RequestParam String name) {return ResponseFactory.success(id + name);}

在這里插入圖片描述

通過DTO

/*** PathVariable+RequestParam+RequestBody* @param id* @return*/@DeleteMapping("/deleteMapping/{id}/2")public ResponseVo deleteMapping2(@PathVariable int id, @RequestParam String name, @RequestBody DogDTO dto) {return ResponseFactory.success(id + name+ dto.getDogName());}

在這里插入圖片描述
注:很奇怪,本來以為會和getMaping 一樣,無法使用RequestBody。
沒想到竟然可以。
在這里插入圖片描述

總結

http既然針對增刪改查的業務場景區分了4種mapping,那么自然是有它的用意的。只是網絡不熟,所以不明白,百度上也幾乎沒有說明。不過照著用總是沒有問題的。寫postMapping時提到了冪等性,我猜測http可能根據請求對數據庫的具體操作,進行了定制優化。

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://808629.com/179471.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 86后生记录生活 Inc. 保留所有权利。

底部版权信息