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不填,点击登录

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/916834.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PyTorch深度学习与企业级项目实战-预训练语言模型GPT

【图书推荐】《PyTorch深度学习与企业级项目实战》-CSDN博客 13个PyTorch深度学习案例简介-CSDN博客 《PyTorch深度学习与企业级项目实战&#xff08;人工智能技术丛书&#xff09;》(宋立桓&#xff0c;宋立林)【摘要 书评 试读】- 京东图书 (jd.com) PyTorch深度学习算法与…

自存 关于RestController请求传参数 前端和后端相关

1.Get请求 Get请求传递参数一般是 1.通过PathVariable来映射 URL 绑定的占位符 后端 GetMapping("test/{id}")public R test(PathVariable Integer id){System.out.println(id);return R.success(id);}前端 export function test(id:any){return request({url:&q…

前端web

题目&#xff1a;制作带有下拉悬停菜单的导航栏 效果图 一、先制作菜单栏 <body> <div id"menu"> <div id"container"> <div class"item">游戏1 <div cla…

RabbitMQ教程:工作队列(Work Queues)(二)

RabbitMQ教程&#xff1a;工作队列&#xff08;Work Queues&#xff09;&#xff08;二&#xff09; 一、引言 在快节奏的软件开发世界中&#xff0c;我们经常面临需要异步处理任务的场景&#xff0c;比如在Web应用中处理耗时的图片处理或数据分析任务。这些任务如果直接在用…

Node.js下载安装及环境配置教程

一、进入官网地址下载安装包 Node.js 中文网 选择对应你系统的Node.js版本&#xff0c;这里我选择的是Windows系统、64位 二、安装程序 &#xff08;1&#xff09;下载完成后&#xff0c;双击安装包&#xff0c;开始安装Node.js (2)直接点【Next】按钮&#xff0c;此处可根据…

免费,WPS Office教育考试专用版

WPS Office教育考试专用版&#xff0c;不仅满足了考试需求&#xff0c;更为教育信息化注入新动力。 https://pan.quark.cn/s/609ef85ae6d4

将 HTML 转换为 JSX:JSX 和 JSX 规则

JSX 是 JavaScript 的语法扩展。您可以在 JavaScript 文件中编写 HTML 格式。 它基于 Web、Html、Css 和 JavaScript。Web 开发人员将页面内容分别编写为 Html 文件&#xff0c;将设计编写为 Css 文件&#xff0c;将逻辑编写为 JavaScript 文件。 须知 &#xff1a; JSX 是一个…

数据结构-二叉树及其遍历

🚀欢迎来到我的【数据结构】专栏🚀 🙋我是小蜗,一名在职牛马。🐒我的博客主页​​​​​​ ➡️ ➡️ 小蜗向前冲的主页🙏🙏欢迎大家的关注,你们的关注是我创作的最大动力🙏🙏🌍前言 本篇文章咱们聊聊数据结构中的树,准确的说因该是只说一说二叉树以及相…

活动|华院计算作为联盟理事单位出席进博会全球人工智能合作论坛

第七届中国国际进口博览会&#xff08;进博会&#xff09;于11月5日至10日在上海举行&#xff0c;作为本次进博会的重要配套活动&#xff0c;首届人工智能全球合作论坛也于9日圆满落幕。本次论坛由全球招商中心委员会、人工智能全球合作论坛组委会主办&#xff0c;中国国际科技…

Selective attention improves transformer详细解读

Selective attention improves transformer Google 2024.10.3 一句话&#xff1a;简单且无需额外参数的选择性注意力机制&#xff0c;通过选择性忽略不相关信息并进行上下文剪枝&#xff0c;在不增加计算复杂度的情况下显著提升了Transformer模型的语言建模性能和推理效率。 论…

shell脚本(1)

声明&#xff1a;学习视频来自b站up主 泷羽sec&#xff0c;如涉及侵权马上删除文章 感谢泷羽sec 团队的教学 视频地址&#xff1a;shell脚本&#xff08;1&#xff09;脚本创建执行与变量使用_哔哩哔哩_bilibili 本文主要讲解shell脚本的创建、执行和变量的使用。 一、脚本执行…

本地 / 网络多绑定用例总结

原文连接&#xff1a;AUTOSAR_EXP_ARAComAPI的7章笔记&#xff08;4&#xff09; 情景设定 在前一节的基础上&#xff0c;假设有类似情景&#xff0c;区别在于服务实例 2 位于与 AP 产品相同以太网的不同 ECU 上&#xff0c;服务消费者及其代理驻留在 AP 产品 ECU 上。因以太网…

通用定时器---输出比较功能

目录 一、概念 二、输出比较的8种模式 三、输出比较输出PWM波形的基本结构 配置步骤 四、示例代码 一、概念 OC&#xff08;OutPut Compare&#xff09;输出比较。输出比较可以通过比较CNT与CCR寄存器的关系&#xff0c;来对输出电平进行置1/置0/翻转的操作&#xff0c;可…

CSS盒子的定位> (下篇)#固定定位#笔记

一、固定定位 1.概念 固定定位其实是绝对定位的子类别&#xff0c;一个设置了position&#xff1a;fixed的元素是相对于视窗固定的&#xff0c;就算页面文档发生了滚动&#xff0c;它也会一直待在相同的地方。 2.代码属性 CSS代码添加 position&#xff1a;fixed 水平方…

leetcode100:相同的树

给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 示例 1&#xff1a; 输入&#xff1a;p [1,2,3], q [1,2,3] 输出&#xff1a;true示例 2&…

我谈二值形态学基本运算——腐蚀、膨胀、开运算、闭运算

Gonzalez从集合角度定义膨胀和腐蚀&#xff0c;不易理解。 Through these definitions, you can interpret dilation and erosion as sliding neighborhood operations analogous to convolution (or spatial filtering). 禹晶、肖创柏、廖庆敏《数字图像处理&#xff08;面向…

【数据结构 | C++】整型关键字的平方探测法散列

整型关键字的平方探测法散列 将给定的无重复正整数序列插入一个散列表&#xff0c;输出每个输入的数字在表中的位置。所用的散列函数是 H(key)key%TSize&#xff0c;其中 TSize 是散列表的表长。要求用平方探测法&#xff08;只增不减&#xff0c;即H(Key)i^2&#xff09;解决冲…

24.11.15 Vue3

let newJson new Proxy(myJson,{get(target,prop){console.log(在读取${prop}属性);return target[prop];},set(target,prop,val){console.log(在设置${prop}属性值为${val});if(prop"name"){document.getElementById("myTitle").innerHTML val;}if(prop…

413: Quick Sort

解法&#xff1a; #include <bits/stdc.h> using namespace std; const int N1e55; int a[N]; int n;int main(int argc, char** argv) {cin>>n;for (int i0;i<n;i) cin>>a[i];sort(a,an);for (int i0;i<n;i) cout<<a[i]<<" "…

麒麟kysec安全

一、kysec安全框架管理 开启kysec getstatus Copy security-switch --set default Copy 重启系统 reboot Copy 刷新页面&#xff0c;等待几分钟&#xff0c;即可完成文件的扫描。 查看kysec状态 getstatus Copy 切换到管理员身份&#xff08;密码&#xff1a;devuser…