【SpringMVC】3—RESTFul风格

news/2024/11/14 19:46:58/

⭐⭐⭐⭐⭐⭐
Github主页👉https://github.com/A-BigTree
笔记链接👉https://github.com/A-BigTree/Code_Learning
⭐⭐⭐⭐⭐⭐

如果可以,麻烦各位看官顺手点个star~😊

如果文章对你有所帮助,可以点赞👍收藏⭐支持一下博主~😆


文章目录

  • 3 RESTFul风格
    • 3.1 RESTFul概述
      • 3.1.1 REST概念
      • 3.1.2 REST规范的内涵
        • 资源
        • 状态转移
      • 3.1.3 REST规范具体要求
        • 四种请求方式
        • URL地址风格
      • 3.1.4 REST风格的好处
        • 含蓄安全
        • 风格统一
        • 无状态
        • 严谨优雅
        • 简洁优雅
        • 丰富的语义
    • 3.2 四种请求方式映射
      • 3.2.1 `HiddenHttpMethodFilter`与装饰模式
        • 简介
        • `HiddenHttpMethodFilter`要点
        • 原始请求对象的包装
      • 3.2.2 PUT请求
        • web.xml
        • 表单
        • 处理方法
      • 3.2.3 DELETE请求
        • 场景
        • 负责转换的表单
        • 删除超链接绑定单击响应函数
        • 编写单击响应函数
        • 处理方法
    • 3.3 `@PathVariable`注解
      • 3.3.1 操作
        • 传一个值
        • 传多个值
        • 处理方法

3 RESTFul风格

3.1 RESTFul概述

3.1.1 REST概念

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

  • 定位:互联网软件架构风格;
  • 倡导者:Roy Thomas Fielding;
  • 文献:Roy Thomas Fielding的博士论文;

3.1.2 REST规范的内涵

资源

URL:Uniform Resource Locator统一资源定位器。意思是网络上的任何资源都可以通过 URL 来定位。但是在实际开发中,我们往往是使用URL来对应一个具体的功能,而不是资源本身。REST规范则倡导使用URL对应网络上的各种资源,任何一个资源都可以通过一个URL访问到,为实现操作幂等性奠定基础。而这个资源可以是网络上的一个文本、音频、视频、图片等等……

幂等性:如果一个操作执行一次和执行N次对系统的影响相同,那么我们就说这个操作满足幂等性。而幂等性正是REST规范所倡导的。

状态转移

REST倡导针对资源本身操作,所以对资源的操作如果满足幂等性,那么操作只会导致资源本身的状态发生变化而不会破坏整个系统数据。

3.1.3 REST规范具体要求

四种请求方式

REST风格主张在项目设计、开发过程中,具体的操作符合HTTP协议定义的请求方式的语义

操作请求方式
查询操作GET
保存操作POST
删除操作DELETE
更新操作PUT

另有一种说法:

  • POST 操作针对功能执行,没有锁定资源 id,是非幂等性操作;
  • PUT 操作锁定资源 id,即使操作失败仍然可以针对原 id 重新执行,对整个系统来说满足幂等性:
    • id对应的资源不存在:执行保存操作
    • id对应的资源存在:执行更新操作

URL地址风格

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

传统 URL 地址REST 风格地址
/remove/emp?id=5/emp/5
  • 用一句话描述当前资源
  • 一句话中各个单词用斜杠分开,从前到后保持完全一致的书写风格
  • 不要使用问号键值对的方式传递数据
  • 需要传递数据时,把数据嵌入到URL地址中,作为地址的一部分
  • 不要使用请求扩展名

3.1.4 REST风格的好处

含蓄安全

使用问号键值对的方式给服务器传递数据太明显,容易被人利用来对系统进行破坏。使用 REST 风格携带数据不再需要明显的暴露数据的名称。

风格统一

URL 地址整体格式统一,从前到后始终都使用斜杠划分各个单词,用简单一致的格式表达语义。

无状态

在调用一个接口(访问、操作资源)的时候,可以不用考虑上下文,不用考虑当前状态,极大的降低了系统设计的复杂度。

严谨优雅

严格按照HTTP1.1协议中定义的请求方式本身的语义进行操作。

简洁优雅

过去做增删改查操作需要设计4个不同的URL,现在一个就够了。

操作传统风格REST 风格
保存/CRUD/saveEmpURL 地址:/CRUD/emp
请求方式:POST
删除/CRUD/removeEmp?empId=2URL 地址:/CRUD/emp/2
请求方式:DELETE
更新/CRUD/updateEmpURL 地址:/CRUD/emp
请求方式:PUT
查询(表单回显)/CRUD/editEmp?empId=2URL 地址:/CRUD/emp/2
请求方式:GET

丰富的语义

通过 URL 地址就可以知道资源之间的关系。它能够把一句话中的很多单词用斜杠连起来,反过来说就是可以在 URL 地址中用一句话来充分表达语义。

3.2 四种请求方式映射

3.2.1 HiddenHttpMethodFilter与装饰模式

简介

在HTML中,GET和POST请求可以天然实现,但是DELETE和PUT请求无法直接做到。SpringMVC提供了HiddenHttpMethodFilter帮助我们将POST请求转换为DELETE或PUT请求。

HiddenHttpMethodFilter要点

默认请求参数名常量:

public static final String DEFAULT_METHOD_PARAM = "_method";

HiddenHttpMethodFilter中,声明了一个常量:DEFAULT_METHOD_PARAM,常量值是"_method"

配套的成员变量:

private String methodParam = DEFAULT_METHOD_PARAM;

之所以会提供这个成员变量和配套的setXxx()方法,是允许我们在配置Filter时,通过初始化参数来修改这个变量。如果不修改,默认就是前面常量定义的值。

在这里插入图片描述

原始请求对象的包装

困难:

  • 包装对象必须和原始对象是同一个类型
  • 保证同一个类型不能通过子类继承父类实现
    • 子类对象:希望改变行为、属性的对象
    • 父类对象:随着Servlet容器的不同,各个容器对HttpServletRequest接口给出的实现不同。如果继承了 A 容器给出的实现类,那么将来就不能再迁移到 B 容器。
  • 只能让包装对象和被包装对象实现相同接口
    • 虽然使用动态代理技术大致上应该能实现,但是一旦应用代理就必须为被包装的对象的每一个方法都进行代理,操作过于繁琐。
  • 如果我们自己创建一个类实现HttpServletRequest接口
    • 困难1:在不确定具体哪一个 Servlet 容器的情况下完全没办法实现
    • 困难2:抽象方法实在太多

HttpServletRequestWrapper

HttpServletRequestWrapper类能够非常好的帮助我们对原始request对象进行包装。它为什么能帮我们解决上面的困难呢?

  • HttpServletRequestWrapper 类替我们实现了HttpServletRequest接口;
  • 为了让包装得到的新对象在任何Servlet容器平台上都能够正常工作,HttpServletRequestWrapper类此处的设计非常巧妙:它借助原始的request对象本身来实现所有的具体功能;
  • 在我们想通过包装的方式来修改原始对象的行为或属性时,只需要在HttpServletRequestWrapper 类的子类中重写对应的方法即可;

HttpMethodRequestWrapper类:

HttpMethodRequestWrapper类就是HiddenHttpMethodFilter 的一个内部类,在HttpMethodRequestWrapper类中有如下行为实现了对原始对象的包装:

  • 继承了官方包装类:HttpServletRequestWrapper
  • 在构造器中将原始request对象传给了父类构造器;
  • 将我们指定的新请求方式传给了成员变量;
  • 重写了父类(官方包装类)的getMethod()方法;
  • 外界想知道新包装对象的请求方式时,会来调用被重写的getMethod()方法,从而得到我们指定的请求方式;
/*** Simple {@link HttpServletRequest} wrapper that returns the supplied method for* {@link HttpServletRequest#getMethod()}.*/
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {private final String method;public HttpMethodRequestWrapper(HttpServletRequest request, String method) {// 在构造器中将原始 request 对象传给了父类构造器super(request);// 将我们指定的新请求方式传给了成员变量this.method = method;}@Overridepublic String getMethod() {return this.method;}
}

3.2.2 PUT请求

web.xml

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

表单

  • 要点1:原请求方式必须是 post
  • 要点2:新的请求方式名称通过请求参数发送
  • 要点3:请求参数名称必须是_method
  • 要点4:请求参数的值就是要改成的请求方式
<!-- 原请求方式必须是 post -->
<form th:action="@{/emp}" method="post"><!-- 通过表单隐藏域携带一个请求参数 --><!-- 请求参数名:_method --><!-- 请求参数值:put --><input type="hidden" name="_method" value="put" /><button type="submit">更新</button>
</form>

处理方法

// 映射请求地址:URL + 请求方式
@RequestMapping(value = "/emp", method = RequestMethod.PUT)
public String updateEmp() {logger.debug("现在执行的是 updateEmp() 方法");return "target";
}

3.2.3 DELETE请求

场景

<h3>将XXX请求转换为DELETE请求</h3>
<table id="dataTable"><tr><th>姓名</th><th>年龄</th><th>删除</th></tr><tr><td>张三</td><td>40</td><td><a th:href="@{/emp}" @click="doConvert">删除</a></td></tr><tr><td>李四</td><td>30</td><td><a th:href="@{/emp}" @click="doConvert">删除</a></td></tr>
</table>

负责转换的表单

<!-- 创建一个通用表单,在删除超链接的单击响应函数中通过这个表单把GET请求转换为POST,进而再转DELETE -->
<form method="post" id="convertForm"><input type="hidden" name="_method" value="delete" />
</form>

删除超链接绑定单击响应函数

<td><!-- /emp/{empId}/{pageNo} --><!-- οnclick="convertMethod(this)" 表示点击这个超链接时,调用 convertMethod() 函数 --><!-- this 代表当前超链接对象 --><!-- event 是代表当前事件的事件对象 --><a onclick="convertMethod(this, event)" th:href="@{/emp}">删除</a>
</td>

编写单击响应函数

<script type="text/javascript">function convertMethod(anchorElement, event) {// 获取超链接原本要访问的目标地址var targetURL = anchorElement.href;// 获取表单对象var formEle = document.getElementById("convertForm");// 把超链接原本要访问的地址设置给表单的 action 属性formEle.action = targetURL;// 提交表单formEle.submit();// 取消控件的默认行为:让超链接不会跳转event.preventDefault();}</script>

处理方法

@RequestMapping(value = "/emp", method = RequestMethod.DELETE)
public String removeEmp() {logger.debug("现在执行的是 removeEmp() 方法");return "target";
}

3.3 @PathVariable注解

3.3.1 操作

传一个值

<a th:href="@{/emp/20}">传一个值</a><br/>
// 实际访问地址:/emp/20
// 映射地址:/emp/{empId}是把变量部分用大括号标记出来,写入变量名
@RequestMapping("/emp/{empId}")
public String getEmpById(@PathVariable("empId") Integer empId) {logger.debug("empId = " + empId);return "target";
}

传多个值

<a th:href="@{/emp/tom/18/50}">传多个值</a><br/>

处理方法

// 实际地址:/emp/tom/18/50
@RequestMapping("/emp/{empName}/{empAge}/{empSalary}")
public String queryEmp(@PathVariable("empName") String empName,@PathVariable("empAge") Integer empAge,@PathVariable("empSalary") Double empSalary
) {logger.debug("empName = " + empName);logger.debug("empAge = " + empAge);logger.debug("empSalary = " + empSalary);return "target";
}

http://www.ppmy.cn/news/39808.html

相关文章

[计算机图形学]重心坐标应用纹理(前瞻预习/复习回顾)

一、重心坐标&#xff0c;插值 上篇的最后我们提到了&#xff0c;当顶点在纹理上的对应uv坐标成功找到之后&#xff0c;三角形三个顶点中间的值需要用三角形的重心坐标插值计算得到&#xff0c;那么这个运算是怎么进行的&#xff0c;本篇我们将介绍。插值的运算不仅仅在纹理中…

Redis第八讲 Redis之Hash底层数据结构实现

Hash底层数据结构 Hash 数据结构底层实现为一个字典( dict )哈希表,也是RedisBb用来存储K-V的数据结构,Redis内存数据库,最底层是一个redisDb;字典中的每一项,使用dictEntry ,代表KV键值;类似于HashMap中的键值对Entry。dict是一种用于维护key和value映射关系的数据结构,…

【微信小程序-原生开发】系列教程目录(已完结)

01-注册登录账号&#xff0c;获取 AppID、下载安装开发工具、创建项目、上传体验 https://sunshinehu.blog.csdn.net/article/details/128663679 02-添加全局页面配置、页面、底部导航 https://sunshinehu.blog.csdn.net/article/details/128705866 03-自定义底部导航&#x…

Point-NeRF总结记录

渲染可以理解为三维模型或场景转换成二维图像的过程&#xff0c;广泛应用于电影、虚拟现实、建筑和产品设计等领域。在计算机图形学中&#xff0c;渲染通常指的是使用计算机程序对三维场景进行可视化的过程。假如游戏中的场景有一个3d模型、一个摄像机和光源&#xff0c;渲染要…

chatGPT写文章提问词-chatGPT生成论文要多久

让ChatGPT写评论文章 好的&#xff0c;下面是一篇由 ChatGPT 撰写的评论文章&#xff1a; 题目&#xff1a;《流浪地球》&#xff1a;中国科幻电影的里程碑 《流浪地球》是一部改变了中国电影市场的重要电影&#xff0c;它迎来了一个新时代的中国科幻电影。这部电影吸引了众…

分布式系统容灾部署方案

本文主要以OceanBase部署来说明分布式系统容灾部署方案 分布式系统提供持续可用的服务尤为重要。 好的分布式系统根据需求提供不同等级的的高可用与容灾级别。 而在分布式系统中&#xff0c;数据库系统又是最核心最关键的系统。 我们以数据库分布式系统为主&#xff0c;考虑…

MongoDB 查询文档(1)

一、查询语法 本篇我们主要讲解在集合或视图中查询文档&#xff0c;首先说明一下查询的语法&#xff1a; db.collection.find(query, projection, options) 此定义可以从集合或者视图中查找文档并返回一个游标(cursor)&#xff1b; 参数 query: 此选项可选&#xff0c;用于…

局域网(LAN),广域网(WAN)和互联网

网络异同 局域网&#xff08;LAN&#xff09;&#xff0c;广域网&#xff08;WAN&#xff09;和互联网是计算机网络的三种类型&#xff0c;它们之间有以下的区别和相同点&#xff1a; 相同点&#xff1a; 它们都是计算机网络的类型。 它们都可以让计算机之间进行数据交换和通…