构建Web应用
前后端采用的语言都是JavaScript,在跨越HTTP进行沟通时的好处;
- 无须切换语言环境,部分知识不会因为语言环境的切换而丢失,会有一些额外的好处;
- 数据(因为JSON)可以很好的实现跨前后端直接使用;
- 一些业务(如模板渲染)可以很自由地轻量地选择前端还是后端进行,因为编程语言相同,所以切换代价小;
1. 基础功能
对于一个Web应用而言,在具体的业务上,可能存在的需求有:
- 请求方法的判断;
- URL的路径解析;
- URL中查询字符串解析;
- Cookie的解析;
- Basic认证
- 表单数据的解析
- 任意格式文件的上传处理;
- Session(会话);
1.1 请求方法
请求方法包括:
- GET;
- POST;
- HEAD;
- DELETE;
- PUT;
- CONNECT;
1.2 路径解析
客户端代理(浏览器)会将完整的URL地址解析成报文,将路径和查询部分放在报文第一行。
最常见的根据路径进行业务处理的应用是静态文件服务器,会根据路径去查找磁盘中的文件,然后将其响应给客户端。
还有一种常见的分发场景是根据路径来选择控制器,预设路径为控制器和行为组合,无须额外配置路由信息。
1.3 查询字符串
字符串会跟随在路径后,形成请求报文首行的第二部分。
在业务调用产生之前,中间件或者框架会将查询字符串转换,然后挂载在对象上供业务使用。
1.4 Cookie
-
初识Cookie
Cookie的处理分为如下几步:
- 服务器向客户端发送Cookie;
- 浏览器将Cookie保存;
- 之后每次浏览器都会将Cookie发向服务器端;
可选参数会影响浏览器在后续发送给服务器端的行为。主要为以下几个选项:
* path;
* Expires和Max-Age
* HttpOnly
* Secure
-
Cookie的性能影响
针对Cookie设置过多,造成的带宽的浪费的性能优化方案:
- 减少Cookie的大小
如果在域名的根节点设置Cookie,几乎所有子路径下的请求都会带上这些Cookie。
- 为静态组件使用不同的域名
为不需要Cookie的组件换个域名可以实现减少无效Cookie的传输,还可以突破浏览器下载线程的限制,但是将会增加域名转换为IP的DNS查询。
- 减少DNS查询
减少DNS查询和使用不同的域名看似冲突,但使用DNS缓存会削弱这个副作用。
- 减少Cookie的大小
1.5 Session
通过Cookie,浏览器和服务器可以实现状态的记录。但Cookie并非完美,缺点是:体积过大;Cookie在前后端均可以被修改,数据极易被篡改和伪造。综上,Cookie对于敏感数据的保护是无效的。
为了解决Cookie敏感数据的问题,Session应运而生。
Session的数据只保留在服务端,客户端无法修改,数据的安全有一定的保障,数据也无需在协议中每次传输。
如何将每个客户的服务器的数据一一对应:
- 基于Cookie来实现用户和数据的映射;
虽然将所有的数据都放在Cookie中不可取,但是将口令放在Cookie中是可以的。口令一旦被篡改,就丢失了映射关系,也无法修改服务器端存在的数据了。
Session的有效期通常较短,普遍的设置是20分钟,如果20分钟之内服务器端和客户端没有交互产生,服务器端将数据删除。由于数据过期时间较短,且在服务器端存储数据,因此安全性相对较高。
口令是如何产生的?
一旦服务器端启用了Session,它将约定一个键值作为Session的口令,这个值可以随意约定。一旦服务器端检查到用户请求Cookie没有携带该值,它就会为之生成一个值,这个值是唯一且不重复的值,并设定超时时间。
这种方案依赖Cookie的实现。
如果客户端禁止使用Cookie,这个世界大多数的网站将无法实现登录等操作。
- 通过查询字符串来实现浏览器端和服务器端数据的对应;
它的原理是检查请求的查询字符串,如果没有值,会先生成新的带值的URL。
- 利用HTTP请求头中的ETag;
-
Session与内存
为了解决性能问题和Session数据无法跨进程共享的问题,常用的方案是将Session集中化,将原本分散在多进程里的数据,统一转移到集中的数据存储中。目前常用的工具是Redis、MEmcached等,通过这些高效的缓存,Node进程无须再内部维护数据对象,垃圾回收问题和内存限制问题都可以迎刃而解。
采用第三方缓存来存储Session会引起的一个问题时引起网络访问。
理论上来说,访问网络中的数据比访问本地磁盘中的数据要慢,涉及到握手、传输、网络终端自身的磁盘I/O等。
采用第三方高速缓存的理由:
- Node与缓存服务保持长连接,而非频繁的短连接,握手导致的延迟只影响初始化;
- 高速缓存直接在内存中进行数据存储和访问;
- 缓存服务通常与Node进程会比在相同的机器上或者相同的机房里,网络速度受到的影响较小;
- Session与安全
Session的安全主要指如何让这个口令更加安全。
一种做法是将这个口令通过私钥加密进行签名,使得伪造的成本较高。
一种方案是将客户端的某些独有信息与口令作为原值,然后签名,这样攻击者一旦不在原始的客户端进行访问,就会导致签名失败。这些独有信息包括用户IP和用户代理(User Agent)。
- XSS漏洞
XSS的全称是跨站脚本攻击(Cross Site Scripting),通常都是网站开发者决定哪些脚本可以执行在浏览器端。
XSS主要形成的原因多数是用户的输入没有被转义,而被直接执行。
1.6 缓存
在HTTP之上构建的应用,其客户端除了比普通桌面应用具备更轻量的升级和部署等特性外,在跨平台、跨浏览器、跨设备上也具有独特的优势。
传统客户端在安装后的应用过程中仅仅需要传输数据,Web应用还需要传输构成界面的组件(HTML、CSS、JavaScript)。
为了提高性能,关于缓存的规则:
- 添加Expires或Cache-Control到报文头中;
- 配置ETags;
- 让Ajax可缓存;
通常来说,POST、DELETE、PUT这类带行为性的请求操作一般不做任何缓存,大多数缓存只应用在GET请求中。
简单来说,本地没有文件时,浏览器会请求服务端的内容,并将这部分内容放置在本地的某个缓存目录中。第二次请求时,它将对本地文件进行检查,如果不能确定这份本地文件是否可以直接使用,它将发起一次请求。
所谓条件请求,就是在普通的GET请求报文中,附带If-Modified-Since字段。
它将询问服务器是否有更新的版本,本地文件的最后修改时间。如果服务器没有新的版本,只需响应一个304状态码,客户端就使用本地版本。如果服务器端有新的版本,就将新的内容发送给客户端,客户端放弃本地版本。
条件请求采用时间戳的方式实现的缺陷:
- 文件的时间戳改定但内容并不一定改定;
- 时间戳只能精确到秒级别,更新频繁的内容将无法生效;
HTTP1.1中引入ETag解决采用时间戳的缺陷。
ETag的全称是Entity Tag,由服务器端生成,服务器端可以决定它的生成规则。如果根据文件内容生成散列值,那么条件请求将不会受到时间戳改动造成的带宽浪费。
尽管条件请求可以在文件内容没有修改的情况下节省带宽,但是依然会发起一个HTTP请求,使得客户端依然会花一定时间来等待响应。最好的方案就是连条件请求都不用发起,在服务器端响应内容时,让浏览器明确地将内存缓存起来。在响应里设置Expires或Cache-Control头,浏览器将根据改值进行缓存。
Expires是一个GMT格式的时间字符串。
浏览器在接到这个过期值后,只有本地还存在这个缓存文件,在到期时间之前我它都不会发起请求。但是Expires的缺席在于浏览器和服务器之间的时间可能不一致,可能导致文件提前过期,或者到期后文件并没有被删除。
Cache-Control能够避免浏览器端与服务器端时间不同步带来的不一致问题,只要进行类似倒计时方式计算过期时间即可。Cache-Control的值还能设置public、private、no-cache、no-store等能够更精细地控制缓存的选项。
在浏览器中,Expires和Cache-Control同时存在的话,且被同时支持时,max-age会覆盖Expires。
-
清除缓存
设置缓存可以达到节省带宽的目的,但是缓存一旦设定,当服务器端意外更新内容时,却无法通知客户端更新。这使得在使用缓存时,也要为其设定版本号,所幸浏览器是根据URL进行缓存。
一般的更新机制有:
- 每次发布,路径中跟随Web应用的版本号:http://love.bleaknight.top/?v=20200110
- 每次发布,路径中跟随该文件内容的hash值:http://love.bleaknight.top/?hash=bleaknight
1.7 Basic认证
Basic认证是当客户端与服务器端进行请求时,允许通过用户名和密码实现的一种身份认证方式。
Basic认证有太多的缺点,虽然经过Base64加密后在网络中传输,但是这近乎明文,十分的危险,一般只有在HTTPS的情况才会使用。
2. 数据上传
在业务中,需要接受的一些数据包括:表单提交、文件提交、JSON上传、XML上传等。
2.1 表单数据
最常见的数据提交就是通过网页表单提交数据到服务器端。
2.2 其他格式
除了表单数据外,常见的提交还有JSON和XML文件等,都是依据Content-Type中的值决定,其中JSON类型的值为appliction/json,XML的值为application/xml。
2.3 附件上传
除了常见的表单和特殊格式的内容提交,还有一种比较独特的表单,特殊表单与普通表单的差异在于该表单中可以含有file类型的控件,以及需要指定表单属性enctype为multipart/form-data。
2.4 数据上传与安全
内存和CSRF相关的安全问题。
-
内存限制
在解析表单、JSON和XML部分,采用的策略是先保存用户提交的所有数据,然后在解析处理,最后才传递给业务逻辑。
这种策略存在潜在的问题时,它仅仅适合数据量小的提交请求,一旦数据量过大,将发生内存被占光的情况。
解决此问题的方案:
- 限制上传内容的大小,一旦超过限制,停止接受数据,并响应400状态码;
- 通过流式解析,将数据流导向磁盘中,Node只保留文件路径等小数据;
- CSRF
CSRF的全称是Cross-SIte Request Forgery(跨站请求伪造)。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。