Rest
2023年11月15日大约 9 分钟约 1725 字
Rest-优雅的url请求风格
基本介绍
REST:即 Representational State Transfer。(资源)表现层状态转化。是目前流行的请求方式。它结构清晰,很多网站采用。
HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应 四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。
实例
传统的请求方法:
getBook?id=1 GET; delete?id=1 GET; update POST; add POST
传统的 url 是通过参数来说明 crud 的类型,rest 是通过 get/post/put/delete 来说明 crud 的类型。
REST的核心过滤器
- 当前的浏览器只支持 post/get 请求,因此为了得到 put/delete 的请求方式需要使用 Spring 提供的 HiddenHttpMethodFilter 过滤器进行转换。
- HiddenHttpMethodFilter:浏览器 form 表单只支持 GET 与 POST 请求,而 DELETE、PUT 等 method 并不支持,Spring 添加了一个过滤器,可以将这些请求转换为标准的 http 方法,使得支持 GET、POST、PUT 与 DELETE 请求 。
- HiddenHttpMethodFilter 能对 post 请求方式进行转换,因此我们需要特别的注意这一点 。
- 这个过滤器需要在 web.xml 中配置。
Rest 风格的 url-完成增删改查
代码演示
修改 web.xml 添加 HiddenHttpMethodFilter
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--
1. 作用是把以 post 方式提交的delete和put请求进行转换
2. 配置url-pattern 是 /* 表示请求都经过 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>
<!--配置前端控制器/中央控制器/分发控制器
用户的请求都会经过它的处理
-->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置属性 contextConfigLocation,指定DispatcherServlet去操作的spring配置文件-->
<!--<init-param>-->
<!-- <param-name>contextConfigLocation</param-name>-->
<!-- <param-value>classpath:WEB-INF/applicationContext-mvc.xml</param-value>-->
<!--</init-param>-->
<!--在web项目启动时,就自动加载DispatcherServlet-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<!--
1.这里我们配置的url-pattern是 / ,表示用户的请求都经过 DispatcherServlet
2.这样配置也支持 rest 风格的url请求
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
修改 springDispatcherServlet-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"
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 http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--配置要自动扫描的包-->
<context:component-scan base-package="com.lzw.web"/>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置属性 suffix 和 prefix-->
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--加入两个常规配置-->
<!--支持SpringMVC的高级功能,别入JSR303校验,映射动态请求-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--将SpringMVC不能处理的请求,交给tomcat处理,比如css,js-->
<mvc:default-servlet-handler/>
</beans>
创建 web\rest.jsp
引入jQuery
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>Rest风格的crud操作案例</h3>
<br><hr>
<h3>rest风格的url 查询书籍[get]</h3>
<a href="user/book/200">点击查询书籍</a>
<br><hr>
<h3>rest风格的url 添加书籍[post]</h3>
<form action="user/book" method="post">
name:<input name="bookName" type="text"><br>
<input type="submit" value="添加书籍">
</form>
<br><hr>
<h3>rest风格的url, 删除一本书</h3>
<a href="user/book/600" id="deleteBook">删除指定id的书</a>
<form action="" method="post" id="hiddenForm">
<input type="hidden" name="_method"/>
</form>
<br><hr>
<h3>rest风格的url 修改书籍[put]~</h3>
<form action="user/book/666" method="post">
<input type="hidden" name="_method" value="PUT">
<input type="submit" value="修改书籍~">
</form>
</body>
</html>
创建 BookHandler.java
package com.lzw.web.rest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @author LiAng
* 处理 rest 风格的请求-增删改查
*/
@RequestMapping("/user")
@Controller
public class BookHandler {
//查询[GET]
@RequestMapping(value = "/book/{id}", method = RequestMethod.GET)
public String getBook(@PathVariable("id") String id){
System.out.println("查询书籍 id = " + id);
return "success";
}
}
完成GET测试
修改 BookHandler.java
//添加[POST]
@PostMapping(value = "/book")
public String addBook(String bookName) {
System.out.println("添加书籍 bookName== " + bookName);
return "success";
}
完成POST测试
(前端表单中输入中文,后端接收到的是乱码,后面处理)
修改 BookHandler.java
//删除[DELETE]
@RequestMapping(value = "/book/{id}", method = RequestMethod.DELETE)
public String delBook(@PathVariable("id") String id) {
System.out.println("删除书籍 id= " + id);
//return "success"; //[如果 这样返 回会报错 JSPs only permit GET POST or HEAD]
//1. redirect:/user/success 重定向
//2. 会被解析成 /springmvc/user/success
return "redirect:/user/success"; //重定向到一个没有指定 method 的 Handler 方 法
}
@RequestMapping(value = "/success")
public String successGenecal() {
return "success"; //由该方法 转发到 success.jsp 页面
}
修改 rest.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script type="text/javascript" src="script/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
$(function(){
$("#deleteBook").click(function(){
// alert("点击。。");
//自己定义个提交行为
$("#hiddenForm").attr("action", this.href);
$(":hidden").val("DELETE");
$("#hiddenForm").submit();
return false;//改变超链接的行为,不再提交
})
})
</script>
</head>
<body>
<h3>Rest风格的crud操作案例</h3>
<br><hr>
<h3>rest风格的url 查询书籍[get]</h3>
<a href="user/book/200">点击查询书籍</a>
<br><hr>
<h3>rest风格的url 添加书籍[post]</h3>
<form action="user/book" method="post">
name:<input name="bookName" type="text"><br>
<input type="submit" value="添加书籍">
</form>
<br><hr>
<h3>rest风格的url, 删除一本书</h3>
<%--
1. 默认情况下 <a href="user/book/600">删除指定id的书</a> 是get
2. 怎么样将 get 请求转成 springmvc 可以识别的 delete 就要考虑HiddenHttpMethodFilter机制
public static final String DEFAULT_METHOD_PARAM = "_method";
---------------------------------------------------
private static final List<String> ALLOWED_METHODS =
Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(),
HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
---------------------------------------------------
if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
if (ALLOWED_METHODS.contains(method)) {
requestToUse = new HttpMethodRequestWrapper(request, method);
}
}
}
3. 上面代码可以看到 HiddenHttpMethodFilter 过滤器可以对以Post方式提交的delete,put,patch进行转换成springmvc
识别的 RequestMethod.DELETE / RequestMethod.PUT /...
4. 我们需要将 get <a href="user/book/600">删除指定id的书</a> 以post方式提交给后端handler, 这样过滤器还会生效
5. 我们可以用jquery来处理-引入jquery
--%>
<a href="user/book/600" id="deleteBook">删除指定id的书</a>
<form action="" method="post" id="hiddenForm">
<input type="hidden" name="_method"/>
</form>
<br><hr>
<h3>rest风格的url 修改书籍[put]~</h3>
<form action="user/book/666" method="post">
<input type="hidden" name="_method" value="PUT">
<input type="submit" value="修改书籍~">
</form>
</body>
</html>
完成DELETE测试
修改 BookHandler.java
//修改[PUT]
@PutMapping(value = "/book/{id}")
public String updateBook(@PathVariable("id") String id) {
System.out.println("修改书籍 id=" + id);
return "redirect:/user/success"; //重定向到一个没有指定 method 的 Handler 方法
}
完成PUT测试
注意事项和细节说明
1、HiddenHttpMethodFilter,在将 post 转成 delete / put 请求时,是按_method 参数名来读取的
2、如果 web 项目是运行在 Tomcat 8 及以上,会发现被过滤成 DELETE 和 PUT 请求,到达控制器时能顺利执行,但是返回时(forward)会报 HTTP 405 的错误提示:消息 JSP 只允许 GET、POST 或 HEAD。(不用纠结,了解即可)
(1)解决方式 1:使用 Tomcat7
(2)解决方式 2:将请求转发(forward)改为请求重定向(redirect):重定向到一个 Handler, 由 Handler 转发到页面
