欢迎您的光临,本博所发布之文章皆为作者亲测通过,如有错误,欢迎通过各种方式指正。由于本站位于香港虚拟主机,故速度比较慢。

文摘  几种ajax跨域方法汇总

其他 网络 184 0评论

首先,我们要明白,什么是跨域,为什么要跨域。 由于JS中存在同源策略。当请求不同协议名不同端口号下面的文件时,将会违背同源策略,无法请求成功!需要进行跨域处理!


关于跨域认识和基础的文章请参考:http://ittxx.cn/view/248 


1.为什么实际开发中会有跨域ajax请求


由于浏览器同源政策的影响,跨域的ajax请求是不被允许。那么在实际的开发、应用中,是否有跨域ajax的场景呢?

答案是肯定的。


那么有哪些场景会有跨域ajax的需求呢?


当你调用一个现有的API或公开API:想象一下,你接到了一个新需求,需要在当前开发的新闻详细页http://www.yournews.com/p/123展示该新闻的相关推荐。令人欣慰的是,推荐的接口已经在你们公司的其他产品线里实现了,你只需要给该接口一个query即可:http://www.mynews.com/recommend?query=123。然而问题来了——你发起了一个跨域请求。

前后端分离的开发模式下,在本地进行接口联调时:也许在你的项目里,你想尝试前后端分离的开发模式。你在本地开发时,mock了一些假数据来帮助自己本地开发。而有一天,你希望在本地和后端同学进行联调。此时,后端rd的接口地址和你发生了跨域问题。这阻止了你们的联调,你只能继续使用你mock的假数据。


上面只是列举了存在跨域的两个最为常见的场景,这足以说明跨域请求在实际开发中确实经常出现。


2.跨域的一些方案


了解了上面的内容后,下面就来介绍一下在实践中常用的三种ajax跨域方案。这部分的实例代码可以在这里看到:cross-domain-demo

假设这样一个跨域场景:目前有两个项目

myweb,这个就是我们目前开发的项目,是一个独立的站点。

thirdparty,表示我们需要调用到的第三方(third-party)后端服务,myweb项目就是需要调用它的接口。


为了简化不必要的代码编写过程,示例使用express-generator来快速生成myweb与thirdparty这两个应用,其中thirdparty我们只需要用到后端接口部分。

npm install express-generator -g

express --view=pug myweb

express --view=pug thirdparty


在myweb中,index页面 http://127.0.0.1:8085需要跨域访问server中的http://127.0.0.1:3000/info/normal这个接口的信息。前端操作是:当点击button时就会去获取info,并alert出来。

跨域访问的接口http://127.0.0.1:3000/info/normal代码如下:

const express = require('express');
const router = express.Router();
const data = {
    name: 'alienzhou',
    desc: 'a developer'
};
router.get('/normal', (req, res, next) => {
    res.json(data);
});


然后是http://127.0.0.1:8085index页面的部分的javascript

// http://127.0.0.1:8085  -- index.js
document.getElementById('btn-1').addEventListener('click', function() {
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4 && xhr.status === 200) {
            alert(xhr.responseText);
        }
    }
    xhr.open('get', 'http://127.0.0.1:3000/info/normal');
    xhr.send(null);
});


点击btn-1,在控制台中就会出现如下错误,这个跨域ajax请求受到了同源策略的限制。

[Error] Origin http://127.0.0.1:8085 is not allowed by Access-Control-Allow-Origin.

[Error] Failed to load resource: Origin http://127.0.0.1:8085 is not allowed by Access-Control-Allow-Origin. (normal, line 0)

[Error] XMLHttpRequest cannot load http://127.0.0.1:3000/info/normal due to access control checks.


下面来讲具体的三种解决方案:


1)使用代理(proxy)

这种方法本质上仍然遵循了同源政策,只是换了一个请求的思路,将请求移至了后端。

我们知道,同源政策是浏览器层面的限制。那么,如果我们不在前端跨域,而将“跨域”的任务交给后端服务,是否就规避了同源政策呢?是的。

这就是“代理”。这个代理可以将我们的请求转发,而后端并不会有所谓的同源政策限制。这个“代理”也可以理解为一个同域的后端服务。

由于我们的myweb是一个完整的web项目(包括前端部分和后端服务部分),因此,我们可以在myweb项目的后端添加一个proxy接口,专门处理跨域ajax请求的转发。

const express = require('express');
const router = express.Router();
const request = require('request');
router.get('*', (req, res, next) => {
    let path = req.path.replace(/^\/proxy/, '');
    request.get(`http://127.0.0.1:3000${path}`, (err, response) => {
        res.json(JSON.parse(response.body));
    });
});

module.exports = router;


这样,我们在前端访问/proxy/info/normal后,就会自动转发到http://127.0.0.1:3000/proxy/info/normal。

前端ajax部分如下:

document.getElementById('btn-1').addEventListener('click', function() {
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4 && xhr.status === 200) {
            alert(xhr.responseText);
        }
    }
    xhr.open('get', '/proxy/info/normal');
    xhr.send(null);
});


该方法的优点很明显:不需要第三方服务http://127.0.0.1:3000/info/normal进行任何改造。

当然,该方法也有一些缺点:

· 首先,需要你有一个自己的后端服务能够接收并转发请求。如果你进行本地的纯静态页面开发,则需要一些浏览器插件或自动化工具中集成的本地服务器来实现。

· 此外,如果请求包含一些特殊的请求头(例如cookie等等),需要在转发时特殊处理。


下面两种方法则需要第三方服务端或多或少进行配合改造。


2)CORS

同源策略往往过于严格了,为了解决浏览器的这个问题,w3c提出了CORS(Cross-Origin Resource Sharing)标准。CORS通过相应的请求头与响应头来实现跨域资源访问。


CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。


整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。


因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

与JSONP比较 :

CORS与JSONP的使用目的相同,但是比JSONP更强大。

JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。


如果我们打开控制台,可以在请求头中发现一个叫origin的头信息,它表明了请求的来源。这是浏览器自动添加的。

Referer: http://127.0.0.1:8085/

Origin: http://127.0.0.1:8085   <============   origin

Accept: */*

Cache-Control: no-cache

User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8

Pragma: no-cache


与之对应的,服务器端的响应头中一个头信息为Access-Control-Allow-Origin,表明接受的跨域请求来源。显而易见,这两个信息如果一致,则这个请求就会被接受。

router.get('/cors', (req, res, next) => {

    res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:8085');

    res.json(data);

});


如果将Access-Control-Allow-Origin的值设置为*,则会接受所有域的请求。这时的客户端不需要任何配置即可进行跨域访问。

然而,还有一个问题,CORS默认是不会发送cookie,但是如果我希望这次的请求也能够带上对方服务所需的cookie怎么办?那就需要再进行一定的改造。

与Access-Control-Allow-Origin相配套的,还有一个叫Access-Control-Allow-Credentials的响应头,如果设置为true则表明服务器允许该请求内包含cookie信息。

router.get('/cors', (req, res, next) => {

    res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:8085');

    res.setHeader('Access-Control-Allow-Credentials', true);

    res.json(data);

});


同时,在客户端,还需要在ajax请求中设置withCredentials属性为true。

document.getElementById('btn-1').addEventListener('click', function() {
    var xhr = new XMLHttpRequest();
    xhr.withCredentials = true;  // 设置withCredentials以便发送cookie
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4 && xhr.status === 200) {
            alert(xhr.responseText);
        }
    }
    xhr.open('get', 'http://127.0.0.1:3000/info/cors');  // 跨域请求
    xhr.send(null);
});


可以看到,CORS方法有如下优点:

· 简单,几乎不需要什么开发量,只需要简单配置相应的请求与响应头信息即可。

· 支持各种类型的请求(get, post, put等等)。


但缺点是:

· 需要对跨域的服务接口进行一定的改造。如果该服务因为某些原因无法改造,则无法实现。但这种改造还是相对较小的。

· 不兼容一些“古董”浏览器。


3)jsonp

JSONP由两部分组成,回调函数和数据。JSONP是被包含在函数调用中的JSON,如:callback({"name":"Judy"});

回调函数一般请求中指定,而数据就是传入回调函数中的JSON数据。


它的基本思想是,网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。


(1)什么是JSONP?

首先抛出浏览器同源策略这个概念,为了保证用户访问的安全,现代浏览器使用了同源策略,即不允许访问非同源的页面。JSONP就是用来解决跨域请求问题的。


(2)JSONP原理

ajax请求受同源策略影响,不允许进行跨域请求,而script标签src属性中的链接却可以访问跨域的js脚本,利用这个特性,服务端不再返回JSON格式的数据,而是返回一段调用某个函数的js代码,在src中进行了调用,这样实现了跨域。


jsonp是跨域领域中历史非常传统的一种方法。如果你还记得第一部分中我们提到过的内容,一些跨域请求是不会受到同源政策的限制的。其中,script标签就是一个。

在script标签中我们可以引用其他服务上的脚本,最常见的场景就是CDN。因此,有人想到,当有跨域请求到来时,如果我们可以把客户端需要的数据写到javascript脚本文件中并返回给客户端,那么客户端就可以拿到这些数据并使用了。具体是怎样一个流程呢?

a. 首先,在myweb端,我们可以预先定义一个处理函数,叫它callback;

b.然后,在myweb端,我们动态创建一个script标签,并将该标签的src属性指向跨域的接口,并将callback函数名作为请求的参数;

c.跨域的thirdparty端接受到该请求后,返回一个javascript脚本文件,用callback函数包裹住数据;

d.这时候,前端收到响应数据会自动执行该脚本,这样便会自动执行预先定义的callback函数。


将上面这个方法具体成下面的代码:

// myweb 部分
// 1. 创建回调函数callback
function myCallback(res) {
    alert(JSON.stringify(res, null , 2));
}
document.getElementById('btn-4').addEventListener('click', function() {
    // 2. 动态创建script标签,并设置src属性,注意参数cb=myCallback
    var script = document.createElement('script');
    script.src = 'http://127.0.0.1:3000/info/jsonp?cb=myCallback';
    document.getElementsByTagName('head')[0].appendChild(script);
});
// thirdparty
router.get('/jsonp', (req, res, next) => {
    var str = JSON.stringify(data);
    // 3. 创建script脚本内容,用`callback`函数包裹住数据
    // 形式:callback(data)
    var script = `${req.query.cb}(${str})`;
    res.send(script);
});
// 4. 前端收到响应数据会自动执行该脚本


当然,如果你是用类似jquery这样的库,其中的$.ajax本身是封装了JSONP方式的:

$.ajax({
    url: 'http://127.0.0.1:3000/info/jsonp?cb=myCallback',
    dataType: 'jsonp', // 注意,此处dataType的值表示请求使用JSONP
    jsonp: 'cb', // 请求query中callback函数的键名
}).done(function (res) {
    alert(JSON.stringify(res, null , 2));
});


JSONP作为一个久远的方法,其最大的优点就是兼容性非常好。

但是其缺点也很明显,由于是通过script标签发起的请求,因此只支持get请求。同时可以看到,较之CORS,其前后端改造开发量要稍高一些。如果跨域服务端不支持改造,那么也无法使用该方法。


上面三个方案的实例代码可以在这里(cross-domain-demo)clone到本地并运行。git clone git@github.com:alienzhou/cross-domain-demo.git


总结

同源策略作为浏览器的安全策略之一,在保证请求的安全性之外,也对我们的一些合理与期望的请求进行了控制。幸好,在面对跨域ajax请求时,我们还有一些方法可以应对它,包括使用代理、CORS和JSONP。在不同场景下合理运用各种方法,可以帮助我们有效解决ajax跨域问题。


Ajax原理及请求过程

1、创建一个XMLHttpRequest对象,也就是创建一个异步调用对象;

2、创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息;

3、设置响应HTTP请求状态变化的函数;

4、发送HTTP请求;

5、获取异步调用返回的数据;

6、使用JS和DOM实现局部刷新。


参考网址:

https://blog.csdn.net/qq_39345165/article/details/80595450 

https://blog.csdn.net/u011897301/article/details/52679486

https://blog.csdn.net/itcats_cn/article/details/82318092

https://blog.csdn.net/Judy_qiudie/article/details/82830278 


原文地址:https://www.jianshu.com/p/438183ddcea8

转载请注明: ITTXX.CN--分享互联网 » 几种ajax跨域方法汇总

最后更新:2019-02-18 20:17:29

赞 (1) or 分享 ()
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽