浏览器-网络相关作业

  1. 列举 CORS 的头信息,并说明作用
  2. 写一段代码让 CORS 跨域请求带上 cookie
  3. 写一段让 http 请求失败后重试的代码

工程目录

启动客户端

client> http-server // 启动客户端,默认127.0.0.1:8080
// 需要自行安装http-server等任意web静态资源服务器

启动服务端

server> npm run dev // 启动服务端,默认127.0.0.1:3000
// 需要自行安装nodejs

Devtool - network面板 - throttling的使用:

  • 使用fast 3g,观察loading状态
  • 使用offline,观察断网重连

标准的CORS跨域方案

概念

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS

与jsonp的区别

  • cors是标准,jsonp是hack技术
    • jsonp是资源的请求,请求方式局限。本地需要写接收和发送资源的逻辑。
    • cors是数据请求(xhr or fetch),请求方式自由。使用xhr和fetch发送和接收数据。
  • 兼容问题
    • jsonp没有兼容问题
    • cors需要ie10以上(不过这已经不是问题)
  • 安全问题
    • jsonp返回的是一个自执行函数,需要对接口提供方足够的信任。安全等级较高的业务无法使用这种方式。

http 头相关字段

/* http request 请求头 */
决定了是简单请求还是非简单请求。
问题:只要跨域,devtool-network-headers-request中看到的一定是`Provisional headers are shown`吗?
/* 使用XMLHttpRequest的说明 */
xhr.withCredentials:true // 带上凭证
/* 使用fetch的options说明 */
credentials: "include", // include, same-origin, *omit
// 不写headers时,默认只包含简单请求相关字段
// 自定义headers时注意,一旦添加了非简单请求字段或值,会多出一个options预检请求
headers: { "x-my-magic": 1,"x-your-romantic":2 }
mode: "cors" // no-cors, *cors, same-origin
/* http response 响应头 */
决定了是否支持cors响应
/* 使用nodejs添加响应头的说明 */
res.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8080"); // 只能写一个key和一个value,可以直接从req里取并填入,不要写成*(请求凭证会无效)
res.setHeader("Access-Control-Allow-Methods", "GET,POST"); // 允许的方法(不建议开放危险方法,比如会让数据减少和删除的。)
res.setHeader(
	"Access-Control-Allow-Headers",
	"DNT,Origin,Referer,User-Agent,content-type,cookie,x-my-magic"
); // 给预检请求使用的headers字段
res.setHeader("Access-Control-Allow-Credentials", true); // 允许请求凭证

简单请求的判断

概念

简单请求和非简单请求,是客户端(浏览器)的内部判断逻辑。
如果浏览器判断该请求为简单请求,则直接从js发出请求开始,直达目标服务器进行请求。
如果浏览器判断该请求为非简单请求,浏览器暂扣下该请求,由浏览器发送一个预检请求(method:options)到目标服务器。然后浏览器拿着返回的Access-Control-Allow-Headers字段和Access-Control-Allow-Methods字段进行检查,看是否与扣下的请求头相匹配。若请求头匹配(服务端支持)则请求放行,否则取消请求。

简单请求的判断标准

符合以下所有条件的为简单请求,否则就是非简单请求:

  • 方法限制
    • GET 获取实体
    • POST 修改实体
    • HEAD 获取头信息
  • 头信息字段限制,且不能自定义值:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type (有特殊要求)
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width
  • Content-Type限制
    • text/plain
    • multipart/form-data
    • application/x-www-form-urlencoded
  • XMLHttpRequestUpload 对象没有设置监听器
  • ReadableStream 对象没有被使用到

http 请求重试实践

当 http 请求无响应或者返回错误状态码(超出(200 ~ 299 && 304))时,客户端需要自动对该请求进行重新连接。
要求做到:

  • 尽可能第一时间获取远端数据,恢复状态。
  • 尽可能不对服务器造成持续压力,比如每次重连的请求时间翻番,以及设定最大请求数量

策略说明

设定初始请求延时,比如 1 秒。
设定每一轮请求的递进延时,比如第二轮翻一番 2 秒,以此类推。
设定最大请求数量,比如 3 次。

方案实施

/* 重连方法 */
function retry(handler,option) {
	let count = 0;
	let timeid = null;
	const maxCount = option.maxCount || 3;
	const ms = option.seconds*1000 || 1000;
	function next(cb) {
		clearTimeout(timeid);
		if (count >= maxCount) {
			cb();
			return;
		}
		// 将next作为参数传递给handler,供业务使用
		timeid = setTimeout(handler, ms * count, next);
		count++;
	}
	next(null);
}
/* 应用重连逻辑 */
retry(function(next) {
	fetch(
		url,
		options
	)
		.then(res => res.json())
		.then(json => {
			Notify.show("fetch请求成功!");
		})
		.catch(err => {
			// 进行下一轮请求
			next(() => {
				Notify.show("ajax请求失败!");
			});
		});
},
// 配置重连策略
{
	maxCount:5,
	seconds:0.5
};