SpringMVC之请求参数的获取方式
  常见的一个web服务,如何获取请求参数?
 
 一般最常见的请求为GET和POST,get请求的参数在url上可以获取,post请求参数除了url上还有可能在表单中,文件上传时,获取方式又和一般的参数获取不一样
 本篇则主要集中在不同请求方式下,获取参数的使用姿势
 首先需要搭建一个后端的请求,为了快速演示
 利用spring-boot创建了一个机器简单的工程,依赖版本 1.5.4.RELEASE
 I. GET请求参数获取
 get请求参数,一般都是直接挂在请求的url上,所以获取这些参数还是比较简单的
 1. 通过 HttpServletRequest获取参数
 这个可以说是最基本最常见的的方式了,javax.servlet.ServletRequest#getParameter 来获取对应的参数,下面各处一个实例
 @RestController @RequestMapping(path = "webs/demo") public class DemoController {      @RequestMapping(path = "req1")     public String req1(HttpServletRequest request) {         String user = request.getParameter("user");         String password = request.getParameter("password");         return "req1 user: " + user + " pwd: " + password;     } } 
 根据上面暴露的接口,我们测试的case就很简单了
 http://127.0.0.1:8080/webs/demo/req1?user=小灰灰Blog&password=123456 ## 输出  req1 user: 小灰灰Blog pwd: 123456  http://127.0.0.1:8080/webs/demo/req1?user=小灰灰Blog ## 输出  req1 user: 小灰灰Blog pwd: null 
 说明
  -  这是一个最基本的获取参数的方式,get,post请求都适用的,通常在filter,intercepter中也是可以通过HttpServletRequest对象来获取请求参数
 
-  除了获取常见的请求参数之外,HttpServletRequest可以获取请求头的完整信息
 
-  在一次请求的生命周期内,可以通过下面的方式获取Request对象(当然也可以获取response对象) HttpServletRequest httpServletRequest = ((ServletRequestAttributes)      RequestContextHolder.getRequestAttributes()).getRequest(); 
 
2. 直接方法参数获取
 直接给出case, 这个方法依然是放在上面的DemoController下面的
 @RequestMapping(path = "req2") public String req2(String user, String password) {     return "req2 user: " + user + " pwd: " + password; } 
 请求验证
 http://127.0.0.1:8080/webs/demo/req2?user=%E5%B0%8F%E7%81%B0%E7%81%B0Blog&password=123456 ## 输出: req2 user: 小灰灰Blog pwd: 123456   http://127.0.0.1:8080/webs/demo/req2?password=123456 ## 输出: req2 user: null pwd: 123456   http://127.0.0.1:8080/webs/demo/req2?password=123456&User=blog ## 输出: req2 user: null pwd: 123456 
 注意:
  - 上面这种使用方式,相当于直接将url参数映射到了Controller方法的参数上了
- 方法参数名必须和url参数名完全一致(区分大小写)
- 顺序无关
- 若参数没传,则默认为null
一个疑问
 上面的demo中Controller的方法参数都是String还好,如果将password改成int,会出现什么情况
 代码稍作修改
 @RequestMapping(path = "req2") public String req2(String user, int password) {     return "req2 user: " + user + " pwd: " + password; } 
 实际测试
 # case1  http://127.0.0.1:8080/webs/demo/req2?password=123456&user=blog ## 输出: req2 user: blog pwd: 123456   # case 2 http://127.0.0.1:8080/webs/demo/req2?password2=123456&user=blog ## 输出: 报错, Optional int parameter 'password' is present but cannot be translated into a null value due to being declared as a primitive type. Consider declaring it as object wrapper for the corresponding primitive type   # case 3 http://127.0.0.1:8080/webs/demo/req2?password=abc&user=blog ## 输出:报错, "Failed to convert value of type 'java.lang.String' to required type 'int'; nested exception is java.lang.NumberFormatException: For input string: "abc"" 
 结果说明
  - 如果请求参数与方法参数类型不一致,会抛出转换异常
- 如果方法参数为非封装基本类型,则url参数必须存在,否则报错
3. RequestParam注解方式获取请求参数
 通过@RequestParam注解获取参数的方式和上面的一种比较类似,case如下
 @RequestMapping(path = "req3", method = RequestMethod.GET) public String req3(@RequestParam("user") String username,                    @RequestParam("password") String pwd) {     return "req3 user: " + username + " pwd: " + pwd; } 
 测试case
 # case1  http://127.0.0.1:8080/webs/demo/req3?password=123456&user=blog ## 输出: req3 user: blog pwd: 123456   # case2 http://127.0.0.1:8080/webs/demo/req3?password=123456 ## 输出:报错, Required String parameter 'user' is not presen 
 说明
  4. Bean方式获取参数
 对于请求参数比较复杂的情况下,我比较喜欢这种使用姿势,管理起来方便简单
 @Data public static class UserDO {     String user;     String password; }  @RequestMapping(path = "req4", method = RequestMethod.GET) public String req4(UserDO userDO) {     return "req4 userDO: " + userDO; } 
 测试case
 # case1 http://127.0.0.1:8080/webs/demo/req4?password=123456&user=%E5%B0%8F%E7%81%B0%E7%81%B0Blog ## 输出: req4 userDO: DemoController.UserDO(user=小灰灰Blog, password=123456)   # case2 http://127.0.0.1:8080/webs/demo/req4?password=123456 ## 输出: req4 userDO: DemoController.UserDO(user=null, password=123456) 
 说明
  - 定义一个bean,内部属性和请求参数对应
- 允许参数不存在的情况,会使用null代替(所以,尽量不要使用非封装基本类型,否则参数不传时,会抛异常)
- bean的属性,可以根据实际情况指定类型
5. ModelAttribute注解方式
 @ModelAttribute注解的方法,会优于Controller之前执行,一般更常见于向视图传输数据使用,此处不详细展开,正常来讲,专门的获取参数不太会用这这种方式来玩
 6. Path参数
 Path参数,专指的是请求路径的参数,如
 http://127.0.0.1:8080/webs/demo/req4?password=123456 
 上面这个url中,password是我们传统意义上的请求参数,其中path参数则是指其中 req4, demo这种path路径中的一环;对此,最常见的一个case就是常见的博客中,如开源中国的一个博客链接
 https://my.oschina.net/u/566591/blog/1601400 
  - 566591 : 这个参数主要用来区分用户
- 1601400 : 这个参数则主要是表示对应的博文
一般path参数的获取方式如下
 @RequestMapping(path = "req6/{user}/info") public String req6(@PathVariable(name = "user") String user) {     return "req6 user: " + user; } 
 测试case
 # case1  http://127.0.0.1:8080/webs/demo/req6/blog/info?user=haha ## 输出:req6 user: blog  # case2 http://127.0.0.1:8080/webs/demo/req6/blog?user=haha ## 输出: 404  # case3 http://127.0.0.1:8080/webs/demo/req6/info?user=haha ## 输出: 404 
 注意:
  - path参数的使用,需要确保参数存在且类型匹配
- path参数和url参数不会相互影响
II. POST请求参数获取
 POST请求参数,更多的是看提交表单参数是否可以获取到,以及如何获取,主要的手段依然是上面几种方式,下面验证下是否ok
 1. HttpServletRequest方式获取参数
 测试case,可以借助curl来实现post请求
 # case1  curl -d "user=小灰灰Blog&password=123456" "http://127.0.0.1:8080/webs/demo/req1" ## 输出: req1 user: 小灰灰Blog pwd: 123456  # case2 curl -d "user=小灰灰Blog" "http://127.0.0.1:8080/webs/demo/req1?password=123456" ## 输出:req1 user: 小灰灰Blog pwd: 12345   # case3 curl -d "user=小灰灰Blog" "http://127.0.0.1:8080/webs/demo/req1?user=greyBlog" ## 输出:req1 user: greyBlog pwd: null 
 curl也可以换成js请求测试方式
 var formData = new FormData(); formData.append("user", "小灰灰Blog");  $.ajax({   url: 'http://127.0.0.1:8080/webs/demo/req1?password=123456',   type: 'post',   cache: false,   data: formData,   processData: false,   contentType: false }); 
 说明
  - 对于HttpServletReuqest方式获取参数时,get和post没什么区别
- 若url参数和表单参数同名了,测试结果显示使用的是url参数(待确认,当然最好不要这么干)
2. 方法参数获取
 几个测试demo如下
 # case 1 curl -d "user=小灰灰Blog&password=123456" "http://127.0.0.1:8080/webs/demo/req2" ## 输出: req2 user: 小灰灰Blog pwd: 123456   # case 2 curl -d "password=123456" "http://127.0.0.1:8080/webs/demo/req2" ## 输出:req2 user: null pwd: 123456   # case 3 curl -d "password=123456" "http://127.0.0.1:8080/webs/demo/req2?user=blog" ## 输出: req2 user: blog pwd: 123456 
 基本上使用姿势和get没什么区别
 3. RequestParam注解方式
 # case 1 curl -d "password=123456&user=blog" "http://127.0.0.1:8080/webs/demo/req3" ## 输出: req3 user: blog pwd: 123456   # case 2 curl -d "password=123456" "http://127.0.0.1:8080/webs/demo/req3?user=blog" ## 输出: req3 user: blog pwd: 123456   # case 3 curl -d "password=123456&user=blog" "http://127.0.0.1:8080/webs/demo/req3?password=900" ## 输出:req3 user: blog pwd: 900,123456 
 注意
  - 和前面的两种方式不同的是,当post表单的参数和url参数同名时,会合并成一个字符串
4. Bean方式
 ## case1  curl -d "password=123456&user=blog" "http://127.0.0.1:8080/webs/demo/req4?password=900" ## 输出 req4 userDO: DemoController.UserDO(user=blog, password=900,123456)   ## case2 curl -d "password=123456&user=blog" "http://127.0.0.1:8080/webs/demo/req4" ## 输出 req4 userDO: DemoController.UserDO(user=blog, password=123456)   ## case3 curl -d "password=123456" "http://127.0.0.1:8080/webs/demo/req4" ## 输出 req4 userDO: DemoController.UserDO(user=null, password=123456) 
 这种方式不区分get,post,所以完全复杂的交互接口,完全可以考虑用bean的方式来定义请求参数
 5. PathVariable
 这个没法玩...
 III. 多媒体上传参数获取
  上传文件的支持,对于传统的spring-mvc来说,可能需要一些添加一些相关配置,不在本文的范畴内,下面默认已经配置好
 
 1. 实例支持
 @RequestMapping(path = {"wx/upload", "wx/wx/upload"}, method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.OPTIONS}) @ResponseBody public String upload(HttpServletRequest request) {     MultipartFile file = null;     if (request instanceof MultipartHttpServletRequest) {         file = ((MultipartHttpServletRequest) request).getFile("image");     }      if (file == null) {         throw new IllegalArgumentException("图片不能为空!");     }          return "success"; } 
 简单来说,主要是利用HttpServletRequest来获取上传的文件
 注意:
  - 如果接口必须要求上传文件,可以直接把参数声明为 MultipartHttpServletRequest, 此时调用方如果不传参数,会被异常拦截(可以通过@ControllerAdvice来拦截全局异常)
- 如果可以不上传文件,则可以用上面的这种猥琐姿势,内部进行判断
- ((MultipartHttpServletRequest) request).getFile(xxx)来获取指定名的上传文件
IV. 小结
 1. 五种获取参数的姿势
   | 方式 | 注意事项 | 
|---|
   | HttpServletRequest获取参数 | 最常见通用 | 
 | 方法参数与请求参数同名 | 注意参数名统一,注意类型一致,尽量不用非包装基本类型 | 
 | @RequestParam注解 | 同上,可注解内指定http参数名 | 
 | Bean方式 | 定义一个bean,会将同名的http参数赋值进去,推荐 | 
 | @PathVariable 注解 | 请求url参数 | 
  
 2. 传文件使用姿势
 使用MultipartHttpServletRequest来获取上传的文件,当然也可以获取基本的请求参数
 V. 其他
 声明
 尽信书则不如,已上内容,纯属一家之言,因本人能力一般,见解不全,如有问题,欢迎批评指正
 扫描关注,java分享
 