学习14:AJAX基础和跨域问题和JSON

340 阅读3分钟

AJAX基础和跨域问题和JSON

这里是knockkey,这是断了昨天但依旧坚持更新的第五天

1. 同源策略

  • 同源策略(Same-Origin-Policy) 最早是由 Netscape 公司提出,是浏览器的一种安全策略。即不同源的页面之间,不准互相访问数据
  • window.origin 或 location.origin 可以得到当前源
  • 协议 域名 端口 都相等, 违背同源策略就是跨域
  • ajax默认是遵循同源策略的。

实现跨域的方式

2. CORS

  • CORS是一个W3C标准,全称是跨域资源共享(Cross-origin resource sharing)。
  • 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
  • 但是IE10以下是不支持的。
  • CORS的整个过程都由浏览器自动完成,前端无需做任何设置,跟平时发送ajax请求并无差异。so,实现CORS的关键在于服务器,只要服务器实现CORS接口,就可以实现跨域通信。

请求类型

  • 简单请求
    • 请求方式使用下列方法之一
      • GET
      • POST
      • HEAD
    • Content-Type 的值仅限于下列三者之一:
      • text/plain
      • multipart/form-data
      • application/x-www-form-urlencoded

对于简单请求,浏览器会直接发送CORS请求,具体说来就是在header中加入origin请求头字段。同样,在响应头中,返回服务器设置的相关CORS头部字段,Access-Control-Allow-Origin字段为允许跨域请求的源。请求时浏览器在请求头的Origin中说明请求的源,服务器收到后发现允许该源跨域请求,则会成功返回

  • 非简单请求

如何使用

因为CORS与AJAX没有什么差别,只不过浏览器发现是AJAX请求跨源,就会在请求中自动添加一些附加的头信息。因此我们只需要以此判断是否可以跨域,在响应头中加入一些信息即可。 所以,CORS跨域的关键是在服务器,只要服务器实现了CORS接口,即可跨源通信。 简单请求:

response.setHeader('Access-Control-Allow-Origin', 'http://wangpf.com:9990')

注意:它的值要么是请求时Origin字段的值(协议 + 域名 + 端口),要么是一个*,表示接受任意域名的请求。

AJAX 和服务器代码展示:

前端内容:


    const req = new XMLHttpRequest()
    req.open('GET', 'http://localhost:3001')
    req.send()
    req.onreadystatechange = () => {
      if (req.readyState === 4) {
        if (req.status >= 200 && req.status < 300) {
          console.log(req.response);
        }
      }
    }

服务端内容

    var express = require('express');
    var app = express();
    var allowCrossDomain = function (req, res, next) {
      res.header('Access-Control-Allow-Origin', 'http://localhost:3001');
      res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
      res.header('Access-Control-Allow-Headers', 'Content-Type');
      next();
    }
    app.use(allowCrossDomain);
  • 接下来,http://localhost:3001下的GET,PUT,POST,DELETE请求,自定义首部字段为Content-Type的非简单请求则会被正常访问,当然,你也可以将Access-control-Allow-Methods和Access-Control-Allow-Headers这两个配置删掉,删掉之后,将仅支持简单请求进行跨域。

CROS字段

  • Access-Control-Allow-Origin
    • 该字段必填,允许请求的域
  • Access-Control-Allow-Methods
    • 该字段必填,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。
  • Access-Control-Allow-Headers
    • 可选。如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。预检请求后,告知发送请求需要有的头部

JSON:前后端交换数据的一种格式

  • JSON字符串:往往都是在后端向前端发送数据时传输的格式。
  • JSON对象:往往都是前端希望得到的。数据或对象或它们之间的嵌套。
  • JSON其实就是JavaScript中的一个对象,跟var obj={}在质上完全一样,只是在量上可以无限扩展。简单地讲,json其实就是JavaScript中的对象(Object)和数组(Array,其实也是对象)这倆在那儿你嵌我我嵌你地套上n多层,以此模拟出许多复杂的数据结构。

JSON字符串与JSON对象之间的转换

  • JSON.parse(JSON格式的字符串):正解析,将JSON字符串转换为JSON对象,没有副作用。
  • JSON.stringify(JSON对象):反解析,将JSON对象转换为JSON字符串,没有副作用。

2. JSONP: 是一种跨域数据交互协议

  • JSONP(JSONP with Padding) 是一大堆牛逼的程序员想出来的跨域方法。
  • json是理想的数据交换格式,但没办法跨域直接获取,于是就将json包裹(padding)在一个合法的js语句中作为js文件传过去
  • 当前网站创造一个script标签去请求另一个网站的JS,然后这个JS文件会夹带一些数据,这些数据会在我的网站上调用全局函数运行。

JSONP是怎么实现的?

  • 利用动态创建一个script标签并利用它的src属性向服务器发送一次HTTP请求,并提前声明好一个回调函数,回调函数的函数名利用callback请求参数传递给后端。后端接收到来自前端的请求后,获取callback请求的请求参数并拼接一个调用函数的JS代码段并将要返回给前端的数据以实参的形式存在。前端接收到来自后端的响应后,会将后端的返回内容当做JS代码来执行即调用一个函数,并用一个形参来接收后端向要传递过来的数据。

JSONP的优点和缺点

优点:
  • 支持IE
缺点:
  • 由于它是script标签,它不能像AJAX一样,读出(拿到)状态码和响应头,它只知道成功和失败(用onload和onerror来监听)。

  • 由于它是script标签,它只能发get请求,不支持POST。

JSONP的原理

  • 网页有一些标签天生具有跨域的能力,比如:img link iframe script

  • JSONP 就是利用 script标签的跨域能力来发送请求的(这也是jsonp跨域只能用get请求的原因所在

  • 首先在客户端注册一个callback,然后把callback的名字传给服务器。此时,服务器先生成 json 数据,然后以 javascript 语法的方式,生成一个function , function 名字就是传递上来的参数 jsonp。最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。

  • jsonp原理是这样的,假设,网站A需要获取网站B的数据,网站B说我给你们一个方法,【1. 你们使用<script src="http://www.B.com/open.js"></script>标签先获取到open.js文件(网站B的责任),这里边有你们需要的数据。2. 你们获取数据后处理数据(总得处理数据吧)的方法名必须命名为foo(数据请求者的责任和义务)】,这里相当于B网站和请求获取数据者之间建立了一个协议,要求请求者务必按照规则办事,如果请求者不能同时遵守上面两条就不能按预期获取数据。

  • 网站A,网站B都非常高兴,那么问题又来了,网站C说也需要获取网站B的数据,网站B把协议甩给它,网站C拿过来一看,foo这个名字已经在自己的脚本文件的6868行用过了,而且已经使用在脚本的各个角落,批量替换会导致很多潜在bug啊,网站B情急之下决定把foo改成fool,网站A立马蹦起来,因为自己的网站已经在很多地方使用foo引用了数据。为了避免上面情况发生,那些牛X哄哄的开发者使用了动态生成js文件的方法.

   //封装JSONP函数  使用JSONP 实现
    function JSONP(url) {
      return new Promise((resolve, reject) => {
        const random = 'WangpfCallbackName' + Math.random()
        window[random] = (data) => {
          resolve(data)
        }
        const script = document.createElement('script')
        script.src = `${url}?callback=${random}`
        document.body.appendChild(script)
        script.onload = () => {
          script.remove()
        }
        script.onerror = () => {
          reject()
        }
      })
    }

    JSONP('http://xxxx.com:8888/friends.js')
      .then((data) => {
        console.log(data);
      })
  • 无封装
   // 回调函数:当成功接收到来自后端的响应之后会自动调用这个函数,并且该函数中的形参就代表了后端要返回的数据
    function jsonpcallback(data) {
      console.log(data[0])
    }

    query.onclick = function () {
      let script = document.createElement('script');
      script.setAttribute('src', "http://xx.xx.xxx.xxx/data?callback=jsonpcallback&name=" + queryname.value)
      document.body.appendChild(script)
    }

AJAX基础

  • POST
    //获取元素对象
    const result = document.getElementById("result");
    //绑定事件
    result.addEventListener("mouseover", function () {
      // 1.创建对象
      const xhr = new XMLHttpRequest();
      // 2. 初始化 设置类型与URL 这是请求类型 和 请求类型 请求行
      xhr.open('POST', 'http://127.0.0.1:8000/server');
      // 3. 发送 这是请求体
      xhr.send('a=100&b=200&c=300');
      // 4.事件绑定
      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
          if (xhr.status >= 200 && xhr.status < 300) {
            //处理服务端返回的结果
            result.innerHTML = xhr.response;
          }
        }
      }
    })
  • GET
    //获取button 元素
    const btn = document.getElementsByTagName('button')[0]
    const result = document.getElementById("result");
    //绑定事件
    btn.onclick = function () {
      // 1. 创建对象
      const xhr = new XMLHttpRequest();
      // 2. 初始化 设置请求方法和 url  第一个参数:发送怎么样的请求 第二个参数:给谁发
      xhr.open('GET', 'http://127.0.0.1:8000/server?a=100&&b=200&&c=300');
      // 3. 发送
      xhr.send();
      // 4.事件绑定 处理服务端返回的结果
      // on when 当...的时候
      // redaystate 是xhr对象中的属性, 表示状态 这个属性有五个值 0(为初始化) 1(表示open方法已经调用完毕) 2(表示send方法已经调用完毕) 3(表示服务端返回了部分的结果) 4(表示服务端返回了所有的结果)
      // change 改变的时候触发
      xhr.onreadystatechange = function () {
        // 要状态为4的时候处理
        if (xhr.readyState === 4) {
          // 判断响应状态码 200(正确拿到服务端的东西) 404 403 401 500
          // 2开头的都是表示成功
          if (xhr.status >= 200 && xhr.status < 300) {
            // 处理结果 行 头 空行 体
            //空行没必要 这是应用层
            // 1. 响应行 状态码 和 状态字符串
            // console.log(xhr.status);//状态码
            // console.log(xhr.statusText);//响应状态字符串
            // console.log(xhr.getAllResponseHeaders);//所有的响应头
            // console.log(xhr.response);//响应体
            
            //设置result的文本
            result.innerHTML = xhr.response;
          }
        }
      }
    }