文章目录
前言
REST(Representational State Transfer,表现层状态转移)是一种软件架构风格,它定义了一组设计原则和约束条件,用于创建Web服务。REST的核心思想是将网络资源视为一系列可以通过HTTP协议进行操作的抽象实体,这些实体以某种形式的表现层来展现其状态,客户端通过操作这些资源的状态来实现与Web服务的交互。
REST的主要原则包括:
-
客户端-服务器架构:将用户界面关注点与应用服务器关注点分离,允许它们独立进化。
-
无状态:服务器不会在客户端请求之间保留应用状态。每个请求都必须包含理解该请求所必需的信息(通常是通过包含先前服务器返回的所有状态)。
-
分层系统:允许将系统分解为若干层,每一层对下一层隐藏其内部实现细节。客户端不需要知道它与之交互的中间层的存在。
-
统一接口:通过四个接口约束(识别资源、通过表述操作资源、自描述消息、超媒体作为应用状态的引擎)来简化并解耦组件。
- 资源识别:每个资源都有一个唯一的URL。
- 通过表述操作资源:客户端通过HTTP方法对资源进行操作(如GET、POST、PUT、DELETE)。
- 自描述消息:消息必须能够自我描述,以便客户端能够正确理解和处理。
- 超媒体作为应用状态的引擎:客户端可以通过阅读返回的文档(通常包含超链接)来发现可以执行的操作。
-
按需代码(可选):通过下载并执行代码来扩展客户端功能。
RESTful Web服务使用HTTP协议的方法(GET、POST、PUT、DELETE等)对资源进行操作,并且通常返回JSON或XML格式的响应。RESTful API的设计目标是简单、可预测和易于使用,使得不同平台和语言之间的交互变得更加容易。
RESTful API的设计通常会遵循一些最佳实践,如使用适当的HTTP方法、使用名词作为资源路径、使用HTTP状态码来传达操作结果等。这些实践有助于确保API的一致性和易用性。
总的来说,REST是一种用于构建Web服务的架构风格,它强调资源的状态和客户端与服务器的交互,通过HTTP协议和统一的接口约束来实现这一目标。
一、rest
1. JSP
<%--
Created by IntelliJ IDEA.
User: 张军国001
Date: 2024/4/28
Time: 20:06
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>rest</title>
</head>
<body>
<form action="/rest/user/1" method="get">
<button type="submit">查询</button>
</form>
<form action="/rest/user/1" method="post">
<button type="submit">新增</button>
</form>
<form action="/rest/user/1" method="put">
<button type="submit">更新</button>
</form>
<form action="/rest/user/1" method="delete">
<button type="submit">删除</button>
</form>
</body>
</html>
2. Controller
package org.example.springmvc.params.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* Create by zjg on 2024/4/28
*/
@Controller
@RequestMapping("/rest")
public class RestController {
String rest="redirect:/params/rest.jsp";
@RequestMapping("/hello-rest")
public String params(){//重定向
return rest;
}
@GetMapping("/user/{id}")
public String queryUser(@PathVariable Integer id){
System.out.println("查询用户信息:"+id);
return rest;
}
@PostMapping("/user/{id}")
public String addUser(@PathVariable Integer id){
System.out.println("新增用户信息:"+id);
return rest;
}
@PutMapping("/user/{id}")
public String updateUser(@PathVariable Integer id){
System.out.println("更新用户信息:"+id);
return rest;
}
@DeleteMapping("/user/{id}")
public String deleteUser(@PathVariable Integer id){
System.out.println("删除用户信息:"+id);
return rest;
}
}
3. 结果
查询用户信息:1
新增用户信息:1
查询用户信息:1
查询用户信息:1
通过JSP测试发现put和delete走进了get方法,未达到预期效果
截止到当前时间(2024年4月28日),HTML规范本身并不直接支持PUT和DELETE方法。虽然HTML允许自定义表单方法,但大多数浏览器主要支持GET和POST方法。这主要有两方面的原因:
- 浏览器兼容性问题:不同浏览器对于PUT和DELETE方法的支持程度不同。由于PUT和DELETE方法在早期互联网应用中使用较少,浏览器开发者没有将其作为高优先级的功能进行开发和测试。因此,在一些浏览器中,对于PUT和DELETE方法的支持存在不一致甚至无法正确处理的情况。
- 安全性问题:PUT和DELETE方法相对于GET和POST方法,操作更加危险,因为它们可能会对服务器上的资源进行更改或删除。对于普通网页中的表单提交,这种操作可能会带来潜在的风险和滥用,因此浏览器厂商倾向于限制对这些方法的支持,以减少安全威胁。
尽管HTML表单本身不支持PUT和DELETE方法,但开发者可以通过JavaScript脚本或其他技术实现这些接口。例如,在RESTful Web服务中,尽管HTML表单只支持GET和POST,但可以通过JavaScript或其他后端技术来处理PUT和DELETE请求。
二、filter
html会将我们的请求转换为get请求,我们可以添加标志位拦截请求,在请求到达控制器之前将请求重新包装,以达到触发rest的效果。
现实中不建议大家这样做,直接用postman测试即可。
1.JSP
增加标识隐藏域_method,作为请求类型转换依据
<%--
Created by IntelliJ IDEA.
User: 张军国001
Date: 2024/4/28
Time: 20:06
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>rest</title>
</head>
<body>
<form action="/rest/user/1" method="get">
<button type="submit">查询</button>
</form>
<form action="/rest/user/1" method="post">
<button type="submit">新增</button>
</form>
<form action="/rest/user/1" method="put">
<input hidden="hidden" name="_method" value="put">
<button type="submit">更新</button>
</form>
<form action="/rest/user/1" method="delete">
<input hidden="hidden" name="_method" value="delete">
<button type="submit">删除</button>
</form>
</body>
</html>
2.自定义过滤器
2.1 RequestFilter
请求过滤器的作用为:当接收到带有标识的请求,根据标识值转换请求类型并存储原请求类型。
package org.example.springmvc.params.controller.filter;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpMethod;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
/**
* Create by zjg on 2024/4/28
*/
@WebFilter(urlPatterns = "/*",dispatcherTypes = DispatcherType.REQUEST)
public class RequestFilter extends HiddenHttpMethodFilter {
private static final List<String> ALLOWED_METHODS;
private String methodParam = "_method";
static {
ALLOWED_METHODS = List.of(HttpMethod.PUT.name(), HttpMethod.DELETE.name());
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String paramValue = request.getParameter(this.methodParam);
String method = request.getMethod();
if ("GET".equals(method) && request.getAttribute("jakarta.servlet.error.exception") == null) {
if (StringUtils.hasLength(paramValue)) {
request.setAttribute(this.methodParam,method);
String newMethod = paramValue.toUpperCase(Locale.ENGLISH);
if (ALLOWED_METHODS.contains(newMethod)) {
request = new HttpMethodRequestWrapper(request, newMethod);
}
}
}
System.out.println("RequestFilter-before");
filterChain.doFilter(request, response);
System.out.println("RequestFilter-after");
}
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
private final String method;
public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
super(request);
this.method = method;
}
public String getMethod() {
return this.method;
}
}
}
2.2 ForwardFilter
转发过滤器的作用为:在转发之前判断请求是否带有标识,有标识则获取原请求类型并进行转换。
package org.example.springmvc.params.controller.filter;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpMethod;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
/**
* Create by zjg on 2024/4/28
*/
@WebFilter(urlPatterns = "/*",dispatcherTypes = DispatcherType.FORWARD)
public class ForwardFilter extends HiddenHttpMethodFilter {
private static final List<String> ALLOWED_METHODS;
private String methodParam = "_method";
static {
ALLOWED_METHODS = List.of(HttpMethod.GET.name(), HttpMethod.POST.name());
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String paramValue = request.getParameter(this.methodParam);
String method = request.getMethod();
if (paramValue!=null && method.equals(paramValue.toUpperCase(Locale.ENGLISH))) {
String newMethod = request.getAttribute(this.methodParam)==null?"":String.valueOf(request.getAttribute(this.methodParam)).toUpperCase(Locale.ENGLISH); ;
if (StringUtils.hasLength(newMethod)) {
if (ALLOWED_METHODS.contains(newMethod)) {
request = new HttpMethodRequestWrapper(request, newMethod);
}
}
}
System.out.println("ForwardFilter-before");
filterChain.doFilter(request, response);
System.out.println("ForwardFilter-after");
}
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
private final String method;
public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
super(request);
this.method = method;
}
public String getMethod() {
return this.method;
}
}
}
3. 执行结果
RequestFilter-before
删除用户信息:1
ForwardFilter-before
ForwardFilter-after
RequestFilter-after
总结
目前这种方式很少涉及到,我这样使用也是为了给大家展示一个过滤器的使用案例。
纸上得来终觉浅,绝知此事要躬行。
与诸君共勉!