[华为云在线课程][SpringMVC入门][六][数据回显和JSON处理][学习笔记]
1.数据回显
数据回显就是当用户数据提交失败时,自动填充好已经输入的数据。一般来说,如果用Ajax来做数据提交,基本没有数据回显的需求,但如果是通过表单做数据提交,那么数据回显就非常有必要了。
1.1.手动数据回显和自动数据回显
1.1.1.手动数据回显(简单数据类型)
手动数据回显也叫简单数据类型,因为框架没有提供任何支持,通过自己手动进行配置。以一个添加学生的数据并进行校验的例子来进行演示,加入提交的学生数据不符合要求,重新返回添加页面,并且预设之前已经填好的数据:
<%--
Created by IntelliJ IDEA.
User: John
Date: 2022/7/19/0019
Time: 15:33
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>一个添加学生的页面</title>
</head>
<body>
<form action="/addstudent" method="post">
<table>
<tr>
<td>学生编号:</td>
<td><input type="text" name="id" value="${id}"></td>
</tr>
<tr>
<td>学生姓名:</td>
<td><input type="text" name="name" value="${name}"></td>
</tr>
<tr>
<td>学生邮箱:</td>
<td><input type="text" name="email" value="${email}"></td>
</tr>
<tr>
<td>学生年龄:</td>
<td><input type="text" name="age" value="${age}"></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="提交">
</td>
</tr>
</table>
</form>
</body>
</html>
在接收数据时,使用简单数据类型去接收:
package org.example.controller;
import org.example.model.Student1;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
public class Student1Controller {
@RequestMapping("/addstu1")
public String addstudent() {
return "addStudent1";
}
@RequestMapping("/addStudent")
public String addStudent2(Integer id, String name, String email, Integer age, Model model) {
model.addAttribute("id", id);
model.addAttribute("name", name);
model.addAttribute("email", email);
model.addAttribute("age", age);
return "addStudent1";
}
}
接下运行项目看看效果:
这种方式,相当手动把数据进行了回显。访问页面服务端会再次定位到该页面,而且数据已经填好了。
1.1.2.自动数据回显(实体类)
可以看到手动进行数据回显相当麻烦,需要开发者在服务端一个个手动配置。如果适用对象就没有这么麻烦,因为SpringMVC在页面跳转时,会自动将对象填充进返回的数据中。
<%--
Created by IntelliJ IDEA.
User: John
Date: 2022/7/19/0019
Time: 15:33
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>一个添加学生的页面</title>
</head>
<body>
<form action="/addstudent" method="post">
<table>
<tr>
<td>学生编号:</td>
<td><input type="text" name="id" value="${student1.id}"></td>
</tr>
<tr>
<td>学生姓名:</td>
<td><input type="text" name="name" value="${student1.name}"></td>
</tr>
<tr>
<td>学生邮箱:</td>
<td><input type="text" name="email" value="${student1.email}"></td>
</tr>
<tr>
<td>学生年龄:</td>
<td><input type="text" name="age" value="${student1.age}"></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="提交">
</td>
</tr>
</table>
</form>
</body>
</html>
在填好的数据中,多了一个student1.前缀。这就是服务端接收数据的变量名,服务端的变量名和这里的student1保持一致。
package org.example.controller;
import org.example.model.Student1;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
public class Student1Controller {
@RequestMapping("/addstu1")
public String addstudent() {
return "addStudent1";
}
@RequestMapping("/addstudent")
//@ResponseBody
public String addStudent(@Validated(ValidationGroup2.class) Student1 student1, BindingResult result) {
if (result != null) {
//校验未通过,获取所有的异常信息并展示出来
List<ObjectError> allErrors = result.getAllErrors();
for (ObjectError allError : allErrors) {
System.out.println(allError.getObjectName() + ":" + allError.getDefaultMessage());
}
return "addStudent1";
}
return "hello";
}
}
运行项目后填入数据:
注意,这里服务的只返回页面就行,student1这个变量会被自动填充到返回的Model中。变量名就是填充时的key。如果想自定义key,可以在参数中写出来Model,手动加入Student1对象,就像手动回显那样操作。
1.2.@ModelAttribute注解用法
另一个定义回显变量别名的方式就是使用@ModelAttribute注解。这个注解有两方面功能:
- 数据回显时,给变量定义别名
- 定于全局数据
1.2.1.定义别名
数据回显时,给变量定义别名,非常容易,直接加注解即可:
package org.example.controller;
import org.example.model.Student1;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
public class Student1Controller {
@RequestMapping("/addstu1")
public String addstudent() {
return "addStudent1";
}
@RequestMapping("/addstudent")
//@ResponseBody
public String addStudent(@ModelAttribute("s") @Validated(ValidationGroup2.class) Student1 student1, BindingResult result) {
if (result != null) {
//校验未通过,获取所有的异常信息并展示出来
List<ObjectError> allErrors = result.getAllErrors();
for (ObjectError allError : allErrors) {
System.out.println(allError.getObjectName() + ":" + allError.getDefaultMessage());
}
return "addStudent1";
}
return "hello";
}
}
完成定义时,在前端再次访问回显的变量时,变量名称就变成了s:
<%--
Created by IntelliJ IDEA.
User: John
Date: 2022/7/19/0019
Time: 15:33
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>一个添加学生的页面</title>
</head>
<body>
<form action="/addstudent" method="post">
<table>
<tr>
<td>学生编号:</td>
<td><input type="text" name="id" value="${s.id}"></td>
</tr>
<tr>
<td>学生姓名:</td>
<td><input type="text" name="name" value="${s.name}"></td>
</tr>
<tr>
<td>学生邮箱:</td>
<td><input type="text" name="email" value="${s.email}"></td>
</tr>
<tr>
<td>学生年龄:</td>
<td><input type="text" name="age" value="${s.age}"></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="提交">
</td>
</tr>
</table>
</form>
</body>
</html>
1.2.2.定义全局数据
假设有一个Controller有很多方法,每个方法都返回数据给前端,但每个方法返回给前端的数据又不太一样,可以把其中的公共部分抽取出来单独封装成一个方法,用@ModelAttribute注解来标记。
比如在一个Controller中,添加如下代码:
@ModelAttribute("info")
public Map<String, Object> info() {
Map<String, Object> map = new HashMap<>();
map.put("username", "hello");
map.put("address", "www.hello.org");
return map;
}
当用户访问当前Controller中的任意一个方法,返回数据时,都会将添加了@ModelAttribute注解的方法返回值,一起返回给前端。@ModelAttribute注解中的info表示返回数据的key。
<%--
Created by IntelliJ IDEA.
User: John
Date: 2022/7/19/0019
Time: 15:33
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>一个添加学生的页面</title>
</head>
<body>
<form action="/addstudent" method="post">
<table>
<tr>
<td>学生编号:</td>
<td><input type="text" name="id" value="${s.id}"></td>
</tr>
<tr>
<td>学生姓名:</td>
<td><input type="text" name="name" value="${s.name}"></td>
</tr>
<tr>
<td>学生邮箱:</td>
<td><input type="text" name="email" value="${s.email}"></td>
</tr>
<tr>
<td>学生年龄:</td>
<td><input type="text" name="age" value="${s.age}"></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="提交">
</td>
</tr>
<tr>
<td>${info.username} - ${info.address}</td>
</tr>
</table>
</form>
</body>
</html>
package org.example.controller;
import org.example.model.Student1;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Controller
public class Student1Controller {
@RequestMapping("/addstu1")
public String addstudent() {
return "addStudent1";
}
@ModelAttribute("info")
public Map<String, Object> info() {
Map<String, Object> map = new HashMap<>();
map.put("username", "hello");
map.put("address", "www.hello.org");
return map;
}
@RequestMapping("/addstudent")
//@ResponseBody
public String addStudent(@ModelAttribute("s") @Validated(ValidationGroup2.class) Student1 student1, BindingResult result) {
if (result != null) {
//校验未通过,获取所有的异常信息并展示出来
List<ObjectError> allErrors = result.getAllErrors();
for (ObjectError allError : allErrors) {
System.out.println(allError.getObjectName() + ":" + allError.getDefaultMessage());
}
return "addStudent1";
}
return "hello";
}
}
结果如下:
2.JSON处理
目前主流的JSON处理工具主要有三种:Jackson、gson、fastjson。
在SpringMVC中,对Jackson和gson都提供了相应的支持,就是如果使用这两个作为JSON转换器,只需要添加对应的依赖就可以了,返回的对象和返回的集合、Map等都会自动转为JSON,但如果使用fastjson,除了添加相应的依赖之外,还需自己手动配置HttpMessageConverter转换器。其实前两个也使用HttpMessageConverter转换器,SpringMVC自动提供,SpringMVC没有给fastjson提供相应的转换器。
2.1.Jackson生成JSON数据
Jackson是一个使用比较多,时间长的JSON处理工具,在SpringMVC中使用Jackson,只需要添加Jackson依赖即可:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>
依赖添加成功后,凡在接口中直接返回的对象,集合等等,都会自动转为JSON。如下:
package org.example.model;
public class Book {
private Integer id;
private String name;
private String author;
public Book() {
}
public Book(Integer id, String name, String author) {
this.id = id;
this.name = name;
this.author = author;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", name='" + name + '\'' +
", author='" + author + '\'' +
'}';
}
}
package org.example.controller;
import org.example.model.Book;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class BookController {
@RequestMapping("/book")
@ResponseBody
public Book getBookById() {
Book book = new Book();
book.setId(1);
book.setName("你好啊");
book.setAuthor("世界");
return book;
}
}
这里返回一个对象,但在前端收到的是一个JSON字符串,这个对象会通过HttpMessageConverter自动转为JSON字符串。
以下方式可以返回一个JSON数组:
@RequestMapping("/books")
@ResponseBody
public List<Book> getAllBooks() {
List<Book> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Book book = new Book();
book.setId(i);
book.setName("你好啊" + i);
book.setAuthor("世界" + i);
list.add(book);
}
return list;
}
结果如下:
添加了Jackson,就能够自动返回JSON,这个依赖于一个名为HttpMessageConverter的类,这本身就是一个接口,它的作用是Http消息转换器,提供了两个功能:
- 将返回的对象转为JSON
- 将前端提交上来的JSON转为对象
但是,HttpMessageConverter只是一个接口,由各个JSON工具提供相应的实现,Jackson的实现名字叫MappingJackson2HttpMessageConverter,初始化由SpringMVC来完成。
下面举一个例子来说明自定义配置MappingJackson2HttpMessageConverter。先给Book加上出版日期。
package org.example.model;
import java.util.Date;
public class Book {
private Integer id;
private String name;
private String author;
private Date publish;
public Book() {
}
public Book(Integer id, String name, String author, Date publish) {
this.id = id;
this.name = name;
this.author = author;
this.publish = publish;
}
public Date getPublish() {
return publish;
}
public void setPublish(Date publish) {
this.publish = publish;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", name='" + name + '\'' +
", author='" + author + '\'' +
", publish=" + publish +
'}';
}
}
在Controller中添加日期属性
@RequestMapping("/book")
@ResponseBody
public Book getBookById() {
Book book = new Book();
book.setId(1);
book.setName("你好啊");
book.setAuthor("世界");
book.setPublish(new Date());
return book;
}
访问/book接口,返回的JSON格式为:
{
"id": 1,
"name": "你好啊",
"author": "世界",
"publish": 1658306264645
}
可以发现返回的是一个时间戳,并不是我们常用的日期格式,可以通过添加注解实现
public class Book {
private Integer id;
private String name;
private String author;
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "Asia/Shanghai")
private Date publish;
}
{
"id": 1,
"name": "你好啊",
"author": "世界",
"publish": "2022-07-20"
}
注意一定要设置时区,但是这样设置比较繁琐,要每个都手动添加配置,这时候就可以在SpringMVC中配置一个全局配置
<mvc:annotation-driven>
<mvc:message-converters>
<ref bean="httpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" id="httpMessageConverter">
<property name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="dateFormat">
<bean class="java.text.SimpleDateFormat">
<constructor-arg name="pattern" value="yyyy-MM-dd HH:mm:ss"/>
</bean>
</property>
<property name="timeZone" value="Asia/Shanghai"/>
</bean>
</property>
</bean>
{
"id": 1,
"name": "你好啊",
"author": "世界",
"publish": "2022-07-20 16:57:08"
}
2.2.Gson生成JSON数据
gson是Google推出的一个JSON解析器,主要用在安卓开发较多,SpringMVC针对gson提供了自动化配置。
添加gson依赖
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.9.0</version>
</dependency>
如果项目中同时存在Jackson和gson的话,默认使用的是Jackson,因为在org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter类的构造方法中,加载顺序是Jackson在前gson在后。
加载完成之后,就可以直接返回JSON字符串了。自定义配置gson如下:
<mvc:annotation-driven>
<mvc:message-converters>
<ref bean="httpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
<bean class="org.springframework.http.converter.json.GsonHttpMessageConverter" id="httpMessageConverter">
<property name="gson">
<bean class="com.google.gson.Gson" factory-bean="gsonBuilder" factory-method="create"/>
</property>
</bean>
<bean class="com.google.gson.GsonBuilder" id="gsonBuilder">
<property name="dateFormat" value="yyyy-MM-dd"/>
</bean>
2.3.FastJson生成JSON数据
SpringMVC没有针对fastjson提供相应的HttpMessageConverter,所以在使用时,要手动配置HttpMessageConverter。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.60</version>
</dependency>
然后在SpringMVC配置文件中配置HttpMessageConverter,fastjson默认中文会有乱码,要添加编码格式
<mvc:annotation-driven>
<mvc:message-converters>
<ref bean="httpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter" id="httpMessageConverter">
<property name="fastJsonConfig">
<bean class="com.alibaba.fastjson.support.config.FastJsonConfig">
<property name="dateFormat" value="yyyy-MM-dd"/>
</bean>
</property>
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=utf-8</value>
</list>
</property>
</bean>
2.4.接收JSON数据
浏览器传来的参数,可以是key/value形式的,也可以是一个JSON字符串。
在jsp/servlet中,通过getParameter方法接收key/value形式的参数。如果客户端传递JSON数据,可以这样解析:
@RequestMapping("/addbook2")
@ResponseBody
public void addBook2(HttpServletRequest request) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
Book book = objectMapper.readValue(request.getInputStream(), Book.class);
System.out.println(book);
}
这种解析方式有点麻烦,在SpringMVC中,我们可以通过一个注解来将一个JSON字符串转为一个对象:
@RequestMapping("/addbook3")
@ResponseBody
public void addBook3(@RequestBody Book book) {
System.out.println(book);
}
这样就可以直接收到前端传来的JSON字符串了。这也是HttpMessageConverter提供的第二个功能。
- 点赞
- 收藏
- 关注作者
评论(0)