[关闭]
@Otokaze 2018-12-10T06:29:12.000000Z 字数 7297 阅读 585

RESTful API 感想

Java

RESTful 是什么意思?

用 URL 定位资源,用 HTTP Method 描述操作(CRUD) -- RESTful

  1. POST /employees # 创建一个员工
  2. GET /employees # 获取所有员工
  3. GET /employees/${id} # 获取指定员工
  4. PUT /employees/${id} # 更新员工信息(完整更新)
  5. PATCH /employees/${id} # 更新员工信息(部分更新)
  6. DELETE /employees/${id} # 删除指定员工
  7. DELETE /employees # 删除所有员工

PUT 是替换资源,需要提供完整的资源信息。而 PATCH 是更新资源,可以局部更新。

RESTful 只是一个规范,并不是强制要求,具体怎么做还是取决于开发者自己!!!

RESTful 用在什么地方?

当然是 API 了,RESTful API 啊!所谓 RESTful API 就是 HTTP 接口。允许使用不同的客户端进行访问,比如手机、电脑、终端、网页等,都使用统一的接口,github api 已经成为 RESTful API 的设计典范。

HTML 表单 vs AJAX 对比

HTML 表单是 HTML 网页与 WEB 服务器进行数据交互的重要方式,HTML 表单是同步的,页面通常会改变。
XmlHttpRequest 是 JavaScript 与 WEB 服务器进行数据交互的主要方式,默认是异步的,不重载页面。

HTML 表单我们已经很熟悉了,就是在页面中填写一些信息,然后点击提交按钮,发送一个 HTTP 请求到服务器,这个请求中附带了我们需要一些信息,服务器处理之后,会返回一个新页面给浏览器,HTML 表单只支持 GET 和 POST 两种请求,GET 请求不太安全,因为信息会暴露在 URL 地址栏,而 POST 是比较安全的(当然要 HTTPS 加密,如果是 HTTP 协议,无论什么方法都不安全),另外 GET 请求不能附带太多数据,因为 WEB 服务器通常会限制 URL 的长度,而 POST 请求一般可以负担更多的数据。

因为 HTML 表单只能支持 GET 和 POST 两种 HTTP 请求方法,所以我们一般不能单纯通过 form 表单来实现 RESTful 风格的 WEB 服务。不过,我们还有 JavaScript 啊,JS 中有一个大名鼎鼎的 XmlHttpRequest 对象,XmlHttpRequest 简称 XHR,所谓 XHR 就是 JS 中的一套 API,可以用来发送 HTTP 请求,支持 GET、HEAD、POST、PUT、PATCH、DELETE 等请求方法,并且默认是异步的,可以实现 RESTful 风格的 WEB 服务。

AJAX 其实就是使用 XHR 来异步更新网页的技术(大致流程就是发送 XHR 请求,然后异步获取请求结果,最后使用 DOM API 来修改 HTML 元素的内容,这样就达到了异步更新网页的技术,而不需要重新加载新页面)。

XHR 有一个限制,即“同源策略”,所谓同源就是指协议域名端口三者相同,只要有一个不同,就不是同源。所谓同源策略就是说 XHR 只能向与当前源相同的服务器发送 HTTP 请求,搞这个策略的一个重要原因是安全。

RESTful 一般用于设计 WEB API,但是,在很多 WEB 网站中,也有很多 RESTful 的身影,HTML 表单虽然只支持 GET 和 POST 请求,但是对于 URL 的设计,我们还是可以参考 RESTful 规范的,建议大家都这么做。

从 RESTful API + AJAX 到前后端分离

我们知道,浏览器的 form 表单是无法支持 PUT、PATCH、DELETE 方法的,所以如果要使用 RESTful API,必须通过 AJAX 来实现。PS:Spring MVC 其实是可以直接使用 form 表单来实现 RESTful API 调用的,那么这个是什么原理呢?其实很简单,通过一个隐藏表单元素 _method,value 为 put/patch/delete,注意表单的 method 要填 post,然后我们在 web.xml 中配置一个过滤器,这个过滤器会使用一个 request wrapper(包装器)替换原来的 request 对象(类似动态代理),request 和 request wrapper 实现同样的接口(我觉得 request wrapper 其实就是 request 的子类),这个 filter 会在 wrapper 中重写 getMethod() 方法,将 POST 替换为对应的 PUT、PATCH、DELETE 方法,然后在 Controller 中处理时获取到的 Method 就是替换后的了,这样就间接地实现了 RESTful API 在 form 表单中的调用。

虽然 Spring MVC 可以实现纯 form 形式的 RESTful API 请求,但绝大多数项目都是 AJAX 来完成的,毕竟更优雅。在这种情况下,服务器和客户端是如何布局的,它们之间又是如何工作的呢?我们来简要的分析一下:

首先我们需要两个服务器,一个是 Nginx(Web 服务器),一个是 Tomcat(应用服务器)。Nginx 上存放 HTML/CSS/JS 等静态资源,开放给外网;Tomcat 则运行 Spring MVC 应用,提供 RESTful API 接口,Tomcat 可以选择开放给外网(比如需要将 API 开放给其它客户端,如手机 APP),也可以运行在内网(如果只是单纯网站的话,建议放到内网,更安全)。

首先,浏览器向 Nginx 发送 HTTP 请求,获取 HTML 页面以及其他资源,HTML 页面一般就是一个骨架,里面有很多 AJAX 请求,调用 Tomcat 提供的 RESTful API,最终完成页面渲染,呈现给用户。分为两种情况:

第一种,Tomcat 运行在内网,那么我们的 AJAX 请求的 URL 就要改一下,改为当前域名的某个 url 下,比如 /api,即实际的 API url 为 /employees/id,那么在 AJAX 中就要改为 /api/employees/id,然后在 Nginx 中配置 /api 路径的反代,将这个 url 下的请求反打到内网中的 Tomcat 中。避开同源策略。

第二种,Tomcat 运行在外网,这种情况下,我们不需要改变 AJAX 里面的 url,假设网站的域名为 www.zfl9.com(Nginx),API 的域名为 api.zfl9.com(Tomcat),那么 AJAX 里面请求的 url 就是 api.zfl9.com/xxx,不过因为浏览器对 JS 脚本发起的 HTTP 请求有同源策略的限制,所以我们不能直接这样做,要解决这个问题也很简单,即使用 CORS,别以为是什么很高大上的东西,其实就是几个 HTTP 头部。首先浏览器的同源限制是这样的,当 www.zfl9.com 域下的 AJAX 向 api.zfl9.com 发出 RESTful 请求后,浏览器并不会直接丢掉这个请求,而是先让它正常发出去,然后检查 api.zfl9.com 返回的响应头部,如果包含 Access-Control-Allow-Origin: *Access-Control-Allow-Origin: www.zfl9.com 头部,则表名 api.zfl9.com 这台服务器做了 CORS 跨域请求支持,浏览器会正常接收响应,所以没有问题;如果不包含这个头部,那么浏览器就会对响应结果进行拦截,然后提示不支持跨域。所以我们只需要在 Tomcat 中配置一个 filter,对 response 添加几个 CORS 头部就行了(浏览器发出的跨域请求会包含一个 Origin 头部,表示请求的来源域,如 Origin: https://www.zfl9.com,可以在 filter 中做些判断,只对指定域名开放)。

这样其实就是所谓的 前后端分离,前端服务器为 Nginx,暴露给浏览器,后端服务器为 Tomcat,只提供 RESTful API,前后端之间使用 AJAX 进行交互,数据格式一般为 JSON。好处是前后端开发人员可以分开,前端做前端的事,后端做后端的事,互不干扰,只需要沟通好 RESTful API 接口就行了。在这之前,大多数公司的 Web 开发模式是这样的:前端负责编写 HTML 页面,然后交给后端人员,后端人员再将这个 HTML 改为动态页面(如 JSP,或者是其它 View 模板),这也就是所谓的“套模版”,有时候容易出错,调试周期长。

因为使用了 AJAX 进行动态页面更新,所以一般我们也将这种网站称为 SPA(single page application,单页面应用),所谓单页面应用就是在 Web 设计上使用单一页面(页面骨架),利用 JavaScript 操作 Dom 来进行动态渲染和动态更新。与 SPA 相对的是 MPA(multiple page application,多页面应用),这是传统的 Web 应用设计模型,通过不同页面的跳转来进行内容的更新(基本上就是 form 表单)。

通常 SPA 就是指代运用 AJAX + RESTful API 技术开发的网站,即应用了前后端分离的网站,虽然 SPA 有很多优点,比如更利于前后端开发者的分工合作,并且很好的做到了前后端的解耦,但是 SPA 仍然有两大缺点:

基于 SPA 的这些缺点,又有人提出了 SSR(Server-Side Render,服务端渲染),与 SSR 相对的是 CSR(Client-Side Render,客户端渲染)。什么是 SSR 呢?举个例子,传统的 JSP 就是 SSR,浏览器访问 jsp 页面,服务器会先将 JSP 转换为 HTML 代码,再返回给浏览器,浏览器获取到的是一个完整的 HTML 页面,所以首次打开很快,没有所谓的白屏时间。而我们上面提到的 SPA 模型就是 CSR 的一个例子,即浏览器获取 HTML 骨架,JS 代码,然后在浏览器环境中执行 JS 脚本,通过 AJAX 来获取 RESTful 接口的数据,才能完成页面渲染,所以首次访问很慢,用户在此期间很容易看到长时间的白屏。

那么我们说的 SSR 和 SPA 有什么关系呢?SPA 不是 CSR 的吗?难道我们可以将它改为 SSR?是的,因为 Node.JS 的出现,使得我们可以在服务端执行 JS 脚本,而又因为 SPA 有 SEO 问题和首屏性能问题,所以有大神就提出了利用 Node.js 作为中间层来实现 SPA 应用的 SSR 服务端渲染,从而完美避免 SEO 问题和首屏性能问题。那么 SPA 的服务端渲染是如何实现的呢?

最简单的改造方式就是使用 Node.js 替换 Nginx,作为 Web 服务器,即从 Nginx + Tomcat 变为 Node.js + Tomcat,当然 Nginx 仍然可以保留,这个是后话,我们就暂且改为 Node.js + Tomcat 架构吧。浏览器第一次访问网站时,请求首先到达 Node.js,Node.js 会先获取到对应的 HTML 代码,然后解析 AJAX 代码,向 API 服务器发起请求,然后在 Node.js 容器中完成渲染,最后将完整的 HTML 页面发送回浏览器,浏览器依旧能够使用 HTML 里面的 AJAX 脚本来进行页面的动态更新,也就是说,第一次访问时是先由 Node.js 代理浏览器来进行渲染的,而后面的的 AJAX 请求可以直接发送给 API 服务器。因为第一次访问获取到的是带有内容的 HTML 页面,所以搜索引擎爬虫可以对里面的内容进行抓取,提取关键字,不影响 SEO 优化,同时因为服务端直接访问 API 服务器很快,所以也解决了首次访问白屏时间长的问题。可谓是两全其美,但是 SSR 的实现往往更加复杂,而且引入了 Node.js 中间层,前端的工作量大大增加,所以除非是非常依赖搜索引擎来吸引流量的网站,或者是对首次加载时间很敏感的,才建议使用 SSR,其他网站使用 SPA 就够了。

注意,因为 SPA + SSR 的方式不同于传统的 SSR 服务端渲染,而是 CSR 和 SSR 混合,所以又有一个新名词来形容这种模式,即 同构渲染,即第一次访问时先由服务端进行渲染,直出 HTML 代码,后面的数据更新依旧使用浏览器解析 AJAX 形式来渲染。同购的意思就是同一份 JS 代码能够同时在客户端运行和服务端运行,在 SPA + SSR 场景中就是指代浏览器和 Node.js 咯。

Angular、React、Vue 三大主流框架都支持所谓的“同构渲染、服务端渲染”。

关于前后端分离的讨论 - 知乎

一开始,html 就是后端渲染的。不过后端发现页面中的 js 好麻烦(虽然简单,但是坑多),于是让公司招聘专门写 js 的人,也就是前端。

前端名义上是程序员,实际上就是在切图(CSS)和做特效(JS),所以所有程序员中前端工资最低,职位也最低。所以前后端的鄙视链就出现了。

node.js 和前端 mvc 的兴起让前端变得复杂起来,前端发现翻身的机会,于是全力支持这两种技术,造成本不该做成 spa 的网站也成了 spa。慢慢地前后端分离运动从大公司开始兴起,目的就是前端脱离后端的指指点点,独立发展。(表面上是为了「代码分离」,实际上是为了「人员分离」,也就是「前后端分家」,前端不再附属于后端团队)

spa 之后发现 seo 问题很大,而且首屏渲染速度贼慢,但是自己选的路再难走也要走下去,于是用 nodejs 在服务端渲染这一条路被看成是一条出路

其实这是第二个翻身的机会,如果 nodejs 服务器渲染成为主流,其实就相当于前端把后端的大部分工作给抢了,工资压过普通后端指日可待

然而结果是 nodejs 服务端渲染始终是小众,因为后端也没那么脆弱,java php rails 十多年沉淀的技术岂是你说推翻就推翻的,已经运行多年的项目又岂是容你随便用 nodejs 重写的,另一方面 golang 等技术的兴起也给 nodejs 不少压力。最终只有少部分前端特别强势的团队成功用上了 Node.js 做渲染(比如阿里的一些团队),大部分公司依然是用 PHP 渲染 HTML。

于是 nodejs 退一步说好好好我不抢你们的工作,我只做中间层(大部分工作就是渲染页面和调用后台接口),绝不越雷池。后端说算你识相。现在 nodejs 主要搞什么微服务,也是为了抢后端还没注意的市场。

你要看一门技术的发展主要应该看背后的人是谁,应用场景是哪些,最后才是技术细节。nodejs 的火在中国早就烧过了,以后估计不会大火了,作为前端了解一下还是不错的,但是如果你是后端的话,看不看都无所谓,nodejs 跟其他后端开发框架差异并不大,单线程异步既是优点也是缺点,你就把它当做一种范式研究就好。

我是一个坚定的『前后端分家』反对者,前后代码可以分离,但是人员绝对不应该分离。前后端撕逼的事情在大公司天天都在发生,全都是因为前后是两个团队,利益不同。实际上前端推 nodejs 渲染就是在试图重新让前后端合成一体。

但是前端不能明说这件事,因为如果要把前后端部门合并,拆掉的肯定是前端部门。

合,则相当于自断前程。
不合,则永远没法解决seo和首屏加载慢的问题。
所以前端真的挺矛盾的。

JS 也有一个矛盾的地方,凡是浏览器上的框架(Vue React)都说自己能适应「复杂」场景,凡是 Node.js 上的框架(express fastify koa)都说自己是「轻量级」框架。

为啥?因为浏览器是 JS 的主战场,而且无敌手。而服务器上,JS 的经验积累还是太少了,搞企业级服务,Node.js 是敌不过 Java、PHP 的,没办法,发展得太晚了。所以目前只能搞「轻量级」咯。egg.js 号称是企业级 Node.js 框架,用过的人来评我就不评了。

有些大佬提出「大前端」的概念,意思是前端也要会后端,但是我们心还是前端的。

这不就是把以前的『前后端一个人做』换了个说法嘛。

反正你现在让后端去学前端,后端肯定是不愿意躺这浑水的。只能前端自己想办法咯。

想来想去就只有 Node.js 中间层做 HTML 渲染了。

当初是你要分开,分开就分开。
现在又要用kpi,把我唤回来。
但是后端kpi跟你前端kpi是不同的呀,所以没戏。

这些话也就我这种脱离了大厂约束的人敢说,在大厂的人根本不敢说,毕竟跟后端低头不见抬头见的。

最后告诉你一个小秘密。由于阿里 nodejs 用得还算多,却招不到人,所以从功利的角度出发,也许你学 nodejs 比学 java 更容易进阿里,毕竟阿里的 java 大神多如云,nodejs 大神却不多。

但是从另外一个角度考虑,SEO 不友好的页面我是支持的。

如果你的页面是对 SEO 不友好的,那么百度的重要性就会被削弱。现在是移动互联网时代,大家在手机上几乎不用百度,都是直接点 App 点微信公众号的,SEO 不友好问题不大。首屏速度随着 5G 网络的普及也不会是问题了。

只要能让百度利益受损,我觉得 SPA 这事还是值得做的。服务端渲染还是直接免了吧,大家都不做 SEO 让百度倒闭就最好咯~(只是我的幻想而已,不要当真,我是百度的脑残黑,黑百度从来不需要理由)

感谢你看我说了这么多,不过说到最后,我也没给出啥结论,只是把我观察到的告诉你了。

你要不要学、要不要用服务器渲染HTML,都是需要你自己思考的事情。

还是那句话,我不喜欢说中庸的观点,我喜欢跟你说一个极端的观点,然后会有人用另一个极端的观点反驳我,他说服不了我,我也说服不了他,但是最终,你会得出自己的观点。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注