HTTP实用指南(下) | 青训营笔记

147 阅读9分钟

HTTP实用指南(下) | 青训营笔记

🍏前言

这是我参与「第四届青训营」笔记创作活动的的第9天😺

由于笔记篇幅较长,所以将HTTP实用指南笔记调整为上下两部分分开发布。HTTP实用指南(下)承接了上部分的内容,继续介绍了HTTP协议的常见场景、实际应用以及拓展。

👨‍🏭常见场景

静态资源

状态码200 === 一定发起了请求?

在谷歌浏览器中打开开发者工具(快捷键F12),切换到“网络”,随后在浏览器中输入juejin.cn并前往:

image.png

我们观察到“状态”一栏的值全为200。随后在资源类型栏选择“CSS”,点击其中的一个文件:

image.png

可以看到状态代码“200”后跟随着一条StatusMessage (来自内存缓存);而观察响应头,由 cache-control: max-age=31536000 可知,这里的缓存策略采用的是强缓存,且缓存的最大周期时间为一年(31536000s)。

在缓存存在且强缓存未过期的情况下,不发起请求,而是直接读取缓存中的资源。所以当观察到状态码等于200时,并不意味着浏览器发起了请求。

除此之外,我们还可以在响应头中读取到其他更多的信息。比如由access-control-allow-origin:*可知,该资源允许所有域名访问;而由content-type:text/css;charset=utf-8可知,资源类型为CSS文件,字符集采用的是utf-8。

CDN

CDN (Content Delivery Network,内容分发网络)

CDN通过将内容缓存在终端用户附近,缩短网络资源的传递时延。通过用户就近性和服务器负载的判断,其确保内容以一种极为高效的方式为用户的请求提供服务。

image.png

静态资源方案:缓存 + CDN + 文件名hash

登录

跨域

常见的登陆方式有账号密码登录,以及扫码登陆。我们以账号密码登录为例,进行分析:

  • 进入toutiao.com并点击登录,打开开发者工具,选择“网络”;
  • 勾选“保留日志(preserve log)”,并过滤“quick_login”;

image.png

  • 登录,并观察请求:

image.png

在观察到的两项请求中,存在一个方法为OPTIONS的请求。那么为什么会存在这一请求呢?

由前述知识可知,OPTIONS请求用于描述目标资源的通信选项。换言之,OPTIONS请求就是预检请求,可用于检测服务器允许的http方法

以下引用块内容来自:HTTP协议中的OPTIONS方法是什么?有什么用_bksqmy的博客-CSDN博客_http option

它用于获取当前URL所支持的方法。若请求成功,则它会在HTTP头中包含一个名为“Allow”的头,值是所支持的方法,如“GET, POST”。

观察控制台中的结果,确实可以在响应头中发现这一信息:access-control-allow-methods: POST

image.png

所以对于OPTIONS请求的出现,原因在于发生了跨域(cross-origin)。在进行跨域资源共享(CORS, Cross-Origin Resource Sharing)之前,需要通过一个预请求来获知服务端是否允许该跨源请求(复杂请求),该预请求即为OPTIONS请求。

⚠️关于跨域:协议(protocol)、域名(domain)、端口(port)不同即为跨域。在这个例子中,我们进入的网页(域名)为toutiao.com,而观察请求报文中的请求网址则可知域名为sso.toutiao.com。两者域名不同,所以说这里发生了跨域。

image.png

相关协议头如下:

image.png

如何解决跨域问题?

跨域问题其实由同源策略导致。需要了解的是,同源策略是浏览器的一种安全策略,并不来源于HTTP。

来自MDN官网:

同源策略是一个重要的安全策略,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。

由于同源策略的存在,跨域时便会出现报错。那么如何解决跨域问题?

以下引用块内容来自:10 种跨域解决方案(附终极方案) - 知乎 (zhihu.com)

1. CORS
跨域资源共享(CORS)是一种机制,它使用额外的HTTP头来告诉浏览器 让运行在一个 origin (domain) 上的 Web 应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求
2. 代理服务器

原理:把不同的域名转换为相同的,即消除了跨域

image.png

3. iframe

结合iframe标签的src属性来解决跨域。

鉴权

下一次进入页面时,为什么还能记住登录态?

观察前述例子在控制台得到的结果,我们发现存在两个请求。一个为已经分析过的OPTIONS请求,另一个即为POST请求。

image.png

在这个 发起POST请求 / 请求得到响应的过程中,请求报文携带的信息有

  • post body,数据格式为form
  • 希望获取的数据格式为json
  • 已有的cookie

🍊需要注意的是,这个“已有的cookie”是由预请求OPTIONS请求的响应报文返回的。

image.png

(请求报文携带的长长的已有cookie)

响应报文返回的信息有

  • 数据格式json
  • 种cookie的信息

下一次进入页面会存在一个鉴权的过程,即对当前用户的权限进行检查。可以采用session+cookietoken两种方式。

image.png

session + cookie

image.png

JWT (JSON web token)

以前述例子为例,浏览器在登陆后已经获得了一个能代表用户身份权限的cookie,下次再发起请求时只需要带上这个cookie,即可完成身份认证,从而实现记住登录态。 这也与web安全有关。相关内容见笔记:WEB开发的安全之旅 (防御篇) | 青训营笔记 - 掘金 (juejin.cn)

SSO

SSO (Single Sign On,单点登录)
用户只需要登陆一次,其他多个互相信任的系统也可以感知到用户的登陆状态。

👨‍🚒实际应用

浏览器

AJAX (Asynchronous JavaScript and XML)

Asynchronous JavaScript + XML(异步 JavaScript 和 XML), 其本身不是一种新技术,而是一个在 2005 年被 Jesse James Garrett 提出的新术语,用来描述一种使用现有技术集合的‘新’方法,包括:HTML 或 XHTMLCSSJavaScriptDOMXMLXSLT, 以及最重要的 XMLHttpRequest。当使用结合了这些技术的 AJAX 模型以后, 网页应用能够快速地将增量更新呈现在用户界面上,而不需要重载(刷新)整个页面。这使得程序能够更快地回应用户的操作。

尽管 X 在 Ajax 中代表 XML,但由于JSON的许多优势,比如更加轻量以及作为 Javascript 的一部分,目前 JSON 的使用比 XML 更加普遍。JSON 和 XML 都被用于在 Ajax 模型中打包信息。

AJAX - XHR

XHR (XMLHttpRequest)

The XMLHttpRequest object is an API for fetching resources.

The name XMLHttpRequest is historical and has no bearing on its functionality.

可以根据readyState属性的值来判断请求资源的操作位于哪个阶段:

属性值所处阶段具体含义
0UNSENT代理被创建,但尚未调用open()方法
1OPENEDopen()方法已被调用
2HEADERS_RECEIVEDsend()方法已经被调用,并且头部和状态已经可以获得
3LOADING下载中;responseText属性已经包含部分数据
4DONE下载操作已经完成

示例代码片段:

(开头处省略了部分代码,此处主要展示XHR中对应HTTP的内容)

if(option.method === 'GET'){
    option.url += location.search.length === 0 ? ''.concat('?',option.data) : ''.concat('&',option.data);
}

对xhr实体的readyState属性的值进行检查,如果该值等于4,则说明资源的获取操作已经完成,接下来还要对http响应报文的statusCode(即xhr实体的status属性值)进行检查,当该值等于200时说明资源获取成功,进行下一步的操作。

var xhr = new XMLHttpRequest();
xhr.responseType = option.responseType || 'json';
xhr.onreadystatechange = function(){
    if(xhr.readyState === 4){
        if(xhr.status === 200){
            if(option.success && typeof option.success === 'function'){
                option.success(xhr.response);
            }
        } else{
            if(option.error && typeof option.error === 'function'){
                option.error();
            }
        }
    }
}
xhr.open(option.method,option.url,true);

if(option.method === 'POST') {
    xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
}

xhr.send(option.method === 'POST' ? option.data : null);

AJAX - Fetch

  • XMLHttpRequest的升级版
  • 使用promise
  • 模块化设计,Response/Request/Header对象
  • 通过数据流处理对象,支持分块读取
postData('http://example.com/answer',{answer:42})
    .then(data => console.log(date)) // JSON from `response.json()` call
    .catch(error => console.error(error))
function postData(url,data) {
    return fetch(url,{
        body:JSON.stringify(data), //must match 'Content-Type' header
        cache:'no-cache', 
        credentials:'same-origin',
        headers:{
            'user-agent':'Mozilla/4.0 MDN Example',
            'content-type':'application/json'
        },
        method:'POST',
        mode:'cors',
        redirect:'follow',
        referrer:'no-referrer'
    }).then(response => response.json());
}

关于是 referer 还是 referrerReferrer 还是 Referer? | JerryQu 的小站 (imququ.com)

node

标准库:HTTP/HTTPS

  • 默认模块,无需安装其他依赖
  • 功能有限/不是十分友好
const https = require('https');

https.get('https://test.com?api_key=DEMO_KEY' , (resp)=>{
    let data = '';
    
    // a chunk of data has been received.
    resp.on('data',(chunk)=>{
        data += chunk;
    });
    
    //the whole response has been received, print out the result.
    resp.on('end',()=>{
        console.log(JSON.parse(data).explanation);
    });
    
}).on("error",(err)=>{
    console.log("Error: "+err.message);
});

常用的请求库:axios

  • 支持浏览器、node.js环境
  • 丰富的拦截器
//全局配置
axios.defaults.baseURL = "https://api.example.com";

//添加请求拦截器
axios.interceptors.request.use(function (config){
    //在发送请求之前做些什么
    return config;
}, function (error){
    // 对请求错误做些什么
    return Promise.reject(error);
});

//发送请求
axios({
    method:'get',
    url:'http://test.com',
    responseType:'stream'
}).then(function(response){
    response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
});

用户体验

网络优化

image.png

预解析和预连接:

<link rel="dns-prefetch" href="//example.com">
<link rel="preconnect" href="//cdn.example.com" crossorigin>

稳定性

image.png

  • 重试是保证稳定的有效手段,但要防止恶劣情况
  • 缓存合理使用,作为最后一道防线

👨‍💻了解更多

WebSocket

  • 浏览器与服务器进行全双工通讯的网络技术
  • 典型场景:实时性要求高,例如聊天室
  • URL使用 ws://wss:// 等开头

image.png

HTTP 对比 WebSocket

QUIC (Quick UDP Internet Connection)

  • 0-RTT建联(首次建联除外)
  • 类似TCP的可靠传输
  • 类似TLS的加密传输,支持完美前向安全
  • 用户空间的拥塞控制,最新的BBR算法
  • 支持h2的基于流的多路复用,但没有TCP的HOL问题
  • 前向纠错FEC
  • 类似MPTCP的Connection migration

image.png

🍎小结

HTTP实用指南(上) 与 HTTP实用指南(下)对HTTP协议相关内容进行了介绍,并拓展了一些其他的通讯技术。对于学习这方面的内容,还是需要掌握一些计算机网络相关的基础知识,并且多记忆和理解。💨

2022/8/2