深入浅出篇 — ajax的诞生 & 跨域

1,284 阅读7分钟

网络-深入浅出篇文章:

一. AJAX

ajax的诞生:

  1. 发送网络请求的方式:
  • 在浏览器中直接输入网址 (其无法用代码控制)
  • 在控制台输入location.href="url" ,可以发送网络请求,但是页面会发生跳转(页面会跳转)
  • 带有src属性的标签 —— 例如img ,请求是可以发出的,服务端是可以处理的并且也是可以返回的,但是返回后,能否被应用,还要看浏览器(页面无法处理返回结果)
  • 带有href属性的标签 —— 例如link ,请求是可以发出的,服务端是可以处理的也是可以返回的,但是返回后,能否被应用,还要看浏览器(页面无法处理返回结果)
  • 带有action属性的标签 例如form表单,也可以向后端发出请求,但form表单发送请求后也会页面跳转(页面会跳转)
  1. ajax诞生:
  • 就是因为希望有一种方式,既可以用代码控制,而且页面不会跳转,并且服务端返回的结果我们可以用JS继续处理,所以就有了ajax

ajax的应用:

  1. ajax 全名: async javascript and xml

  2. ajax 的请求要素:

  • 请求方式: Get/Post
  • url
  • 但ajax受到浏览器同源策略的限制,同源策略是浏览器提出的一种安全机制
  1. 发送请求-兼容写法:
例:
    var xhr = null
    if (window.XMLHttpRequest) {
        xhr = new XMLHttpRequest()                      //常用浏览器自带
    } else {
        xhr = new ActiveXObject("Microsoft.XMLHttp")    //IE6自带
    }
    xhr.open("get", "http://www.baidu.com", true)       // 第三个参数 true 代表异步
    xhr.send()  //这就可以发送了 但有可能有跨域问题,把xhr.send放在xhr.onreadystatechange后比较好

3. 原生js发送ajax

例:
    var xhr = null
    if (window.XMLHttpRequest) {
        xhr = new XMLHttpRequest()                      //常用浏览器自带
    } else {
        xhr = new ActiveXObject("Microsoft.XMLHttp")    //IE6自带
    }
    console.log(xhr.readystate)                         //  0
    xhr.open("get", "http://www.baidu.com", true)       // 第三个参数 true 代表异步
    console.log(xhr.readystate)                         // 1
    
    // 这部分是该部分内容新增
    xhr.onreadystatechange = function () {
    
        console.log(xhr.readystate)  
        
        //readystate 为xhr的一个属性,其会随着ajax发送的进度变化而变化,刚开始创建xhr时是0,open之后是1,如示例
        
        // readystate == 4时 表示请求完成 已经接收到数据
        
        // status == 200 网络请求 ,请求到的结果会有一个状态码,来表示这个请求是否正常
        // 200表示请求成功
        // http状态码
        // 2**表示成功 
        // 3**表示重定向 
        // 4**表示客户端错误,404页面没有找到
        // 5**表示服务端错误
        if (xhr.readyState == 4 && xhr.status == 200) {
        
            console.log(xhr.responseText)
            //如果有返回的结果,返回的结果存在xhr.responseText这个属性里
            
            var data = JSON.parse(xhr.responseText)
            // 将jsonz数据转换为js对象
            
            //扩展知识:将js对象转换为json数据
            //JSON.stringify(data)
            
        }
    }
    xhr.send() 
    //至此就完成了这种方式,既可以用代码控制,而且页面不会跳转,并且服务端返回的结果我们可以用JS继续处理

二. 跨域:

什么是跨域:

  • 页面本身与要请求数据的网址 —— 协议 域名 端口号 有任意一个不一样的 就算跨域

跨域访问资源:

  1. 哪些东西属于资源
  • js文件,但是js文件是允许被跨域请求的
  • css文件 ,jpg,png 等(src属性的资源都是可以被跨域请求的,href 资源大部分都是可以被跨域请求的)
例 —— img请求:
<img src="https://gss0.bdstatic.com/94o3dSag_xI4khGkpoWK1HF6hhy/baike/c0%3Dbaike92%2C5%2C5%2C92%2C30/sign=4b1e0ff44da98226accc2375ebebd264/faf2b2119313b07e6a5add8902d7912396dd8c48.jpg" alt="">
// 虽然也是跨域的但是是可以被跨域请求的

2. 哪些资源算跨域请求的资源

  • 后端接口的数据
  • 其他域的cookie
  • 其他域的缓存

解决跨域的方法 —— 虽然跨域了,但是我们依然需要这个数据:

  1. 后端(这里的后端,是指的别人的后端)配合我们进行跨域:
  • JSONP(正常情况下,返回的数据都是JSON格式,JSONP是一种特殊的格式)
  • 后端设置Access-Control-Allow-Origin属性以支持跨域
  1. 后端不配合我们进行跨域
  • iframe进行跨域 (只能显示不能控制)
例:
<iframe src="https://www.baidu.com" frameborder="0" width="600" height="600"></iframe>
//虽然跨域,但是可以跨域请求到
  • 通过后端代理(指自己的后端)的

JSONP的使用:

  1. JSONP的特殊格式
  • 发送的时候,会带上一个参数callback
  • 返回的结果不是Json 而是 callback(json数据)
  1. 使用JQ发送jsonp请求
  •   $.ajax({
          url:"https://www.baidu.com",
          type:"get",
          dataType:"jsonp" //返回类型
          success : function(data){  //成功后返回的数据
          console.log(data);
          }
      })
    
      //JSonp跨域,只能使用get方法, 如果我们设置的是post方法,JQuery会自动转换为get方法
      //但不是在JSONP里面只能使用get方法
      //首先JQuery会先判断是否同源,若同源,那么设置的是什么就是什么   
      //若不是同源,无论设置的是什么,都改为get
    

封装ajax:

        //封装
        function ajax(mode, url, callback, data, flag) {
        // 兼容
        var xhr = null
        if (window.XMLHttpRequest) {
            xhr = new XMLHttpRequest() //常用浏览器自带
        } else {
            xhr = new ActiveXObject("Microsoft.XMLHttp") //IE6自带
        }

        //事件
        xhr.onreadystatechange = function () {

            if (xhr.readystate == 4) {
                if (xhr.status == 200) {
                    callback(xhr.responseText)
                } else {
                    console.log('error')
                }

            }
        }
        
        //格式要求
        mode = mode.toUpperCase();      // 将请求方式变为大写的
        if (mode == 'GET') {
            // get请求要把请求参数拼接到地址里面
            xhr.open(mode, url + '?' + data, flag) // 第三个参数 true 代表异步
            xhr.send()
        } else if (mode == 'POST') {
            xhr.open(mode, url, flag)
            
            // 设置Content-type 请求头  ,设置成相应的数据格式, 此处为数据传送的编码格式
            xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
            
            //post请求必须把请求参数放到请求体里面
            xhr.sed(data)
        }
    }
    

补充跨域知识

  1. 解决跨域的两种方法:

    • 方法一:整合到一个域
    • 方法二:后端修改跨域策略【配置Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Hearders】
  2. 使用方法二。引发跨域的解决方式

  • 协议、域名、端口 不同引发的跨域,可以通过配置Access-Control-Allow-Origin、结合白名单的方式来解决
  • 请求方式不符合也会引发跨域,通过上述配置后,只能get或post通过跨域,例如当请求方式为put,就还需要通过配置Access-Control-Allow-Methods来解决 【'GET,POAST,PUT、等'】
  • 自定义headers的属性时,也会引发跨域,同理通过配置Access-Control-Allow-Hearders来解决

在使用vite搭建的vue项目中解决跨域

  1. 开发环境中的配置
// vite.config.js
export default defineConfig({
 server:{
    port:8000, //【前端的端口】
    //允许局域网访问vite服务
    host:'0.0.0.0',
    open:true,
    proxy:{
      // 当请求,含有'api'路径的接口时,代理转发到target配置的服务
      'api':{
        //目的地,即想要请求的接口路径,【后端的服务接口】
        target:'http://localhost:8080'
      }
    }
  },
 })
 // 配置完以上这些后, 当我们请求了接口,就会转换到target服务
 
 //home.vue  比如首页请求了接口
  axios({
    url:'/api/2222',  // 这里直接使用相对路径就好
    method:'get',
    timeout:1000
  })
  // 比如 请求网址为 http://localhost:8000
  // 第一步:首先会经过 页面路径的8000端口 , 8000端口是vite服务。
  // 第二步:会转发到target配置的路径
  1. 生产环境中的配置,一般是用nginx

nginx反向代理

  1. 反向代理概念:
  • 反向代理(Reverse Proxy)是Nginx的核心功能之一,它充当客户端与后端服务器之间的“中间人”,接收客户端请求并转发到内部网络中的目标服务器
  1. 基础配置:
server { 
    listen 80; 
    server_name your_domain.com; # 替换为你的域名或IP location / 
    { 
        proxy_pass http://localhost:8080; # 指向后端服务地址 
        proxy_set_header Host $host; # 传递原始Host头 
        proxy_set_header X-Real-IP $remote_addr; # 传递客户端真实IP 
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
    } 
 }

vite处理跨域

同源策略基本介绍

  • 仅在浏览器发生,是浏览器的规则
  • http交互默认情况下,只能在同协议,同域名,同端口的两台终端进行
https://www.baidu.com:443/index.html (我家)
https://www.360.com:443/getUserInfo (你家)

// 我去你家那东西,肯定是不行的,但可以通过门锁,人脸识别等

// 浏览器就相当于门锁或人脸识别
// 当尝试去拿,就会调用浏览器的网络模块
// 浏览器发现两个不一样,就会想你百度去拿360的东西,人家360有没有允许,这时的浏览器是不知道,360是否允许百度访问
// 浏览器会先放行,请求开始传输,达到360服务器
// 响应(360和浏览器说,百度是自己的朋友),那这时,浏览器就会允许访问,也就是说只要360同意,浏览器就会不管
// 但是,假如360有点憨,只要是个请求过来,他都会把响应给出去,但是如果是朋友的话,他会额外加个标记
// 当响应来到以后,浏览器发现360没有特殊强调说百度是自己的朋友,那浏览器说不好意思了,这玩意我收了,你拿不到,同时通知一下百度说:人家360没说你是他朋友,如果你想要的话,你去和他私下聊聊,让他来证明你是他朋友,这个时候,我(指浏览器)再给你

// 跨域(仅发生在浏览器):当a源浏览器的网页向b源的服务器地址 (不满足同源策略,满足同源限制)请求对应消息,就会产生跨域。跨域请求默认情况下会被浏览器拦截,除非对应的请求服务器出具标记说,这个a源是允许拿b源的东西的

  • 注:跨域限制是:服务器已经响应了东西,但是浏览器(作为保安)不给你,不是说服务器没有给。
  • 即浏览器在发送请求的时候,是不可能拦截的,因为他不确保请求的服务器地址不允许跨域。所以只能在响应的时候拦截,无法在请求的时候拦截

解决方式(vite):

server: {  // server 代表开发服务器中的配置
    port: 8000, //【前端的端口】
    //允许局域网访问vite服务
    host: '0.0.0.0',
    open: true,
    proxy: {  // proxy 代表配置跨域解决方案
      // 当请求,含有'api'路径的接口时,代理转发到target配置的服务
      api: {  // 以后再遇到 /apikai
        //目的地,即想要请求的接口路径,【后端的服务接口】
        target: 'http://localhost:8080',
        changeOrigin:true // 换源
      },
    },
  • 当请求一个 /api 接口的过程
    • 首先浏览器会先帮我们做一步拼接
    • http://127.0.0.1:5174/api 浏览器拼接完,会去找vite
    • vite发现这个path (即去掉域名,在这里就是/api),有配置跨域代理策略,然后会根据策略的描述对象,进行再次请求
    • 比如策略里面配置了 /api ,之后就会去找target配置的属性,及rewrite。
    • 然后开发服务器,就会请求目标的服务器(服务器之间没有同源策略)
  • 开发时态:我们一般利用 构建工具或者脚手架或者第三方库的proxy代理,或者我们自己搭一个开发服务器来解决这个问题
  • 但是到了生产,这些就没有用了,生产环境没有vite开发服务器

解决生产时态的跨域

  • 生产环境一般不会有跨域问题
    • 比如我们现在 www.byabc.com 在这个目录下
    • 我们会把dist目录完全给到后端或运维
    • 他们会把我们的后端服务+前端代码 放到一个域下面的
    • 比如后端代码在 www.byabc.com/api/getUuse…
    • 前端代码在www.byabc.com/dist/index.…
    • 以上这种情况就不会跨域
    • 但是跨部门的话,就可能会有跨域问题,比如百度,百度百科,百度文科等
    百度百科 https://baike.baidu.com
    用户信息  https://baidu.com/api/userInfo
    // 这种情况下,运维是不会放在同一个域名下的,会各自维护
    // 这种时候就要采取以下方法了
    
  • 一般是交给后端去处理跨域
    • nginx:代理服务器
    • 配置身份标记:就是Access-Control-Allow-Origin :这代表哪些域是我们的朋友