[华为云在线课程][SpringMVC入门][六][数据回显和JSON处理][学习笔记]

举报
John2021 发表于 2022/07/20 20:28:52 2022/07/20
【摘要】 1.数据回显数据回显就是当用户数据提交失败时,自动填充好已经输入的数据。一般来说,如果用Ajax来做数据提交,基本没有数据回显的需求,但如果是通过表单做数据提交,那么数据回显就非常有必要了。 1.1.手动数据回显和自动数据回显 1.1.1.手动数据回显(简单数据类型)手动数据回显也叫简单数据类型,因为框架没有提供任何支持,通过自己手动进行配置。以一个添加学生的数据并进行校验的例子来进行演示...

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.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消息转换器,提供了两个功能:

  1. 将返回的对象转为JSON
  2. 将前端提交上来的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提供的第二个功能。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。