前言
关于ajax的原生实现是一个老生常谈的话题,之所以再提出,一是熟悉原生写法,二也是最重要的,跨域,众所周知,ajax不支持跨域,这一定言为广大程序猿所熟知,但很多新手都只到这一层就放弃了继续深入的契机,再者随着时代的发展,一系列支持跨域的websocket、ajax2.0等新特性出现,联系之前的jsonp,小菜决定来次小小的总结,如有纰漏望看官不吝赐教,
========================================================= 写着写着发现太长了,就分段了,本文实现了原生ajax,解析SOP同源策略,解决原生AJAX跨域==========================================================
本文主要实现
- 原生实现ajax
- 原生ajax跨域解决
- 跨域三剑客:websocket、ajax2.0、jsonp
正文
原生实现ajax
- 目录结构
- 用node搭建一个简单的服务器 若不了解node可以点击下面链接,我在其中有详细步骤 juejin.im/post/5cadc2…
const http = require('http');
const fs = require('fs');
const url = require('url');
http.createServer((req,res)=>{
let {pathname,query} = url.parse(req.url,true);
// console.log(pathname,query)
//将所有要响应的数据放在data文件夹下
pathname = `/data${pathname}`;
//解析路径进行判断,若请求正确才返回
if(pathname === '/data/data.json'){
fs.readFile('./data/data.json',(error,buffer)=>{
//避免返回中文乱码
res.writeHeader(200,{"Content-Type":"text/html;charset=utf-8"});
res.write(buffer);
res.end();
})
}else{
res.writeHeader(404);
res.write('NOT Found');
res.end();
}
}).listen(8080)
复制代码
- 准备数据 因为只是简单测试ajax,就不上数据库了,建一个json文件,存些数据,包结构如上,数据如下
{
"name":"小菜",
"dream":"家人身體健康"
}
复制代码
- 此时浏览器直接访问,响应如下
- 前端实现原生XMLHttpRequest发起请求
<script>
const xhr = new XMLHttpRequest();
xhr.open('get','./data.json',true);
xhr.send();
xhr.onreadystatechange = function(){
// 连接状态 枚举类型 0 初始化 1 已连接 2 已发送 3 接收当响应头 4 接收到响应体(即完成)
if(xhr.readyState == 4){
// 正则 ,意为首位为2的三位数 状态码304 也表示请求成功,只不过是告诉浏览器要去缓存中拿数据
if(/^2\d\d$/.test(xhr.status) || xhr.status ==304 ){
//响应数据
alert(xhr.responseText)
}
}
}
</script>
复制代码
这时候,如果直接打开html,就会报如下错误,原因是跨域了(跨域:协议,域名,端口三者有一不同即为跨域)
const http = require('http');
const fs = require('fs');
const url = require('url');
http.createServer((req,res)=>{
let {pathname,query} = url.parse(req.url,true);
// console.log(pathname,query)
//将所有要响应的数据放在data文件夹下
pathname = `/data${pathname}`;
console.log(pathname)
//解析路径进行判断,若请求正确才返回
if(pathname === '/data/data.json'){
fs.readFile('./data/data.json',(error,buffer)=>{
//避免返回中文乱码
res.writeHeader(200,{"Content-Type":"text/html;charset=utf-8"});
res.write(buffer);
res.end();
})
}else if(pathname === '/data/index.html'){
fs.readFile('./data/index.html',(error,buffer)=>{
res.write(buffer);
res.end();
})
}
else{
res.writeHeader(404);
res.write('NOT Found');
res.end();
}
}).listen(8080)
复制代码
这个时候我们再访问localhost:8080/index.html
就会惊讶(常理)的发现,成啦,哈哈 这个时候我们也就实现原生的异步请求啦,但是,这不是我们的目的,我们现在回过头来,找到刚刚害我们一顿折腾的跨域报错,同源策略
同源策源SOP
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。 ------------------------------我是分割线--------------------------------------------------- 以上解释来自百度百科,非小菜所言
其实就一句话:跨域请求响应回来的数据,浏览器会默认丢掉,并顺手给你报个大大的红错,hhh
其实这正是一种安全访问思想的体现,也正是我们废弃jsonp的原因(稍后解释);但这样我们如果有需求咋办?总不可能和客户解释SOP吧,那会被打死;这难不倒我们万能的程序猿,没有女友有右手,换个角度曲线救国;其实从上面的定义我们可以得到一个信息:
- 服务器是有响应的,只不过浏览器觉得会对服务器造成安全问题,就拒绝了这次响应;这也就意为着,前台无论做什么,都是不可能解决原生跨域的,那怎么办,后台呗,其实只要后台响应时加上一个响应头,声明“这是我兄弟,放心”,就可以了;废话不多说,上代码;
const http = require('http');
const fs = require('fs');
const url = require('url');
http.createServer((req,res)=>{
let {pathname,query} = url.parse(req.url,true);
// console.log(pathname,query)
//将所有要响应的数据放在data文件夹下
pathname = `/data${pathname}`;
console.log(pathname)
//解析路径进行判断,若请求正确才返回
if(pathname === '/data/data.json'){
fs.readFile('./data/data.json',(error,buffer)=>{
//避免返回中文乱码
// res.setHeader('access-control-allow-origin', '*');
//重点
res.writeHeader(200,{"Content-Type":"text/html;charset=utf-8",'access-control-allow-origin':'*'});
res.write(buffer);
res.end();
})
}else if(pathname === '/data/index.html'){
// res.setHeader();
res.writeHeader(200,{"Content-Type":"text/html;charset=utf-8"});
fs.readFile('./data/index.html',(error,buffer)=>{
res.write(buffer);
res.end();
})
}
else{
res.writeHeader(404);
res.write('NOT Found');
res.end();
}
}).listen(8080)
复制代码
- 重点:res.writeHeader(200,{"Content-Type":"text/html;charset=utf-8",'access-control-allow-origin':'*'});