1.缘起非同源,根在axios
1.同域带cookie,非同域选择性带
- 真实项目中,突然由于变动需要访问非同域的域名,按理说会跨域。
- 由于是公司内网,也他们最后的
根域名
都是一样的,请求并没有跨域,但是cookie却无法携带。 - 我们尝试自己请求的时候携带cookie,发现原因自己携带,也会不生效
axios({
url:a.com,
headers:{
Cookie:"aaaaaaaaaa"
}
})
2.域名服务器之间的映射
- 域名和服务器之间是多对一,服务器地址ip是唯一的,用户访问域名,域名通过解析到服务器对应的的端口。
- 由于cookie是种在根域名下,也就是类似服务器的ip,所以这两个域请求并不会跨域
- 产生的真正原因是,以为axios检测不到是不是根域名下,所以处于保护措施,axios会自动禁止非同源请求携带cookie的,
3.axios的withCredentials属性解决问题
- withCredentials 默认为fasle
- withCredentials属性可以允许非同源的域名携带cookie
- 当我们开启以后就可以携带cookie了
axios.defaults.withCredentials = true
// 后端如果没开启的话,也需要开启下
2.axios我们最熟悉的陌生人(手动封装)
1.ajax四部曲
- 创建一个XMLHttpRequest类
- 用什么方式来打开来访问这个URL以及是否为异步
- 监听这个实例的状态变化
- 然后调用这个实例来发送数据
var ajax = new XMLHttpRequest();
ajax.open(method, url, true);
ajax.onreadystatechange = function() {
if(ajax.readyState === 4 && ajax.status === 200){
// 处理哦返回逻辑
}
}
ajax.send()
2.基于promise解决异步并发问题
- promise作为解决回调地狱的问题和并发的最优解,我们利用特性去封装
- Axios({...}).then(res=>{...});直接用默认配置的封装
function Axios(obj) {
const {method,url, params} =obj;
return new Promise(function(resolve,reject) {
var ajax = new XMLHttpRequest();
ajax.open(method, url, true);
ajax.onreadystatechange = function () {
if (ajax.readyState === 4 && ajax.status === 200) {
// 处理返回逻辑
let data = JSON.parse(ajax.responseText)
resolve(data)
}
}
ajax.send(params)
}).catch(err=>{
resolve(err)
})
}
3. new 一个Axios_ 类,并实现我们想要的
- 我们需要先构造一个类
- 我们只模拟核心逻辑,所以我们在类的原型上,需要提供get,post,create,interceptors这四种方法
class Axios_{
constructor(props){// 把需要暴露出去的api进行return }
interceptors={// 里面包含了请求拦截器和响应拦截器}
myAxios(obj){ // 这里放我们刚才封装的异步axios}
get(...arg){// get方法 }
post(...arg){// post 方法}
create(...arg){// 创建实例方法 }
}
Axios.create({})会返回一个可配置的axios实例
create(...arg){
return new Axios_(...arg)
}
get 和post 等均需需要调用我们封装的promise版ajax
get(...arg){
const [url,params] = arg;
console.log(url,params,arg);
return this.myAxios({method:'get',url,params})
}
post(...arg){
const [url,params] = arg;
return this.myAxios({method:'post',url,params})
}
constructor中将方法报露出来
constructor(props){
return {
get:this.get,
post:this.post,
create:this.create,
myAxios:this.myAxios,
interceptors:this.interceptors
}
}
4.基于实例可配置拦截器
我们先看下拦截器的基本使用
- 请求执行前,可对请求进行处理和配置
- 数据响应之后发送到前端之前,可对结果进行处理和配置
let http = axios.create({})
http.interceptors.request.use((config)=>config)
http.interceptors.response.use((response)=>response)
Axios.interceptors中包含请求拦截器和响应拦截器、 根据调用我们构造interceptors对象
interceptors={
request:{
use:(obj)=>obj
},
response:{
use:(obj)=>obj
}
}
myAxios 利用我们封装的promise 请求拦截器在生成实例前,执行请求拦截器,在响应时先经过响应拦截器 注意promise闭包里的this,由于this指向问题,所以需要存储下this
myAxios(obj){
// 请求拦截器
const beforeRequetMap = this.interceptors.request.use(obj)
const {method,url, params} =beforeRequetMap;
const that =this ; // 作用域存储
return new Promise(function(resolve,reject) {
var ajax = new XMLHttpRequest();
ajax.open(method, url, true);
ajax.onreadystatechange = function () {
if (ajax.readyState === 4 && ajax.status === 200) {
let data = JSON.parse(ajax.responseText);
// 响应拦截器
console.log(this,that,'this');
const beforeResponseMap = that.interceptors.response.use(data)
resolve(beforeResponseMap || data)
}
}
ajax.send(params)
})
}
5. withCredentials的庐山真面目
cookie是根据种在域名下cookie去携带的。 也就是说,所有的设置配置cookie的方法,全是基于document.cookie去设置的
withCredentials 也是基于ajax的withCredentials属性去透传并做了一些判断。 这边判断了withCredentials
是否为是或者是不是非同源
,从而去判断是否去写入cookie 下面我们看下源码
if (utils.isStandardBrowserEnv()) {
var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ?
cookies.read(config.xsrfCookieName) :
undefined;
if (xsrfValue) {
requestHeaders[config.xsrfHeaderName] = xsrfValue;
}
}
// cookies.read
read: function read(name) {
var match = document.cookie.match(new RegExp('(^|;\\s*)(' + name + ')=([^;]*)'));
return (match ? decodeURIComponent(match[3]) : null);
},
我们需要模拟这个过程,axios调用
axios.get('./a.json',{
headers:{
cookie:'document.cookie'
}
})
axios类上增加defaults,存储了一些默认配置 isURLSameOrigin方法回去判断baseURL是否是同域 setCookie 通过循环拼接将传递过来的cookie进行拼接
const.defaults={
withCredentials:false
}
isURLSameOrigin(){...}
setCookie(){...}
...
myAxios(obj){
// 请求拦截器
const beforeRequetMap = this.interceptors.request.use(obj)
const {method,url, params} =beforeRequetMap;
const that =this ; // 作用域存储
return new Promise(function(resolve,reject) {
var ajax = new XMLHttpRequest();
ajax.open(method, url, true);
if(that.defaults.withCredentials || this.isURLSameOrigin(url)){
this.setCookie(params?.headers?.cookie)
}
// 省略无关代码
ajax.send(params)
})
}
3.蓦然回首整体思路
1.先解决当前问题,找到问题所在
- withCredentials默认为false,
- withCredentials 并不会考虑到内网及其多域名打到统一服务器上的异常场景
- 按理说,就算允许跨域携带cookie,但是大多数还是不一致的,还是会资源跨域
- 顺便自我人之下,域名和服务器之间的映射关系
2.开启自我提升,尝试自己手写axios
- 选择合适的底层,我们利用ajax去封装
- 观察axios的调用方式,尝试模拟实现,多试多查
- 具体细节可以忽略,大致思路去模拟
- 先猜想,再去看源码,对比实现
3.要在正确的认知阶段做正确的事
-
初出茅庐:
多看多cv,cv后一定要总结他人的方法,并尝试你自己写,哪怕最后实现不了,也没问题,尝试的经验才是最宝贵的
-
小试牛刀:
渐渐的你脑海里已经有模块,组件,封装的模糊概念了。并且你可以正确的查找和阅读各种api,这时候虽然很痛苦,但是一定多积累,多读api,多写demo,你已经可以完整简单组件的封装了,不积跬步无以至千里。
-
略有小成:
你已经可以carry住大多数简易需求,你可以用简单单一的代码和模式去写项目了,习惯是好东西,但是也有不好的方法,你需要渐渐的尝试新东西,新思想,新写法,逐步应用到你得项目里,学海无涯
-
渐入佳境:
这个阶段我理解的为思绪爆炸,就是目前你已经可以做一块砖了,哪里需要哪里搬了,但是你遇到的问题,已经不仅仅局限于,解决问题,而是想要往深处理解,甚至挖他的源码。这时候,将会是我们知识融合融汇的阶段,因为你去挖源码,看设计模式,看思路,会将你所有的经验串联起来,最后形成你得独特的见解,将来你得路将不仅仅局限于js,感觉自己已经摸到了一丝架构或者全栈的感觉了。
-
学无止境:
可以自己尝试拓展,node方向的知识,因为node可以平滑过渡,尝试自己node搭建服务器,自己配置Nginx,补充自己数据库知识,学无止境,等我们用node搭建一个服务后,我们将会站在后端的角度,审视我们的字段,审视接口存在必要性,审视接口的设计是否合理。