SpringMVC学习笔记(二)
五、Rest风格编程
(一)Rest风格URL规范介绍
1、什么是restful
RESTful架构,就是目前最流行的一种互联网软件架构风格。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。REST这个词,是Roy Thomas Fielding在他2000年的博士论文中提出的. Fielding将他对互联网软件的架构原则,定名为REST,即Representational State Transfer的缩写。即"表现层状态转化"。如果一个架构符合REST原则,就称它为RESTful架构。值得注意的是 REST 并没有一个明确的标准,而更像是一种设计的格。
它本身并没有什么实用性,其核心价值在于如何设计出符合 REST 风格的网络接口。
2、restful的优点:
它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。
3、restful 的特性:
(1)资源(Resources)
网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。可以用一个 URI(统一资源定位符)指向它,每种资源对应一个特定的 URI 。要获取这个资源,访问它的 URI 就可以,因此 URI 即为每一个资源的独一无二的识别符。
(2)表现层(Representation)
把资源具体呈现出来的形式,叫做它的表现层 (Representation)。比如,文本可以用 txt 格式表现,也可以用 HTML 格式、XML 格式、JSON 格式表现,甚至可以采用二进制格式。
(3)状态转化(State Transfer)
每发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP 协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”(State Transfer)。而这种转化是建立在表现层之上的,所以就是“表现层状态转化”。具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET 、POST 、PUT、DELETE。它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。
(4)传统请求url
新增:http://localhost:8888/annotation/addPerson POST
修改:http://localhost:8888/annotation/updatePerson POST
删除:http://localhost:8888/annotation/deletePerson?id=1 GET
查询:http://localhost:8888/annotation/findPerson?id=1 GET
(4)REST风格请求
新增:http://localhost:8888/annotation/person POST
修改:http://localhost:8888/annotation/person PUT
删除:http://localhost:8888/annotation/person/1 DELETE
查询:http://localhost:8888/annotation/person/1 GET
(二)PathVariable注解详解
该注解用于绑定 url 中的占位符。例如:请求 url 中/annotation/test9/{id},这个{id}就是 url 占位符。url 支持占位符是 spring3.0 之后加入的。是springmvc 支持 rest 风格 URL 的一个重要标志。
属性:
value:用于指定 url 中占位符名称。
required:是否必须提供占位符。
(三)PathVariable案例
1、构建页面发起请求
REST风格编程:
增删改查
<h2>REST风格编程</h2>
<br>
新增:
<form action="person" method="post">
name :<input type="text" name="name"><br>
age :<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>
<br>
修改:
<form action="person" method="post">
<input type="hidden" name="_method" value="put">
name :<input type="text" name="name"><br>
age :<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>
<br>
删除:
<form action="person/1" method="post">
<input type="hidden" name="_method" value="delete">
<input type="submit" value="提交">
</form>
<br>
查询:
<a href="person/1">查询</a>
2、定义控制层执行器处理请求
//测试添加
@RequestMapping(value = "user", method = RequestMethod.POST)
public String addUser(String name,int age){
System.out.println("新增用户:" + name+"\t"+age);
return "RequestSuccessful";
}
//测试修改
@RequestMapping(value = "user", method = RequestMethod.PUT)
public String updateUser(String name,int age){
System.out.println("修改用户:" + name+"\t"+age);
return "RequestSuccessful";
}
//测试删除
@RequestMapping(value = "user/{id}", method = RequestMethod.DELETE)
public String deleteUser(@PathVariable(value = "id")int id){
System.out.println("删除用户:" + id);
return "RequestSuccessful";
}
//查询用户
@RequestMapping(value = "user/{id}", method = RequestMethod.GET)
public String getUser(@PathVariable(value = "id")int id){
System.out.println("查询用户:" + id);
return "RequestSuccessful";
}
3.测试出现错误
4.引入请求方式转换过滤器
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class></filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
5.双出现错误
类型 状态报
消息 JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS
描述 请求行中接收的方法由源服务器知道,但目标资源不支持
Apache Tomcat/8.5.85
6.再解决错误
在JSP文件的page标签中添加:isErrorPage=“true”
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isErrorPage="true" %>
7.双又错啦
但是丝毫不影响测试结果,查阅资料显示Tomcat的版本太高,内部抛弃了那些什么某种约束不允许除了get\post以外的请求方式,但是不影响测试。
8.测试结果
六、响应数据和结果视图
(一)返回值分类
1、返回值为字符串
用于指定返回的逻辑视图名称;
控制器代码:
//测试返回值为字符串类型
@RequestMapping("testString")
public String testString(){
System.out.println("返回String类型测试");
return "RequestSuccessful";
}
运行结果:
2、void类型
通常使用原始servlet处理请求时,返回该类型;
控制器代码:
//测试返回值类型为void类型
@RequestMapping("testVoid")
public void testVoid(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("返回void类型测试");
request.getRequestDispatcher("/testString").forward(request,response);
}
运行结果:
3、ModelAndView
用于绑定参数和指定返回视图名称;
控制器代码:
//返回ModelAndView类型测试
@RequestMapping("testModelAndView")
public ModelAndView testModelAndView(ModelAndView mv){
mv.addObject("aa","AA");
mv.setViewName("RequestSuccessful");
return mv;
}
JSP页面代码:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>请求处理成功</h2>
<h2>a:${requestScope.aa}</h2>
</body>
</html>
运行结果:
(二)转发和重定向
1、forward请求转发
控制器代码:
//forword请求转发测试
@RequestMapping("testForward")
public String testForward(){
System.out.println("请求转发");
return "forward:/testString";
}
运行结果:
2、redirect重定向
控制器代码:
//redirect重定向测试
@RequestMapping("testRedirect")
public String testRedirect(){
System.out.println("请求重定向");
return "redirect:http://www.baidu.com";
}
运行结果:
3、携带参数的重定向
控制器代码:
//携带参数的重定向测试
@RequestMapping("testRedirectWithParam")
public String testRedirectWithParam(RedirectAttributes ra,String name){
System.out.println("携带参数的重定向");
ra.addAttribute("name",name);//访问的参数在进行重定向的时候会被显示
//ra.addFlashAttribute("name",name);
return "redirect:/testRedirectParam";
}
//接收重定向请求参数的测试案例
@RequestMapping("testRedirectParam")
public String testRedirectParam(String name){
System.out.println("接收重定向请求参数");
System.out.println(name);
return "RequestSuccessful";
}
运行结果:
注意:重定向携带参数,需要使用对象RedirectAttributes,该对象提供两个方法封装参数addAttribute()和addFlashAttribute(),第一个方法参数会明文显示在浏览器地址栏,第二个方法参会会隐藏,使用第二种方法传参时,获取参数时需要加注解@ModelAttribute;
//接收重定向请求参数的测试案例
@RequestMapping("testRedirectParam")
public String testRedirectParam(@ModelAttribute(value = "name") String name) {
System.out.println("接收重定向请求参数");
System.out.println(name);
return "RequestSuccessful";
}
(三)ResponseBody 响应 json 数据
1、ResponseBody使用介绍
@ResponseBody的作用其实是将java对象转为json格式的数据。
2、ResponseBody使用案例:
(1)引入json相关依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
(2)编写jsp页面,发送异步请求
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
</head>
<body>
<button id="btn">发送ajax请求</button>
<button onclick="getUser()">获得json数据</button>
<script>
//页面加载时间
$(function () {
//为按钮绑定点击事件
$("#btn").click(function () {
$.ajax({
url: "ajaxTest",
data: '{"name":"Tom","age":18}',
type: "POST",
contentType: "application/json",
success: function (obj) {
//将控制层操作成功响应信息通过弹窗展示
alert(obj);
},
})
})
});
function getUser() {
$.ajax({
type: "GET",
url: "jsonTest",
dataType: "json", // 添加这一行,明确指定期望的响应数据类型为JSON
success: function (msg) {
alert("Data Saved: " + msg);
}
});
}
</script>
</body>
</html>
(3)编写控制器方法添加ResponseBody注解
//获取json数据的测试
@RequestMapping("jsonTest")
@ResponseBody
public User testJson(){
User user = new User();
user.setName("张三");
user.setAge(20);
Car car = new Car();
car.setName("奔驰");
car.setPrice(100000);
user.setCar(car);
return user;
}
(4)测试出现错误
GET http://localhost:8080/SpringMVC01_war_exploded/jsonTest 406 (Not Acceptable)
(5)解决了一上午的错误
发现是Springmvc配置文件里面多配置了两个处理器映射器以及处理器适配器,如图:
(6)修改完成后的配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--扫描注解的包-->
<context:component-scan base-package="com.jn"/>
<!--视图解析器:解析视图-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 配置 Web 数据绑定 -->
<mvc:annotation-driven conversion-service="conversionService">
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
<!--开启静态资源配置 第一种方式
<mvc:default-servlet-handler></mvc:default-servlet-handler>
-->
<!--开启静态资源配置 第二种方式-->
<mvc:resources mapping="/images/**" location="/images/"/>
<!--配置类型转换器
将自定义的 MyDateConverter 注册到 Spring 的类型转换服务中,使得在应用中可以使用 MyDateConverter 来进行字符串到日期的转换。-->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.jn.utils.MyDateConverter" />
</set>
</property>
</bean>
</beans>
(7)终极测试
七、Postman工具使用
(一)Postman工具介绍
用户在开发或者调试网络程序或者是网页B/S模式的程序的时候是需要一些方法来跟踪网页请求的,用户可以使用一些网络的监视工具比如著名的Firebug等网页调试工具。今天给大家介绍的这款网页调试工具不仅可以调试简单的css、html、脚本等简单的网页基本信息,它还可以发送几乎所有类型的HTTP请求!Postman在发送网络HTTP请求方面可以说是Chrome插件类产品中的代表产品之一。
(二)Postman工具的下载安装
1、下载地址:https://www.postman.com/downloads/
2、安装步骤:
(1)下载安装文件
(2)运行安装程序
(3)重启电脑自动安装
(4)运行
(三)Postman工具的使用
1、启动Tomcat服务器
2、打开postMan新建连接
3、测试添加
4、测试修改
5、测试删除
6、测试查询
八、SpringMVC中的父子容器解析
(一)SpringMVC中的父子容器解析
Spring和SpringMVC的容器具有父子关系。Spring容器为父容器,SpringMVC为子容器,子容器可以引用父容器中的Bean,而父容器不可以引用子容器中的Bean。
配置spring的配置文件时,排出扫描控制层注解:
<context:component-scan base-package="com.jn">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
配置springMVC的配置文件时,扫描控制层注解:
<context:component-scan base-package="com.jn.controller">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
九、SpringMVC中的文件上传
(一)文件上传的必要前提:
1、form 表单的 enctype 取值必须是multipart/form-data(默认值是:application/x-www-form-urlencoded)enctype:是表单请求正文的类型
2、 method 属性取值必须是 Post
3、提供一个文件选择域<input type=”file”/>
(二)文件上传原理分析
当 form 表单的 enctype 取值不是默认值后,request.getParameter()将失效。 enctype=”application/x-www-form-urlencoded”时,form 表单的正文内容是: key=value&key=value&key=value;
当 form 表单的 enctype 取值为 Mutilpart/form-data 时,请求正文内容就变成: 每一部分都是 MIME 类型描述的正文;
(三)SpringMVC的文件上传
1、构建maven工程添加依赖
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jn</groupId>
<artifactId>SpringMVC02</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>SpringMVC02 Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!--spring-webmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.4</version>
</dependency>
<!--spring-web-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.4</version>
</dependency>
<!--spring-context-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.4</version>
</dependency>
<!--servlet-api依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<!--fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
</dependencies>
<build>
<finalName>SpringMVC02</finalName>
</build>
</project>
2、SpringMVC.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--扫描注解的包-->
<context:component-scan base-package="com.jn"></context:component-scan>
<!--视图解析器:解析视图-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 配置 Web 数据绑定 -->
<mvc:annotation-driven conversion-service="conversionService">
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
<!--配置类型转换器
将自定义的 MyDateConverter 注册到 Spring 的类型转换服务中,使得在应用中可以使用 MyDateConverter 来进行字符串到日期的转换。-->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
</bean>
</beans>
3、web.xml文件的配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_9" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<!-- 配置Spring MVC的前端控制器 -->
<servlet>
<!-- servlet的名称 -->
<servlet-name>spring-mvc</servlet-name>
<!-- servlet的类路径,指向Spring的请求分发器 -->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 初始化参数,指定Spring MVC配置文件的位置 -->
<init-param>
<!-- 参数名称:配置文件位置 -->
<param-name>contextConfigLocation</param-name>
<!-- 参数值:配置文件在类路径下的位置 -->
<param-value>classpath:SpringMVC.xml</param-value>
</init-param>
</servlet>
<!-- 配置spring-mvc servlet的URL映射规则 -->
<servlet-mapping>
<!-- 对应的servlet名称 -->
<servlet-name>spring-mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--配置post请求时的编码过滤器-->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--引入请求方式转换过滤器-->
<!-- 配置HiddenHttpMethodFilter -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
4、编写 jsp 页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isErrorPage="true" %>
<html>
<head>
</head>
<body>
<form action="upload" method="post" enctype="multipart/form-data">
<input type="file" name="file"/>
<input type="submit" value="上传"/>
</form>
</body>
</html>
6、编写控制器
package com.jn.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
@Controller
public class FileController {
//测试文件上传
@RequestMapping("upload")
public String upload(MultipartFile file){
File dest = new File("C:/photos/"+file.getOriginalFilename());
try {
file.transferTo(dest);
} catch (Exception e) {
e.printStackTrace();
}
return "successful";
}
}
6、配置文件解析器
<!--配置文件解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
7、测试文件上传的运行结果
十、SpringMVC中的异常处理
(一)项目开发中异常处理的方式介绍
系统中异常包括两类:预期异常和运行时异常 RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试等手段减少运行时异常的发生。
(二)异常处理的设计思路
系统的 dao、service、controller出现都通过 throws Exception 向上抛出,最后由 springmvc 前端控制器交由异常处理器进行异常处理。
(三)异常处理的步骤
1、编写异常类和错误页面
//测试异常处理
@RequestMapping("error")
public String error(){
System.out.println("error");
int a = 1/0;
return "error";
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<style>
@import url('https://fonts.googleapis.com/css?family=Montserrat:400,600,700');
@import url('https://fonts.googleapis.com/css?family=Catamaran:400,800');
.error-container {
text-align: center;
font-size: 106px;
font-family: 'Catamaran', sans-serif;
font-weight: 800;
margin: 70px 15px;
}
.error-container > span {
display: inline-block;
position: relative;
}
.error-container > span.four {
width: 136px;
height: 43px;
border-radius: 999px;
background:
linear-gradient(140deg, rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 0.07) 43%, transparent 44%, transparent 100%),
linear-gradient(105deg, transparent 0%, transparent 40%, rgba(0, 0, 0, 0.06) 41%, rgba(0, 0, 0, 0.07) 76%, transparent 77%, transparent 100%),
linear-gradient(to right, #d89ca4, #e27b7e);
}
.error-container > span.four:before,
.error-container > span.four:after {
content: '';
display: block;
position: absolute;
border-radius: 999px;
}
.error-container > span.four:before {
width: 43px;
height: 156px;
left: 60px;
bottom: -43px;
background:
linear-gradient(128deg, rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 0.07) 40%, transparent 41%, transparent 100%),
linear-gradient(116deg, rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 0.07) 50%, transparent 51%, transparent 100%),
linear-gradient(to top, #99749D, #B895AB, #CC9AA6, #D7969E, #E0787F);
}
.error-container > span.four:after {
width: 137px;
height: 43px;
transform: rotate(-49.5deg);
left: -18px;
bottom: 36px;
background: linear-gradient(to right, #99749D, #B895AB, #CC9AA6, #D7969E, #E0787F);
}
.error-container > span.zero {
vertical-align: text-top;
width: 156px;
height: 156px;
border-radius: 999px;
background: linear-gradient(-45deg, transparent 0%, rgba(0, 0, 0, 0.06) 50%, transparent 51%, transparent 100%),
linear-gradient(to top right, #99749D, #99749D, #B895AB, #CC9AA6, #D7969E, #ED8687, #ED8687);
overflow: hidden;
animation: bgshadow 5s infinite;
}
.error-container > span.zero:before {
content: '';
display: block;
position: absolute;
transform: rotate(45deg);
width: 90px;
height: 90px;
background-color: transparent;
left: 0px;
bottom: 0px;
background:
linear-gradient(95deg, transparent 0%, transparent 8%, rgba(0, 0, 0, 0.07) 9%, transparent 50%, transparent 100%),
linear-gradient(85deg, transparent 0%, transparent 19%, rgba(0, 0, 0, 0.05) 20%, rgba(0, 0, 0, 0.07) 91%, transparent 92%, transparent 100%);
}
.error-container > span.zero:after {
content: '';
display: block;
position: absolute;
border-radius: 999px;
width: 70px;
height: 70px;
left: 43px;
bottom: 43px;
background: #FDFAF5;
box-shadow: -2px 2px 2px 0px rgba(0, 0, 0, 0.1);
}
.screen-reader-text {
position: absolute;
top: -9999em;
left: -9999em;
}
@keyframes bgshadow {
0% {
box-shadow: inset -160px 160px 0px 5px rgba(0, 0, 0, 0.4);
}
45% {
box-shadow: inset 0px 0px 0px 0px rgba(0, 0, 0, 0.1);
}
55% {
box-shadow: inset 0px 0px 0px 0px rgba(0, 0, 0, 0.1);
}
100% {
box-shadow: inset 160px -160px 0px 5px rgba(0, 0, 0, 0.4);
}
}
/* demo stuff */
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
body {
background-color: #FDFAF5;
margin-bottom: 50px;
}
html, button, input, select, textarea {
font-family: 'Montserrat', Helvetica, sans-serif;
color: #bbb;
}
h1 {
text-align: center;
margin: 30px 15px;
}
.zoom-area {
max-width: 490px;
margin: 30px auto 30px;
font-size: 19px;
text-align: center;
}
.link-container {
text-align: center;
}
a.more-link {
text-transform: uppercase;
font-size: 13px;
background-color: #de7e85;
padding: 10px 15px;
border-radius: 0;
color: #fff;
display: inline-block;
margin-right: 5px;
margin-bottom: 5px;
line-height: 1.5;
text-decoration: none;
margin-top: 50px;
letter-spacing: 1px;
}
</style>
<body>
<h1>404 Error Page #2</h1>
<p class="zoom-area"><b>CSS</b> animations to make a cool 404 page. </p>
<section class="error-container">
<span class="four"><span class="screen-reader-text">4</span></span>
<span class="zero"><span class="screen-reader-text">0</span></span>
<span class="four"><span class="screen-reader-text">4</span></span>
</section>
<div class="link-container">
<a target="_blank" href="https://www.silocreativo.com/en/creative-examples-404-error-css/" class="more-link">Visit the original article</a>
</div>
</body>
</html>
2、自定义异常处理器
package com.jn.utils;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyExceptionHandler implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
e.printStackTrace();
System.out.println("自定义异常处理");
ModelAndView mv = new ModelAndView();
mv.setViewName("error");
return mv;
}
}
3、配置异常处理器
<!--配置自定义的异常处理器-->
<bean class="com.jn.utils.MyExceptionHandler">
</bean>
4、测试异常处理的运行结果
十一、SpringMVC中的拦截器使用
(一)拦截器的介绍和作用
SpringMVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。用户可以自己定义一些拦截器来实现特定的功能。谈到拦截器,还要向大家提一个词——拦截器链(Interceptor Chain)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
(二)拦截器与过滤器的区别:
过滤器是 servlet 规范中的一部分,任何 java web 工程都可以使用。拦截器是 SpringMVC 框架自己的,只有使用了SpringMVC框架的工程才能用。过滤器在web.xml中的 url-pattern 标签中配置了/*之后,可以对所有要访问的资源拦截。拦截器它是只会拦截访问的控制器方法,如果访问的是 jsp,html,css,image 或者js是不会进行拦截的。它也是 AOP 思想的具体应用。
我们要想自定义拦截器, 要求必须实现:HandlerInterceptor 接口。
(三)自定义拦截器的步骤
1、编写一个普通类实现 HandlerInterceptor 接口
package com.jn.utils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
//控制层执行之前的拦截器
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("控制层执行之前的拦截器");
return true;
}
//控制层执行器方法返回时拦截器
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("控制层执行器方法返回时拦截器");
}
//控制层结束之后的拦截器
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("控制层结束之后的拦截器");
}
}
2、配置拦截器
<!--配置SpringMVC的拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.jn.utils.MyInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
3、测试拦截器的运行结果
随便执行一个方法
(四)拦截器的注意事项
1、拦截器的放行
拦截器中的放行指的是:如果有下一个拦截器就执行下一个,如果该拦截器处于拦截器链的最后一个,则执行控制器中的方法。
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)方法返回true表示继续执行控制层执行器方法,返回false表示方法结束,不会执行控制层执行器方法。
2、拦截器中方法的说明
preHandle方法说明
控制层执行之前的拦截器。
改方法在控制层执行器方法前调用,该方法返回结果为true则继续调用下一个拦截器。
改方法返回结果为false则不会调用下一个拦截器,也不会调用控制层执行器方法。
/*
控制层执行之前的拦截器。
改方法在控制层执行器方法前调用,该方法返回结果为true则继续调用下一个拦截器。
改方法返回结果为false则不会调用下一个拦截器,也不会调用控制层执行器方法。
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("控制层执行之前的拦截器");
return true;
}
postHandle方法说明
控制层执行器方法返回时拦截器 该方法在控制层执行器方法返回时调用,由DispatcherServlet在将结果响应给浏览器前的调用。
/*
控制层执行器方法返回时拦截器
该方法在控制层执行器方法返回时调用,由DispatcherServlet在将结果响应给浏览器前的调用。
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("控制层执行器方法返回时拦截器");
}
afterCompletion方法说明
控制层结束之后的拦截器 该方法在DispatcherServlet将结果响应给浏览器后调用。
/*
控制层结束之后的拦截器
该方法在DispatcherServlet将结果响应给浏览器后调用。
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("控制层结束之后的拦截器");
}
3、拦截器的作用路径
<!--配置SpringMVC的拦截器-->
<!--用于定义一组拦截器-->
<mvc:interceptors>
<!--定义一个具体的拦截器-->
<mvc:interceptor>
<!--指定拦截器应用的路径。/** 表示拦截所有路径-->
<mvc:mapping path="/**"/>
<!--指定拦截器类的全限定名-->
<bean class="com.jn.utils.MyInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
(五)、多个拦截器的执行顺序
1、多个拦截器放行的情况:
拦截器1:
package com.jn.utils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyFirstInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("控制层执行之前的拦截器1");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("控制层执行器方法返回时拦截器1");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("控制层结束之后的拦截器1");
}
}
拦截器2:
package com.jn.utils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MySecondInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("控制层执行之前的拦截器2");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("控制层执行器方法返回时拦截器2");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("控制层结束之后的拦截器2");
}
}
2、配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.jn.utils.MyFirstInterceptor"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.jn.utils.MySecondInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
3、多个拦截器阻断的情况:
第一个拦截器放回true,第二个拦截器返回true时
第一个拦截器放回true,第二个拦截器返回false时
第一个拦截器放回false,第二个拦截器返回true时
十二、拦截器的简单案例(验证用户是否登录)
(一)实现思路分析
1、定义登录页面,并定义请求映射。
2、判断用户名密码是否正确
3、如果正确 向 session 中写入用户信息
4、返回登录成功。
5、拦截用户请求,判断用户是否登录
6、如果用户已经登录。放行
7、如果用户未登录,跳转到登录页面
(二)案例代码
1、登录页面login.jsp定义
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录页面</title>
<style>
* {
margin: 0;
padding: 0;
}
html {
height: 100%;
}
body {
height: 100%;
}
.container {
height: 100%;
background-image: linear-gradient(to right, #fbc2eb, #a6c1ee);
}
.login-wrapper {
background-color: #fff;
width: 358px;
height: 588px;
border-radius: 15px;
padding: 0 50px;
position: relative;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.header {
font-size: 38px;
font-weight: bold;
text-align: center;
line-height: 200px;
}
.input-item {
display: block;
width: 100%;
margin-bottom: 20px;
border: 0;
padding: 10px;
border-bottom: 1px solid rgb(128, 125, 125);
font-size: 15px;
outline: none;
}
.input-item:placeholder {
text-transform: uppercase;
}
.btn {
text-align: center;
padding: 10px;
width: 100%;
margin-top: 40px;
background-image: linear-gradient(to right, #a6c1ee, #fbc2eb);
color: #fff;
}
.msg {
text-align: center;
line-height: 88px;
}
a {
text-decoration-line: none;
color: #abc1ee;
}
</style>
</head>
<body>
<div class="container">
<div class="login-wrapper">
<div class="header">Login</div>
<form action="login" method="post">
<div class="form-wrapper">
<input type="text" name="username" placeholder="username" class="input-item">
<input type="password" name="password" placeholder="password" class="input-item">
<input type="submit" value="Login" class="btn"> <!-- 将原来的div.btn改为input提交按钮 -->
</div>
<div class="msg">
Don't have account?
<a href="#">Sign up</a>
</div>
</form>
</div>
</div>
</body>
</html>
2、控制器实现
//测试拦截实现登录
@RequestMapping("login")
public String login(String username,String password,HttpSession session){
System.out.println("登录校验成功");
session.setAttribute("username",username);
return "successful";
}
3、拦截器实现
package com.jn.utils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class MyInterceptor implements HandlerInterceptor {
/*
控制层执行之前的拦截器。
改方法在控制层执行器方法前调用,该方法返回结果为true则继续调用下一个拦截器。
改方法返回结果为false则不会调用下一个拦截器,也不会调用控制层执行器方法。
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("控制层执行之前的拦截器");
//校验用户是否登录
HttpSession session = request.getSession();
String username = (String) session.getAttribute("username");
if (username == null){
return true;
}
else {
//当前用户未登录,拦截跳转到登录页面
request.getRequestDispatcher("/login.jsp").forward(request,response);
return false;
}
}
/*
控制层执行器方法返回时拦截器
该方法在控制层执行器方法返回时调用,由DispatcherServlet在将结果响应给浏览器前的调用。
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("控制层执行器方法返回时拦截器");
}
/*
控制层结束之后的拦截器
该方法在DispatcherServlet将结果响应给浏览器后调用。
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("控制层结束之后的拦截器");
}
}
4、注册拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="login"/>
<bean class="com.jn.utils.MyInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
5、测试结果
此时输入用户名,然后点击login
然后username不填,点击登录
然后还是跳转到了登录界面