SpringMVC-快速入门(三)- Action参数类型

概述

Spring MVC中每个控制器中可以定义多个请求处理方法,我们把这种请求处理方法简称为Action,每个请求处理方法可以有多个不同的参数,以及一个多种类型的返回结果。

1、自动参数映射

1.1、基本数据类型

方法的参数可以是任意基本数据类型,如果方法参数名与 http 中请求的参数名称相同时会进行自动映射。

@RequestMapping("/action0")
public String action0(Model model, int id, String name) {
    model.addAttribute("msg", "name=" + name + ",id=" + id);
    return "hi";
}

hi.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>${msg}</title>
</head>
<body>
<h2>${msg}</h2>
</body>
</html>

请求地址:http://localhost:8080/action0?id=215&name=xyz

1.2、自定义数据类型

除了基本数据类型,也可以自定义的数据类型,如一个自定义的 POJO 对象,Spring MVC会通过反射把请求中的参数设置到对象中,转换类型。

Product :

package com.ymqx.pojo;

public class Product {
    private int id;
    private String name;
    private float price;
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public float getPrice() { return price; }
    public void setPrice(float price) { this.price = price; }
}

Controller

@RequestMapping("/action01")
public String action01(Model model, Product product) {
    model.addAttribute("msg", product);
    return "hi";
}

请求地址:http://localhost:8080/action01?id=1001&name=lili&price=1.2

因为Product类没有编写 toString 方法,故打印的是类对象的引用 。类 Product 增加toString方法。

@Override
public String toString() {
   return "编号(id):" + this.getId() + ",名称(name):" + this.getName() + ",价格(price):" + this.getPrice();
}

请求地址:http://localhost:8080/action01?id=1001&name=lili&price=1.2

model.addAttribute("msg", product); 其实调用了product.toString()方法,将生成的字符串赋值给msg。如果请求上送参数有误,比如,请求地址:http://localhost:8080/action01?id=1001&name=lili&verson=1.2

1.3、复杂数据类型

复杂数据类型指的是一个自定义类型中还包含另外一个对象类型,如用户类型中包含产品对象。

package com.ymqx.pojo;

public class User {
    private String username;
    private Product product;

    public String getUsername() {return username;}
    public void setUsername(String username) {this.username = username;}
    public Product getProduct() {return product;}
    public void setProduct(Product product) {this.product = product;}
}
// 自动参数映射复杂数据类型
@RequestMapping("/action02")
public String action02(Model model, User user) {
    model.addAttribute("msg", user.getUsername() + "," + user.getProduct().getName());
    return "hi";
}

访问地址:http://localhost:8080/action02?username =Tom&product.name=book

1.4、数组

提交时使用 param1=aaa&param1=bbb&param1=3,接收时使用String param1[] 这种参数既可以获取数组的值。

//3.自动参数映射数组数据类型
@RequestMapping("/act03")
public String act03(Model model,Integer[] id){
    model.addAttribute("msg",Arrays.toString(id));
    return "hi";
}

访问地址:http://localhost:8080/act03?id=123&id=456&id=789

1.5、List、Map集合类型

不能直接在action的参数中指定List<T>和Map<E,U>类型,需要定义一个类型包装List、Map集合。而且访问的时候,需要在URL指明内容,这样会导致通过URL反推后台类信息以及代码逻辑,不建议使用URL上送请求具体参数。建议表单方式提交。


2、@RequestParam参数绑定

虽然自动参数映射很方便,但有些细节是不能处理的,如参数是否为必须参数名称没有办法指定参数的默认值就没有有办法做到了。如果使用@RequestParam可以实现请求参数绑定,Spring MVC会自动查找请求中的参数类型并将与参数进行绑定。

2.1、基本数据类型绑定与注解属性

@Controller
@RequestMapping("/foo")
public class FooController {
    @RequestMapping("/action1")
    public String action1(Model model, @RequestParam(required = false, defaultValue = "99") int id) {
        model.addAttribute("msg", id);
        return "hi";
    }
}

@RequestParam共有4个注解属性:

  • required属性表示是否为必须,默认值为true,如果请求中没有指定的参数会报异常;
  • defaultValue用于设置参数的默认值,如果不指定值则使用默认值,只能是String类型的
  • namevalue互为别名关系用于指定参数名称。

2.2、List与数组绑定基本数据类型

在上一节中我们使用自动参数映射是不能直接完成 List 与数组绑定的,结合@RequestParam可以轻松实现。

// List集合与数组类型
@RequestMapping("/action05")
public String action05(Model model, @RequestParam("u") List<String> users) {
    model.addAttribute("msg", Arrays.deepToString(users.toArray()));
    return "hi";
}

@RequestParam("u")是必须的,页面服务器在收集数据时使用 u ,赋值给 users。


3、@RequestBody

@RequestBody注解将HTTP请求正文插入方法中,使用适合的 HttpMessageConverter 将请求体写入某个对象

该注解用于读取 Request 请求的 body 数据,使用系统默认配置的 HttpMessageConverter 进行解析,再把 HttpMessageConverter 返回的对象数据绑定到 controller中方法的参数上。

使用要求:

  • GETPOST方式提时,根据request header Content-Type的值来判断:
    • application/x-www-form-urlencoded:@RequestParam@ModelAttribute@RequestBody(一般不用)均能处理;
    • multipart/form-data:不能处理(即使使用 @RequestBody 不能处理这种格式的数据);
    • 其他格式(application/json, application/xml等):这些格式的数据,必须使用@RequestBody来处理
  • PUT方式提交时,根据request header Content-Type的值来判断:
    • application/x-www-form-urlencoded:必须使用@RequestBody来处理
    • multipart/form-data:不能处理;
    • 其他格式:必须使用@RequestBody来处理

注1:request 的 body 部分的数据编码格式由 header 部分的Content-Type指定;
注2:常见的Content-Type:

  • application/x-www-form-urlencoded:是浏览器默认的编码格式。参数的格式为key=value&key=value,将键值对的参数用&连接起来,如果有空格,将空格转换为+加号;有特殊符号,将特殊符号转换为ASCII HEX值。对于Get请求,是将参数转换?key=value&key=value格式,连接到url后,比如:http://localhost:8080/action11?id=215&name=xyz。
  • multipart/form-data:常用于文件等二进制
  • application/json:参数的格式为 Json 格式的字符串。
  • application/xml:参数的格式为 xml 格式的字符串。
@Controller
@RequestMapping("/param")
public class ParamController {
    @RequestMapping("/action1")
    public String action1(Model model, @RequestBody Product product){
        //向模型中添加属性msg与值,将与页面模板渲染后输出
        System.out.println(product);
        model.addAttribute("msg", product);
        return "hi";
    }
}

Postman发送报文:

报错415:

The server refused this request because the request entity is in a format not supported by the requested resource for the requested method.

查找资料,发现出现该问题的原因:

  1. 请求头 contentType参数一定要是:“application/json”,否则有可能会报该错误;
  2. 项目路径要有Jackson的相关类包:jackson-databind-2.7.5.jar、jackson-annotations-2.7.0.jar及jackson-core-2.7.5.jar
  3. spring的配置里面要有<mvc:annotation-driven />,即启用了注解识别;
  4. spring的配置里面一定要加上Json转换器。

修改pom.xml,添加jackson依赖:

<!--jackson-->
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.7.4</version>
</dependency>

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.7.4</version>
</dependency>

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-annotations</artifactId>
  <version>2.7.4</version>
</dependency>

Postman发送报文:

如果请求上送参数有误,比如上送 version ,不上送price:

3.1 @RequestBody与@RequestParam区别

一个请求中,@RequestBody@RequestParam()可以同时使用,@RequestBody最多只能有一个,而@RequestParam()可以有多个。

当同时使用 @RequestParam() 和 @RequestBody 时,@RequestBody 接收的是请求体里面的数据;而@RequestParam()接收的是key-value里面的参数,@RequestParam() 会被切面处理从而可以用普通元素、数组、集合、对象等接收。

如果参数时放在请求体中,application/json 传入后台的话,那么后台要用 @RequestBody 才能接收到;如果不是放在请求体中的话,那么后台接收前台传过来的参数时,要用 @RequestParam 来接收,或则形参前什么也不写也能接收。

如果参数前写了 @RequestParam(xxx),那么前端必须有对应的xxx名字才行(不管其是否有值,当然可以通过设置该注解的required属性来调节是否必须传),如果没有xxx名的话,那么请求会出错,报400。

@RequestMapping("/action3")
public String action1(@RequestBody Product product, @RequestParam String name, @RequestParam String price){
    System.out.println(product);
    System.out.println(name + ":" + price);
    return "hi";
}

Postman发送报文:

后台打印:

编号(id)1001,名称(name):lucy,价格(price)1.2
11:22

3.2 @RequestBody绑定List

public String action4(@RequestBody List<Product> products, @RequestParam String name, @RequestParam String price){
    System.out.println(products);
    System.out.println(name + ":" + price);
    return "hi";
}

Postman发送报文:

后台打印:

[编号(id)1001,名称(name):lucy,价格(price)1.2, 编号(id)1002,名称(name):lili,价格(price)1.5]
11:22

注:post的报文体一定要是Json数组,用"[]"包括起来,不然解析报错。

3.2 @RequestBody绑定复杂类型

新增Factory类,包含List类型属性:

public class Factory {
    private String factoryName;
    private List<Product> products;

    public String getFactoryName() {return factoryName;}
    public void setFactoryName(String factoryName) {this.factoryName = factoryName;}
    public List<Product> getProducts() {return products;}
    public void setProducts(List<Product> products) {this.products = products;}

    @Override
    public String toString() {
        return "Factory{" +
                "factoryName='" + factoryName + '\'' +
                ", products=" + products +
                '}';
    }
}
@RequestMapping("/action5")
public String action5(@RequestBody Factory factory){
    System.out.println(factory);
    return "hi";
}

Postman发送报文:

后台打印:

Factory{factoryName='DaZhong', products=[编号(id)1001,名称(name):lucy,价格(price)1.2, 编号(id)1002,名称(name):lili,价格(price)1.5]}

不管类型多复杂,只要JSON字符串和@RequestBody注解的类对应,就能转换成功。


4、@ModelAttribute

@ModelAttribute最主要的作用是将数据添加到模型对象中,用于视图页面展示时使用。但是根据 @ModelAttribute 注释的位置不同,和其他注解组合使用,致使含义有所不同。

4.1 应用在方法上

@ModelAttribute注解的方法会在Controller每个方法执行之前都执行,因此对于一个Controller中包含多个URL的时候,要谨慎使用。

1. 使用@ModelAttribute注解无返回值的方法

@RestController
public class HelloController {
    
    @ModelAttribute
    public void populateModel(@RequestParam String param, Model model) {
        model.addAttribute("attributeName", param);
    }

    @RequestMapping(value = "/helloWorld")
    public String helloWorld(Model model) {
        System.out.println(model);
        System.out.println(model.asMap().get("attributeName"));
        return "helloWorld";
    }
}

Postman请求地址:http://192.168.1.166:8080/helloWorld?param=aaa

在这里插入图片描述

请求http://192.168.1.166:8080/helloWorld?param=aaa后,会先执行populateModel方法,然后接着执行helloWorld方法,参数 param 的值被放到 Model 中后,接着被带到helloWorld方法中。

2. 使用@ModelAttribute注解带有返回值的方法

@RestController
public class HelloController {
    
    @ModelAttribute
    public UserInfo populateModel() {
        UserInfo info = new UserInfo();
        info.setAge("20");
        info.setGender("M");

        return info;
    }

    @RequestMapping(value = "/helloWorld")
    public String helloWorld(Model model) {
        System.out.println(model);
        //在 Model 中的 key 为返回类型首字母小写,value为返回的值。
        System.out.println(model.asMap().get("userInfo"));
        return "helloWorld";
    }
}

Postman请求地址:http://192.168.1.166:8080/helloWorld

在这里插入图片描述

返回值对象会被默认放到隐含的 Mode l中,在 Model 中的 key 为返回类型首字母小写,value 为返回的值。

3. @ModelAttribute(value=“”)注释返回具体类的方法

当然,也可以通过@ModelAttribute(value = "info")指定返回的 key

@RestController
public class HelloController {
    
    @ModelAttribute(value = "info")
    public UserInfo populateModel() {
        ...
    }

    @RequestMapping(value = "/helloWorld")
    public String helloWorld(Model model) {
        System.out.println(model);
        System.out.println(model.asMap().get("info"));
        return "helloWorld";
    }
}

在这里插入图片描述

相当于model.addAttribute("info", info);

4. @ModelAttribute和@RequestMapping同时注释一个方法

@RestController
public class HelloController {
    
    @RequestMapping(value = "/helloWorld.do")   
    @ModelAttribute("attributeName")   
    public String helloWorld() {   
         return "hi";   
    }   
}

这时这个方法的返回值并不是表示一个视图名称,而是model属性的值,视图名称由 RequestToViewNameTranslator 根据请求 "/helloWorld.do"转换为逻辑视图 helloWorld。Model 属性名称由@ModelAttribute(value=””)指定,相当于在request中封装了key=attributeName,value=hi。

4.2 应用在方法的参数上

使用@ModelAttribute注解的参数,意思是从前面的 Model 中提取对应名称的属性

@Controller   
public class HelloWorldController {   
   @ModelAttribute("info")
    public UserInfo populateModel() {
        UserInfo info = new UserInfo();
        info.setAge("20");
        info.setGender("M");

        return info;
    }

    @RequestMapping(value = "/helloWorld")
    public String helloWorld(@ModelAttribute("info") UserInfo info) {
        System.out.println(info);
        return "helloWorld";
    }
} 

Postman请求地址:http://192.168.1.166:8080/helloWorld

在这里插入图片描述


参考文章:
@ModelAttribute注解的使用


5、@RequestHeader

通过@RequestHeader可以将请求头中的所有参数放到map中。

@Controller   
public class HelloWorldController {   
   @RequestMapping(value = "/helloHead")
    public String helloHead(@RequestHeader("User-Agent") String agent,
                            @RequestHeader Map<String,String> headers) {

        System.out.println("agent=" + agent);
        for (Map.Entry<String, String> entry : headers.entrySet())  {
            System.out.println(entry.getKey() + "=" + entry.getValue());
        }
        return "helloHead";
    }
}  

Postman请求地址:http://192.168.1.166:8080/helloWorld

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会叫的狼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值