天天看点

Spring MVC 参数接收常见注解学习

前端让我传 json,我发现我傻逼了

由于没搞清楚 @RequestBody 和 @ModelAttribute 的区别,我以为 @ModelAttribute 是 Swagger 的一个注解,结果我明明使用对象接收参数的,结果对象里面都是 null。

后来把 @ModelAttribute 去掉,发现就可以接收到 json 了。

既然遇到了这个问题,那么还是了解一下这两个注解以及它们的不同之处吧。

@RequestBody

@RequestBody 注解常用来处理 content-type 不是默认的 application/x-www-form-urlcoded 编码的内容,比如说:application/json 或者是 application/xml 等。一般情况下来说常用其来处理 application/json 类型。

现在有如下的 ajax 请求:

$.ajax({
    url:"/login",
    type:"POST",
    data:'{"userName":"admin","pwd","admin123"}',
    content-type:"application/json charset=utf-8",
    success:function(data){
        alert("request success ! ");
    }
});
           

此时作为后台开发,你可以有两种方式接收参数:

  1. 将 json 字符串中的两个变量分别赋予两个字符串
    @RequestMapping("/login")
    public void login(@RequestBody String userName, @RequestBody String pwd){
        System.out.println(userName + " :" + pwd);
    }                
    这种方法我倒是用的比较少,一般都是用第二种方法。
  2. 构造一个 User 类,这个 User 类有如下的字段
    String userName;
    String pwd;                
    然后接收方式如下:
    @RequestMapping("/login")
    public void login(@RequestBody User user){
        System.out.println(user.getUserName + " :" + user.getPwd);
    }                
    这样也可以接收参数,而且是最常用的。

在一些特殊情况 @RequestBody 也可以用来处理 content-type 类型为 application/x-www-form-urlcoded 的内容,只不过这种方式不是很常用,在处理这类请求的时候,@RequestBody 会将处理结果放到一个 MultiValueMap<String, String> 中。

@RequestBody 用于 post 请求,不能用于 get 请求。(某些时候还是可以用于 GET 请求的,只不过 HTTP 协议不推荐这么做,因为 GET 一般不在 请求体中带上数据,这个和实现有关。)

@ModelAttribute

在 Spring MVC 中,注解 @ModelAttribute 是一个非常常用的注解,其功能主要在两方面:

  1. 运用在参数上,会将客户端传递过来的参数(请求参数或者表单字段)按名称注入到指定对象中,并且会将这个对象自动加入 ModelMap 中,便于 View 层使用;
  2. 运用在方法上,会在每一个 @RequestMapping 标注的方法前执行,如果有返回值,则自动将该返回值加入到 ModelMap 中;

一般开发中,第一种用法居多,第二种用法可以节省 controller 层的一些代码。

  1. @ModelAttribute 注释 void 返回值的方法
    @Controller  
    public class HelloWorldController {  
    
        @ModelAttribute  
        public void populateModel(@RequestParam String abc, Model model) {  
            model.addAttribute("attributeName", abc);  
        }  
    
        @RequestMapping(value = "/helloWorld")  
        public String helloWorld() {  
            return "helloWorld";  
        }  
    }                

    这个例子,在获得请求 /helloWorld 后,populateModel 方法在 helloWorld 方法之前先被调用,它把请求参数(/helloWorld?abc=text)加入到一个名为 attributeName 的 model 属性中,在它执行后 helloWorld 被调用,返回视图名 helloWorld 和 model 已由 @ModelAttribute 方法生产好了。例子中 model 属性名称和 model 属性对象由 model.addAttribute() 实现,不过前提是要在方法中加入一个 Model 类型的参数。

    当 URL 或者 post 中不包含次参数时,会报错,其实不需要这个方法,完全可以把请求的方法写成,这样缺少此参数也不会出错。

    @RequestMapping(value = "/helloWorld")  
    public String helloWorld(String abc) {  
        return "helloWorld";  
    }                
  2. @ModelAttribute 注释返回具体类的方法
    @ModelAttribute  
    public Account addAccount(@RequestParam String number) {  
        return accountManager.findAccount(number);  
    }                  

    这种情况,model 属性的名称没有指定,它由返回类型隐含表示,如这个方法返回 Account 类型,那么这个 model 属性的名称是 account。

    这个例子中 model 属性名称有返回对象类型隐含表示,model 属性对象就是方法的返回值。它无须要特定的参数。

  3. @ModelAttribute(value="") 注释返回具体类的方法
    @Controller  
    public class HelloWorldController {  
    
        @ModelAttribute("attributeName")  
        public String addAccount(@RequestParam String abc) {  
            return abc;  
        }  
    
        @RequestMapping(value = "/helloWorld")  
        public String helloWorld() {  
            return "helloWorld";  
        }  
    }                
    这个例子中使用 @ModelAttribute 注释的 value 属性,来指定model属性的名称。model属性对象就是方法的返回值。它无须要特定的参数。
  4. @ModelAttribute 和 @RequestMapping 同时注释一个方法
    @Controller  
    public class HelloWorldController {  
    
        @RequestMapping(value = "/helloWorld.do")  
        @ModelAttribute("attributeName")  
        public String helloWorld() {  
            return "hi";  
        }
    }                

    这时这个方法的返回值并不是表示一个视图名称,而是 model 属性的值,视图名称由RequestToViewNameTranslator 根据请求 "/helloWorld.do" 转换为逻辑视图 helloWorld。

    Model 属性名称由 @ModelAttribute(value="") 指定,相当于在 request 中封装了 key=attributeName,value=hi。

  5. @ModelAttribute 注释一个方法的参数
    • 从model中获取
      @Controller  
      public class HelloWorldController {  
      
          @ModelAttribute("user")  
          public User addAccount() {  
              return new User("jz","123");  
          }  
      
          @RequestMapping(value = "/helloWorld")  
          public String helloWorld(@ModelAttribute("user") User user) {  
              user.setUserName("jizhou");  
              return "helloWorld";  
          }  
      }                
      在这个例子里,@ModelAttribute("user") User user 注释方法参数,参数 user 的值来源于 addAccount() 方法中的 model 属性。此时如果方法体没有标注 @SessionAttributes("user"),那么scope 为 request,如果标注了,那么 scope 为 session
    • 从 Form 表单或 URL 参数中获取(实际上,不用此注解也能拿到 user 对象)
      @Controller  
      public class HelloWorldController {  
      
          @RequestMapping(value = "/helloWorld")  
          public String helloWorld(@ModelAttribute User user) {  
              return "helloWorld";  
          }  
      }                
      注意这时候这个User类一定要有没有参数的构造函数。

这他妈的,ModelAttribute 这么麻烦。。。

@RequestParam

GET 和 POST 请求传的参数会自动转换赋值到 @RequestParam 所注解的变量上。

用来处理 Content-Type 为 application/x-www-form-urlencoded 编码的内容。提交方式为 get 或 post。

(HTTP 协议中,如果不指定 Content-Type,则默认传递的参数就是 application/x-www-form-urlencoded 类型)

RequestParam 实质是将 Request.getParameter() 中的 Key-Value 参数 Map 利用 Spring 的转化机制ConversionService 配置,转化成参数接收对象或字段。

可以通过required=false或者true来要求@RequestParam配置的前端参数是否一定要传。

如果用@RequestMapping注解的参数是int基本类型,但是required=false,这时如果不传参数值会报错,因为不传值,会赋值为null给int,这个不可以 。

@PathVariable

@RequestParam 和 @PathVariable 都能够完成类似的功能:因为本质上,它们都是用户的输入,只不过输入的部分不同,一个在URL路径部分,另一个在参数部分。

使用这个注解前提是在 @RequestMapping 中必须指定模板。

References

https://www.cnblogs.com/qiankun-site/p/5774300.html

https://blog.csdn.net/zz210891470/article/details/59719251

https://www.cnblogs.com/zeroingToOne/p/8992746.html

https://www.jianshu.com/p/bf2e67abe8f7

转载于:https://www.cnblogs.com/tuhooo/p/10902602.html