解决跨域的方法

文章目录


跨域

  • 跨域的原理是:协议、域名、端口都相同才同域,否则都是跨域 跨域就是指浏览器不允许当前页面的所在源,去请求另一个源的数据

同源
再说一个概念就是同源,同源指的是协议,端口,域名全部相同。

同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。

同源策略是处于对用户安全的考虑,如果非同源就会受到以下限制:

  • cookie不能读取
  • dom无法获得
  • ajax请求不能发送

但是事实是经常需要借助非同源来提供数据,所以就需要进行跨域请求。

JSONP
JSONP是指JSON Padding,JSONP是一种非官方跨域数据交换协议,由于script的src属性可以跨域请求,所以JSONP利用的就是浏览器的这个“漏洞”,需要通信时,动态的插入一个script标签。请求的地址一般带有一个callback参数,假设需要请求的地址为http://localhost:666?callback=show,服务端返回的代码一般是show(数据)的JSON数据,而show函数恰恰是前台需要用这个数据的函数。JSONP非常的简单易用,自动补全API利用的就是JSONP,下面来看一个例子:

// 前端请求代码
function jsonp (callback) {
    var script = document.createElement("script"),
        url = `http://localhost:666?callback=${callback}`;
    script.setAttribute("src", url);
    document.querySelector("head").appendChild(script);
}
function show (data) {
    console.log(`学生姓名为:${data.name},年龄为:${data.age},性别为${data.sex}`);
}
jsonp("show");
// 后端响应代码
const student = {
    name: "zp1996",
    age: 20,
    sex: "male"
};
var callback = url.parse(req.url, true).query.callback;
res.writeHead(200, {
    "Content-Type": "application/json;charset=utf-8"
});
res.end(`${callback}(${JSON.stringify(student)})`);
  • JSONP虽说简单易用,但是有一个很大问题,那就是JSONP只能进行get请求

CORS
cors是目前主流的跨域解决方案,跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。

  • 请求方式为GET或者POST
  • 假若请求是POST的话,Content-Type必须为下列之一:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain
  • 不含有自定义头(类似于segmentfault自定义的头X-Hit)
    对于简单请求的跨域只需要进行一次http请求:
function ajaxPost (url, obj, header) {
    return new Promise((resolve, reject) => {
        var xhr = new XMLHttpRequest(),
        str = '',
        keys = Object.keys(obj);
      for (var i = 0, len = keys.length; i < len; i++) {
        str += `${keys[i]}=${obj[keys[i]]}&`;
      }
      str = str.substring(0, str.length - 1);
      xhr.open('post', url);
      xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
      if (header instanceof Object) {
        for (var k in header) 
            xhr.setRequestHeader(k, header[k]);
      }
      xhr.send(str);
      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
          if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
            resolve(xhr.responseText);
          } else {
            reject();
          }
        }
      }
    });
}
ajaxPost("http://localhost:666?page=cors", {
    name: "zp1996",
    age: 20,
    sex: "male"
})
.then((text) => { console.log(text); }, 
            () => { console.log("请求失败"); });

// 后端处理
var postData = "";
// 注释下,下面示例后台代码补充在此处
req.on("data", (data) => {
    postData += data;
});
req.on("end", () => {
    postData = querystring.parse(postData);
    res.writeHead(200, {
        "Access-Control-Allow-Origin": "*",
        "Content-Type": "application/json;charset=utf-8"
    });
    if (postData.name === student.name &&
        Number(postData.age) === student.age &&
        postData.sex === student.sex
         ) {
        res.end(`yeah!${postData.name} is a good boy~`);
    } else {
        res.end("No!a bad boy~");
    }
});


  • vue代理服务器proxy跨域:通过请求本地的服务器,然后本地的服务器再去请求远程的服务器(后端部署接口的服务器),最后本地服务器再将请求回来的数据返回给浏览器(本地服务器和浏览器之前不存在跨域)

两个关键点:
本地服务器(利用node.js创建的本地服务器进行代理,也叫代理服务器)和浏览器之间不存在跨域
服务器和服务器之间不存在跨域

首先创建一个 vue.config.js文件

// 假设要请求的接口是:http://40.00.100.100:3002/api/user/add
module.exports = {
    devServer:{
      host:'localhost',  // 本地主机
        port:5000,  // 端口号的配置
        open:true,  // 自动打开浏览器
        proxy:{
          '/api': {   //  拦截以 /api 开头的接口
            target: 'http://40.00.100.100:3002',//设置你调用的接口域名和端口号 别忘了加http
            changeOrigin: true,    //这里true表示实现跨域
            secure: false, // 如果是https接口,需要配置这个参数
            pathRewrite: {
              '^/api':'/'  //这里理解成用‘/api’代替target里面的地址,后面组件中我们掉接口时直接用api代替 比如我要调用'http://40.00.100.100:3002/api/user/add',直接写‘/api/user/add’即可
            }
          },
    // 假如又有一个接口是:http://40.00.100.100:3002/get/list/add
    // 那就再配置一个 get的,如下:
          '/get': {   //  拦截以 /get 开头的接口
            target: 'http://40.00.100.100:3002',//设置你调用的接口域名和端口号 别忘了加http
            changeOrigin: true,    //这里true表示实现跨域
            secure: false, // 如果是https接口,需要配置这个参数
            pathRewrite: {
              '^/api':'/'  //这里理解成用‘/api’代替target里面的地址,
            }
          }
			// 调用的时候直接  /get/list/add  就可以了
        }
    }
  }

  // 注意:修改了配置文件后一定要重启才会生效;


我们可以利用axios的baseUrl直接默认值是 api,这样我们每次访问的时候,自动补上这个api前缀,就不需要我们自己手工在每个接口上面写这个前缀了
在入口文件里面配置如下:

import axios from 'axios'

Vue.prototype.$http = axios
axios.defaults.baseURL = 'api'  // 后面发现,其实不加这个感觉也好像可以

如果这配置 'api/' 会默认读取本地的域

如果只是开发环境测试,上面那种就足够了,如果区分生产和开发环境
就需要如下配置

分环境配置跨域: 创建一个 api.config.js 文件(其实随便命名都可以)

const isPro = Object.is(process.env.NODE_ENV, 'production')
// 如果是生产环境,就用线上的接口;
module.exports = {
    baseUrl: isPro ? 'http://www.vnshop.cn/api/' : 'api/'
}

经过上面配置后,在dom里面可以这样轻松的访问,也不需要在任何组件里面引入axios模块了

 async getData(){
        const res = await this.$http.get('/api/user/add');
        console.log(res);
    },

代理跨域的主要方式是利用服务器请求服务器的方式避过跨域问题来实现的.大概的流程: 浏览器===>代理服务器===>目标服务器.

上一篇:最精简使用MORMOT


下一篇:xhr的常用属性方法整理