五 域对象共享数据

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

  • 就是使用servlet的原生api向request对象中按照kv对的方式存放数据

    1. // 使用servletAPi 想request域对象中共享数据
    2. @RequestMapping("/testRequestByServletAPI")
    3. public String testRequestServlet(HttpServletRequest httpServletRequest) {
    4. httpServletRequest.setAttribute("testRequestScope", "hello,servletAPI");
    5. return "success";
    6. }
    7. // 吧 servlet的reqeust对象作为形参传入 ,之后调用并进行kv赋值

    2 使用ModelAndView 向request域对象共享数据

    ```java @RequestMapping(“/testModelAndView”) public ModelAndView testModelAndView() {

    1. // 如果是modelandViedw 的方式的话则一定需要给予model的属性以及view的视图返回名称
    2. // 这种方式必须将 modelandview 作为返回值返回给前端的dispatchservlet 进行解析
    3. ModelAndView modelAndView = new ModelAndView();
    4. // 处理model数据,即向请求域request共享数据
    5. modelAndView.addObject("testRequestScope", "hello,ModelAndView");
    6. // 设置返回的试图名称
    7. modelAndView.setViewName("success");
    8. System.out.println(modelAndView.getClass().getName());
    9. //org.springframework.web.servlet.ModelAndView
    10. return modelAndView;

    }

— 对应前端页面编码

<!DOCTYPE html>

这是一个成功页面

<a name="VFyeF"></a>
### 前端展示

<a name="U72Hi"></a>
## 3 使用Model 向request域对象共享数据
```java
    // 通过model向request域共享数据
    @RequestMapping("/testModel")
    public String testModel(Model model) {
        model.addAttribute("testRequestScope", "hello,model");
        System.out.println(model.getClass().getName());
        // org.springframework.validation.support.BindingAwareModelMap
        return "success";
    }
-- 前端同前面相同

前端页面展示

4 使用map 向域对象共享数据

    // 使用map向request域对象共享数据
    @RequestMapping("/testMap")
    public String testMap(Map<String, Object> map) {
        map.put("testRequestScope", "hello ,map");
        System.out.println(map);

        return "success";
    }

5 使用ModelMap 向request域对象共享数据

    // modelMap 向request域对象中共享数据
    @RequestMapping("/testModelMap")
    public String testModelMap(ModelMap modelMap) {
        modelMap.addAttribute("testRequestScope","hellp, modelMap");
        System.out.println(modelMap.getClass().getName());
        // org.springframework.validation.support.BindingAwareModelMap
        return "success";
    }

6 Model, ModelMap,map 的关系

  • model.modelMap,map 类型参数本质上都是 BindingAwareModelMap类型的

    public interface Model{}
    public class ModelMap extends LinkedHashMap<String, Object> {}
    public class ExtendedModelMap extends ModelMap implements Model {}
    public class BindingAwareModelMap extends ExtendedModelMap {}
    

    7 向session域对象共享数据

    @RequestMapping("/testSession")
      public String testSession(HttpSession httpSession){
          httpSession.setAttribute("testRequestScope","hello ,sessiona方式 添加域数据");
          return "success";
      }
    

    8 向application域对象共享数据

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

    六 SpringMVC 的视图

  • springMVC 中的视图是view接口,视图的作用渲染数据,将模型Model中的数据展示给用户

  • springMVC 视图的种类有很多,默认有转发视图和重定向视图
  • 当工程引入jstl的依赖,转发视图会自动转换为 JstlView
  • 若使用的视图技术为 Thymeleaf 在SpringMVC的配置文件中配置了Thymeleaf的视图解析器,由此视图解析器解析之后得到的是ThymeleafView

    1 ThymeleafView

  • 当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被springMvc配置文件中配置的视图解析器解析,视图名称拼接视图前缀和后缀得到最终路径,会通过转发的方式实现跳转

      @RequestMapping("/testThymeleafView")
      public String testThymeleafView() {
    
          return "success"; // 通过了视图解析器
          // 如果返回的字符串前面没有任何标注,则默认走解析器
      }
    

    2 转发视图

  • springMVC 中默认的转发视图是 InternalResourceView

  • SpringMVC中创建转发视图的情况

    • 当控制器方法中所设置的视图以 “forward:/viewName” 这样的格式进行设置的时候,
    • 这是视图名称不会被springMVC配置文件中所配置的视图解析器解析,而是会将前缀 forward: 去掉,剩余部分作为最终路径通过转发的方式实现跳转
    • 例如”forward:/“,”forward:/employee”

      
      @RequestMapping("/testForward")
      public String testForward() {
      
         return "forward:/testThymeleafView"; // 通过了视图解析器
         // 加上forward 的话则是转发到其他的请求解析器进一步解析
      }
      

      3 重定向视图

  • SpringMVC 中默认的重定向视图是 RedirectView

  • 当控制器方法中所设置的视图名称以 “redirect:” 为前缀时,创建RedirectView视图,此时视图的名称不会被springMVC配置文件所配置的视图解析器解析,而是会将前缀 “redirect:”去掉,剩余部分作为最终路径通过重定向方式实现跳转
  • 例如”redirect:/“,”redirect:/employee”

      @RequestMapping("/testRedirect")
      public String testRedirect(){
    
          return "redirect:/";  // 表示重定向到首页
          // 重定向也是一个具体的请求,而非一个视图文件
      }
    

    转发 与 重定向的区别

    // 重定向与转发的区别 TODO
      // 重定向相当于浏览器发送了两次请求,前后的 域对象不是同一个
      // 转发则是发送了一次请求,前后域对象为同一个,对象内的数据可以共享
      // 转发不能跨域,重定向可以跨域
    
      /*
      1、请求次数:重定向是浏览器向服务器发送一个请求并收到响应后再次向一个新地址发出请求,转发是服务器收到请求后为了完成响应跳转到一个新的地址;重定向至少请求两次,转发请求一次;
    
      2、地址栏不同:重定向地址栏会发生变化,转发地址栏不会发生变化;
    
      3、是否共享数据:重定向两次请求不共享数据,转发一次请求共享数据(在request级别使用信息共享,使用重定向必然出错);
    
      4、跳转限制:重定向可以跳转到任意URL,转发只能跳转本站点资源;
    
      5、发生行为不同:重定向是客户端行为,转发是服务器端行为;
    
       */
    

    注:
    重定向视图在解析时,会先将redirect:前缀去掉,然后会判断剩余部分是否以/开头,若是则会自动拼接上下文路径

    4 视图控制器 view-controller

  • 当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用view-controller 标签进行配置。

  • 实际应用多为页面之间的跳转

      <mvc:view-controller path="/toAdd" view-name="employee_add"></mvc:view-controller>
      <mvc:view-controller path="/" view-name="index"></mvc:view-controller>
      <mvc:view-controller path="/msg_convert" view-name="msg_convert"></mvc:view-controller>
      <mvc:view-controller path="/file" view-name="file"></mvc:view-controller>
    

    七 RESTful 风格

    1 RESTful 简介

  • Rest: Representational State Transfer,表现层资源状态转移

    a>资源

  • 资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成,每个资源是服务器上一个可以命名的抽象概念

  • 因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件,数据库中的一张表等等具体的东西
  • 可以将资源涉及的要多抽象就有多抽象,只要想象力允许而且客户端应用开发者可以理解。与面向对象涉及蕾丝,资源是以名次为核心来组织的,首先关注是名次,一个资源可以由一个或者多个URI来标示,URI既是资源的名称,也是资源在web上的地址,对某个资源感兴趣的客户端应用,可以通过资源的URI与其进行交互

    b>资源的表述

  • 资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端-服务端之间转移(交换

  • 资源的标示可以有多种形式 ,例如HTML/XML/JSON/纯文本/图片/视频/音频等等。
  • 资源的表达格式可以通过协商机制来确定,请求-响应方向的表述通常使用不同的格式

    c>状态转移

  • 状态转移: 在客户端和服务端之间转移(transfer)代表资源状态的表述,通过转移和炒作资源的表述,来间接实现操作资源的目的

    2 RESTful 的实现

  • 具体说,就是HTTP协议里面,四个代表操作方式的动词 GET POST PUT DELETE

  • GET 用来获取 POST 用来新建 PUT 用来更新 DELETE 用来删除资源
  • REST 风格提倡url地址使用统一的风格涉及,从钱到后各个单词使用斜杠分开,不使用问号kv对的方式携带请求参数,而是将要发送给服务器的数据作为url地址的一部分,以保证整体风格的一致性 | 操作 | 传统方式 | REST风格 | | —- | —- | —- | | 查询操作 | getUserById?id=1 | user/1—>get请求方式 | | 保存操作 | saveUser | user—>post请求方式 | | 删除操作 | deleteUser?id=1 | user/1—>delete请求方式 | | 更新操作 | updateUser | user—>put请求方式 |

3 HiddenHttpMethodFilter

  • 由于浏览器只支持发送get和post方式的请求,俺么如何发送put与delete方式的请求呢?
  • springMVC 提供了 HiddenHttpMethodFIlter 将post请求转换为delete 或者put请求
  • HiddenHttpMethodFilter 处理put和delete请求的条件
    • a> 当前请求的请求方式必须为post
    • b> 当前请求必须传输请求参数 _method
  • 满足以上条件 ,HiddenHttpMethodFIlter 过滤器就会将当前请求的请求方式转换为请求参数 _method 的值,因此请求参数_method 的值才是最终的请求方式

    # 在web.xml 中注册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>
    

    注:
    目前为止,SpringMVC中提供了两个过滤器:CharacterEncodingFilter和HiddenHttpMethodFilter
    在web.xml中注册时,必须先注册CharacterEncodingFilter,再注册HiddenHttpMethodFilter

    原因

  • 在 CharacterEncodingFIlter 中通过 request.setCharacterEncoding(encoding)方法设置字符集的 request.setCharacterEncoding(encoding) 方法要求前面不能有任何获取请求参数的操作

  • 而 HiddenHttpMethodFilter 恰恰有一个获取请求方式的操作:

    String paramValue = request.getParameter(this.methodParam);
    

    八 RESTful 案例

    1 准备工作

  • 搭建环境

  • 准备实体类 ```java package com.atguigu.mvc.bean;

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模拟器
```java
package com.atguigu.mvc.dao;

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

import com.atguigu.mvc.bean.Employee;
import org.springframework.stereotype.Repository;


@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;

   public void save(Employee employee){
      if(employee.getId() == null){
         employee.setId(initId++);
      }
      employees.put(employee.getId(), employee);
   }

   public Collection<Employee> getAll(){
      return employees.values();
   }

   public Employee get(Integer id){
      return employees.get(id);
   }

   public void delete(Integer id){
      employees.remove(id);
   }
}

2 功能清单

功能 URL 地址 请求方式
访问首页√ / GET
查询全部数据√ /employee GET
删除√ /employee/2 DELETE
跳转到添加数据页面√ /toAdd GET
执行保存√ /employee POST
跳转到更新数据页面√ /employee/2 GET
执行更新√ /employee PUT

3 具体功能 : 访问首页

  • 使用前面提到的 view-controller 在配置文件中直接进行注册

    <mvc:view-controller path="/" view-name="index"/>
    
  • 创建页面前端代码编写 ```java <!DOCTYPE html> 查看所有员工信息

<a name="VlkwD"></a>
## 4 具体功能 : 查询所有员工数据
<a name="l7cId"></a>
### a>控制器方法
```java
    @RequestMapping(value = "/employee", method = RequestMethod.GET)
    public String getAllEmployee(Model model){
        Collection<Employee> employeeList = employeeDao.getAll();
        model.addAttribute("employeeList", employeeList);
        return "employee_List";
    }

b>前端页面代码编写

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Employee Info</title>
    <script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
</head>
<body>

    <table border="1" cellpadding="0" cellspacing="0" style="text-align: center;" id="dataTable">
        <tr>
            <th colspan="5">Employee Info</th>
        </tr>
        <tr>
            <th>id</th>
            <th>lastName</th>
            <th>email</th>
            <th>gender</th>
            <th>options(<a th:href="@{/toAdd}">add</a>)</th>
        </tr>
        <tr th:each="employee : ${employeeList}">
            <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 class="deleteA" @click="deleteEmployee" th:href="@{'/employee/'+${employee.id}}">delete</a>
                <a th:href="@{'/employee/'+${employee.id}}">update</a>
            </td>
        </tr>
    </table>
</body>
</html>

5 具体功能 : 删除

a>创建处理delete请求方式的表单

  • 至于为什么使用表单来发送delete请求,是因为浏览器中的表单只支持post请求,但是我们需要使用rest风格,则需要以表单的形式,来发起,并想后端传输hiddenHttpMethod的参数

    <!-- 作用:通过超链接控制表单的提交,将post请求转换为delete请求 -->
    <form id="delete_form" method="post">
      <!-- HiddenHttpMethodFilter要求:必须传输_method请求参数,并且值为最终的请求方式 -->
      <input type="hidden" name="_method" value="delete"/>
    </form>
    

    b>删除超链接绑定点击事件

  • 引入vue.js

    <script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
    
  • 删除超链接

    <a class="deleteA" @click="deleteEmployee" th:href="@{'/employee/'+${employee.id}}">delete</a>
    
  • 通过vue处理点击事件

    <script type="text/javascript">
      var vue = new Vue({
          el:"#dataTable",
          methods:{
              //event表示当前事件
              deleteEmployee:function (event) {
                  //通过id获取表单标签
                  var delete_form = document.getElementById("delete_form");
                  //将触发事件的超链接的href属性为表单的action属性赋值
                  delete_form.action = event.target.href;
                  //提交表单
                  delete_form.submit();
                  //阻止超链接的默认跳转行为
                  event.preventDefault();
              }
          }
      });
    </script>
    

    c>控制器方法

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

    6 具体功能 : 跳转到添加数据页面

    a>配置view-controller

  • 配置一个跳转页面的 path ,前端页面使用path作为超链接提供跳转

    <mvc:view-controller path="/toAdd" view-name="employee_add"></mvc:view-controller>
    

    b>创建employee_add.html

    ```java <!DOCTYPE html>

lastName:
email:
gender:male female

<a name="VLHV5"></a>
## 7 具体功能 : 执行保存
<a name="wObBd"></a>
### a>控制器方法
```java
    @RequestMapping(value = "/employee",method = RequestMethod.POST)
    public String addEmp(Employee employee){
        employeeDao.save(employee);
        return "redirect:/employee";
    }

8 具体功能 : 跳转到更新数据页面

a>修改超链接为

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

b>控制器方法

    @RequestMapping(value = "/employee/{id}",method = RequestMethod.GET)
    public String getEmployeeById(@PathVariable("id") Integer id ,Model model){
        Employee employee = employeeDao.get(id);
        model.addAttribute("employee",employee);
        return "employee_update";

    }

c>创建employee_update.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Update Employee</title>
</head>
<body>

<form th:action="@{/employee}" method="post">
    <input type="hidden" name="_method" value="put">
    <input type="hidden" name="id" th:value="${employee.id}">
    lastName:<input type="text" name="lastName" th:value="${employee.lastName}"><br>
    email:<input type="text" name="email" th:value="${employee.email}"><br>
    <!--
        th:field="${employee.gender}"可用于单选框或复选框的回显
        若单选框的value和employee.gender的值一致,则添加checked="checked"属性
    -->
    gender:<input type="radio" name="gender" value="1" th:field="${employee.gender}">male
    <input type="radio" name="gender" value="0" th:field="${employee.gender}">female<br>
    <input type="submit" value="update"><br>
</form>

</body>
</html>

9 具体功能 : 执行更新

a>控制器方法

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