• 《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

    最后

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

    程序yuan
  • 相关阅读:
    Socket.IO API Server
    Socket.IO 中文笔记
    Express 中文API 笔记
    JWT
    Sass 记录
    CSS高级技巧(二)背景和边框
    CSS高级技巧(一)常见的注意事项
    CSS进阶(二十四)流向的改变
    linux应用之test命令详细解析
    数字证书原理(ssl,https)
  • 原文地址:https://www.cnblogs.com/ooyhao/p/11561790.html
Copyright © 2020-2023  润新知