目录

前言

1、什么是 REST 风格

1.1  资源(Resources)

1.2  表现层(Representation)

1.3  状态转化(State Transfer)

1.4  综述

2、实操SpringBoot 实现REST风格的表单提交

2.1   非REST 风格的代码

2.2   REST风格的代码

3、源码分析

3.1 Springboot 的处理思路

3.2 HiddenHttpMethodFilter 处理 PUT 和DELETE


前言

工作中面试中,总能提到REST风格 ?

但你真的会使用到它吗?知道怎么去修改底层配置吗?

是不是在繁杂业务代码中早就迷失了 ? ...

1、什么是 REST 风格

很多解释概念的博客,个人感觉阮一峰讲的最透彻,

原文链接 https://blog.csdn.net/zhangliangzi/article/details/51428171

此链接不是原创,为防止他删文章,我把精华摘抄点过来,

首先 RESTful架构,就是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。

REST这个词,是 Roy Thomas Fielding 在他2000年的博士论文中提出的。

REST,即 Representational State Transfer 的缩写,如果一个架构符合REST原则,就称它为RESTful架构。

要理解RESTful架构,最好的方法就是去理解Representational State Transfer这个词组到底是什么意思,它的每一个词代表了什么涵义。如果你把这个名称搞懂了,也就不难体会REST是一种什么样的设计。

1.1  资源(Resources)

REST的名称"表现层状态转化"中,省略了主语。"表现层"其实指的是"资源"(Resources)的"表现层"。

所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。

所谓"上网",就是与互联网上一系列的"资源"互动,调用它的URI。

1.2  表现层(Representation)

"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)。

比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。

URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。

1.3  状态转化(State Transfer)

访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。

互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。

客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。

1.4  综述

综合上面的解释,我们总结一下什么是RESTful架构:

  (1)每一个URI代表一种资源;

  (2)客户端和服务器之间,传递这种资源的某种表现层;

  (3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。

摘抄结束:希望了解更多,去上面的原文链接(希望他还在)

思考 :我个人理解一言以蔽之:它是一种前端获取资源的 原则,这个原则更多的在客户端通过有目的的动词表示本次请求的含义。

2、实操SpringBoot 实现REST风格的表单提交

2.1   非REST 风格的代码

举例说明:对用户表的的操作,增删改查平衡车那个都是这么写:

    @RequestMapping(value = "/getUser",method = RequestMethod.POST)
public String getUser(){
return "GET-张三";
} @RequestMapping(value = "/saveUser",method = RequestMethod.POST)
public String saveUser(){
return "POST-张三";
} @RequestMapping(value = "/putUser",method = RequestMethod.POST)
public String putUser(){
return "PUT-张三";
} @RequestMapping(value = "/deleteUser",method = RequestMethod.POST)
public String deleteUser(){
return "DELETE-张三";
}

每种业务都有自己的路径,增删改查前端会看到四种不同的路径,代码整体看起来比较冗余

2.2   REST风格的代码

@RequestMapping(value = "/user",method = RequestMethod.GET)
public String getUser(){
return "GET-张三";
} @RequestMapping(value = "/user",method = RequestMethod.POST)
public String saveUser(){
return "POST-张三";
} @RequestMapping(value = "/user",method = RequestMethod.PUT)
public String putUser(){
return "PUT-张三";
} @RequestMapping(value = "/user",method = RequestMethod.DELETE)
public String deleteUser(){
return "DELETE-张三";
}

很明显的区别,请求路径统一,通过请求方式区分获取、删除、修改、保存 等动作,清爽多了

既然这么简洁,我们实操一下吧

第1步:application.propertities 文件配置 spring.mvc.hiddenmethod.filter.enable=true,开启表单的REST风格支持(这个源码分析部分会讲到)

第2部:编写一个前端页面实测一下 REST 风格的提交:

注意下页面的 hidden隐藏域参数细节(后面源码部分会提到)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>测试 REST 风格</h1>
<form action="/user" method="get">
<input value="GET提交" type="submit">
</form>
<form action="/user" method="post">
<input value="POST提交" type="submit">
</form>
<form action="/user" method="post">
<input name="_method" type="hidden" value="PUT" />
<input value="PUT提交" type="submit">
</form>
<form action="/user" method="post">
<input name="_method" type="hidden" value="DELETE" />
<input value="DELETE提交" type="submit">
</form>
</body>
</html>
点击并拖拽以移动

点击四个按钮,发现跳转正确!

他是怎么跳转的,看源码分析  ↓

3、源码分析

3.1 Springboot 的处理思路

SpringBoot的处理思路是:既然浏览器天然只能发送 GET 和 POST 请求,那可以利用前后的参数配合,根据约定好的参数,把POST或者GET 定向地转成 PUT 或 DELETE 请求

3.2 HiddenHttpMethodFilter 处理 PUT 和DELETE

实现对REST 请求风格的支持主要得益于HiddenMethodFilter 类,它同样是在WebMvcAutoConfiguration 这个自动配置类中在项目启动时自动注入的。

  // 类WebMvcAutoConfiguration下
// spring.mvc.hiddenmethod.filter.enabled 为 true 才会加载过滤器,这里就解释了前面的配置项 @Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled")
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}

着重看一下 HiddenHttpMethodFilter ,顾名思义 隐藏HTTP方法过滤器,它的类注释翻译完粘贴过来:

由于浏览器目前仅支持 GET 和 POST,因此一种常用技术(例如 Prototype 库使用的技术)是使用带有附加隐藏表单字段 ( _method ) 的普通 POST 来传递“真正的”HTTP 方法。 此过滤器读取该参数并相应地更改HttpServletRequestWrapper.getMethod()返回值。 只允许"PUT" 、 "DELETE"和"PATCH" HTTP 方法。

请求参数的名称默认为_method ,但可以通过methodParam属性进行调整

看下 HiddenHttpMethodFilter 的核心方法 doFilterInternal() , 他是如何吧我们的隐藏表单解析的

 // 核心方法
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException { HttpServletRequest requestToUse = request;
// 首先必须满足为POST提交,然后再根据表单隐藏域名叫”_method“ 的参数进行判断
if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
// 获取 名称为 ”_method“ 的参数
String paramValue = request.getParameter(this.methodParam);
// 获取到的参数不为空
if (StringUtils.hasLength(paramValue)) {
// 把类型名称帮我们转为大写
String method = paramValue.toUpperCase(Locale.ENGLISH);
// 只允许 PUT DELETE PATCH
if (ALLOWED_METHODS.contains(method)) {
// 重新包装下这个类型为POST的request 成为客户端想让它成为的样子
requestToUse = new HttpMethodRequestWrapper(request, method);
}
}
} filterChain.doFilter(requestToUse, response);
}

【源码分析】- 在SpringBoot中你会使用REST风格处理请求吗?的更多相关文章

  1. SpringBoot源码分析之SpringBoot的启动过程

    SpringBoot源码分析之SpringBoot的启动过程 发表于 2017-04-30   |   分类于 springboot  |   0 Comments  |   阅读次数 SpringB ...

  2. angular源码分析:angular中脏活累活的承担者之$interpolate

    一.首先抛出两个问题 问题一:在angular中我们绑定数据最基本的方式是用两个大括号将$scope的变量包裹起来,那么如果想将大括号换成其他什么符号,比如换成[{与}],可不可以呢,如果可以在哪里配 ...

  3. angular源码分析:angular中入境检察官$sce

    一.ng-bing-html指令问题 需求:我需要将一个变量$scope.x = '<a href="http://www.cnblogs.com/web2-developer/&qu ...

  4. angular源码分析:angular中各种常用函数,比较省代码的各种小技巧

    angular的工具函数 在angular的API文档中,在最前面就是讲的就是angular的工具函数,下面列出来 angular.bind //用户将函数和对象绑定在一起,返回一个新的函数 angu ...

  5. angular源码分析:angular中的依赖注入式如何实现的

    一.准备 angular的源码一份,我这里使用的是v1.4.7.源码的获取,请参考我另一篇博文:angular源码分析:angular源代码的获取与编译环境安装 二.什么是依赖注入 据我所知,依赖注入 ...

  6. SpringBoot事件监听机制源码分析(上) SpringBoot源码(九)

    SpringBoot中文注释项目Github地址: https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE 本篇接 SpringApplicat ...

  7. angular源码分析:angular中$rootscope的实现——scope的一生

    在angular中,$scope是一个关键的服务,可以被注入到controller中,注入其他服务却只能是$rootscope.scope是一个概念,是一个类,而$rootscope和被注入到cont ...

  8. angular源码分析:angular中jqLite的实现——你可以丢掉jQuery了

    一.从function JQLite(element)函数开始. function JQLite(element) { if (element instanceof JQLite) { //情况1 r ...

  9. lodash源码分析之compact中的遍历

    小时候, 乡愁是一枚小小的邮票, 我在这头, 母亲在那头. 长大后,乡愁是一张窄窄的船票, 我在这头, 新娘在那头. 后来啊, 乡愁是一方矮矮的坟墓, 我在外头, 母亲在里头. 而现在, 乡愁是一湾浅 ...

随机推荐

  1. 谁动了我的 Linux?原来 history 可以这么强大!

    当我们频繁使用 Linux 命令行时,有效地使用历史记录,可以大大提高工作效率. 在平时 Linux 操作过程中,很多命令是重复的,你一定不希望大量输入重复的命令.如果你是系统管理员,你可能需要对用户 ...

  2. Java 进行时间处理

    Java 进行时间处理 一.Calendar (1).Calender介绍 Calendar的中文翻译是日历,实际上,在历史上有着许多种计时的方法.所以为了计时的统一,必需指定一个日历的选择.那现在最 ...

  3. 编译课设·CLion到VS踩坑·解决·备忘录

    应试用,VS使用习惯和JB系差别还是蛮大的 打不过他们就加入他们 键位修改 工具-选项 键盘:改keymap 字体和颜色:宋体必改. 自动恢复:自动保存默认3分钟 CMake:自救时可以看一下 键位名 ...

  4. [MySQL数据库之表的约束条件:primary key、auto_increment、not null与default、unique、foreign key:表与表之间建立关联]

    [MySQL数据库之表的约束条件:primary key.auto_increment.not null与default.unique.foreign key:表与表之间建立关联] 表的约束条件 约束 ...

  5. 缓存架构中的服务详解!SpringBoot中二级缓存服务的实现

    创建缓存服务 创建缓存服务接口项目 创建myshop-service-redis-api项目,该项目只负责定义接口 创建项目的pom.xml: <?xml version="1.0&q ...

  6. 风变编程(Python自学笔记)第12关-我们都是中国人

    1.类的个例叫做实例:类,是对某个群体的统称(类是某个特定的群体),实例是群体中某个具体的个体. 2.Python中的对象等于类和实例的集合. 3. 类的创建:class+类名+冒号,后面语句要缩进. ...

  7. [设计模式] 设计模式课程(二十)--命令模式(Command)

    概述 "行为变化"模式:组件构建过程中,组件行为的变化经常会导致组件本身剧烈的变化."行为变化"模式将组件的行为和组件本身进行解耦,从而支持组件行为的变化,实现 ...

  8. [刷题] PTA 查验身份证

    题目: 7-63 查验身份证 (15 分)  一个合法的身份证号码由17位地区.日期编号和顺序编号加1位校验码组成.校验码的计算规则如下: 首先对前17位数字加权求和,权重分配为:{7,9,10,5, ...

  9. HTML中的全局属性

    一.全局属性和局部属性 每种元素都有自己规定的属性,这种属性成为局部属性.还有另外一种属性,他可以用来配置所有元素的共有行为,这种属性成为称为全局属性.全局属性可以用在任何一个元素身上,但是不一定会带 ...

  10. dstat命令

    dstat命令 dstat命令是一个用来替换vmstat.iostat.netstat.nfsstat和ifstat这些命令的工具,是一个全能系统信息统计工具.与sysstat相比,dstat拥有一个 ...