《Spring in action 4》(六)渲染Web视图

渲染Web视图

莫言君行早,更有早行人

理解视图解析

在前面我们已经接触了一个Springmvc中的视图解析器,InternalResourceViewResolver。下图是其继承结构:

InternalResourceViewResolver:这个视图解析器应该不陌生,在SSM项目中整合JSP的时候,一般都会使用到这个视图解析器。下面这段代码应该也似曾相识,在SpringMVC.xml文件中都会配置到,
这里只是以代码的方式进行配置,最熟悉的莫过于prefix 和 suffix ,分别是指定视图的前缀和后缀,即当视图解析器根据逻辑视图名映射视图的时候,会在Controller的返回值分别在其前后拼接配置好的前后缀,就形成了物理视图,比如Controller中返回的是home.其物理地址就是/WEB-INF/views/home.jsp 。

@Bean
public ViewResolver viewResolver(){
  InternalResourceViewResolver resolver =
    new InternalResourceViewResolver();
  resolver.setPrefix("/WEB-INF/views/");
  resolver.setSuffix(".jsp");
  resolver.setExposeContextBeansAsAttributes(true);
  /*将视图解析为JstlView
        * 查看源码可以看出,默认解析为InternalResourceView
        * */
  resolver.setViewClass(JstlView.class);
  return resolver;
}

我们看一下视图解析器返回的ViewResolver的代码:

public interface ViewResolver {
	@Nullable
	View resolveViewName(String viewName, Locale locale) throws Exception;
}

当给resolveViewName方法传入一个视图名和Locale对象时,会返回一个View实例,View是另外一个接口:

public interface View {

	String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() +
      										".responseStatus";

	String PATH_VARIABLES = View.class.getName() + ".pathVariables";

	String SELECTED_CONTENT_TYPE = View.class.getName() +
      										".selectedContentType";

	@Nullable
	default String getContentType() {
		return null;
	}
	void render(@Nullable Map<String, ?> model, HttpServletRequest
                request, HttpServletResponse response)throws Exception;
}

View接口的任务就是接受模型以及Servlet的request和response对象,并将输出结果渲染到response中。

https://docs.spring.io/spring/docs/5.1.9.RELEASE/spring-framework-reference/web.html#mvc-viewresolver

spring官网介绍了几种视图解析器。InternalResourceViewResolver一般会用于JSP。TilesViewResolver用于Apache Tiles视图,而FreeMarkerViewResolver和VelocityViewResolver分别用于FreeMarker和Velocity模板视图。

解析JSTL视图

使用InternalResourceViewResolver进行视图解析,默认会将视图解析为InternalResourceView实例,但是如果我们在JSP文件中使用JSTL标签去处理了例如国际化的这个格式化和信息的化,我们希望将视图解析为JSTLView。JSTL的格式化标签需要一个Locale对象,以便于恰当的格式化地域相关的值,如日期和货币。

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
....
<body>
       <!--    <fmt:setLocale value="${param.setLocale}"/>  区域语言的值从传过来的参数中得到 --> 
       <fmt:setLocale value="en_US"/>           <!--指定区域语言-->
       <fmt:bundle basename="globalMessages">   <!-- 指定使用basename为globalMessages的资源文件,也即资源文件第一个单词为globalMessages-->
           <center>
           <table>
               <tr>
                   <td><fmt:message key="email"/></td>
                   <td><input type="text" name="email"></td>   
               </tr> 
           </table>
           </center>   
       </fmt:bundle>
  </body>

如果我们想要将视图渲染为JSTL视图,只需要在下面添加配置:setViewClass即可。

@Bean
public ViewResolver viewResolver(){
  InternalResourceViewResolver resolver =
    new InternalResourceViewResolver();
  resolver.setPrefix("/WEB-INF/views/");
  resolver.setSuffix(".jsp");
  resolver.setExposeContextBeansAsAttributes(true);
  /*将视图解析为JstlView
        * 查看源码可以看出,默认解析为InternalResourceView
        * */
  resolver.setViewClass(JstlView.class);
  return resolver;
}

Spring的JSP库

​ Spring提供了两个JSP标签库,用来帮助定义SpringMVC Web的视图。其中一个标签库会用来渲染HTML表单标签,这些标签可以绑定model中的某个属性。另外一个标签库包含了一些工具类标签,我们随时都可以非常便利地使用它们。

JSP标签

借助Spring表单绑定标签库中所包含的标签,我们能够将模型对象绑定到渲染后的HTML表单中。

JSP标签 描述
<sf:checkbox> 渲染成一个HTML<input>标签,其中type属性设置为checkbox
<sf:checkboxs> 渲染成多个HTML<input>标签,其中type属性设置为checkbox
<sf:errors> 在一个HTML<span>中输入输入域的错误
<sf:form> 渲染成一个HTML <form>标签,并为其内部标签暴露绑定路径,用于数据绑定
<sf:hidden> 渲染成一个HTML<input>标签,其中type属性设置为hidden
<sf:input> 渲染成一个HTML<input>标签,其中type属性设置为text
<sf:label> 渲染成一个HTML<label>标签
<sf:option> 渲染成一个HTML<option>标签,其selected属性根据所绑定的执行设置
<sf:options> 按照绑定的集合、数组或Map,渲染成一个HTML<option>标签列表
<sf:password> 渲染成一个HTML<input>标签,其中type属性设置为password
<sf:radiobutton> 渲染成一个HTML<input>标签,其中type属性设置为radio
<sf:radiobuttons> 渲染成多个HTML<input>标签,其中type属性设置为radio
<sf:select> 渲染为一个HTML<select>标签
<sf:textarea> 渲染为一个HTML<textarea>标签

将标签绑定到模型上

如果在jsp中引入spring标签呢?

<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>

sf:form会渲染成一个HTML

标签,但它也会通过 modelAttribute 属性构建针对某个模型对象的上下文信息。在其他的表单绑定标签,会引用这个模型对象的属性。

最初的注册表单:

<form method="post" >
  	用户名:<input name="username" type="text" ><br>
  	密码:<input name="password" type="password" ><br>
  	年龄:<input name="age" type="number" ><br>
  	<input type="submit" value="提交"><br>
</form>

使用Spring的标签库:

<sf:form method="post" modelAttribute="user" >
    账号:<sf:input path="username"/>
    <sf:errors path="username" cssClass="error" /><br>
    密码:<sf:password path="password"/>
    <sf:errors path="password" cssClass="error" /><br>
    年龄:<sf:input path="age"/>
    <sf:errors path="age" cssClass="error"  /><br>
    <input type="submit" value="提交" ><br>
</sf:form>

这里需要自定义一个error的css样式

span.error{
 	color:red;
}

如果不填数据提交,则会出现响应的错误提示。

为什么会出现这种提示信息呢?当然不仅仅是因为将前面的标签换成了Spring的标签。还有就是在Controller中绑定了对象。

@GetMapping("/register")
public String toRegister(Model model){
    model.addAttribute("user",new User());
    return "register";
}

/*处理表单数据,并验证*/
@PostMapping("/register")
public String register(@Valid User user, Errors errors){
    if (errors.hasErrors()){
      return "register";//注册失败,重新返回到注册页面
    }
    userService.saveUser(user);
    return "redirect:/registerSuccess";
}

User:

public class User implements Serializable {

    @NotNull
    @Size(min = 4,max = 20, message = "{username.size}")
    private String username;

    @NotNull
    @Size(min = 6,max = 32,message = "密码需要在{min} 到 {max} 位之间")
    private String password;

    @NotNull(message = "年龄不能为空")
    @Min(value = 1,message = "年龄要大于等于1")
    @Max(value = 150,message = "年龄需要小于150")
    private Integer age;
	
  	//all or noArgsConstructor
  	//getter and setter
  	//toString
}

可以看出,username的@size使用{}大括号来读取配置文件信息,解决了硬编码问题。由于这是输入数据验证,所以默认读取的是classpath下的,ValidationMessages.properties文件。如下:

username.size = 账号需要在{min} 到 {max} 位之间

而{min}和{max}可以读取到@size中的min和max属性。

如何将错误信息显示到一个地方呢?

<sf:form method="post" modelAttribute="user" >
    <sf:errors element="div" path="*" cssClass="errors"/>
    账号:<sf:input path="username"/><br>
    密码:<sf:password path="password"  /><br>
    年龄:<sf:input path="age"/><br>
    <input type="submit" value="提交" ><br>
</sf:form>

可以看出,将之前每一个输入框后都有一个对应的sf:errors 用来显示错误信息。而这里进行修改了,将错误信息全部放到了一个div元素中,并且path使用通配符*来描述。

并且css样式定义为了errors:

div.errors{
 	background-color: #ffcccc;
  	border: 2px solid red;
}

此时,测试结果如下:

通过上面的测试结果图片可以看出,此时虽然错误信息全部显示在一起了,但是并没有明显的显示是哪一个输入框或是输入属性填写有问题。下面,再次修改:

<sf:form method="post" modelAttribute="user" >
  <sf:errors element="div" path="*" cssClass="errors"/>
  <sf:label path="username" cssErrorClass="error">账号:</sf:label>	
  <sf:input cssErrorClass="error" path="username"/><br>
  <sf:label path="password" cssErrorClass="error">密码:</sf:label>
  <sf:password cssErrorClass="error" path="password"  /><br>
  <sf:label path="age" cssErrorClass="error">年龄:</sf:label>
  <sf:input cssErrorClass="error" path="age"/><br>
  <input type="submit" value="提交" ><br>
</sf:form>

如上代码,我们已经使用div类型的sf:errors来统一显示错误信息,并且将提示信息使用了sf:label进行修饰,使用path进行绑定数据,同时使用cssErrorClass属性来引用出现错误时的css样式。

label.error{
  	color:red;
}
input.error{
  	background-color: #ffcccc;
}

所以,上述的代码运行结果如:

至此,Spring的JSP标签就告一段落了。

Spring 通用的标签库

如何引入在JSP文件中引入spring通用的标签库?

<%@ taglib prefix="s" uri="http://www.springframework.org/tags" %>

以上标签大部分使用得很少,因为部分标签已被Spring所淘汰了。

展现国际化信息

​ 我们都知道,如果将文本内容硬编码到网页里面,就无法实现动态改变网页显示内容,比如我们在没学习JavaScript之前,构建网页只是使用了HTML+CSS,那么我们的页面一旦写好了,
在运行过程中是无法动态修改的。而我们后台系统中,绝大部分数据是以图表的形式展示,而表格的数据是动态切换的,比如:表格分页。分页的时候,网页URL并没有变化,而数据却修改了。
而我们如果需要实现国际化也是一样的。所有需要进行国际化编码显示的内容都不可以硬编码到网页中,那么如何实现呢?下面看看Spring提供的方式:

使用到的标签是上面表格中的<s:message code = "">

home.jsp

<%@ taglib prefix="s" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Blog</title>
</head>
<body>

    <h1><s:message code="article.welcome"/></h1>
</body>
</html>

配置文件中配置引用文件:

/*查询类路径下的message.properties文件*/
/*@Bean
    public MessageSource messageSource(){
        ResourceBundleMessageSource messageSource =
                new ResourceBundleMessageSource();
        messageSource.setBasename("message");
        return messageSource;
    }*/

@Bean
public MessageSource messageSource(){
  ReloadableResourceBundleMessageSource messageSource =
    new ReloadableResourceBundleMessageSource();
  messageSource.setBasenames("classpath:message");
  messageSource.setCacheSeconds(10);
  return messageSource;
}

上述配置类中,这两个类都可以使用,但是有一定的区别:ResourceBundleMessageSource是从classpath中查询message文件,但是ReloadableResourceBundleMessageSource既可以从文件系统file:/,也可以使用classpath来指定类路径下引用,或是web应用的根目录下(没有前缀)查找。

-- zh-CN:
article.welcome = 欢迎来到spring的大世界

-- en-US:
article.welcome = welcome to Spring's world

如上图所示:创建两个properties文件,名称如上,en-US表示英文,zh-CN表示中文,由于浏览器更改语言环境不方便,这里我们使用Postman来测试。如下:

注意:Accept-Language的value值,比如zh-CN,需要将-写成中划线,不能写成下划线,否则不会生效。

创建URL

链接对于HTML页面来说,是一种最寻常不过的元素了,下面我们来了解一下spring URL标签的神奇:

<%@ taglib prefix="s" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Blog</title>
</head>
<body>

<a href="<s:url value="/articles"/>" > 所有文章 </a>   |
<a href="<s:url value="/register"/>"> 注册 </a>
<br>

<h2>使用spring的标签库</h2>
<%--定义一个变量,url的作用域默认在页面,但是可以通过scope进行修改--%>
<s:url value="/articles" scope="session" var="articlesUrl"/>
<%--在连接中使用,--%>
<a href="<s:url value="${articlesUrl}"/> ">所有文章</a><br>
<%--传参数--%>
<s:url value="/article" var="article" >
    <s:param name="page" value="1"/>
    <s:param name="size" value="2"/>
</s:url>
<a href="${article}">分页查询文章,page=1 size=2</a><br/>

<%--路径参数,如果有参数不匹配,则会转为普通参数传递。http://localhost:8080/article/3?name=ouyang--%>
<s:url value="/article/{id}" var="articleId">
    <s:param name="id" value="3"/>
    <s:param name="name" value="ouyang"/>
</s:url>
<a href="${articleId}">使用路径变量来获取文章{id = 3}</a>

</body>
</html>
  1. 普通使用

<a href="<s:url value="/articles"/>" >` ,使用spring url标签,jsp页面渲染时,会将项目名自动与/articles 拼接。当我们将鼠标放在所有文章上面,可以看到下面的提示:

即:单纯普通使用spring的url标签,与jstl标签类似,也会自动拼接上项目名。

  1. 定义成变量,设置scope
<%--定义一个变量,url的作用域默认在页面,但是可以通过scope进行修改--%>
<s:url value="/articles" scope="session" var="articlesUrl"/>
<%--在连接中使用,--%>
<a href="<s:url value="${articlesUrl}"/> ">所有文章</a><br>

上述两行代码则说明,可以先将连接独立成一个变量,然后在进行引用,如使用var来定义连接变量。则在需要使用的地方可以使用${}来引用。同时我们可以看到,可以进行scope的设置,即:设置链接的作用域,链接本身的默认的作用域是page,而我们可以设置为request,session,application等作用域。

  1. 查询参数传递
<%--传参数--%>
<s:url value="/article" var="article" >
    <s:param name="page" value="1"/>
    <s:param name="size" value="2"/>
</s:url>
<a href="${article}">分页查询文章,page=1 size=2</a><br/>

我们直接看效果:

  1. 传递路径参数
<%--路径参数,如果有参数不匹配,则会转为普通参数传递。http://localhost:8080/article/3?name=ouyang--%>
<s:url value="/article/{id}" var="articleId">
    <s:param name="id" value="3"/>
    <s:param name="name" value="ouyang"/>
</s:url>
<a href="${articleId}">使用路径变量来获取文章{id = 3}</a>

效果:

结合上述说明和效果图可以发现,我们可以使用{}来定义路径参数,如果存在参数匹配不到路径变量,则自动转为查询参数拼接到请求URL上。

转义内容

<%--内容转义--%>
<s:escapeBody htmlEscape="true">
    <h1>这是转义的一级标题标签</h1>
</s:escapeBody>
<h1>这是没有转义的一级标题标签</h1>

我们可以使用<s:escapeBody>来进行内容转义。效果如下:

使用Thymeleaf

如今我们可以发现,jsp慢慢不再使用了,而转向了Thymeleaf,jsp本质来说不是HTML,并且它是依赖于Servlet的,这也就说明,JSP不能独立于Servlet,必须建立于基于Servlet的web容器上。JSP模板不能作为通用的模板(如格式化EMail),也不能用于非Servlet的web应用。

官网:https://www.thymeleaf.org/

Spring整合Thymeleaf

  1. 添加POM依赖。

可以进到Thymeleaf官网:https://www.thymeleaf.org/download.html

<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf</artifactId>
    <version>3.0.11.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.thymeleaf</groupId>
  <artifactId>thymeleaf-spring5</artifactId>
  <version>3.0.9.RELEASE</version>
</dependency>

此时,项目的完整POM文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.ooyhao.spring</groupId>
  <artifactId>spring-in-action-06-01</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>spring-in-action-06-01 Maven Webapp</name>
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>
  <dependencies>
    <!--导入SpringMVC依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.1.9.RELEASE</version>
    </dependency>

    <!--导入Servlet依赖-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
    </dependency>

    <!--hibernate参数校验依赖-->
    <dependency>
      <groupId>org.hibernate.validator</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>6.1.0.Alpha3</version>
    </dependency>

    <!--导入Jstl标签依赖-->
    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>

    <!--导入Jackson依赖-->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.8</version>
    </dependency>

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

    <!--Junit测试依赖-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!--整合Thymeleaf-->
    <dependency>
      <groupId>org.thymeleaf</groupId>
      <artifactId>thymeleaf</artifactId>
      <version>3.0.11.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.thymeleaf</groupId>
      <artifactId>thymeleaf-spring5</artifactId>
      <version>3.0.9.RELEASE</version>
    </dependency>
  </dependencies>
</project>

配置整合Thymeleaf

在ServletConfig配置文件中配置相应的信息来整合Thymeleaf模板。

配置Thymeleaf视图解析器:

为了要在Spring中使用Thymeleaf,我们需要配置三个启用Thymeleaf与Spring集成的bean:

  • ThymeleafViewResolver:将逻辑视图名称解析为Thymeleaf模式视图;
  • SpringTemplateEngine:处理模板并渲染结果;
  • TemplateResolver:加载Thymeleaf模板;
/*-------------配置Thymeleaf模板引擎------------*/
@Bean
public SpringResourceTemplateResolver templateResolver(){
  SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
  resolver.setTemplateMode(TemplateMode.HTML);
  resolver.setPrefix("/WEB-INF/templates/");
  resolver.setSuffix(".html");
  resolver.setCharacterEncoding("utf-8");
  resolver.setCacheable(true);
  return resolver;
}

@Bean
public SpringTemplateEngine templateEngine(){
  SpringTemplateEngine engine = new SpringTemplateEngine();
  engine.setTemplateResolver(templateResolver());
  engine.setEnableSpringELCompiler(true);
  return engine;
}

@Bean
public ThymeleafViewResolver viewResolver(){
  ThymeleafViewResolver resolver = new ThymeleafViewResolver();
  resolver.setTemplateEngine(templateEngine());
  resolver.setCharacterEncoding("utf-8");
  return resolver;
}

注意:需要在templateResolver和viewResolver中设置字符编码,否则会出现中文乱码的情况。

此时,ServletConfig.java的完整文件如下:

/*相当于springmvc.xml*/
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.ooyhao.spring.**.controller")
public class ServletConfig implements WebMvcConfigurer {

    /*配置JSP视图解析器*/
    /*@Bean
    public ViewResolver viewResolver(){
        InternalResourceViewResolver resolver =
                new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        resolver.setExposeContextBeansAsAttributes(true);
        *//*将视图解析为JstlView
        * 查看源码可以看出,默认解析为InternalResourceView
        * *//*
        resolver.setViewClass(JstlView.class);
        return resolver;
    }*/


    /*查询类路径下的message.properties文件*/
    /*@Bean
    public MessageSource messageSource(){
        ResourceBundleMessageSource messageSource =
                new ResourceBundleMessageSource();
        messageSource.setBasename("message");
        return messageSource;
    }*/

    @Bean
    public MessageSource messageSource(){
        ReloadableResourceBundleMessageSource messageSource =
                new ReloadableResourceBundleMessageSource();
        messageSource.setBasenames("classpath:message");
        messageSource.setCacheSeconds(10);
        return messageSource;
    }

    /*配置静态资源的处理*/
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    /*使用Apache Tiles来进行布局,此处不做研究*/
   /* @Bean
    public TilesConfigurer tilesConfigurer(){
        TilesConfigurer tilesConfigurer = new TilesConfigurer();
        tilesConfigurer.setDefinitions("/WEB-INF/layout/tiles.xml");
        tilesConfigurer.setCheckRefresh(true);
        return tilesConfigurer;
    }

    @Bean
    public TilesViewResolver tilesViewResolver(){
        return new TilesViewResolver();
    }*/

   /*-------------配置Thymeleaf模板引擎------------*/
    @Bean
    public SpringResourceTemplateResolver templateResolver(){
        SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
        resolver.setTemplateMode(TemplateMode.HTML);
        resolver.setPrefix("/WEB-INF/templates/");
        resolver.setSuffix(".html");
        resolver.setCharacterEncoding("utf-8");
        resolver.setCacheable(true);
        return resolver;
    }

    @Bean
    public SpringTemplateEngine templateEngine(){
        SpringTemplateEngine engine = new SpringTemplateEngine();
        engine.setTemplateResolver(templateResolver());
        engine.setEnableSpringELCompiler(true);
        return engine;
    }

    @Bean
    public ThymeleafViewResolver viewResolver(){
        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setTemplateEngine(templateEngine());
        resolver.setCharacterEncoding("utf-8");
        return resolver;
    }
}

通过上述配置 Thymeleaf templateResolver 视图解析器,与之前配置JSP视图解析器类似,都是通过逻辑视图名来定位文件,但是这里需要依赖templateEngine。

接下来按照上述代码配置或图中所示的位置编写相应的html页面即可。

Thymeleaf实现表单绑定

html文件

<!DOCTYPE html>
<html  xmlns="http://www.w3.org/1999/xhtml"
       xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Blog</title>
    <meta charset="utf-8">
</head>

    <style>
        span.error{
            color:red;
        }
        div.errors{
            background-color: #ffcccc;
            border: 2px solid red;
        }
        label.error{
            color:red;
        }
        input.error{
            background-color: #ffcccc;
        }
    </style>

</head>
<body>

    <h1>欢迎加入Spring的大家庭</h1>

    <form method="post" th:object="${user}" >

        <div class="errors" th:if="${#fields.hasErrors('*')}" >
            <ul>
                <li th:each="err : ${#fields.errors('*')}" th:text="${err}"  >
                    Input is Incorrect
                </li>
            </ul>
        </div>
        <label th:class="${#fields.hasErrors('username')} ? 'error' ">账号:</label>
        <input type="text"  th:field="*{username}"  th:class="${#fields.hasErrors('username')} ? 'error'"/><br>

        <label th:class="${#fields.hasErrors('password')} ? 'error' ">密码:</label>
        <input type="password" th:field="*{password}"  th:class="${#fields.hasErrors('password')} ? 'error'" /><br>

        <label th:class="${#fields.hasErrors('age')} ? 'error' ">年龄:</label>
        <input th:field="*{age}"  th:class="${#fields.hasErrors('age')} ? 'error'"/><br>

        <input type="submit" value="提交" ><br>
    </form>
</body>
</html>

可以看出:代码中是通过使用fields.hasErrors以及后台的数据校验来判断是否有错误的。后台代码与之前的jsp略有不同:

Controller文件

//jsp
//处理表单数据,并验证*//*
@PostMapping("/register")
public String register(@Valid User user, Errors errors){
    if (errors.hasErrors()){
      return "register";//注册失败,重新返回到注册页面
    }
    userService.saveUser(user);
    return "redirect:/registerSuccess";
}

//thymeleaf
/*处理表单数据,并验证*/
@PostMapping("/register")
public String register(@Valid User user, BindingResult bindingResult,Model model){
    if (bindingResult.hasErrors()){
      System.out.println("错误数目:" + bindingResult.getErrorCount());
      model.addAttribute(user);
      return "register";//注册失败,重新返回到注册页面
    }
    userService.saveUser(user);
    return "redirect:/registerSuccess";
}

jsp使用spring的标签时是用Errors来判断,而html使用Thymeleaf时是使用BindingResult来进行判断。上述代码显示了不同之处。

效果图

总结:

​ 至此,本节已经学习了Spring如何整合JSP,使用Spring的标签,同时也接触了当前正在逐渐替代JSP的Thymeleaf,也实现了之前使用JSP同样地表单双向绑定的效果。

使用Thymeleaf实现表单双向绑定参考了

https://blog.csdn.net/z28126308/article/details/54429853

源码:

https://gitee.com/ooyhao/JavaRepo_Public/tree/master/Spring-in-Action/spring-in-action-06

最后

如果觉得不错的话,那就关注一下小编哦!一起交流,一起学习

posted @ 2019-09-21 10:41  ooyhao  阅读(361)  评论(0编辑  收藏  举报