ajax跨域

一、同源策略

1、同源策略是对javascript代码能够操作哪些web内容的一条完整的安全限制。

     脚本只能读取和所属文档来源相同的窗口和文档的属性。

     文档的属性包括:协议主机,以及载入文档的URL端口

2、同源策略应用

a.脚本可以打开或者关闭一个窗口,但是不能以任何方式查看窗口内部。

b.使用XMLHttpRequest生成Http请求。XMLHttpRequest对象允许客户端脚本生成的http请求到与所属文档来源相同的web服务器,但是不允许脚本和其他web服务器通信。

c.防止脚本窃取私有的信息

二、ajax跨域

1.什么是ajax跨域

前端代码:文件在本地,vscode可以安装live server插件,让本地的html文件以服务器模式在浏览器打开

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>cross-domain-test</title>
    <link rel="stylesheet" href="http://dwedg.cn/tools/css/common.css">
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <style>
        .content-area{
            width: 500px;
            height: 300px;
            word-wrap: normal;
        }
        #result_area{
            width: 100%;
            height: 200px;
            font-size: 20px;
            color:black;
            border:2px solid black ;
        }
        #get_data_btn{
            margin-top: 5px;
            width: 100px;
            height: 40px;
            text-align: center;
            background: rgba(241, 236, 236, 0.9);
            border: solid 1px coral;
            border-radius: 10px;
            float: right;
            color: coral;
        }
    </style>
</head>
<body>
    <div class="content-area">
        <div id="result_area"></div>
        <button type="button" id="get_data_btn">获取数据</button>
    </div>
</body>
<script>
    let _dom1=null;
    let _dom2=null;
    function request(dom){
        axios({
            method:'get',
            url:'http://dwedg.cn/php/rank/interface/getZone.php?action=getZone',
            data:{
                action:"getZone"
            }
        })
        .then(function(res){
            if(res && res.status==200 && res.data){
                let str = '';
                for(let [key,val] of Object.entries(res.data)){
                    if(typeof val === "object"){
                        str+=`<div>${key}=======${JSON.stringify(val)}</div>`
                    }
                    else if(typeof val === "string" || typeof val === "number"){
                        str+=`<div>${key}=======${val}</div>`
                    }
                }
                dom.innerHTML=str;
            }
        })
        .catch(function(err){
            console.log("出现了错误,错误原因是"+err);
        })
    }
    function init(){
        _dom1=document.getElementById("result_area"); 
        _dom2=document.getElementById("get_data_btn");
        _dom2.addEventListener("click",function(event){
            request(_dom1)
        })
    }
    init();
</script>
</html>

后端代码:php写的接口,放在服务器上

<?php
    $action = $_GET["action"];
    
    switch($action)
    {
        case "getZone":
            get_zone();
            break;
        default:
            echo "1111111";
            break;
    }
    
    function get_zone(){
        $temp_data = file_get_contents('../test_data/zone_data.json');
        $json = json_encode(array(
            "resultCode"=>200,
            "message"=>"查询成功!",
            "data"=>json_decode($temp_data,true),
        ),JSON_UNESCAPED_UNICODE);

        //转换成字符串JSON
        echo($json);
    }
?>

当本地文件发送一个http请求时,浏览器报错,如下图:

浏览器报错:服务器由于CORS(跨域资源共享)策略阻止了来自127.0.0.1:5500的http请求.这个http请求没有通过预检查.请求的服务器文件头部没有出现”Access-Control-Allow-Origin“.说明出现了ajax跨域问题。

2.出现的原因

a、浏览器的限制

b、跨域。即协议,域名,端口有一个不一样

c、XHR请求

以上abc同时满足即会产生Ajax跨域问题

3.解决思路

a.浏览器限制=======》浏览器设置允许跨域

b.XHP请求========》JSONP

c.跨域=======》调用接口的一方,设置白名单(cors),调用接口的一方设置代理

三、ajax跨域解决方案

  1. 浏览器禁止检查
  • 新建一个名为chromeDevData的文件夹,并且设置禁止检查

终端命令行:open -n /Applications/Google\ Chrome.app/ --args --disable-web-security --user-data-dir=/Users/zhangdan/cache/chromeDevData

设置成功后重新打开的浏览器窗口提示稳定性和安全性有所下降,如下图所示:

  • 打开前端页面重新发起请求,浏览器没有报错,请求成功

该方法需要客户端进行修改,适用于开发阶段开发人员调试时使用。正式环境下不可能让每一个用户都这样设置。

2.JSONP

  • 利用script标签请求资源可以跨域来解决跨域

前端代码:

<script>
    //省略重复代码
    function axios_extend_jsonp(){
        axios.jsonp = (url,data)=>{
            if(!url){
                throw new Error('url is not exit')
            }
            const callback = 'callback'+Math.random().toString().substring(3,9);
            const JSONP = document.createElement('script');
            JSONP.setAttribute('type','text/javascript');
            const headEle = document.getElementsByTagName('head')[0];

            //参数拼接
            let search = '';
            if(data){
                if(typeof data == 'string'){
                    search+="&"+data;
                }
                else if(typeof data == 'object'){
                    for(let key in data){
                        search+="&"+key+"="+encodeURIComponent(data[key]);
                    }
                }
                search+='&_time='+ Date.now();
            }
            JSONP.src=`${url}?callback=${callback}${search}`;
            return new Promise((resolve,reject)=>{
                //将jsonp返回的脚本挂在window上
                window[callback]=result =>{
                    resolve(result)
                    headEle.removeChild(JSONP);
                    delete window[callback]
                }
                headEle.appendChild(JSONP)
            })
        }
    }
    function init(){
        _dom1=document.getElementById("result_area"); 
        _dom2=document.getElementById("get_data_btn");
        //给axios扩展一个jsonp方法
        axios_extend_jsonp();
        _dom2.addEventListener("click",function(event){
            // request(_dom1);
            axios.jsonp("http://dwedg.cn/php/rank/interface/getZone_jsonp.php")
            .then((res)=>{
                let temp_obj = {};
                temp_obj.data = res;
                temp_obj.status = res.status;
                renderResult(temp_obj,_dom1)
            })
            .catch((err)=>{
                console.log("接口异常:"+err)
            })
        })
    }
    init();
</script>

后台代码:

<?php
    //默认返回text/html类型script标签应该返回的是js脚本类型
    header('Content-Type: application/x-javascript;charset=utf-8');
    $temp_data = file_get_contents('../test_data/zone_data.json');
    $result = json_encode(array(
        "status"=>200,
        "message"=>"jsonp方法查询成功!",
        "data"=>json_decode($temp_data,true),
    ),JSON_UNESCAPED_UNICODE);
    $callback = $_GET['callback'];
    echo $callback."($result)";
?>

输出结果:

jsonp原理:

抓出接口:

http://dwedg.cn/php/rank/interface/getZone_jsonp.php?callback=callback248058

浏览器刷接口的返回值

callback248058({"status":200,"message":"jsonp方法查询成功!","data":{"zone":["LPL","LCK","LDL","CK","LEC","LCS","其他"]}})

由此可见:

  1. 前端和后台共同约定了一个callback参数,前端在发送请求之前,会将callback参数的值callback248058作为全局对象的一个属性名,它的值是一个方法。当后台接收到callback参数时,知道这是一个jsonp请求,会返回js脚本。不一定要用callback,也可以约定其他参数值。
  2. 后台callback参数的值callback248058(随机数是为了防止缓存)作为函数名,接口要返回的数据作为函数的参数,返回的全部内容其实是一个函数的调用。
  3. 前端接收js脚本开始解析执行。

jsonp优缺点:

优点:1、不受同源限制2、兼容性更好

缺点:1、xhr很多新的特性,以及事件没有

            2、只支持get请求

            3、安全性

3.CORS

原理:后台添加允许访问的域名信息

<?php
    header('Access-Control-Allow-Origin: http://127.0.0.1:5500');//支持哪些跨域来源的请求
    header('Access-Control-Allow-Methods: *');//服务器支持跨域请求的方法

    header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies
    header('Access-Control-Max-Age:3000');//制定本次预检查的有效期,单位秒
    
    header('Access-Control-Expose-Headers: *');//Access-Control-Expose-Headers里面指定
    //getResponseHeader()方法拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、
    //Last-Modified、Pragma之外其他字段
    
     header('Access-Control-Allow-Headers: *');//如果浏览器请求包括Access-Control-Request-Headers字段,
                                                //则Access-Control-Allow-Headers字段是必需的,允许的头部信息字段
    $action = $_GET["action"];
    
    switch($action)
    {
        case "getZone":
            get_zone();
            break;
        default:
            echo "1111111";
            break;
    }
    
    function get_zone(){
        $temp_data = file_get_contents('../test_data/zone_data.json');
        $json = json_encode(array(
            "resultCode"=>200,
            "message"=>"查询成功!",
            "data"=>json_decode($temp_data,true),
        ),JSON_UNESCAPED_UNICODE);

        //转换成字符串JSON
        echo($json);
    }
?>

执行结果:

请求接口及响应详情:

补充:

  • 如果是非简单请求,会先发送预请求,判断通过再执行
  • 如果是带cookie的请求,origin必须是全匹配,且必须增加“Access-Control-Allow-Credentials:true”字段
  • 常见简单请求:a.方法为get,post,head b.请求head里1.没有自定义头2.content-type 为text/plain,multipart/form-data,application/x-www-form-urlencode中的一种
  • 常见非简单请求:put/delete方法的ajax请求,json格式的ajax请求,带自定义头的Ajax请求

4.nginx配置

1.登录服务器,查找(命令:ps -ef | grep nginx)到nginx目录下的conf目录

2.查看nginx的配置文件,Nginx引入的配置文件放在如下目录

3.查看接口域名的那个配置文件:

5.打开这个配置文件,添加如下请求头信息:(也就是把之前接口添加的请求头信息添加到这个配置文件中来)

6.测试配置文件是否有误:nginx -t

7.进入sbin目录下,重载Nginx服务 :./nginx -s reload

8.测试结果:成功

5.Nginx反向代理:

需要调用方提供一个nginx服务器,在该服务器上配置该服务,使得页面所有的请求都被nginx监听。在服务上配置多个前缀,然后将匹配到的http/https请求转发到多个真实的服务器。这样对于浏览器来说,这些url都是同源的,没有跨域限制。

三、总结:

在实际开发项目中,前端开发人员大部分业务设置浏览器不要检查跨域就可进行开发,正式环境下,前端代码和后台接口,一般是放在同一台服务器上的。

如果确实需要解决跨域,也需要前端开发人员和后台开发对接。

nginx配置一般是运维人员操作。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值