SpringMVC learning code 3

Tue, Mar 16, 2021 5-minute read

This is my learning note about SpringMVC with Code Examples .

SpringMVC 的视图

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

SpringMVC 视图的种类很多,默认有转发视图重定向视图

当工程引入 jstl 的依赖,转发视图会自动转换为 JstlView

若使用的视图技术为 Thymeleaf,在 SpringMVC 的配置文件中配置了 Thymeleaf 的视图解析器,由此视图解析器解析之后所得到的是 ThymeleafView

、ThymeleafView

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

、转发视图

SpringMVC 中默认的转发视图是 InternalResourceView

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

当控制器方法中所设置的视图名称以"forward:“为前缀时,创建 InternalResourceView 视图,此时的视图名称不会被 SpringMVC 配置文件中所配置的视图解析器解析,而是会将前缀"forward:“去掉,剩余部分作为最终路径通过转发的方式实现跳转

例如"forward:/",“forward:/employee”

@RequestMapping("/testForward")
public String testForward(){
    return "forward:/testHello";
}

、重定向视图

游览器发送了两次请求, 可以跨域

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

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

例如"redirect:/",“redirect:/employee”

@RequestMapping("/testRedirect")
public String testRedirect(){
    return "redirect:/testHello";
}

注:

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

、视图控制器 view-controller

当控制器方法中,仅仅用来实现页面跳转,没有任何其他请求,即只需要设置视图名称时,可以将处理器方法使用 view-controller 标签进行表示

<!--
	path:设置处理的请求地址
	view-name:设置请求地址所对应的视图名称
-->
<mvc:view-controller path="/testView" view-name="success"></mvc:view-controller>

注:

当 SpringMVC 中设置任何一个 view-controller 时,其他控制器中的请求映射将全部失效,此时需要在 SpringMVC 的核心配置文件中设置开启 mvc 注解驱动的标签:

<mvc:annotation-driven />

RESTful

、RESTful 简介

REST:Representational State Transfer,表现层资源状态转移。

、RESTful 的实现

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

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

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

操作 传统方式 REST 风格
查询操作 getUserById?id=1 user/1–>get 请求方式
保存操作 saveUser user–>post 请求方式
删除操作 deleteUser?id=1 user/1–>delete 请求方式
更新操作 updateUser user–>put 请求方式

、RESTful 代码

controller

image

springmvc

image

springmvc

、HiddenHttpMethodFilter

“method = RequestMethod.PUT” 是无法修改数据的,默认 get 方法, 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);

image

springmvc

RESTful Example

、搭建: 模块 -》 web.xml (2 filter/ servlet) -> springMVC.xml –>controller –>springMVC.xml(component-scan/ thymeleaf)

、功能清单

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

controller, dao 对象

image

springmvc

要用到依赖注入, 在 controller 文件上,加入 private EmployeeDao employeeDao

在 springMVC.xml 配置文件上加入扫描包

<!-- 扫描组件   -->
    <context:component-scan base-package="com.learn.rest"/>

具体功能:访问首页

image

springmvc

具体功能:实现列表功能

没有用 axois, 所以要页面显示数据,必须要通过控制层处理

image

springmvc

image

springmvc

、具体功能:删除

方法 1:

image

springmvc

方法 2:

delete

在 static 下面创建一个 js 文件,不能放 templates 下面,因为默认都是转发

删除超链接绑定点击事件

引入 vue.js

<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>

删除超链接: 点击超链接, 取消默认行为, 而去提交表单, post, “_method”, 将请求方式转为 delete

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

控制器方法

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

springmvc

image

springmvc

报错

image

springmvc

重新打包后,依然 404

20:54:29.327 [http-nio-8080-exec-38] DEBUG org.springframework.web.servlet.DispatcherServlet - GET "/springMVC/employee/1001", parameters={}
20:54:29.331 [http-nio-8080-exec-38] WARN org.springframework.web.servlet.PageNotFound - No mapping for GET /springMVC/employee/1001
DispatcherServlet 无法访问静态资源!!!! 要用默认 servlet

image

springmvc

405 请求行中接收的方法由源服务器知道,但目标资源不支持

原来是注册器路径写错了

@RequestMapping(value = “/employee/${id}”, method = RequestMethod.DELETE)

参数多加个 $,

@RequestMapping(value = “/employee/{id}”, method = RequestMethod.DELETE)

上述是日志功能, logback-classic

、具体功能:添加功能

页面无法跳转:

image

springmvc

image

springmvc

、具体功能:修改功能

image

springmvc

修改超链接

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

控制器方法

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

创建 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>

控制器方法

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

修改方式要先回显,然后确定请求方式,因为是 put,所以要特殊处理,确保表单 method 是 post, 里面有个 hidden input, value 是 put

HttpMessageConverter

HttpMessageConverter,报文信息转换器,将请求报文转换为 Java 对象,或将 Java 对象转换为响应报文

HttpMessageConverter 提供了两个注解和两个类型:@RequestBody,@ResponseBody,RequestEntity,ResponseEntity

@RequestBody, RequestEntity 将请求报文转换为 Java 对象,

@ResponseBody, ResponseEntity 将 Java 对象转换为响应报文

、@RequestBody

@RequestBody 可以获取请求体,需要在控制器方法设置一个形参,使用@RequestBody 进行标识,当前请求的请求体就会为当前注解所标识的形参赋值

<form th:action="@{/testRequestBody}" method="post">
    用户名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    <input type="submit">
</form>
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String requestBody){
    System.out.println("requestBody:"+requestBody);
    return "success";
}

、RequestEntity

RequestEntity 封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过 getHeaders()获取请求头信息,通过 getBody()获取请求体信息

@RequestMapping("/testRequestEntity")
public String testRequestEntity(RequestEntity<String> requestEntity){
    // 当前requestEntity 表示整个请求报文的信息
    System.out.println("requestHeader:"+requestEntity.getHeaders());
    System.out.println("requestBody:"+requestEntity.getBody());
    return "success";
}
输出结果
����ͷ: [host:"localhost:8080", connection:"keep-alive", content-length:"25", cache-control:"max-age=0", sec-ch-ua:"" Not;A Brand";v="99", "Google Chrome";v="97", "Chromium";v="97"", sec-ch-ua-mobile:"?0", sec-ch-ua-platform:""Windows"", upgrade-insecure-requests:"1", origin:"http://localhost:8080", user-agent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36", accept:"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", sec-fetch-site:"same-origin", sec-fetch-mode:"navigate", sec-fetch-user:"?1", sec-fetch-dest:"document", referer:"http://localhost:8080/springMVC/", accept-encoding:"gzip, deflate, br", accept-language:"zh-CN,zh;q=0.9", Content-Type:"application/x-www-form-urlencoded;charset=UTF-8"]
������: username=123&password=erg

、@ResponseBody

@ResponseBody 用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器

通过 servletAPI 响应

    @RequestMapping("/testResponse")
    public void testResponse(HttpServletResponse response) throws IOException {
        response.getWriter().print("hello");
    }

结果:浏览器页面显示 hello

通过 SpringMVC 注解 响应


    @RequestMapping("/testResponse")
    @ResponseBody
    public String testResponse(){
       return "hello";

    }

结果:浏览器页面显示 hello, 直接把 hello 响应到游览器

、SpringMVC 处理 json

@ResponseBody 处理 json 的步骤:

a>导入 jackson 的依赖

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.1</version>
</dependency>

在 SpringMVC 的核心配置文件中开启 mvc 的注解驱动,此时在 HandlerAdaptor 中会自动装配一个消息转换器:MappingJackson2HttpMessageConverter,可以将响应到浏览器的 Java 对象转换为 Json 格式的字符串

<mvc:annotation-driven />

在处理器方法上使用@ResponseBody 注解进行标识

将 Java 对象直接作为控制器方法的返回值返回,就会自动转换为 Json 格式的字符串

    @RequestMapping("/testResponseUser")
    @ResponseBody
    public User testResponseUser(){
        return new User(1001, "aa", "123", "female", 20);
    }

浏览器的页面中展示的结果:

{“id”:1001,“username”:“aa”,“password”:“123”,“sex”:“female”,“age”:20}

、SpringMVC 处理 ajax

请求超链接

<div id="app">
	<a th:href="@{/testAjax}" @click="testAjax">testAjax</a><br>
</div>

通过 vue 和 axios 处理点击事件

<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
<script type="text/javascript" th:src="@{/static/js/axios.min.js}"></script>
<script type="text/javascript">
    var vue = new Vue({
        el:"#app",
        methods:{
            testAjax:function (event) {
                axios({
                    method:"post",
                    url:event.target.href,
                    params:{
                        username:"admin",
                        password:"123456"
                    }
                }).then(function (response) {
                    alert(response.data);
                });
                event.preventDefault();
            }
        }
    });
</script>

控制器方法

@RequestMapping("/testAjax")
@ResponseBody
public String testAjax(String username, String password){
    System.out.println("username:"+username+",password:"+password);
    return "hello,ajax";
}

**点击链接后,页面无需跳转就可以显示信息, 注意在 index 页面, methods 写成了 method, 导致 param 传不过去

image

springmvc

、@RestController 注解

@RestController 注解是 springMVC 提供的一个复合注解,标识在控制器的类上,就相当于为类添加了@Controller 注解,并且为其中的每个方法添加了@ResponseBody 注解, 基本微服务中 springboot 都要加

、ResponseEntity

ResponseEntity 用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文


感谢尚硅谷 springmvc1

springmvc2

springmvc4

springmvc5

spring

javaweb