目录
1. 预检请求
预检请求(Pre-flight Request)是CORS(跨源资源共享)机制中的一部分。在浏览器的同源策略(Same-origin policy)下,一个域名下的脚本不能读取或写入另一个域名下的数据。但是,为了支持现代Web应用的复杂需求,CORS提供了一种安全的方式,允许服务器指定哪些来源可以访问其资源。
当一个HTTP请求是“简单请求”(Simple Request)以外的情况时,浏览器会先发送一个预检请求来确认服务器是否允许该跨域请求。预检请求是一个OPTIONS
方法的请求,它包含了实际请求可能用到的所有关键信息,如请求方法、请求头等,以便服务器判断是否授权此跨域请求。
预检请求包含以下关键部分:
- 请求方法:实际请求将要使用的方法(如
PUT
,DELETE
等)。 - 请求头:实际请求可能携带的非简单头部,如
Content-Type
、Authorization
等。 - 源:发起请求的源(origin),即发起请求的页面所在的域名和协议。
服务器收到预检请求后,需要通过响应头中的Access-Control-Allow-Origin
来指示是否接受来自特定源的请求。此外,服务器还可以设置以下响应头来进一步控制跨域行为:
Access-Control-Allow-Methods
:列出允许的请求方法。Access-Control-Allow-Headers
:列出允许的请求头部。Access-Control-Max-Age
:预检请求的有效期,单位为秒,在这段时间内,相同的预检请求不需要重复发送。
如果服务器的响应允许了跨域请求,那么浏览器会继续执行实际的请求。否则,跨域请求会被阻止,并且不会到达服务器。这种机制保证了跨域请求的安全性和可控性。
简单请求
在CORS(跨源资源共享)机制中,“简单请求”(Simple Request)是指那些满足一定条件的跨域HTTP请求,这些请求不需要发送预检请求(Pre-flight Request)。简单请求直接进行,没有额外的延迟,因此对于常见的GET、POST和HEAD请求来说,效率更高。
一个请求被认为是简单请求,需要满足以下所有条件:
-
请求方法:请求必须使用以下三种方法之一:
GET
,POST
, 或HEAD
。 -
请求头:请求头中只能包含简单的头部字段,包括:
Accept
Accept-Language
Content-Language
- 以及任何以
Content-Type
开头,但值仅限于application/x-www-form-urlencoded
,multipart/form-data
, 或text/plain
的头部。
-
Content-Type:如果请求体有内容,
Content-Type
必须是上述提到的三个值之一。
如果一个请求不满足以上所有条件,则被认为是“非简单请求”,在这种情况下,浏览器会在发送实际请求前先发送一个OPTIONS
方法的预检请求,以确认服务器是否允许该跨源请求。
简单请求的设计理念是为了提高效率并减少网络往返次数,因为大多数的Web请求都是相对简单的GET或POST请求,而且通常携带的头部也是标准的。通过区分简单请求与非简单请求,CORS机制能够更灵活地处理不同类型的跨源请求,同时保持安全性。
2. 浏览器缓存
强缓存(Strong Cache)和协商缓存(Negotiated Cache)是HTTP缓存机制的两种主要类型,它们分别在不同的场景下优化网络性能和资源的利用。下面是这两种缓存的主要区别:
强缓存(Strong Cache)
- 定义:强缓存基于服务器响应中的缓存控制指令,如
Expires
或Cache-Control
头,来决定资源是否可以被缓存以及缓存的有效期。 - 操作:一旦资源被缓存,只要它还在有效期内,浏览器就会直接从缓存中加载资源,而不会向服务器发送任何请求。这避免了网络延迟,提高了加载速度。
- 指令:强缓存主要依赖
Cache-Control
中的max-age
或Expires
头来确定资源的过期时间。 - 优点:减少服务器负载,节省网络带宽,提高网页加载速度。
- 缺点:可能导致浏览器显示的资源不是最新的,尤其是在资源更新频繁的情况下。
协商缓存(Negotiated Cache)
- 定义:协商缓存是一种在浏览器和服务器之间进行协商,以确定缓存资源是否需要更新的机制。
- 操作:浏览器在请求资源时会包含
If-None-Match
或If-Modified-Since
头,服务器根据这些头判断资源是否需要重新发送。 - 指令:协商缓存依赖于
ETag
(实体标签,一种资源变更的唯一标识符)或Last-Modified
(资源的最后修改时间)头来检查资源的时效性。 - 优点:确保资源是最新的,适合更新频繁的资源。
- 缺点:每次请求都需要与服务器进行额外的通信,增加了网络开销,尽管这种开销通常比重新下载整个资源要小得多。
总结
- 强缓存适用于那些更新频率较低的资源,如图片、样式表和脚本文件,因为它们的更新周期较长,可以长时间缓存在客户端。
- 协商缓存更适合那些更新频率较高的资源,如动态生成的内容,因为它能确保客户端始终获取到最新的版本。
3. 状态码301和302的区别
状态码301和302都是HTTP状态码,它们用于指示Web服务器如何处理请求并响应客户端,特别是在涉及资源位置变化的重定向情况下。以下是301和302状态码的区别:
-
301 Moved Permanently(永久重定向)
- 这个状态码表示请求的资源已被永久移动到一个新的位置,通常是因为URL的长期变更,比如网站改版或域名迁移。
- 浏览器会自动缓存301重定向响应,这意味着对于同一个资源的未来请求将会自动使用新的URL,而无需每次都重新执行重定向。
- 搜索引擎和其他代理服务器会更新其索引和记录中的URL,指向新的位置,以反映永久性的变化。
-
302 Found(临时重定向)
- 这个状态码表示请求的资源已被临时移动到一个新的位置,可能是由于临时的系统维护、登录状态检查或其他临时性的原因。
- 浏览器不会像301那样缓存302重定向,因为资源的位置只是暂时的。这意味着对于同一个资源的未来请求,浏览器会重新执行重定向,以获取最新的位置信息。
- 搜索引擎和其他代理服务器通常不会更新其索引中的URL,因为302表示的只是暂时的重定向。
在实际应用中,选择正确的状态码很重要,以确保客户端和搜索引擎正确地处理重定向。如果资源位置的变化是永久性的,应使用301状态码,以通知客户端和搜索引擎更新其链接和索引。如果变化是临时的,应使用302状态码,以避免不必要的URL更新。不过,值得注意的是,现代的搜索引擎可能会忽略302状态码的临时性,并将其视为永久重定向,尤其是当重定向持续一段时间后。因此,在实践中,对于SEO目的,推荐使用301状态码来处理永久性的URL变更。
4. 状态码401和403的区别
状态码401和403是HTTP协议中用来表示客户端请求失败的两种不同类型的响应,它们各自代表了不同的问题:
401 Unauthorized(未授权)
- 定义:此状态码表示请求需要用户的身份验证。服务器无法完成未经验证的请求。客户端应该使用适合的认证机制重新发起请求。常见的情况是客户端没有提供必要的身份验证信息,或者提供的信息是无效的。
- 原因:
- 用户未登录或没有提供认证信息。
- 提供的认证信息(如用户名/密码、OAuth token等)无效或已过期。
- 解决方案:
- 提供正确的认证信息重新发起请求。
- 检查并更新过期或无效的认证信息。
403 Forbidden(禁止访问)
- 定义:此状态码表示服务器理解请求客户端的请求,但是拒绝执行此请求。拒绝的原因可能是服务器只有部分用户有权限访问,而当前用户不在有权限的范围内。
- 原因:
- 用户虽然通过了身份验证,但没有足够的权限访问请求的资源。
- 请求的资源对当前用户关闭,可能由于策略、权限设置或其他限制。
- 解决方案:
- 确认用户是否具有访问所需资源的权限。
- 如果权限设置不当,联系管理员修改权限设置。
- 更换有适当权限的用户账户进行请求。
总结
- 401 关注的是认证问题,即客户端没有正确地证明其身份。
- 403 关注的是授权问题,即客户端已经通过了身份验证,但没有被授予访问特定资源的权限。
这两种状态码虽然都表示拒绝,但它们的区别在于401通常意味着缺少或错误的身份验证,而403意味着即使用户被识别,他们也没有适当的权限来访问资源。
5. 跨域请求的处理方式CORS
跨域请求的处理通常涉及克服浏览器的同源策略(Same-Origin Policy),这项安全措施限制了一个源的脚本对另一个源的文档或脚本的访问。当Web应用需要从不同源请求资源时,就需要解决跨域问题。以下是一些常见的跨域请求处理方式:
-
CORS(Cross-Origin Resource Sharing)
- CORS 是一种基于 HTTP 响应头的机制,允许服务器指定哪些源可以访问其资源。服务器通过设置
Access-Control-Allow-Origin
头来表明允许的源,还可以通过其他响应头来控制请求的方法、缓存策略等。
- CORS 是一种基于 HTTP 响应头的机制,允许服务器指定哪些源可以访问其资源。服务器通过设置
-
JSONP(JSON with Padding)
- JSONP 利用
<script>
标签没有同源策略限制的特性,通过动态创建<script>
标签并设置src
属性为跨域请求的 URL,服务器将 JSON 数据包装在一个回调函数中返回,客户端预先定义的函数会被调用来处理数据。
- JSONP 利用
-
代理服务器
- 在客户端和目标服务器之间建立一个代理,客户端向代理服务器发送请求,代理服务器再向目标服务器发送请求,并将响应转发回客户端,这样可以绕过同源策略限制。
-
WebSocket
- WebSocket 协议允许在不同源之间建立持久的双向通信通道,它本身支持跨域连接,只需要在建立连接时处理好认证和权限问题。
-
Document.domain
- 如果两个页面共享相同的顶级域名(如
a.example.com
和b.example.com
),可以通过设置document.domain
为相同的子域名来允许跨域脚本访问,但这种方法有一定的局限性。
- 如果两个页面共享相同的顶级域名(如
-
PostMessage
- HTML5 引入了
window.postMessage
方法,允许不同源的窗口之间发送消息,接收方可以通过事件监听器捕获并处理消息。
- HTML5 引入了
-
使用子资源完整(SRI,Subresource Integrity)
- 虽然 SRI 主要是用于验证外部资源的完整性,但它也可以帮助在跨域场景中加载CSS和JavaScript资源。
-
修改服务器配置
- 反向代理、配置服务器端的CORS支持等,这些都需要在服务器端进行适当的配置更改。
每种方法都有其适用场景和潜在的限制,选择哪种方式取决于具体的应用需求、安全性要求和可实施性。例如,CORS 是现代Web应用中最常用的方法,因为它既安全又灵活,而 JSONP 由于只支持 GET 请求,现在较少使用。在实际应用中,开发者可能需要根据项目特点和浏览器兼容性来选择最适合的跨域处理方案。
6. websocket
WebSocket是一种在单个TCP连接上进行全双工通讯的协议。它提供了浏览器和服务器之间持续开放的双向通信渠道,克服了HTTP协议的无状态和请求-响应模型的限制。WebSocket在建立连接时使用HTTP进行握手,之后就使用自己的二进制协议进行数据交换,这样可以显著降低延迟和带宽消耗。
WebSocket的主要特点包括:
- 全双工通信:客户端和服务器可以同时发送数据,无需等待对方确认。
- 持久连接:一旦建立连接,就可以保持直到任何一方断开连接。
- 轻量级:数据包比HTTP更小,因为它们使用了更简单的格式。
- 实时性:非常适合需要实时数据更新的应用场景。
前端使用WebSocket的典型场景包括:
- 实时聊天应用:即时通讯、在线客服、多人聊天室等,需要实时发送和接收消息。
- 在线协作编辑:多人同时编辑文档,如Google Docs或石墨文档,实时同步编辑操作。
- 金融交易系统:实时推送股票价格、外汇汇率等金融数据。
- 在线游戏:实时同步玩家位置、游戏状态更新、多人游戏互动等。
- 实时数据监控:如物联网设备状态、传感器数据的实时监控。
- 多媒体应用:实时音视频通话、会议、直播等。
- 体育赛事实时更新:比分、比赛状态等实时信息推送。
- 教育平台:在线课堂、白板共享、实时笔记等。
- 智能家居:设备控制、状态反馈等实时交互。
- 社交应用:动态更新、通知推送、状态变化等实时信息。
前端如何使用WebSocket?
在现代浏览器中,可以直接使用WebSocket
对象来创建和管理WebSocket连接。以下是一个简单的示例:
if (window.WebSocket) {
// 创建WebSocket实例
var socket = new WebSocket('ws://example.com/ws');
// 连接打开时的回调
socket.onopen = function(event) {
console.log("Connection opened");
};
// 接收消息时的回调
socket.onmessage = function(event) {
console.log("Received: ", event.data);
};
// 连接关闭时的回调
socket.onclose = function(event) {
console.log("Connection closed");
};
// 错误处理
socket.onerror = function(error) {
console.error("Error occurred: ", error);
};
// 发送数据
socket.send(JSON.stringify({ action: 'join', room: 'main' }));
} else {
console.log("WebSocket is not supported by this browser.");
}
这个示例展示了如何创建WebSocket连接、处理连接的生命周期事件以及如何发送和接收数据。在实际应用中,你可能需要添加更多的错误处理和重连逻辑。