文章目录
概述
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
![](https://i-blog.csdnimg.cn/blog_migrate/80323713a60f3897bc11eaf49c116842.png)
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
![](https://i-blog.csdnimg.cn/blog_migrate/379c97814335516e7470a60baab1bfef.png)
因为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
![](https://i-blog.csdnimg.cn/blog_migrate/c8e80c628987528dd6b07133375ff48a.png)
model.addAttribute("msg", product);
其实调用了product.toString()方法,将生成的字符串赋值给msg。如果请求上送参数有误,比如,请求地址:http://localhost:8080/action01?id=1001&name=lili&verson=1.2
![](https://i-blog.csdnimg.cn/blog_migrate/86adbf3424545a391e5e3c140782fb5a.png)
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¶m1=bbb¶m1=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类型的。name
与value
互为别名关系用于指定参数名称。
![](https://i-blog.csdnimg.cn/blog_migrate/41fd5849c98b7108d23b8a0a79fca467.png)
![](https://i-blog.csdnimg.cn/blog_migrate/8f5f6a7d30c7e40c95ffc73f85ef2d53.png)
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";
}
![](https://i-blog.csdnimg.cn/blog_migrate/6971853ecd023a93ca8044fd0548a302.png)
@RequestParam("u")
是必须的,页面服务器在收集数据时使用 u ,赋值给 users。
3、@RequestBody
@RequestBody
注解将HTTP请求正文插入方法中,使用适合的 HttpMessageConverter 将请求体写入某个对象。
该注解用于读取 Request 请求的 body 数据,使用系统默认配置的 HttpMessageConverter 进行解析,再把 HttpMessageConverter 返回的对象数据绑定到 controller中方法的参数上。
使用要求:
- GET、POST方式提时,根据request header
Content-Type
的值来判断:- application/x-www-form-urlencoded:
@RequestParam
、@ModelAttribute
、@RequestBody
(一般不用)均能处理; - multipart/form-data:不能处理(即使使用 @RequestBody 不能处理这种格式的数据);
- 其他格式(application/json, application/xml等):这些格式的数据,必须使用@RequestBody来处理;
- application/x-www-form-urlencoded:
- 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发送报文:
![](https://i-blog.csdnimg.cn/blog_migrate/65b731d1de7c684f6bf347cc5f16892a.png)
报错415:
The server refused this request because the request entity is in a format not supported by the requested resource for the requested method.
查找资料,发现出现该问题的原因:
- 请求头 contentType参数一定要是:“application/json”,否则有可能会报该错误;
- 项目路径要有Jackson的相关类包:jackson-databind-2.7.5.jar、jackson-annotations-2.7.0.jar及jackson-core-2.7.5.jar
- spring的配置里面要有<mvc:annotation-driven />,即启用了注解识别;
- 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