前言
面试的时候经常会被问到网络相关的问题,我对网络相关的知识可以说是知之甚少,最近趁此机会学习和总结一下,希望在以后的面试当中不会再被难倒了。
URL组成
以下面这个url为例
- protocol 协议:http、https、file等。通过
location.protocol
获取; - host域名:
www.example.com
就是url当中的域名。通过location.host
获取; - prot端口:通常被忽略,http默认是80、https默认是443。通过
location.port
获取; - path路径:
/path/to/myfile.html
,网络资源在服务器中的路径。通过location.pathname
获取; - query参数:
?key1=value1&key2=value2
,提供给网络服务器的额外参数。这些参数是用&
符号分隔的键/值对列表;用location.search
获取 - hash锚点:
#SomewhereInTheDocument
,是资源本身的另一部分的锚点,值得注意的是,#后面的部分(也称为片段标识符)从来没有发送到请求的服务器。通过location.hash
获取。
跨域
发起请求的资源所在域不同于该请求所指向资源所在的域的HTTP请求就叫做跨域HTTP请求
什么叫跨域?协议+域名+端口有一个不同就是跨域。比如http://www.example.com:80
向https://www.example.com:80
发送请求。跨域的问题其实来自于浏览器的同源策略。
同源策略是一个重要的安全策略,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。
其实跨域也就是说不同源,我们项目里经常需要跨域才能完成请求,怎么跨源网络访问呢?
document.domain
document.domain
类似于location.href
,调用获取域名,赋值修改域名。这个方法只适用于两个基础域名相同的子域名之间,比如aaa.example.com 和 bbb.example.com
。在两个不同域名的页面下分别赋值document.domain='example.com'
,这样就实现跨域通信,不过这种方式已经不建议使用了。
postMessage
通过window.postMessage()
方法向跨源页面发射一些信息,在接收的页面通过监听message
事件来拿到发射的信息
需要注意的是这里的window必须是其他窗口的一个引用,比如 iframe 的 contentWindow 属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames
跨源资源共享(CORS)
跨源资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是
GET
以外的 HTTP 请求,或者搭配某些 MIME 类型 的POST
请求),浏览器必须首先使用OPTIONS
方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨源请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证 相关数据)。
简单请求
某些请求并不会触发cors预检请求,请求满足所有下述条件即可以视为简单请求
-
使用下列方法之一:
-
除了被用户代理自动设置的首部字段(例如
Connection
,User-Agent
)和在 Fetch 规范中定义为 禁用首部名称 的其他首部,允许人为设置的字段为 Fetch 规范定义的 对 CORS 安全的首部字段集合。该集合为:Accept
Accept-Language
Content-Language
Content-Type
(需要注意额外的限制)
-
Content-Type
的值仅限于下列三者之一:text/plain
multipart/form-data
application/x-www-form-urlencoded
-
请求中的任意
XMLHttpRequest
对象均没有注册任何事件监听器;XMLHttpRequest
对象可以使用XMLHttpRequest.upload
属性访问。 -
请求中没有使用
ReadableStream
对象。
预检请求
预检请求必须首先使用options
方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。比如下面就是一个需要执行"预检请求的http请求",因为请求头中加入了X-PINGOTHER
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://www.dotlog.kaochong.com/');
xhr.setRequestHeader('X-PINGOTHER', 'pingpong');
xhr.setRequestHeader('Content-Type', 'application/xml');
xhr.onreadystatechange = function (res) {
console.log(res);
};
xhr.send('<person><name>Arun</name></person>');
这里可以看到这里多了一个
preflight
类型options
请求,告诉他我要发送一个post请求,如果有凭证信息,这里也会校验凭证信息。
HTTP 响应首部字段
这里主要涉及到前端和后端的交互了,即xmlhttprequest请求,通常的做法是在http请求头和响应头上分别加上规范定义的首部字段。
- Access-Control-Allow-Origin 参数指定了允许访问资源的外域,也可以用
*
允许所有域的请求。比如这里指定了只有从kaochong.com发起的请求不会出现跨域
- Access-Control-Allow-Methods 首部字段用于预检请求的响应。其指明了实际请求中允许携带的首部字段。比如这里如果用put请求就是不允许的
- Access-Control-Allow-Headers 首部字段用于预检请求的响应。其指明了实际请求中允许携带的首部字段
- Access-Control-Allow-Credentials 指定了当浏览器的
credentials
设置为 true 时是否允许浏览器读取 response 的内容。比如我们向后端发起请求的时候需要携带cookie,但是响应头里没有设置true
,即使请求头中写携带了cookie,浏览器也不会把得到的响应内容返回给请求者。
HTTP 请求首部字段
- Origin 表明预检请求或实际请求的源站。
结语
这里只总结了一小部分,之后会总结其他的部分。写的不好,各位大佬多多指教,感恩家人🙏