SSM框架-SpringMVC(一)

6 篇文章 0 订阅
3 篇文章 0 订阅

目录

1 SpringMVC简介

1.1 什么是mvc

1.2 什么是SpringMVC

1.3 SpringMVC的特点

2 入门案例

2.1 开发环境

2.2 创建maven工程

2.3 配置web.xml

2.4 创建请求控制器

2.5 创建SpringMVC配置文件

2.6 测试HelloWorld

2.7 优化配置

3 @RequestMapping注解

3.1 @RequestMapping注解的功能

3.2 @RequestMapping注解的位置

3.3 @RequestMapping注解的value属性

3.4 @RequestMapping注解的method属性

3.5 @RequestMapping注解的params属性(了解)

3.6 @RequestMapping注解的headers属性(了解)

3.7 SpringMVC支持ant风格的路径

3.8 SpringMVC支持路径中的占位符(重点)

4 SpringMVC获取请求参数

4.1 通过ServletAPI获取

4.2 通过控制器方法的形参获取请求参数

4.3 @RequestParam

4.4 @RequestHeader

4.5 @CookieValue

4.6 通过pojo获取请求参数

4.7 解决获取请求参数乱码的问题

5 域对象共享数据

5.1 使用ServletAPI向request域对象共享数据

5.2 使用ModelAndView向Request域对象共享数据

5.3 使用Model向request对象共享数据

5.4 使用Map向request对象共享数据

5.5 使用ModelMap向request对象共享数据

5.6 Model、ModelMap、Map的关系

5.7 向session域共享数据

5.8 向Application域共享数据

6 SpringMVC的视图

6.1 ThymeleafView

6.2 转发视图

6.3 重定向视图

6.4 视图控制器view-controller

7 RESTful

7.1 RESTful简介

7.2 RESTful的实现

7.3 RESTful之查询和保存功能

7.4 RESTful之删除和更新功能

8 RESTful案例

8.1 准备工作

8.2 功能清单

8.3 访问首页

8.4 查询所有员工信息

8.5 添加员工信息

8.6 修改信息

8.7 删除信息


1 SpringMVC简介

1.1 什么是mvc

之前我们就接触过这个概念,MVC是一种软件架构思想,将软件按照模型、视图、控制器划分

M Model ,模型层,指工程中的 JavaBean ,作用是处理数据 
JavaBean分为两类:
一类称为实体类 Bean :专门存储业务数据的,如 Student User
一类称为业务处理 Bean :指 Service Dao 对象,专门用于处理业务逻辑和数据访问。
V View ,视图层,指工程中的 html jsp 等页面,作用是与用户进行交互,展示数据
C Controller ,控制层,指工程中的 servlet ,作用是接收请求和响应浏览器
MVC 的工作流程: 用户通过视图层发送请求到服务器,在服务器中请求被 Controller 接收, Controller 调用相应的Model 层处理请求,处理完毕将结果返回到 Controller Controller 再根据请求处理的结果 找到相应的View 视图,渲染数据后最终响应给浏览器

1.2 什么是SpringMVC

SpringMVC Spring 的一个后续产品,是 Spring 的一个子项目
SpringMVC Spring 为表述层开发提供的一整套完备的解决方案。在表述层框架历经 Strust
WebWork Strust2 等诸多产品的历代更迭之后,目前业界普遍选择了 SpringMVC 作为 Java EE 项目 表述层开发的首选方案

1.3 SpringMVC的特点

  •  Spring 家族原生产品,与 IOC 容器等基础设施无缝对接
  • 基于原生的Servlet,通过了功能强大的前端控制器DispatcherServlet,对请求和响应进行统一
  • 处理
  • 表述层各细分领域需要解决的问题全方位覆盖,提供全面解决方案
  • 代码清新简洁,大幅度提升开发效率
  • 内部组件化程度高,可插拔式组件即插即用,想要什么功能配置相应组件即可
  • 性能卓著,尤其适合现代大型、超大型互联网项目要求  

2 入门案例

2.1 开发环境

IDE idea 2021.2.3
构建工具: maven3.3.9
服务器: tomcat9
Spring 版本: 5.3.1
jdk版本:1.8

2.2 创建maven工程

①添加web模块

我们这次直接添加默认maven

②打包方式:war

我们打开模块添加一个web.xml,注意改好文件路径

这样就创建好了

③引入依赖

    <dependencies>
        <!-- SpringMVC -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.1</version>
        </dependency>
        <!-- 日志 -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
        <!-- ServletAPI -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <!-- Spring5和Thymeleaf整合包 -->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
            <version>3.0.12.RELEASE</version>
        </dependency>
    </dependencies>

2.3 配置web.xml

注册 SpringMVC 的前端控制器 DispatcherServlet
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

这里和之前学的javaweb配置的一样只不过servlet-class标签使用了前端控制器DispatcherServlet,还有要注意的是:url-pattern的设置:我们这里使用“/”,它表示匹配浏览器向服务器发送的所有请求(不包括.jsp的文件),而“/*”表示匹配浏览器向服务器发送的所有请求(包括.jsp的文件),而我们的DispatcherServlet是无法访问jsp文件的。

2.4 创建请求控制器

SpringMVC中封装了servlet,我们不需要自己写servlet了,我们只需要创建一个普通java类即可也就是pojo类,通过@Controller注解将其标识为一个控制层组件,交给 spring容器的IoC容器管理,此时SpringMVC才能够识别控制器的存在。

package com.itzw.controller;

import org.springframework.stereotype.Controller;

@Controller
public class HelloController {
}

2.5 创建SpringMVC配置文件

DispatcherServlet初始化的时候就会加载SpringMVC配置文件,它是自动完成的,所以我们不能像之前加载spring配置文件那样随便起名字随便放位置。它有规定好的文件名和路径。SpringMVC配置文件默认的位置和名称:位置:WEB-INF下;名称:<servlet-name>-servlet.xml。就是servlet-name标签的值加上-servlet.xml,比如本次配置文件的名称为springmvc-servlet.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"
       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">

    <!--自动扫描包-->
    <context:component-scan base-package="com.itzw.controller"/>

    <!-- 配置Thymeleaf视图解析器 -->
    <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
        <property name="order" value="1"/>
        <property name="characterEncoding" value="UTF-8"/>
        <property name="templateEngine">
            <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
                <property name="templateResolver">
                    <bean
                            class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
                        <!-- 视图前缀 -->
                        <property name="prefix" value="/WEB-INF/templates/"/>
                        <!-- 视图后缀 -->
                        <property name="suffix" value=".html"/>
                        <property name="templateMode" value="HTML5"/>
                        <property name="characterEncoding" value="UTF-8" />
                    </bean>
                </property>
            </bean>
        </property>
    </bean>
</beans>

值得注意的是:视图前缀+逻辑视图+视图后缀=物理视图,也就是说当我们想访问/WEB-INF/templates/index.html我们只要访问index即可。

2.6 测试HelloWorld

我们先简单配置tomcat服务器,这是我们很熟悉的操作:

注意我们已经写了一个前端页面:

它的位置如上所示,我们此时启动服务器肯定是访问不到这个文件的。这个文件必须是在webapp目录下才是默认可以访问。那么我们怎么设置?

@Controller
public class HelloController {

    @RequestMapping("/")
    public String index(){
        return "index";
    }
}

如上,@RequestMapping注解可以接收到浏览器的请求,但是它怎么知道要接收哪个路径的请求呢?我们需要设置注解的value值为“/”,这个斜杠就表示当前工程的上下文路径,也就是:http://localhost:8080/springmvc/,然后我们返回逻辑视图的名称,因为之前我们已经设置好视图前缀和视图后缀,我们直接返回逻辑视图的名字即可。这就访问到了:

下面我们通过超链接跳转到指定界面:

注意th:是thymeleaf的语法,他有啥用呢?我们这里的hello路径前面的路径为上下文路径也就是http://localhost:8080/springmvc/

还记得我们之前想获取项目名称非常的麻烦,这里就很简单。但是下面的hello路径就没有上下文路径,我们看:

访问这个路径后我们要返回给它一个文件:

    @RequestMapping("/hello")
    public String hello(){
        return "success";
    }

注意这里的注解值就要 写上“/hello”了因为我们访问的路径是http://localhost:8080/springmvc/hello

2.7 优化配置

之前我们是将springmvc配置文件放在WEB-INF下的,那是默认位置还有默认的名称,我们可以自己设置位置和名称:

        <!--设置SpringMVC配置文件的位置和名称-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!--将DispatcherServlet初始化时间提前到服务器启动时-->
        <load-on-startup>1</load-on-startup>

classpath表示从类路径查找文件,也就是resources路径下,而后面跟着的就是文件名称,我们可以把springmvc配置文件放在resources中了。

当我们浏览器第一次发送请求时会比较慢,这是因为DispatcherServlet需要初始化,我们可以如上设置,在服务器启动时就初始化。

3 @RequestMapping注解

3.1 @RequestMapping注解的功能

这个注解我们在上面就使用过从注解名称上我们可以看到,@RequestMapping注解的作用就是将请求和处理请求的控制器方法关联 起来,建立映射关系。

SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。

3.2 @RequestMapping注解的位置

  • @RequestMapping标识一个类:设置映射请求的请求路径的初始信息
  • @RequestMapping标识一个方法:设置映射请求请求路径的具体信息

演示:

我们重新配置一个模块:

还是上面一样的步骤引入依赖-配置web.xml文件-配置springmvc配置文件,然后编写pojo类,一定要注意pojo类要使用@Controller注解,将类交给spring容器管理

其它都一样,主要写pojo类和html界面:

package com.itzw.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IndexPage {
    @RequestMapping("/")
    public String index(){
        return "index";
    }
}
package com.itzw.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/xyz")
public class RequestMappingTest {

    @RequestMapping("/test")
    public String testRequestMapping(){
        return "success";
    }
}

注意这里我们使用@RequestMapping注解标注了类和方法,那么这个方法的路径最终是/xyz/test,所以我们在前端访问这个路径就要如下设置:

3.3 @RequestMapping注解的value属性

这个我们也已经使用过了,之前在这个注解写的值就是对value属性赋值,只不过它是可以省略不写直接写值的。

  • @RequestMapping注解的value属性通过请求的请求地址匹配请求映射
  • @RequestMapping注解的value属性是一个字符串类型的数组,表示该请求映射能够匹配多个请求地址所对应的请求
  • @RequestMapping注解的value属性必须设置,至少通过请求地址匹配请求映射

我们可以设置多个值:

这两个路径都能访问到资源。

3.4 @RequestMapping注解的method属性

method属性就是用来设置请求方式的,就是我们之前学的get请求和post请求

@RequestMapping注解的method属性通过请求的请求方式(getpost)匹配请求映射 @RequestMapping注解的method属性是一个RequestMethod类型的数组,表示该请求映射能够匹配多种请求方式的请求

若当前请求的请求地址满足请求映射的value属性,但是请求方式不满足method属性,则浏览器报错 405:Request method 'POST' not supported

演示:

我们设置Method值为get请求

我们的请求地址一个是get请求一个是post请求:

结果是get请求没问题,post请求会出错:

注:
1 、对于处理指定请求方式的控制器方法, SpringMVC 中提供了 @RequestMapping 的派生注解
处理 get 请求的映射 -->@GetMapping
处理 post 请求的映射 -->@PostMapping
处理 put 请求的映射 -->@PutMapping
处理 delete 请求的映射 -->@DeleteMapping
如下:我们要在GetMapping注解中指定路径,和之前的效果是一样的
 

3.5 @RequestMapping注解的params属性(了解)

  • @RequestMapping注解的params属性通过请求的请求参数匹配请求映射
  • @RequestMapping注解的params属性是一个字符串类型的数组,可以通过四种表达式设置请求参数 和请求映射的匹配关系
  • "param":要求请求映射所匹配的请求必须携带param请求参数
  • "!param":要求请求映射所匹配的请求必须不能携带param请求参数
  • "param=value":要求请求映射所匹配的请求必须携带param请求参数且param=value
  • "param!=value":要求请求映射所匹配的请求必须携带param请求参数但是param!=value

简单举个例子:

以上表示请求的参数必须携带username并且携带password并且password的值不能为123

以上第一个是访问不成功了,报400错误,因为它的password值为123,值得注意的是我们之前想设置请求参数就是按照上面的第一种方式,但是这种方式在这里报错,但是运行是正常的,我们可以选择使用下面的方式,都是可以的效果一样。

3.6 @RequestMapping注解的headers属性(了解)

  • @RequestMapping注解的headers属性通过请求的请求头信息匹配请求映射
  • @RequestMapping注解的headers属性是一个字符串类型的数组,可以通过四种表达式设置请求头信 息和请求映射的匹配关系
  • "header":要求请求映射所匹配的请求必须携带header请求头信息
  • "!header":要求请求映射所匹配的请求必须不能携带header请求头信息
  • "header=value":要求请求映射所匹配的请求必须携带header请求头信息且header=value
  • "header!=value":要求请求映射所匹配的请求必须携带header请求头信息且header!=value
  • 若当前请求满足@RequestMapping注解的valuemethod属性,但是不满足headers属性,此时页面 显示404错误,即资源未找到

3.7 SpringMVC支持ant风格的路径

  • ?:表示任意的单个字符
  • *:表示任意的0个或多个字符
  • **:表示任意层数的任意目录
  • 注意:在使用**时,只能使用/**/xxx的方式

这里的路径我们使用到了一个

在写浏览器请求路径的时候这个问号就可以随意赋值,如下:

<a th:href = "@{/xyz/hello}">测试@RequestMapping的ant风格</a><br>

3.8 SpringMVC支持路径中的占位符(重点)

SpringMVC 路径中的占位符常用于 RESTful 风格中,当请求路径中将某些数据通过路径的方式传输到服 务器中,就可以在相应的@RequestMapping 注解的 value 属性中通过占位符 {xxx} 表示传输的数据,再通过@PathVariable 注解,将占位符所表示的数据赋值给控制器方法的形参
  • 原始方式:/deleteUser?id=1
  • rest方式:/user/delete/1

举个例子:

<a th:href = "@{/test/1/admin}">测试@RequestMapping的占位符</a><br>

这里的1和admin都是我想传的值,但是咋接收呢?

    @RequestMapping("/test/{id}/{username}")
    public String testRest(@PathVariable("id") Integer id,@PathVariable("username") String username){
        System.out.println("id:"+id);
        System.out.println("username:"+username);
        return "success";
    }

在路径中使用大括号将这些值括起来,然后在下面的方法中使用注解 @PathVariable接收

4 SpringMVC获取请求参数

4.1 通过ServletAPI获取

HttpServletRequest 作为控制器方法的形参,此时 HttpServletRequest 类型的参数表示封装了当前请 求的请求报文的对象
前端发送请求:
<form th:action="@{/param}" method="get">
    username:<input type="text" name="username">
    password:<input type="password" name="password">
    <input type="submit" value="submit">
</form>
获取参数:
    @RequestMapping("/param")
    public String testParams(HttpServletRequest request){
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        System.out.println("username:"+username);
        System.out.println("password:"+password);
        return "success";
    }

这种方式就是我们之前学过的方式,那么我们学了springmvc当然是有更简单的方式的。

4.2 通过控制器方法的形参获取请求参数

在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在DispatcherServlet中就会将请求参数赋值给相应的形参
    @RequestMapping("/param2")
    public String testParams2(String username,String password){
        System.out.println("username:"+username);
        System.out.println("password:"+password);
        return "success";
    }

这样很方便

4.3 @RequestParam

上面虽然方便,但是如果名字对应不上呢,控制器方法的形参就是对应不上请求的参数怎么办,虽然目前来看解决方法就是你直接写对不就完事了,但是我就是故意不写对呢?

我们可以使用@RequestParam参数,@RequestParam是将请求参数和控制器方法的形参创建映射关系。@RequestParam注解一共有三个属性:

  • value:指定为形参赋值的请求参数的参数名
  • required:设置是否必须传输此请求参数,默认值为true,若设置为true时,则当前请求必须传输value所指定的请求参数,若没有传输该请求参数,且没有设置 defaultValue属性,则页面报错400Required String parameter 'xxx' is not present;若设置为false,则当前请求不是必须传输value所指定的请求参数,若没有传输,则注解所标识的形参的值为null。
  • defaultValue:不管required属性值为truefalse,当value所指定的请求参数没有传输或传输的值""时,则使用默认值为形参赋值

我们来演示一下:

这里的参数对应不上,看看结果:

username接收不到了,我们使用那个参数 :

这次就能接受到了

再简单测试一下其它属性:

4.4 @RequestHeader

  • @RequestHeader是将请求头信息和控制器方法的形参创建映射关系
  • @RequestHeader注解共有仨属性:valuerequireddefaultValue,类似@RequestParam

请求头信息就是如下信息: 

注意这里我们必须使用注解才能获取请求头信息,因为不使用注解默认是获取请求参数信息

没什么毛病,它的其它属性和上面是一样的,懒得测了。

4.5 @CookieValue

我们先获取session对象,这样发送请求才会携带cookie:

接收到cookie值:

4.6 通过pojo获取请求参数

我们也发现一个问题,就是参数太多怎么办,我们可以使用pojo类,可以在控制器方法的形参位置设置一个实体类类型的形参,此时若浏览器传输的请求参数的参数名和实体类中的属性名一致,那么请求参数就会为此属性赋值。

package com.itzw.springmvc.pojo;

public class User {
    private Integer id;
    private String username;
    private String password;

    public User() {
    }

    public User(Integer id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

我们创建一个类如上,它里面的属性有username和password,控制器方法的参数就可以直接使用user类

    @RequestMapping("/param2")
    public String testParam3(User user){
        System.out.println(user);
        return "success";
    }

4.7 解决获取请求参数乱码的问题

目前我们并没有发现乱码,我们使用post请求试试:

如上使用post请求的中文出现 乱码 。

解决获取请求参数的乱码问题,可以使用 SpringMVC 提供的编码过滤器 CharacterEncodingFilter ,但是 必须在web.xml 中进行注册,如下:
    <filter>
        <filter-name>CharacterEncodingFilter</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>
        <!--设置响应编码-->
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <!--对所有请求处理-->
        <url-pattern>/*</url-pattern>
    </filter-mapping>

这样中文就不会乱码了。

5 域对象共享数据

5.1 使用ServletAPI向request域对象共享数据

<a th:href="@{/servletAPI}">测试通过servletAPI向request域共享数据</a>
    @RequestMapping("/servletAPI")
    public String testRequestByServletAPI(HttpServletRequest request){
        request.setAttribute("testRequestScope","Hello,ServletAPI");
        return "success";
    }
<p th:text="${testRequestScope}"></p>

共享数据后我们在前端接收数据,只需要把数据的键值放在${}中即可。

上面的方式是传统方式,既然我们学了springmvc那么我们肯定有别的方法 

5.2 使用ModelAndView向Request域对象共享数据

    @RequestMapping("/modelAndView")
    public ModelAndView testmodelAndView(){
        ModelAndView mav = new ModelAndView();
        //向请求域共享数据
        mav.addObject("testRequestScope","hello,ModelAndView");
        //设置视图,实现页面跳转
        mav.setViewName("success");
        return mav;
    }

这种方式比上面还麻烦一点

5.3 使用Model向request对象共享数据

    @RequestMapping("/model")
    public String testmodel(Model model){
        model.addAttribute("testRequestScope","hello,model");
        return "success";
    }

这种方式就比较简单

5.4 使用Map向request对象共享数据

    @RequestMapping("/map")
    public String testMap(Map<String,Object> map){
        map.put("testRequestScope","helo,map");
        return "success";
    }

5.5 使用ModelMap向request对象共享数据

这个和Model方式一样,只是名字不一样:

    @RequestMapping("/modelMap")
    public String testModelMap(ModelMap modelMap){
        modelMap.addAttribute("testRequestScope","hello,modelMap");
        return "success";
    }

5.6 ModelModelMapMap的关系

    @RequestMapping("/test")
    public String test(Model model,ModelMap modelMap,Map<String,Object> map){
        System.out.println(modelMap.getClass().getName());
        System.out.println(map.getClass().getName());
        System.out.println(model.getClass().getName());
        return "success";
    }

我们输出它们的名称:

ModelModelMapMap类型的参数其实本质上都是 BindingAwareModelMap 类型的,所以效果一样,那我们就挑一个最简单的来用就好了,比如Model。

5.7 向session域共享数据

这里使用springmvc的方式就复杂了,我们使用传统方式:使用HttpSession对象共享数据

    @RequestMapping("/session")
    public String testSession(HttpSession session){
        session.setAttribute("testSessionScope","hello,session");
        return "success";
    }
<p th:text="${session.testSessionScope}"></p>

5.8 向Application域共享数据

    @RequestMapping("/application")
    public String testApplication(HttpSession session){
        ServletContext application = session.getServletContext();
        application.setAttribute("testApplicationScope","hello,application");
        return "success";
    }

注意:

  • request域每次请求都会创建request对象,请求结束就销毁
  • session域关闭浏览器时才会销毁
  • application域服务器关闭才会销毁

以上方式,我不管我们使用哪个方式往域对象中共享数据,不管用什么方式设置逻辑视图,最终都会封装到一个ModelAndView中。

6 SpringMVC的视图

SpringMVC 中的视图是 View 接口,视图的作用渲染数据,将模型 Model中的数据展示给用户,SpringMVC视图的种类很多,默认有转发视图和重定向视图。若使用的视图技术为Thymeleaf ,在 SpringMVC 的配置文件中配置了 Thymeleaf 的视图解析器,由此视图解析器解析之后所得到的是ThymeleafView。

6.1 ThymeleafView

当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被 SpringMVC 配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式实现跳转。我们之前一直使用的就是ThymeleafView。
    @RequestMapping("/thymeleaf")
    public String testThymeleaf(){
        return "success";
    }

6.2 转发视图

还记得我们之前学过转发和重定向,它们的区别是:

  • 重定向向服务器发送一个请求收到响应后再次向一个新地址返送请求;转发是收到请求后跳转到一个新的地址。重定向至少请求两次;转发请求一次。
  • 重定向会改变地址;转发不会改变地址
  • 重定向两次请求不共享数据;转发一次请求共享数据
  • 重定向可以跳转到任意URL,转发只能跳转到本站点资源
SpringMVC 中默认的转发视图是 InternalResourceView
当控制器方法中所设置的视图名称以 "forward:" 为前缀时,创建 InternalResourceView 视图,此时的视图名称不会被SpringMVC 配置文件中所配置的视图解析器解析,而是会将前缀 "forward:" 去掉,剩余部分作为最终路径通过转发的方式实现跳转
例如:
    @RequestMapping("/forward")
    public String testForward(){
        return "forward:/thymeleaf";
    }

我们转发到上一个资源路径。因为我们前端页面使用了ThymeleafView语言,所以必须要使用ThymeleafView进行渲染才能识别,我们只能这样转发,不能直接转发到一个资源

6.3 重定向视图

SpringMVC 中默认的重定向视图是 RedirectView
当控制器方法中所设置的视图名称以 "redirect:" 为前缀时,创建 RedirectView 视图,此时的视图名称不会被SpringMVC 配置文件中所配置的视图解析器解析,而是会将前缀 "redirect:" 去掉,剩余部分作为最终路径通过重定向的方式实现跳转
    @RequestMapping("/redirect")
    public String testRedirect(){
        return "redirect:/thymeleaf";
    }

注意我们这里的转发路径没有写上下文路径,它会自动添加。

6.4 视图控制器view-controller

当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用 view-controller标签进行表示
<mvc:view-controller path="/" view-name="index"/>

这里的path设置处理的请求地址,view-name设置请求地址所对应的视图名称

但是我们使用这个方法访问index页面发现其它地址无法访问了。这是因为试图控制器为当前的请求设置视图名称实现页面跳转,若设置视图控制器,则只有视图控制器所设置的请求会被处理,其它请求全部404,此时需要配置一个标签<mvc:annotation-driven/>就好了。

7 RESTful

7.1 RESTful简介

REST Re presentational S tate T ransfer ,表现层资源状态转移。
①资源
资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个 可命名的抽象概念。因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西,可以将资源设计的要多抽象有多抽象,只要想象力允许而且客户端 应用开发者能够理解。与面向对象设计类似,资源是以名词为核心来组织的,首先关注的是名词。一个资源可以由一个或多个URI 来标识。 URI 既是资源的名称,也是资源在 Web 上的地址。对某个资源感兴趣的客户端应用,可以通过资源的URI 与其进行交互。
②资源的表述
资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端 - 服务器端之间转移(交
换)。资源的表述可以有多种格式,例如 HTML/XML/JSON/ 纯文本 / 图片 / 视频 / 音频等等。资源的表述格式可以通过协商机制来确定。请求- 响应方向的表述通常使用不同的格式。
③状态转移
状态转移说的是:在客户端和服务器端之间转移( transfer )代表资源状态的表述。通过转移和操作资源的表述,来间接实现操作资源的目的。

7.2 RESTful的实现

具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GETPOSTPUTDELETE

它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE用来删除资源。

REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。如下:​​​​​​​

我们来演示一下

7.3 RESTful之查询和保存功能

查询信息我们使用get请求:

<a th:href="@{/user}">查询所有信息</a>
<a th:href="@{/user/1}">查询id为1的信息</a>
    @RequestMapping(value = "/user",method = RequestMethod.GET)
    public String getAll(){
        System.out.println("查询所有信息成功");
        return "success";
    }

    @RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
    public String getById(@PathVariable("id") Integer id){
        System.out.println("查询id为"+id+"的信息成功");
        return "success";
    }

没什么问题,这两个我们在之前都使用过

保存信息我们使用post请求:

<form th:action="@{/user}" method="post">
    <input type="submit" value="保存信息">
</form>
    @RequestMapping(value = "/user",method = RequestMethod.POST)
    public String save(){
        System.out.println("保存信息成功");
        return "success";
    }

这也没什么问题,将请求都改为post就好了。

发现一个有意思的事情,之前我们说请求控制器中的路径不能用一样的,因为当浏览器发送那个路径的请求时就不知道该去访问哪个。但是我们发现即使路径一样 但是请求方式不一样依然没问题,比如上面的一个是get请求一个是post请求但是路径都是user。

7.4 RESTful之删除和更新功能

删除使用delete请求方式,但是我们没学过,这个咋搞?我们直接在form表单的method属性上修改为delete行不行?经我们测试不行。

浏览器只能发送get和post请求要想使用get和post之外的请求方式我们需要以下操作:

SpringMVC 提供了 HiddenHttpMethodFilter 帮助我们 POST 请求转换为 DELETE PUT 请求 HiddenHttpMethodFilter 处理 put delete 请求的条件:
  • 当前请求的请求方式必须为post
  • 当前请求必须传输请求参数_method
满足以上条件, HiddenHttpMethodFilter 过滤器就会将当前请求的请求方式转换为请求参数
_method 的值,因此请求参数 _method 的值才是最终的请求方式
web.xml 中注册 HiddenHttpMethodFilter
    <!--注册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>

注意以上过滤器必须放在CharacterEncodingFilter过滤器后面

前端代码如下:​​​​​​​

<form th:action="@{/user/2}" method="post">
    <input type="hidden" name="_method" value="delete">
    <input type="submit" value="删除信息">
</form>

method必须设置为post请求,下面的type设置为hidden,因为我们不需要输入什么信息,所以隐藏就好。name必须设置为_method,value就是用来设置我们最终需要的请求方式的。

    @RequestMapping(value = "/user/{id}",method = RequestMethod.DELETE)
    public String deleteById(@PathVariable("id") Integer id){
        System.out.println("删除id为"+id+"信息成功");
        return "success";
    }

更新信息使用put请求,前端代码我们只需修改value部分即可

<form th:action="@{/user}" method="post">
    <input type="hidden" name="_method" value="put">
    <input type="submit" value="删除信息">
</form>
    @RequestMapping(value = "/user",method = RequestMethod.PUT)
    public String update(){
        System.out.println("更新信息成功");
        return "success";
    }

8 RESTful案例

8.1 准备工作

搭建环境:我们还是用之前的环境
准备实体类:
package com.itzw.demo.pojo;

public class Employee {
    private Integer id;
    private String lastName;

    private String email;
    //1 male, 0 female
    private Integer gender;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getGender() {
        return gender;
    }

    public void setGender(Integer gender) {
        this.gender = gender;
    }

    public Employee(Integer id, String lastName, String email, Integer gender) {
        super();
        this.id = id;
        this.lastName = lastName;
        this.email = email;
        this.gender = gender;
    }

    public Employee() {
    }
}

准备dao模拟数据,本次案例没有连接数据库:

package com.itzw.demo.dao;

import com.itzw.demo.pojo.Employee;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

@Repository
public class EmployeeDao {
    private static Map<Integer, Employee> employees = null;

    static{
        employees = new HashMap<Integer, Employee>();

        employees.put(1001, new Employee(1001, "E-AA", "aa@163.com", 1));
        employees.put(1002, new Employee(1002, "E-BB", "bb@163.com", 1));
        employees.put(1003, new Employee(1003, "E-CC", "cc@163.com", 0));
        employees.put(1004, new Employee(1004, "E-DD", "dd@163.com", 0));
        employees.put(1005, new Employee(1005, "E-EE", "ee@163.com", 1));
    }

    private static Integer initId = 1006;

    /**
     * 保存或修改信息
     * @param employee
     */
    public void save(Employee employee){
        if(employee.getId() == null){
            employee.setId(initId++);
        }
        employees.put(employee.getId(), employee);
    }

    /**
     * 获取所有信息
     * @return
     */
    public Collection<Employee> getAll(){
        return employees.values();
    }

    /**
     * 根据id获取信息
     * @param id
     * @return
     */
    public Employee get(Integer id){
        return employees.get(id);
    }

    /**
     * 删除信息
     * @param id
     */
    public void delete(Integer id){
        employees.remove(id);
    }

}

8.2 功能清单

8.3 访问首页

配置view-controller:

<mvc:view-controller path="/" view-name="index"/>
编写首页:
<a th:href="@{/employee}">访问员工信息</a>

8.4 查询所有员工信息

我们上面的首页访问员工信息,路径是/employee,我们在控制层查询信息:

package com.itzw.demo.controller;

import com.itzw.demo.dao.EmployeeDao;
import com.itzw.demo.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.util.Collection;

@Controller
public class EmployeeController {

    @Autowired
    private EmployeeDao employeeDao;

    @RequestMapping(value = "/employee",method = RequestMethod.GET)
    public String getAllEmployee(Model model){
        Collection<Employee> allEmployee = employeeDao.getAll();
        model.addAttribute("allEmployee",allEmployee);
        return "employee_list";
    }
}

在employee_list.html展示信息:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>员工信息</title>
</head>
<body>
<table border="1">
    <tr>
        <th colspan="5">员工信息</th>
    </tr>
    <tr>
        <th>编号</th>
        <th>姓名</th>
        <th>邮箱</th>
        <th>性别</th>
        <th>选项</th>
    </tr>
    <!--${allEmployee}是(接收到的)员工集合,employee是集合中的一个员工-->
    <tr th:each="employee : ${allEmployee}">
        <td th:text="${employee.id}"></td>
        <td th:text="${employee.lastName}"></td>
        <td th:text="${employee.email}"></td>
        <td th:text="${employee.gender}"></td>
        <td>
            <a href="">删除</a>
            <a href="">修改</a>
        </td>
    </tr>
</table>
</body>
</html>

8.5 添加员工信息

我们先跳转到添加信息页面:

    @RequestMapping(value = "/to/add",method = RequestMethod.GET)
    public String addEmployeePage(){
        return "employee_add";
    }

编写employee_add.html页面:

注意添加信息要使用post请求了

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加信息</title>
</head>
<body>
<h1>添加信息页面</h1>
<form th:action="@{/addEmployee}" method="post">
    姓名:<input type="text" name="lastName"><br>
    邮件:<input type="text" name="email"><br>
    性别:<input type="radio" name="gender" value="1">男
    <input type="radio" name="gender" value="0">女<br>
    <input type="submit" value="添加">
</form>
</body>
</html>
    @RequestMapping(value = "/addEmployee",method = RequestMethod.POST)
    public String addEmployee(Employee employee){
        //保存信息
        employeeDao.save(employee);
        //重定向到信息列表页面
        return "redirect:/employee";
    }

8.6 修改信息

注意:修改时的路径要如下修改

<a th:href="@{'/updateEmployeePage/'+${employee.id}}">修改</a>

不能直接在后面加上employee.id,这浏览器会认为是一个名为“employee.id”的路径,我们需要用${}括起来,然后前面的路径要用引号括起来。

我们还是先跳转到更新界面,这里需要回显数据:

    @RequestMapping(value = "/updateEmployeePage/{id}",method = RequestMethod.GET)
    public String updateEmployeePage(@PathVariable("id") Integer id,Model model){
        //获取到员工信息
        Employee employee = employeeDao.get(id);
        //响应到域中
        model.addAttribute("employee",employee);
        return "employee_update";
    }

编写employee_update.html界面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>修改页面</title>
</head>
<body>
<h1>修改员工信息</h1>
<form th:action="@{/updateEmployee}" method="post">
    <input type="hidden" name="_method" value="put">
    <input type="hidden" name="id" th:value="${employee.id}">
    姓名:<input type="text" name="lastName" th:value="${employee.lastName}"><br>
    邮箱:<input type="text" name="email" th:value="${employee.email}"><br>
    性别:<input type="radio" name="gender" value="1" th:field="${employee.gender}">男
    <input type="radio" name="gender" value="0" th:field="${employee.gender}">女<br>
    <input type="submit" value="修改">
</form>
</body>
</html>

注意我们需要回显数据所以注意这里的value值的修改,还有在回显性别时使用的属性(th:field)这里我们使用的是put请求,注意如何设置put请求。其中id值我们可以隐藏,因为它不需要修改。

    @RequestMapping(value = "/updateEmployee",method = RequestMethod.PUT)
    public String updateEmployee(Employee employee){
        //修改信息
        employeeDao.save(employee);
        return "redirect:/employee";
    }

修改完还是重定向到信息列表界面方便我们查看

比如我修改第一位员工信息 :

8.7 删除信息

删除信息会有一点麻烦,因为我们需要使用delete请求,但是删除操作不像前面的修改操作需要先跳转到一个界面然后再进行修改操作,我们需要使用vue语法,但是我不会,所以这次先跳转到一个界面然后再进行delete请求:

<a th:href="@{'/deleteEmployeePage/'+${employee.id}}">删除</a>

接收到信息跳转到一个新的页面:

    @RequestMapping(value = "/deleteEmployeePage/{id}",method = RequestMethod.GET)
    public String deleteEmployeePage(@PathVariable("id") Integer id,Model model){
        //获取到员工信息
        Employee employee = employeeDao.get(id);
        //响应到域中
        model.addAttribute("employee",employee);
        return "employee_delete";
    }

编写employee_delete.html页面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>删除信息</title>
</head>
<body>
<h1>删除信息页面</h1>
<form th:action="@{'/deleteEmployee/'+${employee.id}}" method="post">
    <input type="hidden" name="_method" value="delete">
    是否删除id为<p th:text="${employee.id}"></p>的信息?
    <input type="submit" value="确认删除">
</form>
</body>
</html>

执行删除操作:

    @RequestMapping(value = "/deleteEmployee/{id}",method = RequestMethod.DELETE)
    public String deleteEmployee(@PathVariable("id") Integer id){
        //删除信息
        employeeDao.delete(id);
        return "redirect:/employee";
    }

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值