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风格编程:

增删改查
XML 复制代码
<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、定义控制层执行器处理请求

java 复制代码
    //测试添加
    @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.引入请求方式转换过滤器

XML 复制代码
<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"

XML 复制代码
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isErrorPage="true" %>

7.双又错啦

但是丝毫不影响测试结果,查阅资料显示Tomcat的版本太高,内部抛弃了那些什么某种约束不允许除了get\post以外的请求方式,但是不影响测试。

8**.测试结果**

六、响应数据和结果视图

(一)返回值分类

1、返回值为字符串

用于指定返回的逻辑视图名称;

控制器代码:
java 复制代码
    //测试返回值为字符串类型
    @RequestMapping("testString")
    public String testString(){
        System.out.println("返回String类型测试");
        return "RequestSuccessful";
    }
运行结果:

2、void类型

通常使用原始servlet处理请求时,返回该类型;

控制器代码:
java 复制代码
    //测试返回值类型为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

用于绑定参数和指定返回视图名称;

控制器代码:
java 复制代码
    //返回ModelAndView类型测试
    @RequestMapping("testModelAndView")
    public ModelAndView testModelAndView(ModelAndView mv){
        mv.addObject("aa","AA");
        mv.setViewName("RequestSuccessful");
        return mv;
    }
JSP页面代码:
XML 复制代码
<%@ 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请求转发

控制器代码:
java 复制代码
    //forword请求转发测试
    @RequestMapping("testForward")
    public String testForward(){
        System.out.println("请求转发");
        return "forward:/testString";
    }
运行结果:

2、redirect重定向

控制器代码:
java 复制代码
//redirect重定向测试
    @RequestMapping("testRedirect")
    public String testRedirect(){
        System.out.println("请求重定向");
        return "redirect:http://www.baidu.com";
    }
运行结果:

3、携带参数的重定向

控制器代码:
java 复制代码
    //携带参数的重定向测试
    @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;*

java 复制代码
    //接收重定向请求参数的测试案例
    @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相关依赖
XML 复制代码
      <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-databind</artifactId>
          <version>2.15.2</version>
      </dependency>
(2)编写jsp页面,发送异步请求
XML 复制代码
<%@ 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注解
java 复制代码
    //获取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 复制代码
<?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的配置文件时,排出扫描控制层注解:

XML 复制代码
<context:component-scan base-package="com.jn">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

配置springMVC的配置文件时,扫描控制层注解:

XML 复制代码
<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工程添加依赖

XML 复制代码
<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 复制代码
<?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 复制代码
<?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 页面

XML 复制代码
<%@ 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++、编写控制器

java 复制代码
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、配置文件解析器

XML 复制代码
<!--配置文件解析器-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    </bean>

7、测试文件上传的运行结果

十、SpringMVC中的异常处理

(一)项目开发中异常处理的方式介绍

系统中异常包括两类:预期异常和运行时异常 RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试等手段减少运行时异常的发生。

(二)异常处理的设计思路

系统的 dao、service、controller出现都通过 throws Exception 向上抛出,最后由 springmvc 前端控制器交由异常处理器进行异常处理。

(三)异常处理的步骤

1、编写异常类和错误页面

java 复制代码
    //测试异常处理
    @RequestMapping("error")
    public String error(){
        System.out.println("error");
        int a = 1/0;
        return "error";
    }
XML 复制代码
<%@ 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、自定义异常处理器

java 复制代码
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、配置异常处理器

XML 复制代码
<!--配置自定义的异常处理器-->
    <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 接口

java 复制代码
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、配置拦截器

XML 复制代码
<!--配置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则不会调用下一个拦截器,也不会调用控制层执行器方法。

java 复制代码
    /*
     控制层执行之前的拦截器。
     改方法在控制层执行器方法前调用,该方法返回结果为true则继续调用下一个拦截器。
     改方法返回结果为false则不会调用下一个拦截器,也不会调用控制层执行器方法。
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("控制层执行之前的拦截器");
        return true;
    }
postHandle方法说明

控制层执行器方法返回时拦截器 ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ 该方法在控制层执行器方法返回时调用,由DispatcherServlet在将结果响应给浏览器前的调用。

java 复制代码
    /*
    控制层执行器方法返回时拦截器
    该方法在控制层执行器方法返回时调用,由DispatcherServlet在将结果响应给浏览器前的调用。
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("控制层执行器方法返回时拦截器");
    }
afterCompletion方法说明

控制层结束之后的拦截器 该方法在DispatcherServlet将结果响应给浏览器后调用。

java 复制代码
    /*
    控制层结束之后的拦截器
    该方法在DispatcherServlet将结果响应给浏览器后调用。
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("控制层结束之后的拦截器");
    }

3、拦截器的作用路径

XML 复制代码
<!--配置SpringMVC的拦截器-->
    <!--用于定义一组拦截器-->
    <mvc:interceptors>
        <!--定义一个具体的拦截器-->
        <mvc:interceptor>
            <!--指定拦截器应用的路径。/** 表示拦截所有路径-->
            <mvc:mapping path="/**"/>
            <!--指定拦截器类的全限定名-->
            <bean class="com.jn.utils.MyInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

(五)、多个拦截器的执行顺序

1、多个拦截器放行的情况:

拦截器1:
java 复制代码
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:
java 复制代码
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、配置拦截器

XML 复制代码
    <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定义

XML 复制代码
<%@ 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、控制器实现

java 复制代码
    //测试拦截实现登录
    @RequestMapping("login")
    public String login(String username,String password,HttpSession session){
        System.out.println("登录校验成功");
        session.setAttribute("username",username);
        return "successful";
    }

3、拦截器实现

java 复制代码
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、注册拦截器

XML 复制代码
    <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不填,点击登录

然后还是跳转到了登录界面

相关推荐
vortex54 分钟前
Vim 编辑器学习笔记
学习·编辑器·vim
源于花海7 分钟前
论文学习(四) | 基于数据驱动的锂离子电池健康状态估计和剩余使用寿命预测
论文阅读·人工智能·学习·论文笔记
心怀梦想的咸鱼9 分钟前
Ue5 umg学习(一)
学习·ue5
楚疏笃11 分钟前
鸿蒙学习生态应用开发能力全景图-开发者支持平台(5)
学习·华为·harmonyos
4v1d11 分钟前
边缘计算的学习
人工智能·学习·边缘计算
B20080116刘实2 小时前
CTF攻防世界小白刷题自学笔记13
开发语言·笔记·web安全·网络安全·php
xiaoyaolangwj3 小时前
高翔【自动驾驶与机器人中的SLAM技术】学习笔记(十三)图优化SLAM的本质
学习·机器人·自动驾驶
爱吃生蚝的于勒4 小时前
C语言内存函数
c语言·开发语言·数据结构·c++·学习·算法
L_cl6 小时前
Python学习从0到1 day26 第三阶段 Spark ④ 数据输出
学习