概述:
上一章主要关注于如何编写处理Web请求的控制器。我们也创建了一些简单的视图,用来渲染控制器产生的模型数据,但我们并没有花太多时间讨论视图,也没有讨论控制器完成请求到结果渲染到用户的浏览器中的这段时间内到底发生了什么,而这正是本章的主要内容。
本章涉及内容
- 将模型数据渲染为HTML
- 使用JSP视图
- 通过tiles定义视图布局
- 使用Thymeleaf视图
6.1 理解视图解析
jsp是JavaServer Page简写 它对应的视图解析器是InternalResourceViewResolver
具体细节类SpringMVC定义一个名为ViewResolver接口
public interface ViewResolver {
View resolveViewName(String viewName, Locale locale) throws Exception;
}
View 接口
package org.springframework.web.servlet;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
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";
String getContentType();
void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}
Spring自带13个视图解析器
常用的 FreeMarkerViewResolver、InternalResourceViewResolver、JasperReportsViewResolver、TilesViewResolver、VelocityViewResolver
6.2 创建JSP视图Spring提供两种支持JSP视图的方式:
- InternalResourceViewResolver,它可以解析JSTL标签库(JavaServer Pages Standard Tag Libarary)
- Spring提供两个标签库,表单相关,工具特性的
6.2.1 配置适用于JSP的视图解析器
/**
* 配置JSP视图解析器
* @return
* /WEB-INF/views/home.jsp
*/
@Bean
public ViewResolver viewResolver(){
InternalResourceViewResolver resolver =
new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
主要包括前缀和后缀
如果是xml
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/views"
p:suffix=".jsp"/>
总结:上面p是用了Spring的命名空间
- home将会解析为:"/WEB-INF/views/home.jsp"
- fruits/favor 将会解析为 "/WEB-INF/views/fruits/favor.jsp"
解析JSTL视图
JSTL 呈现者, JstlView中介 Spring 和 Locale是供应商,最终显示的内容取决Spring提供啥
/**
* 配置JSP视图解析器
* @return
* /WEB-INF/views/home.jsp
*/
@Bean
public ViewResolver viewResolver(){
InternalResourceViewResolver resolver =
new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
resolver.setViewClass(org.springframework.web.servlet.view.JstlView.class);
return resolver;
}
xml格式为:
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/views/"
p:suffix=".jsp"
p:viewClass="org.springframework.web.servlet.view.JstlView"/>
6.2.2 适用Spring的JSP库
Spring的表单绑定JSP标签库包含14个标签,依据一个模型对象,也就是具体的DTO
为了适用Spring标签库需要引用头部信息
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf" %>
- <sf:checkbox>渲染成一个HTML <input>标签,其中type属性设置为checkbox
- <sf:checkboxes>渲染成多个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>标签
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>RegisterPage</title>
<link rel="stylesheet"
type="text/css"
href="<c:url value="/resources/style.css"/>">
<style type="text/css">
span.error{
color:red;
}
</style>
</head>
<body>
<h1>注册</h1>
<sf:form method="POST" commandName="spitter">
First Name : <sf:input path="firstName"/>
<sf:errors path="firstName" cssClass="error"></sf:errors>
<br/>
Last Name : <sf:input path="lastName"/> <br/>
Email : <sf:input type="email" path="email"/> <br/>
Username:<sf:input path="username"/> <br/>
Password:<sf:password path="password"/><br/>
<input type="submit" value="Register">
</sf:form>
<!-- <form action="" method="POST">
First Name:<input type="text" name="firstName"/><br/>
Last Name:<input type="text" name="lastName" /> <br/>
UserName: <input type="text" name="username"/><br/>
Password: <input type="password" name="password"/><br/>
<input type="submit" value="Register" />
</form> -->
</body>
</html>
总结:
1、commandName="spitter" 设置跳转此页面会传过一个key为spitter对象,所以这个需要到controller配置,这个目的就是保存用户之前输入的内容
2、每个input 都有一个属性path="" 这个就是spitter对应dto的字段名,如果没有对应的就会报错
3、errors标签表示用户不合法的时候就是显示错误的信息,约束来自于对于Spitter的注解 或者标签本身的特性 例如 @NotNull @Size
4、errors最终解析成html的标签为:<span id="firstName.errors" >个数必须在2到30之间</span> 所以我们可以写一些样式,例如:
<style type="text/css">
span.error{
color:red;
}
</style>
controller的方法修改
@RequestMapping(value = "/register", method=RequestMethod.GET)
public String showRegistrationForm(Model model){
model.addAttribute(new Spitter());
return "registerForm";
}
这时候错误提示信息会出现在后面span
现在我们想要更好的布局显示在最上面的
<h1>注册</h1>
<sf:form method="POST" commandName="spitter">
<sf:errors path="*" element="div" cssClass="errors"/>
<sf:label path="firstName" cssErrorClass="error">First Name :</sf:label>
<sf:input path="firstName" cssErrorClass="error"/><br/>
Last Name : <sf:input path="lastName"/> <br/>
Email : <sf:input type="email" path="email"/> <br/>
Username:<sf:input path="username"/> <br/>
Password:<sf:password path="password"/><br/>
<input type="submit" value="Register">
</sf:form>
css样式:
<style type="text/css">
span.error{
color:red;
}
div.errors {
background-color:#ffcccc;
border:2px solid red;
}
label.error{
color:red;
}
input.error{
background-color:#ffcccc;
}
</style>
总结:
1.cssClass=“errors” 这个属性意思就是在有个class的css属性 div.errors 表示div 下erros class 的css样式
2.errors 标签表示他是错误信息框,path="*" 表示匹配所有字段出现的错误的信息
3.element="div" 表示最后的错误的展示为div标签
为更加精准提示错误:我们会在提示标签和输入Input下手
<sf:label path="firstName" cssErrorClass="error">First Name :</sf:label>
<sf:input path="firstName" cssErrorClass="error"/><br/>
label.error{
color:red;
}
input.error{
background-color:#ffcccc;
}
总结:
1、label需要绑定输入框,通过path进行绑定,cssErrorClass="error" 表示如果输入有误,标签需要用error的class样式
2、Input更简单一些,就是输入有误绑定error的class属性
3、spring标签中两个好用的class 样式,一个是错误样式 cssErrorClass 一个是cssClass
效果图:
自定义信息
@NotNull
@Size(min=2, max=30, message="First name 必须在 {min} 到 {max} 字符长度之间.")
private String firstName;
@NotNull
@Size(min=2, max=30,message="last name 必须在 {min} 到 {max} 字符长度之间")
private String lastName;
@NotNull
@Email(message="email 地址必须有效")
private String email;
@NotNull
@Size(min=5, max=16,message="username 必须在 {min} 到 {max} 字符长度之间.")
private String username;
@NotNull
@Size(min=5, max=25,message="password必须在 {min} 到 {max} 字符长度之间.")
private String password;
Spring通用标签库
<%@ taglib uri="http://www.springframework.org/tags" prefix="s"%>
<s:bind> 将绑定属性的状态导出到一个名为status的页面作用域属性中,与<s:path>组合使用获取绑定属性的值
<s:escapeBody>将标签体中的内容进行HTML和/或JavaScript转义
<s:hasBindErrors> 根据指定模型对象(在请求属性中)是否有绑定错误,有条件地渲染内容
<s:htmlEscape>为当前页面设置默认的HTML转义值
<s:message>根据给定的编码获取信息,然后要么进行渲染(默认行为),要么将其设置为页面作用域、请求作用域、会话作用域或应用作用域的变量(通过使用var和scope属性实现)
<s:nestedPath>设置嵌入式的path,用于<s:bind>之中
<s:theme>根据给定的编码获取主题信息,然后要么进行渲染(默认行为),要么将其设置为页面作用域、请求作用域、会话作用域或应用作用域的变量(通过使用var和scope属性实现)
<s:transform> 使用命令对象的属性编辑器转换命令对象中不包含的属性<s:url>创建相对于上下文的URL,支持URI模板变量以及HTML/XML/JavaScript转义。可以渲染URL(默认行为),也可以将其设置为页面作用域、请求作用域、会话作用域或应用作用域的变量(通过使用var和scope属性实现)
<s:eval> 计算符合Spring表达式语言(Spring Expression Language,SpEL)语法的某个表达式的值,然后要么进行渲染(默认行为),要么将其设置为页面作用域、请求作用域、会话作用域或应用作用域的变量(通过使用var和scope属性实现)
<s:message> 实现国际化
<h1><s:message code="spittr.welcome"></s:message></h1>
<h1>注册</h1>
<sf:form method="POST" commandName="spitter">
<sf:errors path="*" element="div" cssClass="errors"/>
<sf:label path="firstName" cssErrorClass="error">First Name :</sf:label>
<sf:input path="firstName" cssErrorClass="error"/><br/>
Last Name : <sf:input path="lastName"/> <br/>
Email : <sf:input type="email" path="email"/> <br/>
Username:<sf:input path="username"/> <br/>
Password:<sf:password path="password"/><br/>
<input type="submit" value="Register">
</sf:form>
总结:在享受便利的时候,很多人在后面默默为你服务,message也不例外,它的后面的服务者是:
服务者1
/**
* 实现不重启加载资源
* @return
*/
@Bean
public MessageSource messageSource(){
ReloadableResourceBundleMessageSource messageSource =
new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setDefaultEncoding("UTF-8");
messageSource.setCacheSeconds(10);
return messageSource;
}
服务者 2
/*@Bean
public MessageSource messageSource(){
ResourceBundleMessageSource messageSource =
new ResourceBundleMessageSource();
messageSource.setBasename("messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}*/
服务者1和服务者2任选一个
在类路径下创建属性文件 messages.properties
spittr.welcome=Welcome to Spitrr 简单基督教
注意:如果你出现乱码,要不你将属性文件设置为UTF-8,而读取文件对象也要设置UTF-8,否则就不要动。
<s:url value="/spittles" htmlEscape="true">
<s:param name="max" value="60"></s:param>
<s:param name="count" value="60"></s:param>
</s:url>
<s:escapeBody htmlEscape="true" >
<h1>ddddddd</h1>
</s:escapeBody>
总结:Escape目的就是转义,通俗一点就是原样输出
感觉有点乱,不过都是知识点。。。。。。。。。。。。。。。。。。。