javascript
Spring MVC 接收请求参数所有方式总结!
來源:簡書,作者:zhrowable
鏈接:https://www.jianshu.com/p/5f6abd08ee08
SpringMVC請求參數(shù)接收
其實(shí)一般的表單或者JSON數(shù)據(jù)的請求都是相對簡單的,一些復(fù)雜的處理主要包括URL路徑參數(shù)、文件上傳、數(shù)組或者列表類型數(shù)據(jù)等。
另外,關(guān)于參數(shù)類型中存在日期類型屬性(例如java.util.Date、java.sql.Date、java.time.LocalDate、java.time.LocalDateTime),解析的時(shí)候一般需要自定義實(shí)現(xiàn)的邏輯實(shí)現(xiàn)String->日期類型的轉(zhuǎn)換。
其實(shí)道理很簡單,日期相關(guān)的類型對于每個(gè)國家、每個(gè)時(shí)區(qū)甚至每個(gè)使用者來說認(rèn)知都不一定相同。在演示一些例子主要用到下面的模特類:
@Datapublic class User {private String name;private Integer age;private List<Contact> contacts;}@Datapublic class Contact {private String name;private String phone;}表單參數(shù)
非對象類型單個(gè)參數(shù)接收:
這種是最常用的表單參數(shù)提交,ContentType指定為application/x-www-form-urlencoded,也就是會進(jìn)行URL編碼。
對應(yīng)的控制器如下:
@PostMapping(value = "/post")public String post(@RequestParam(name = "name") String name,@RequestParam(name = "age") Integer age) {String content = String.format("name = %s,age = %d", name, age);log.info(content);return content; }說實(shí)話,如果有毅力的話,所有的復(fù)雜參數(shù)的提交最終都可以轉(zhuǎn)化為多個(gè)單參數(shù)接收,不過這樣做會產(chǎn)生十分多冗余的代碼,而且可維護(hù)性比較低。這種情況下,用到的參數(shù)處理器是RequestParamMapMethodArgumentResolver。
對象類型參數(shù)接收:
我們接著寫一個(gè)接口用于提交用戶信息,用到的是上面提到的模特類,主要包括用戶姓名、年齡和聯(lián)系人信息列表,這個(gè)時(shí)候,我們目標(biāo)的控制器最終編碼如下:
@PostMapping(value = "/user") public User saveUser(User user) {log.info(user.toString());return user; }我們還是指定ContentType為application/x-www-form-urlencoded,接著我們需要構(gòu)造請求參數(shù):
因?yàn)闆]有使用注解,最終的參數(shù)處理器為ServletModelAttributeMethodProcessor,主要是把HttpServletRequest中的表單參數(shù)封裝到MutablePropertyValues實(shí)例中,再通過參數(shù)類型實(shí)例化(通過構(gòu)造反射創(chuàng)建User實(shí)例),反射匹配屬性進(jìn)行值的填充。
另外,請求復(fù)雜參數(shù)里面的列表屬性請求參數(shù)看起來比較奇葩,實(shí)際上和在.properties文件中添加最終映射到Map類型的參數(shù)的寫法是一致的。那么,能不能把整個(gè)請求參數(shù)塞在一個(gè)字段中提交呢?
直接這樣做是不行的,因?yàn)閷?shí)際提交的form表單,key是user,value實(shí)際上是一個(gè)字符串,缺少一個(gè)String->User類型的轉(zhuǎn)換器,實(shí)際上RequestParamMethodArgumentResolver依賴WebConversionService中Converter列表進(jìn)行參數(shù)轉(zhuǎn)換:
解決辦法還是有的,添加一個(gè)org.springframework.core.convert.converter.Converter實(shí)現(xiàn)即可:
@Componentpublic class StringUserConverter implements Converter<String, User> {private static final ObjectMapper MAPPER = new ObjectMapper();@Overridepublic User convert(String source) {try {return MAPPER.readValue(source, User.class);} catch (IOException e) {throw new IllegalArgumentException(e);}} }上面這種做法屬于曲線救國的做法,不推薦使用在生產(chǎn)環(huán)境,但是如果有些第三方接口的對接無法避免這種參數(shù),可以選擇這種實(shí)現(xiàn)方式。
JSON參數(shù)
一般來說,直接POST一個(gè)JSON字符串這種方式對于SpringMVC來說是比較友好的,只需要把ContentType設(shè)置為application/json,提交一個(gè)原始的JSON字符串即可:
Spring Boot 返回 JSON 數(shù)據(jù),一分鐘搞定!
后端控制器的代碼也比較簡單:
@PostMapping(value = "/user-2") public User saveUser2(@RequestBody User user) {log.info(user.toString());return user; }因?yàn)槭褂昧?#64;RequestBody注解,最終使用到的參數(shù)處理器為RequestResponseBodyMethodProcessor,實(shí)際上會用到MappingJackson2HttpMessageConverter進(jìn)行參數(shù)類型的轉(zhuǎn)換,底層依賴到Jackson相關(guān)的包。
URL參數(shù)
URL參數(shù),或者叫請求路徑參數(shù)是基于URL模板獲取到的參數(shù),例如/user/{userId}是一個(gè)URL模板(URL模板中的參數(shù)占位符是{}),實(shí)際請求的URL為/user/1,那么通過匹配實(shí)際請求的URL和URL模板就能提取到userId為1。
在SpringMVC中,URL模板中的路徑參數(shù)叫做PathVariable,對應(yīng)注解@PathVariable,對應(yīng)的參數(shù)處理器為PathVariableMethodArgumentResolver。
注意一點(diǎn)是,@PathVariable的解析是按照value(name)屬性進(jìn)行匹配,和URL參數(shù)的順序是無關(guān)的。舉個(gè)簡單的例子:
后臺的控制器如下:
@GetMapping(value = "/user/{name}/{age}") public String findUser1(@PathVariable(value = "age") Integer age, @PathVariable(value = "name") String name) {String content = String.format("name = %s,age = %d", name, age);log.info(content);return content; }這種用法被廣泛使用于Representational State Transfer(REST)的軟件架構(gòu)風(fēng)格,個(gè)人覺得這種風(fēng)格是比較靈活和清晰的(從URL和請求方法就能完全理解接口的意義和功能)。下面再介紹兩種相對特殊的使用方式。
帶條件的URL參數(shù)
其實(shí)路徑參數(shù)支持正則表達(dá)式,例如我們在使用/sex/sex}接口的時(shí)候,要求sex必須是F(Female)或者M(jìn)(Male),那么我們的URL模板可以定義為/sex/{sex:MF,代碼如下:
只有/sex/F或者/sex/M的請求才會進(jìn)入findUser2控制器方法,其他該路徑前綴的請求都是非法的,會返回404狀態(tài)碼。這里僅僅是介紹了一個(gè)最簡單的URL參數(shù)正則表達(dá)式的使用方式,更強(qiáng)大的用法可以自行摸索。
@MatrixVariable的使用
MatrixVariable也是URL參數(shù)的一種,對應(yīng)注解@MatrixVariable,不過它并不是URL中的一個(gè)值(這里的值指定是兩個(gè)"/"之間的部分),而是值的一部分,它通過";"進(jìn)行分隔,通過"="進(jìn)行K-V設(shè)置。
說起來有點(diǎn)抽象,舉個(gè)例子:假如我們需要打電話給一個(gè)名字為doge,性別是男,分組是碼畜的程序員,GET請求的URL可以表示為:/call/doge;gender=male;group=programmer,我們設(shè)計(jì)的控制器方法如下:
@GetMapping(value = "/call/{name}") public String find(@PathVariable(value = "name") String name,@MatrixVariable(value = "gender") String gender,@MatrixVariable(value = "group") String group) {String content = String.format("name = %s,gender = %s,group = %s", name, gender, group);log.info(content);return content; }當(dāng)然,如果你按照上面的例子寫好代碼,嘗試請求一下該接口發(fā)現(xiàn)是報(bào)錯(cuò)的:400 Bad Request - Missing matrix variable 'gender' for method parameter of type String。
這是因?yàn)?#64;MatrixVariable注解的使用是不安全的,在SpringMVC中默認(rèn)是關(guān)閉對其支持。要開啟對@MatrixVariable的支持,需要設(shè)置RequestMappingHandlerMapping#setRemoveSemicolonContent方法為false:
@Configurationpublic class CustomMvcConfiguration implements InitializingBean {@Autowiredprivate RequestMappingHandlerMapping requestMappingHandlerMapping;@Overridepublic void afterPropertiesSet() throws Exception {requestMappingHandlerMapping.setRemoveSemicolonContent(false);} }除非有很特殊的需要,否則不建議使用@MatrixVariable。
文件上傳
文件上傳在使用POSTMAN模擬請求的時(shí)候需要選擇form-data,POST方式進(jìn)行提交:
假設(shè)我們在D盤有一個(gè)圖片文件叫doge.jpg,現(xiàn)在要通過本地服務(wù)接口把文件上傳,控制器的代碼如下:
@PostMapping(value = "/file1") public String file1(@RequestPart(name = "file1") MultipartFile multipartFile) {String content = String.format("name = %s,originName = %s,size = %d",multipartFile.getName(), multipartFile.getOriginalFilename(), multipartFile.getSize());log.info(content);return content; }控制臺輸出是:
name = file1,originName = doge.jpg,size = 68727可能有點(diǎn)疑惑,參數(shù)是怎么來的,我們可以用Fildder抓個(gè)包看下:
可知MultipartFile實(shí)例的主要屬性分別來自Content-Disposition、content-type和content-length,另外,InputStream用于讀取請求體的最后部分(文件的字節(jié)序列)。參數(shù)處理器用到的是RequestPartM
ethodArgumentResolver(記住一點(diǎn),使用了@RequestPart和MultipartFile一定是使用此參數(shù)處理器)。
在其他情況下,使用@RequestParam和MultipartFile或者僅僅使用MultipartFile(參數(shù)的名字必須和POST表單中的Content-Disposition描述的name一致)也可以接收上傳的文件數(shù)據(jù),主要是通過RequestParamMethodArgumentResolver進(jìn)行解析處理的,它的功能比較強(qiáng)大,具體可以看其supportsParameter方法,這兩種情況的控制器方法代碼如下:
@PostMapping(value = "/file2") public String file2(MultipartFile file1) {String content = String.format("name = %s,originName = %s,size = %d",file1.getName(), file1.getOriginalFilename(), file1.getSize());log.info(content);return content;}@PostMapping(value = "/file3")public String file3(@RequestParam(name = "file1") MultipartFile multipartFile) {String content = String.format("name = %s,originName = %s,size = %d",multipartFile.getName(), multipartFile.getOriginalFilename(), multipartFile.getSize());log.info(content);return content; }其他參數(shù)
其他參數(shù)主要包括請求頭、Cookie、Model、Map等相關(guān)參數(shù),還有一些并不是很常用或者一些相對原生的屬性值獲取(例如HttpServletRequest、HttpServletResponse等)不做討論。
請求頭
請求頭的值主要通過@RequestHeader注解的參數(shù)獲取,參數(shù)處理器是RequestHeaderMethodArgumentResolver,需要在注解中指定請求頭的Key。簡單實(shí)用如下:
控制器方法代碼:
@PostMapping(value = "/header") public String header(@RequestHeader(name = "Content-Type") String contentType) {return contentType; }Cookie
Cookie的值主要通過@CookieValue注解的參數(shù)獲取,參數(shù)處理器為ServletCookieValueMethodArgumentResolver,需要在注解中指定Cookie的Key。控制器方法代碼如下:
@PostMapping(value = "/cookie") public String cookie(@CookieValue(name = "JSESSIONID") String sessionId) {return sessionId;}Model類型參數(shù)
Model類型參數(shù)的處理器是ModelMethodProcessor,實(shí)際上處理此參數(shù)是直接返回ModelAndViewContainer實(shí)例中的Model(ModelMap類型),因?yàn)橐獦蚪硬煌慕涌诤皖惖墓δ?#xff0c;因此回調(diào)的實(shí)例是BindingAwareModelMap類型,此類型繼承自ModelMap同時(shí)實(shí)現(xiàn)了Model接口。舉個(gè)例子:
@GetMapping(value = "/model") public String model(Model model, ModelMap modelMap) {log.info("{}", model == modelMap);return "success"; }注意調(diào)用此接口,控制臺輸出Info日志內(nèi)容為:true。ModelMap或者M(jìn)odel中添加的屬性項(xiàng)會附加到HttpRequestServlet中帶到頁面中進(jìn)行渲染。
@ModelAttribute參數(shù)
@ModelAttribute注解處理的參數(shù)處理器為ModelAttributeMethodProcessor,@ModelAttribute的功能源碼的注釋如下:
Annotation that binds a method parameter or method return value to a named model attribute, exposed to a web view.簡單來說,就是通過key-value形式綁定方法參數(shù)或者方法返回值到Model(Map)中,區(qū)別下面三種情況:
-
1、@ModelAttribute使用在方法(返回值)上,方法沒有返回值(void類型), Model(Map)參數(shù)需要自行設(shè)置。
-
2、@ModelAttribute使用在方法(返回值)上,方法有返回值(非void類型),返回值會添加到Model(Map)參數(shù),key由@ModelAttribute的value指定,否則會使用返回值類型字符串(首寫字母變?yōu)樾?。
-
3、@ModelAttribute使用在方法參數(shù)中。
在一個(gè)控制器(使用了@Controller)中,如果存在一到多個(gè)使用了@ModelAttribute的方法,這些方法總是在進(jìn)入控制器方法之前執(zhí)行,并且執(zhí)行順序是由加載順序決定的(具體的順序是帶參數(shù)的優(yōu)先,并且按照方法首字母升序排序),舉個(gè)例子:
@Slf4j @RestController public class ModelAttributeController {@ModelAttributepublic void before(Model model) {log.info("before..........");model.addAttribute("before", "beforeValue");}@ModelAttribute(value = "beforeArg")public String beforeArg() {log.info("beforeArg..........");return "beforeArgValue";}@GetMapping(value = "/modelAttribute")public String modelAttribute(Model model, @ModelAttribute(value = "beforeArg") String beforeArg) {log.info("modelAttribute..........");log.info("beforeArg..........{}", beforeArg);log.info("{}", model);return "success";}@ModelAttributepublic void after(Model model) {log.info("after..........");model.addAttribute("after", "afterValue");}@ModelAttribute(value = "afterArg")public String afterArg() {log.info("afterArg..........");return "afterArgValue";} }調(diào)用此接口,控制臺輸出日志如下:
after.......... before.......... afterArg.......... beforeArg.......... modelAttribute.......... beforeArg..........beforeArgValue {after=afterValue, before=beforeValue, afterArg=afterArgValue, beforeArg=beforeArgValue}可以印證排序規(guī)則和參數(shù)設(shè)置、獲取。
Errors或者BindingResult參數(shù)
Errors其實(shí)是BindingResult的父接口,BindingResult主要用于回調(diào)JSR參數(shù)校驗(yàn)異常的屬性項(xiàng),如果JSR校驗(yàn)異常,一般會拋出MethodArgumentNotValidException異常,并且會返回400(Bad Request),見全局異常處理器DefaultHandlerExceptionResolver。Errors類型的參數(shù)處理器為ErrorsMethodArgumentResolver。舉個(gè)例子:
@PostMapping(value = "/errors") public String errors(@RequestBody @Validated ErrorsModel errors, BindingResult bindingResult) {if (bindingResult.hasErrors()) {for (ObjectError objectError : bindingResult.getAllErrors()) {log.warn("name={},message={}", objectError.getObjectName(), objectError.getDefaultMessage());}}return errors.toString();}//ErrorsModel@Data@NoArgsConstructorpublic class ErrorsModel {@NotNull(message = "id must not be null!")private Integer id;@NotEmpty(message = "errors name must not be empty!")private String name; }調(diào)用接口控制臺Warn日志如下:
name=errors,message=errors name must not be empty!一般情況下,不建議用這種方式處理JSR校驗(yàn)異常的屬性項(xiàng),因?yàn)闀婕暗酱罅康闹貜?fù)的硬編碼工作,建議直接繼承ResponseEntityExceptionHandler,覆蓋對應(yīng)的方法。
@Value參數(shù)
控制器方法的參數(shù)可以是@Value注解修飾的參數(shù),會從Environment中裝配和轉(zhuǎn)換屬性值到對應(yīng)的參數(shù)中(也就是參數(shù)的來源并不是請求體),參數(shù)處理器為ExpressionValueMethodArgumentResolver。舉個(gè)例子:
@GetMapping(value = "/value") public String value(@Value(value = "${spring.application.name}") String name) {log.info("spring.application.name={}", name);return name; }Map類型參數(shù)
Map類型參數(shù)的范圍相對比較廣,對應(yīng)一系列的參數(shù)處理器,注意區(qū)別使用了上面提到的部分注解的Map類型和完全不使用注解的Map類型參數(shù),兩者的處理方式不相同。下面列舉幾個(gè)相對典型的Map類型參數(shù)處理例子。
不使用任何注解的Map<String,Object>參數(shù)
這種情況下參數(shù)實(shí)際上直接回調(diào)ModelAndViewContainer中的ModelMap實(shí)例,參數(shù)處理器為MapMethodProcessor,往Map參數(shù)中添加的屬性將會帶到頁面中。
使用@RequestParam注解的Map<String,Object>參數(shù)
這種情況下的參數(shù)處理器為RequestParamMapMethodArgumentResolver,使用的請求方式需要指定ContentType為x-www-form-urlencoded,不能使用application/json的方式:
控制器代碼為:
@PostMapping(value = "/map") public String mapArgs(@RequestParam Map<String, Object> map) {log.info("{}", map);return map.toString(); }使用@RequestHeader注解的Map<String,Object>參數(shù)
這種情況下的參數(shù)處理器為RequestHeaderMapMethodArgumentResolver,作用是獲取請求的所有請求頭的Key-Value。
使用@PathVariable注解的Map<String,Object>參數(shù)
這種情況下的參數(shù)處理器為PathVariableMapMethodArgumentResolver,作用是獲取所有路徑參數(shù)封裝為Key-Value結(jié)構(gòu)。
MultipartFile集合-批量文件上傳
批量文件上傳的時(shí)候,我們一般需要接收一個(gè)MultipartFile集合,可以有兩種選擇:
-
1、使用MultipartHttpServletRequest參數(shù),直接調(diào)用getFiles方法獲取MultipartFile列表。
-
2、使用@RequestParam注解修飾MultipartFile列表,參數(shù)處理器是RequestParamMethodArgumentResolver,其實(shí)就是第一種的封裝而已。
控制器方法代碼如下:
@PostMapping(value = "/parts") public String partArgs(@RequestParam(name = "file") List<MultipartFile> parts) {log.info("{}", parts);return parts.toString(); }日期類型參數(shù)處理
日期處理個(gè)人認(rèn)為是請求參數(shù)處理中最復(fù)雜的,因?yàn)橐话闳掌谔幚淼倪壿嫴皇峭ㄓ玫?#xff0c;過多的定制化處理導(dǎo)致很難有一個(gè)統(tǒng)一的標(biāo)準(zhǔn)處理邏輯去處理和轉(zhuǎn)換日期類型的參數(shù)。
不過,這里介紹幾個(gè)通用的方法,以應(yīng)對各種奇葩的日期格式。下面介紹的例子中全部使用Jdk8中引入的日期時(shí)間API,圍繞java.util.Date為核心的日期時(shí)間API的使用方式類同。另外推薦大家關(guān)注下微信公眾號Java技術(shù)棧,在后臺回復(fù)Java可以獲取我整理的 N 篇 Java 8+?教程,都是干貨。
一、統(tǒng)一以字符串形式接收
這種是最原始但是最奏效的方式,統(tǒng)一以字符串形式接收,然后自行處理類型轉(zhuǎn)換,下面給個(gè)小例子:
@PostMapping(value = "/date1") public String date1(@RequestBody UserDto userDto) {UserEntity userEntity = new UserEntity();userEntity.setUserId(userDto.getUserId());userEntity.setBirthdayTime(LocalDateTime.parse(userDto.getBirthdayTime(), FORMATTER));userEntity.setGraduationTime(LocalDateTime.parse(userDto.getGraduationTime(), FORMATTER));log.info(userEntity.toString());return "success"; }@Datapublic class UserDto {private String userId;private String birthdayTime;private String graduationTime;}@Datapublic class UserEntity {private String userId;private LocalDateTime birthdayTime;private LocalDateTime graduationTime; }二、使用注解@DateTimeFormat或者@JsonFormat
@DateTimeFormat注解配合@RequestBody的參數(shù)使用的時(shí)候,會發(fā)現(xiàn)拋出InvalidFormatException異常,提示轉(zhuǎn)換失敗,這是因?yàn)樵谔幚泶俗⒔獾臅r(shí)候,只支持form提交(ContentType為x-www-form-urlencoded),例子如下:
@Datapublic class UserDto2 {private String userId;@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime birthdayTime;@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime graduationTime; }@PostMapping(value = "/date2") public String date2(UserDto2 userDto2) {log.info(userDto2.toString());return "success"; }//或者像下面這樣 @PostMapping(value = "/date2") public String date2(@RequestParam("name"="userId")String userId,@RequestParam("name"="birthdayTime")LocalDateTime birthdayTime,@RequestParam("name"="graduationTime")LocalDateTime graduationTime) {return "success"; }而@JsonFormat注解可使用在form或者Json請求參數(shù)的場景,因此更推薦使用@JsonFormat注解,不過注意需要指定時(shí)區(qū)(timezone屬性,例如在中國是東八區(qū)"GMT+8"),否則有可能導(dǎo)致出現(xiàn)"時(shí)差",舉個(gè)例子:
@PostMapping(value = "/date2") public String date2(@RequestBody UserDto2 userDto2) {log.info(userDto2.toString());return "success"; }@Data public class UserDto2 {private String userId;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private LocalDateTime birthdayTime;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private LocalDateTime graduationTime; }三、Jackson序列化和反序列化定制
因?yàn)镾pringMVC默認(rèn)使用Jackson處理@RequestBody的參數(shù)轉(zhuǎn)換,因此可以通過定制序列化器和反序列化器來實(shí)現(xiàn)日期類型的轉(zhuǎn)換,這樣我們就可以使用application/json的形式提交請求參數(shù)。關(guān)于Java序列化你應(yīng)該知道的一切,這篇推薦看下。
這里的例子是轉(zhuǎn)換請求Json參數(shù)中的字符串為LocalDateTime類型,屬于Json反序列化,因此需要定制反序列化器:
@PostMapping(value = "/date3")public String date3(@RequestBody UserDto3 userDto3) {log.info(userDto3.toString());return "success"; }@Data public class UserDto3 {private String userId;@JsonDeserialize(using = CustomLocalDateTimeDeserializer.class)private LocalDateTime birthdayTime;@JsonDeserialize(using = CustomLocalDateTimeDeserializer.class)private LocalDateTime graduationTime;}public class CustomLocalDateTimeDeserializer extends LocalDateTimeDeserializer {public CustomLocalDateTimeDeserializer() {super(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));} }四、最佳實(shí)踐
前面三種方式都存在硬編碼等問題,其實(shí)最佳實(shí)踐是直接修改MappingJackson2HttpMessageConverter中的ObjectMapper對于日期類型處理默認(rèn)的序列化器和反序列化器,這樣就能全局生效,不需要再使用其他注解或者定制序列化方案(當(dāng)然,有些時(shí)候需要特殊處理定制),或者說,在需要特殊處理的場景才使用其他注解或者定制序列化方案。使用鉤子接口Jackson2ObjectMapperBuilderCustomizer可以實(shí)現(xiàn)ObjectMapper的屬性定制:
@Bean public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer(){return customizer->{customizer.serializerByType(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));customizer.deserializerByType(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));}; }這樣就能定制化MappingJackson2HttpMessageConverter中持有的ObjectMapper,上面的LocalDateTime序列化和反序列化器對全局生效。
請求URL匹配
前面基本介紹完了主流的請求參數(shù)處理,其實(shí)SpringMVC中還會按照URL的模式進(jìn)行匹配,使用的是Ant路徑風(fēng)格,處理工具類為org.springframework.util.AntPathMatcher,從此類的注釋來看,匹配規(guī)則主要包括下面四點(diǎn):
-
1、?匹配1個(gè)字符。
-
2、*匹配0個(gè)或者多個(gè)字符。
-
3、**匹配路徑中0個(gè)或者多個(gè)目錄。
-
4、{spring:[a-z]+}將正則表達(dá)式[a-z]+匹配到的值,賦值給名為spring的路徑變量。
舉些例子:
?形式的URL:
@GetMapping(value = "/pattern?")public String pattern() {return "success"; }/pattern 404 Not Found /patternd 200 OK /patterndd 404 Not Found /pattern/ 404 Not Found /patternd/s 404 Not Found*形式的URL:
@GetMapping(value = "/pattern*") public String pattern() {return "success"; }/pattern 200 OK /pattern/ 200 OK /patternd 200 OK /pattern/a 404 Not Found**形式的URL:
@GetMapping(value = "/pattern/**/p")public String pattern() {return "success"; }/pattern/p 200 OK /pattern/x/p 200 OK /pattern/x/y/p 200 OK{spring:[a-z]+}形式的URL:
@GetMapping(value = "/pattern/{key:\[a-c\]+}") public String pattern(@PathVariable(name = "key") String key) {return "success"; }/pattern/a 200 OK /pattern/ab 200 OK /pattern/abc 200 OK /pattern 404 Not Found /pattern/abcd 404 Not Found上面的四種URL模式可以組合使用,千變?nèi)f化。RESTful API設(shè)計(jì)技巧經(jīng)驗(yàn)總結(jié)推薦看看。
URL匹配還遵循精確匹配原則,也就是存在兩個(gè)模式對同一個(gè)URL都能夠匹配成功,則選取最精確的URL匹配,進(jìn)入對應(yīng)的控制器方法,舉個(gè)例子:
@GetMapping(value = "/pattern/**/p") public String pattern1() {return "success"; }@GetMapping(value = "/pattern/p") public String pattern2() {return "success"; }上面兩個(gè)控制器,如果請求URL為/pattern/p,最終進(jìn)入的方法為pattern2。
最后,org.springframework.util.AntPathMatcher作為一個(gè)工具類,可以單獨(dú)使用,不僅僅可以用于匹配URL,也可以用于匹配系統(tǒng)文件路徑,不過需要使用其帶參數(shù)構(gòu)造改變內(nèi)部的pathSeparator變量,例如:
AntPathMatcher antPathMatcher = new AntPathMatcher(File.separator);小結(jié)
筆者在前一段時(shí)間曾經(jīng)花大量時(shí)間梳理和分析過Spring、SpringMVC的源碼,但是后面一段很長的時(shí)間需要進(jìn)行業(yè)務(wù)開發(fā),對架構(gòu)方面的東西有點(diǎn)生疏了,畢竟東西不用就會生疏,這個(gè)是常理。
這篇文章基于一些SpringMVC的源碼經(jīng)驗(yàn)總結(jié)了請求參數(shù)的處理相關(guān)的一些知識,希望幫到自己和大家。
總結(jié)
以上是生活随笔為你收集整理的Spring MVC 接收请求参数所有方式总结!的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring 和 Spring Boot
- 下一篇: 点赞功能,用 MySQL?还是 Redi