SpringMVC learning code 3
This is my learning note about SpringMVC with Code Examples .
- SpringMVC 的视图
- RESTful
- RESTful Example
- HttpMessageConverter
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
、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);
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 对象
要用到依赖注入, 在 controller 文件上,加入 private EmployeeDao employeeDao
在 springMVC.xml 配置文件上加入扫描包
<!-- 扫描组件 -->
<context:component-scan base-package="com.learn.rest"/>
具体功能:访问首页
具体功能:实现列表功能
没有用 axois, 所以要页面显示数据,必须要通过控制层处理
、具体功能:删除
方法 1:
方法 2:
在 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";
}
报错
重新打包后,依然 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
405 请求行中接收的方法由源服务器知道,但目标资源不支持
原来是注册器路径写错了
@RequestMapping(value = “/employee/${id}”, method = RequestMethod.DELETE)
参数多加个 $,
@RequestMapping(value = “/employee/{id}”, method = RequestMethod.DELETE)
上述是日志功能, logback-classic
、具体功能:添加功能
页面无法跳转:
、具体功能:修改功能
修改超链接
<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 传不过去
、@RestController 注解
@RestController 注解是 springMVC 提供的一个复合注解,标识在控制器的类上,就相当于为类添加了@Controller 注解,并且为其中的每个方法添加了@ResponseBody 注解, 基本微服务中 springboot 都要加
、ResponseEntity
ResponseEntity 用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文
感谢尚硅谷 springmvc1