4

开篇引言

前面已经写过 SSM 三大框架的一些入门文章,在 SpringMVC 部分,关于参数的绑定提的不是太多,重新整理了一下,就当做一个补充,时间匆匆,可能会有一些错误,大家可以共同交流,一起探讨!

注:下面的文章,重点还是参数绑定的使用,关于导包或者一些注解的讲解,我没有多说,之前的文章一些常用的也都还介绍过,如果有必要,我再整理一篇关于注解的总结也可以哈 ~

【万字长文】Spring MVC 层层递进轻松入门 !

https://juejin.im/post/5e781c...

(一) 基本类型、包装类型参数绑定

我们假定要请求的参数为 age ,那么我们有两种选择 :即 ① 传入基本类型 int ② 传入包装类型 Integer ,我们这一块的讲解,就用它们两个来讲解

注:我们将重心放在参数绑定上,项目名或者Controller上的@RequestMapping注解,我都没加

(1) 基本类型 int

@RequestMapping("baseType.do")
@ResponseBody
public String baseType(int age) {
    return "age:" + age;
}

http://localhost:8080/baseType.do?age=30

当我们请求时,返回结果:age:30

  • 这种情况下,首先 key 值必须传入,否则会报 500 错误,提示当前 age 不能为空
  • 其次,参数只能为 int 类型,否则报 400 参数异常错误

这里有一个问题需要提一下,大家应该知道一个注解 @RequestParam ,我们是否可通过这个注解的 required 属性,帮助我们规避这个请求参数为空的问题呢?

答案是否定的,虽然这个注解设置 required = false 后不传值后台也不会报错,但是如果其中指定了基本数据类型,例如我们代码中的 int 这个时候如果不传值是依旧会报一个 500 错误

因为其不传值就赋 null,但是 int 类型却不能为null

所以想要规避这个参数为空的问题,我们就可以选择包装类型 Integer

(2) 包装类型

@RequestMapping("packingType.do")
@ResponseBody
public String packingType(Integer age) {
    return "age:" + age;
}

http://localhost:8080/packingType.do?age=30

正常返回:age:30

http://localhost:8080/packingType.do

http://localhost:8080/packingType.do?

http://localhost:8080/packingType.do?=

参数为空不报错,均返回:age:null

  • 可以不传 key,后台接收到的数据则为 age=null
  • 所以开发中,对于参数可能为空的数据,建议使用包装类型
  • 当然,我们也可以使用 @RequestParam 注解 来设置是否请求中必须包含该参数,此注解默认就是必须传参,否则报错

(二) 对象的参数绑定

(1) 多层级对象的绑定

什么是多层级对象,先别急,先看一个最基础的例子

我们首先创建一个用户类

public class User {
    private String id;
    private String name;
    ......补充其 get set toString 方法
}

直接在参数中写上对应 User 类型就可以了

@RequestMapping("objectType.do")
@ResponseBody
public String objectType(User user) {
    return user.toString();
}

http://localhost:8080/objectType.do?id=001&name=Steven

返回结果:User{uid='001', name='Steven'}

如果这个时候,我创建一个新的类 UserDetails

public class UserDetails {
    private Integer age;
    private String address;
    ......补充其 get set toString 方法
}

在 User 类中引入这个类,这种情况又该如何绑定参数呢

public class User {
    private String id;
    private String name;
    
    private UserDetails userDetails;
    
    ......补充其 get set toString 方法
}

http://localhost:8080/objectType.do?id=1&name=Steven&userDetails.age=20&userDetails.address=BeiJing

返回结果:User{uid='1', name='Steven', userDetails=UserDetails{age=20, address='BeiJing'}}

  • 对于引入的对象成员复赋值,格式就例如:userDetails.address=xxxxx
  • 这里地址我没用中文,是因为我是直接返回的,没经过编码的处理,不然会显示 ? ?

(2) 同属性对象参数绑定

如果我们想要直接接收两个对象,有时候免不了有相同的成员,例如我们的 User 和 Student 类中均含有

Integer id 、String name 两个成员,我们试着请求一下

@RequestMapping("objectType2.do")
@ResponseBody
public String objectType2(User user, Student student) {
    return user.toString() + " " + student.toString();
}

http://localhost:8080/objectType2.do?id=8&name=Steven

返回结果:User{id='8', name='Steven'} Student{id='8', name='Steven'}

可以看到,两个对象的值都被赋上了,但是,大部分情况下,不同的对象的值一般都是不同的,为此,我们还有解决办法

@InitBinder 注解可以帮助我们分开绑定,下面的代码也就是说分别给 user、student 指定一个前缀

@InitBinder("user")
public void initUser(WebDataBinder binder) {
    binder.setFieldDefaultPrefix("user.");
}

@InitBinder("student")
public void initStudent(WebDataBinder binder) {
    binder.setFieldDefaultPrefix("stu.");
}

http://localhost:8080/objectType2.do?user.id=1&name=Steven&stu.id=002

当发起这样一个请求后,我们分别指定了 user 和 student 的 id 值,而 name 则是同样的 Steven

返回结果:User{id='1', name='Steven', userDetails=null} Student{id='2', name='Steven'}

(三) 数组类型参数绑定

@RequestMapping("arrayType.do")
@ResponseBody
public String arrayType(String[] nickname) {
    StringBuilder sb = new StringBuilder();
    for (String s : nickname) {
        sb.append(s).append(", ");
    }
    return sb.toString();
}

http://localhost:8080/arrayType.do?nickname=Jack&nickname=Steven&nickname=Tom

返回结果:Jack, Steven, Tom,

(四) 集合类型参数绑定

(1) List 类型

集合是不能直接进行参数绑定的,所以我们需要创建出一个类,然后在类中进行对 List 的参数绑定

首先创建 UserList 类,其中我为了演示,只放了 private List<User> users 补充好 get set toString 方法

控制层方法中,参数就是这个创建出来的类

@RequestMapping("listType.do")
@ResponseBody
public String listType(UserList userList) {
    return userList.toString();
}

http://localhost:8080/listType.do?users[0].id=1&users[0].name=Jack&users[1].id=2&users[1].name=Marry

我们的请求,分别将两个user信息存入了 List<User> users

特别注意:如果你的 Tomcat 版本是 7.0 左右 那么上述请求是没问题的,但是如果版本比较高,例如我本身所用的 Tomcat 8.5 ,如果执行上述请求就会报 400 错误

HTTP Status 400 – Bad Request

Message Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986

这是因为Tomcat高的版本地址中不能使用“[”和“]” ,我们可以将其换成对应的16进制,即 “[” 换成 %5B,“]” 换成 %5D

http://localhost:8080/listType.do?users%5B0%5D.id=1&users%5B0%5D.name=Jack&users%5B1%5D.id=2&users%5B1%5D.name=Marry

或者直接用 post 请求也是可以的哈

(2) Map 类型

map 类型是一样的套路,我们先创建一个 UserMap类,然后在其中声明 private Map<String,User> users 进而绑定参数

@RequestMapping("mapType.do")
@ResponseBody
public String mapType(UserMap userMap) {
    return userMap.toString();
}

http://localhost:8080/mapType.do?users['userA'].id=1&users['userA'].name=Jack&users['userB'].id=2&users['userB'].name=Tom

同样 “[]” 会遇到上面的错误,所以如果想要在地址栏请求访问,就需要替换字符,或者发起一个 post 请求

http://localhost:8080/mapType.do?users%5B%27userA%27%5D.id=1&users%5B%27userA%27%5D.name=Jack&users%5B%27userB%27%5D.id=2&users%5B%27userB%27%5D.name=Tom

返回结果:UserMap{users={userA=User{id='1', name='Jack'}, userB=User{id='2', name='Tom'}}}

(五) JSON 参数绑定

除了前面表单等提交的方式,我们还有一种ajax的提交方式,常常用来向后端传递以及接受 json 格式的数据,关于 json 字符串和对象之间的转换会用到下面的 jar包

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

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

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

这一块的演示,我们创建一个 Admin 实体类

public class Admin {
    private Integer id;
    private String name;
    ......补充其 get set toString 方法
}

(1) 实体参数绑定

当 ajax 传递的参数很多的时候,使用参数名匹配,会非常麻烦,如果请求的参数在后台中有一个匹配的实体类,我们就可以选择前台传一个 json 到后台,后台使用匹配的实体类进行接收

提交 JSON: {"id": "37","name": "张三"}

$(function () {
    $("#btn").click(function () {
        //发送ajax请求
        $.ajax({
            url:"ajaxType1.do",
            contentType:"application/json",
            data:'{"id":"37","name":"张三"}',
            dataType:"json",
            type:"post",
            success:function (data) {
            //解析响应数据
            alert(data.id);
            alert(data.name);
            }
         })
    });
});

Contoller

//用实体接参数
@RequestMapping("ajaxType1.do")
@ResponseBody
public Admin ajaxType1(@RequestBody Admin admin) {
    System.out.println(admin.toString());
    admin.setName("测试管理员");
    return admin;
}

后端参数中的 admin 就会被绑定好参数,供开发者使用

@RequestBody 注解常用来处理 content-type不是默认的application/x-www-form-urlcoded编码的内容,说常处理application/json类型

(2) Map 参数绑定

还有一种情况,那就是请求的参数仍然挺多,但是后台也没有一个合适的实体进行匹配,我们也可以考虑使用map来接收

依旧提交 JSON: {"id": "37","name": "张三"}

$(function () {
    $("#btn").click(function () {
        //发送ajax请求
        $.ajax({
            url:"ajaxType1.do",
            contentType:"application/json",
            data:'{"id":"37","name":"张三"}',
            dataType:"json",
            type:"post",
            success:function (data) {
            //解析响应数据
            alert(data.id);
            alert(data.name);
            }
         })
    });
});

但是 Controller 中我们使用 map 来进行接收,然后简单给了一个实例,将map值封装到 Admin 对象中,然后返回到前端去

@RequestMapping("ajaxType3.do")
@ResponseBody
public Admin ajaxType3(@RequestBody Map<String, String> map)  {
    Integer id = null;
    String name = null;

    if (map.containsKey("id")){
        id = Integer.parseInt(map.get("id"));
    }
    if (map.containsKey("name")){
        name = map.get("name");
    }

    Admin admin = new Admin();
    admin.setId(id);
    admin.setName(name);
    return admin;
}

(3) List 参数绑定

同样的,我们还可以使用 list 方式进行接收,它时以 json 数组的形式传递的

var listType=[];
var admin={};
admin.id=1;
admin.name='汤姆';
listType.push(admin);

var admin2={};
admin2.id=2;
admin2.name='杰克';
listType.push(admin2);

$(function () {
    $("#btn").click(function () {
        //发送ajax请求
        $.ajax({
            url:"ajaxType1.do",
            contentType:"application/json",
            data:JSON.stringify(listType),
            dataType:"json",
            type:"post",
            success:function (data) {
            //解析响应数据
            alert(data.id);
            alert(data.name);
            }
         })
    });
});

去后台看一下

@RequestMapping("ajaxType5.do")
@ResponseBody
public void ajaxType5(@RequestBody List<Admin> list)  {

    System.out.println(list);

    for (Admin admin : list){
        System.out.println(admin.getId() + " " + admin.getName());
    }

}

看一下控制台的输出:

[Admin{id='1', name='汤姆'}, Admin{id='2', name='杰克'}]
1 汤姆
2 杰克

(4) 补充

这是用来提交的表单

<form id="ajaxForm"  method="post">
    id:<input type="text" name="id">
    name:<input type="text" name="name">
</form>

这是 ajax请求,我们也常用$("#ajaxForm").serialize() 进行一个表单的序列化,然后提交,但是它只是将Form序列化拼接成了简单的字符串,并不是JSON格式,它是例如这样的:

id=111&name=Steven

所以刚才所说的json那一套就不管用了

$(function () {
    $("#btn").click(function () {
        //发送ajax请求
        $.ajax({
            url:"ajaxType1.do",
            contentType:"application/json",
            data:$("#ajaxForm").serialize(),
            dataType:"json",
            type:"post",
            success:function (data) {
            //解析响应数据
            alert(data.id);
            alert(data.name);
            }
         })
    });
});

如果想要使用序列化,同时还想要传递 json 格式到后台,也不是没办法

我们需要添加一个方法

$.fn.serializeObject = function() {
    var o = {};
    var a = this.serializeArray();
    $.each(a, function() {
        if (o[this.name]) {
             if (!o[this.name].push) {
                o[this.name] = [o[this.name]];
             }
             o[this.name].push(this.value || '');
         } else {
             o[this.name] = this.value || '';
         }
    });
    return o;
};

同时将下面 ajax 中的 data修改为

 data:JSON.stringify($("#ajaxForm").serializeObject()),

后台就能获取到 json 格式了

{"id":"111","name":"Steven"}

(六) XML 参数绑定

后台非常简单,和前面没什么区别,我们需要在实体中进行一些操作

@RequestMapping("xmlType.do")
@ResponseBody
public String xmlType(@RequestBody Student student) {
    return student.toString();
}

这种情况下,我们需要借助一个jar包 —— spring-oxm,自己可以导入一下

然后我们需要在接受的实体那里,添加 @XmlRootElement 和 @XmlElement 注解,来代表根节点和子节点

package cn.ideal.Object;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "student")
public class Student {
    private Integer id;
    private String name;

    @XmlElement(name = "id")
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @XmlElement(name = "name")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

例如我们xml为

<?xml version="1.0" encoding="UTF-8" ?>
<student>
    <id>66</id>
    <name>Steven</name>
</student>

然后发起请求,注意将Content-Type 改为 application/xml

返回的结果:Student{id='66', name='Steven'}

(七) 结尾

如果文章中有什么不足,欢迎大家留言交流,感谢朋友们的支持!

如果能帮到你的话,那就来关注我吧!如果您更喜欢微信文章的阅读方式,可以关注我的公众号

在这里的我们素不相识,却都在为了自己的梦而努力 ❤

一个坚持推送原创开发技术文章的公众号:理想二旬不止


二境志
191 声望26 粉丝

引用和评论

0 条评论