Home  >  Article  >  Web Front-end  >  AJAX principles and CORS cross-domain methods

AJAX principles and CORS cross-domain methods

一个新手
一个新手Original
2017-10-25 14:24:384793browse

ajax is one of the basic capabilities necessary for front-end development. You may use it, but you may not necessarily understand its principles and more in-depth knowledge related to server communication. During the past two days of sorting out, I read a lot of articles and found that my back-end capabilities have limited my exploration of network communication-related knowledge areas, and I should make up for my shortcomings as soon as possible.

Let’s talk about ajax related things, including part of xhr/xdr/ajax/cors/http, some of which will be abandoned. Historical baggage, such as IE6/7, etc.

The emergence of Ajax

In 2005, Jesse James Garrett proposed Ajax technology, its full name is Asynchronous Javascript and XML, the core of Ajax It is the XMLHttpRequest object, referred to as XHR. It is used to make the browser request additional data from the server without unloading the page, which greatly improves the user experience. Prior to this, this technology actually existed and was implemented by some people, but it was not popular and was not supported by browsers. However, after that, IE5 introduced the XHR object for the first time and supported the ajax technology, which was subsequently supported by all browsers.

XMLHttpRequestObjects and requests

XHR is an API that provides the client with the function of communication between the server and the client, and does not The page will be refreshed. It can not only retrieve XML type data, but can retrieve all types of data. In addition to http protocol, it also supports file and ftp protocols. We can create a new XHR object through its constructor. This operation needs to be completed before all other operations:

var xhr = new XMLHttpRequest();

We can easily see XHR through the console 's prototype chain: Object -> EventTarget -> XMLHttpRequestEventTarget -> XMLHttpRequest. It has methods and properties on the prototype chain and itself. Now let’s look at our commonly used methods:
AJAX principles and CORS cross-domain methods

Let’s explain several of its main methods. After creating a new xhr object, first call its open() method:

// 第一个参数可以为get/post等,表示该请求的类型
// 第二个参数是请求的url,可以为相对路径或绝对路径
// 第三个参数代表是否异步,为true时异步,为false时同步
// 第四五个参数为可选的授权使用的参数,因为安全性不推荐明文使用
xhr.open('get', 'example.php', true, username, password);

is affected by the same-origin policy here, when the second parameter url is cross-domain A security error will be reported by the browser. The same origin policy means that the protocol, domain name and port of the current page and the target URL are the same. As will be mentioned later, browsers other than IE implement cross-domain requests through XHR objects, just set the url to an absolute url.

After the initialization request is completed, we call the send() method to send the request:

var data = new FormData();
data.append('name', 'Nicholas');
// 接受一个请求主体发送的数据,如果不需要,传入null
xhr.send(data);

When the request type is get/head, The parameters of send() will be ignored and set to null. The parameters passed by send() will affect the default value of content-type in the header of our request. This field represents the type of resource content returned. Used for browser processing. If not set or in some scenarios, the browser will perform MIME sniffing to determine how to handle the returned resources.

FormData data is defined in XHR2 level, used for common class form data serialization:

// 直接传入表单id
var data = new FormData(document.getElementById('user-form'));

// 创建类表单数据
var data = new FormData();
data.append('name', 'Nicholas');

// `FormData`可以直接被send()调用,会自动修改xhr的content-type头部
xhr.send(data);

// 请求头部的content-type: multipart/form-data; boundary=----WebKitFormBoundaryjn3q2KKRYrEH55Vz

// 请求的上传数据 Request Payload:
------WebKitFormBoundaryjn3q2KKRYrEH55Vz
Content-Disposition: form-data; name="name"

Nicholas
------WebKitFormBoundaryjn3q2KKRYrEH55Vz--

FormDataCommonly used methods include append/delete/entries/forEach/get/getAll/has/keys/set/values, which are all commonly used methods similar to arrays and will not be explained again.

Request method

GET is the most common request type. Query string parameters can be added to the end of the URL. For XHR, the query string must go through Correct encoding, each key-value pair must be encoded using encodeURIComponent(), and the key-value pairs are separated by &:

// 封装序列化键值对
function addURLParam(url, name, value) {
    url += (url.indexOf('?') === -1 ? '?' : '&';
    url += encodeURIComponent(name) + '=' + encodeURIComponent(value);
    return url;
}

POSTThe frequency of request usage is second only to the GET request. It usually sends more data, and the format is not limited. The data is passed to send() as a parameter.

HTTP provides a total of nine request methods. Each verb represents a different semantics, but only the above two are commonly used:

 - OPTIONS:返回服务器针对特定资源所支持的HTTP请求方法。也可以利用向Web服务器发送'*'的请求来测试服务器的功能性。 
 - HEAD:向服务器索要与GET请求相一致的响应,只不过响应体将不会被返回。这一方法可以在不必传输整个响应内容的情况下,就可以获取包含在响应消息头中的元信息。 
 - GET:向特定的资源发出请求。 
 - POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的创建和/或已有资源的修改。 
 - PUT:向指定资源位置上传其最新内容。 
 - DELETE:请求服务器删除Request-URI所标识的资源。 
 - TRACE:回显服务器收到的请求,主要用于测试或诊断。 
 - CONNECT:HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
 - PATCH: 用于对资源进行部分修改

HTTP header information

Each Both HTTP requests and responses carry header information, and the xhr object allows us to manipulate some of the header information. We can set custom header information or modify the browser's default normal header information through the xhr.setRequestHeader() method. Commonly used request headers:

// 下面的实例是从我本地的一次请求取出的

Accept: 浏览器能够处理的内容类型。// */*
Accept-Charset: 浏览器能够显示的字符集。// 未取到
Accept-Encoding: 浏览器能够处理的压缩编码。// gzip,deflate
Accept-Language: 浏览器当前设置的语言。// zh-CN,zh;q=0.8,en;q=0.6
Connection: 浏览器与服务器之间连接的类型。// keep-alive
Cookie: 当前页面设置的任意Cookie。// JlogDataSource=jomodb
Host: 发出请求的页面所在域。// gzhxy-cdn-oss-06.gzhxy.baidu.com:8090
Referer: 发出请求的页面URI。// http://gzhxy-cdn-oss-06.gzhxy.baidu.com:8090/jomocha/index.php?r=tools/offline/index
User-Agent: 浏览器的用户代理字符串。// Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36

We generally do not modify the normal browser header information, which may affect the server response. If necessary, you can modify it through xhr.setRequestHeader():

// 传入头部键值对,键值不区分大小写,如果多次设置,则追加
// 此时请求头部的content-type: application/json, text/html
xhr.setRequestHeader('content-type', 'application/json');
xhr.setRequestHeader('content-type', 'application/json');

Setting the header information needs to be done after open(), send() before calling. The response header information is processed at the backend and will not be explained here. Some request header information is not allowed to be set, such as Accept-Encoding, Cookie, etc.

After the request returns, we can get the response header:

// 获取指定项的响应头
xhr.getResponseHeader('content-type'); // application/json;charset=utf-8
// 获取所有的响应头部信息
xhr.getAllResponseHeaders();

Here is a brief explanation of the content-type value, which refers to the HTTP content type of the request and response, which affects the server and The browser's data processing method, the default is text/html, commonly used ones are:

// 包含资源类型,字符编码, 边界字符串三个参数,可选填

text/html;charset=utf-8    // html标签文本
text/plain    // 纯文本
text/css    // css文件
text/javascript    // js文件
// 普通的表单数据,可以通过表单标签的enctype属性指定
application/x-www-form-urlencode
// 发送文件的POST包,包过大需要分片时使用`boundary`属性分割数据作边界
multipart/form-data; boundary=something
// json数据格式
application/json
// xml类型的标记语言
application/xml

XHR对象的响应

我们现在对请求的发起很了解了,接着看下如何拿到响应数据。如果我们给open()传递的第三个参数是true,则代表为同步请求,那么js会被阻塞直到拿到响应,而如果为false则是异步请求,我们只需要绑定xhr.onreadystatechange()事件监听响应即可。最上面的图已经说明了readystate的值含义,所以我们可以:

// xhr v1 的写法,检测readystate的值,为4则说明数据准备完毕,需要在open()前定义
xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
        if (xhr.status === 200 || xhr.status === 304) {
            console.log(xhr.responseText);
        } else {
            console.log(xhr.statusText);
        }
    }   
}

// xhr v2 的写法,onload()事件说明数据准备完毕
xhr.onload = function () {
    if (xhr.status === 200 || xhr.status === 304) {
        console.log(xhr.responseText);
    } else {
        console.log(xhr.statusText);
    }
}

xhr对象的响应数据中包含几个属性:

response    // 响应的数据
responseURL    // 发起响应的URL
responseType    // 响应的类型,用于浏览器强行重置响应数据的类型
responseText    // 如果为普通文本,则在这显示
responseXML    // 如果为xml类型文本,在这里显示

数据会出现在responseText/responseXML中的哪一个,取决于服务器返回的MIME类型,当然我们也有一些方式在浏览器端设置如何处理这些数据:

// xhr v1 的写法,设置响应资源的处理类型
xhr.overrideMimeType('text/xml');

// xhr v2 的写法, 可用值为 arraybuffer/blob/document/json/text
xhr.responseType = 'document';

响应数据相关的属性默认为null / '',只有当请求完成并被正确解析的时候才会有值,取决于responseType的值,来确定response/responseText/responseXML谁最终具有值。

XHR的高级功能

xhr v2里提供了超时和进度事件。

超时

xhr.timeout = 1000;    // 1分钟,单位为ms
xhr.ontimeout = function () {};

在请求send()之后开始计时,等待timeout时长后,如果没有收到响应,则触发ontimeout()事件,超时会将readystate=4,直接触发onreadystatechange()事件。

请求进度

像上图所示,xhr v2定义了不同的进度事件:loadstart/progress/error/abort/load/loadend,这其中我们已经说过了onload()事件为内容加载完成可用。现在说一下onprogress()进度事件:

xhr.onprogress = function (event) {
    if (event.lengthComputable) {
        console.log(event.loaded / event.total);
    }
}

该事件会接收一个event对象,其target属性为该xhr对象,lengthComputable属性为total size是否已知,即是否可用进度信息,loaded属性为已经接收的字节数,total为总字节数。该事件会在数据接收期间不断触发,但间隔不确定。

跨域CORS

提到XHR对象,我们就会讲到跨域问题,它是为了预防某些恶意行为的安全策略,但有时候我们需要跨域来实现某些功能。需要注意的是跨域并不仅仅是前端单方面的事情,它需要后端代码进行配合,我们只是通过一些方式跳过了浏览器的阻拦。

对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。

CORS(Cross-Origin Resource Sharing, 跨域资源共享)的思想是浏览器和服务端通过头部信息来进行沟通确认是否给予响应。如:

Origin: http://www.baidu.com    // 浏览器的头部信息

// 如果服务端认可这个域名的跨域请求,如下设置就可跨域访问资源
Access-Control-Allow-Origin: http://www.baidu.com

如上就可以实现最简单的跨域访问,但是此时不能携带任何的cookie,如果我们需要传递cookie进行身份认证,需要设置:

xhr.withCredentials = true;    // 浏览器端
Access-Control-Allow-Credentials: true;    // 服务端

这样我们就可以传递认证信息了,但如果允许认证,Access-Control-Allow-Origin不能设置为*,而一定是具体的域名信息。

现在的浏览器都对CORS有了实现,如IE使用XDomainRequest对象,其它浏览器使用XMLHttpRequest对象。所以在此之前有很多奇技淫巧,如通过jsonp/图像 Ping方法都不再详述,而且其都需要服务端配合并且有很多局限性。

IE实现: XDomainRequest

var xdr = new XDomainRequest();
xdr.open('get', 'http://www.site.com/page');
xdr.send(null);

XDR区别于普通XHR:

  • 不能传输cookie

  • 只能设置请求头部的content-type

  • 不能访问响应头部信息

  • 只支持get/post方法

通过这些区别可以阻止一部分的CSRF(Cross-Site Request Forgery,跨站点请求伪造)XSS(Cross-Site Scripting,跨站点脚本)

XDR与XHR的使用上非常相似,区别有几点:

  • open()方法只接受两个参数,请求类型和URL

  • 只允许异步请求

  • 响应完成触发onload()事件,但我们只能访问responseText原始文本,并且无法获取响应的status.

  • 异常事件都会触发error事件,并且无错误信息可用。

其余浏览器实现: XMLHttpRequest

其余浏览器通过XHR对象直接实现了CORS,你只需要做的就是open()方法中传入一个绝对URL。

xhr.open('get', 'http://www.site.com/page', true);

相对于普通的XHR对象,CORS-XHR依然有部分限制:

  • 不能使用setRequestHeader()定义头部

  • 不能传递cookie

  • 调用getAllResponseHeaders(),结果为空

其余跨域方法

上面的两种方法已经很成熟了,但是仍然有一部分方法可以跨域,比如图像Ping

var img = new Image();
img.onload = img.onerror = function () {
    console.log('done');
}
img.src = 'http://www.site.com/test?name=Nicholas';

这种方式常用于服务端统计广告的点击次数,其缺陷为:

  • 只能是GET请求

  • 单向通信,无法获取响应文本

另外还有JSONP

function handleResponse(response) {
    console.log(response.ip, response.city);
}

var script = document.createElement('script');
script.src = 'http://freegeoip.net/json?callback=handleResponse';
document.body.insertBefore(script, document.body.firstChild);

这种方式通过和服务器配合,跨域请求一个js文件并被服务器处理后传回:

handleResponse({'name': 'Nicholas'});

然后直接在浏览器调用了该函数,传回的数据被当做response形参进行处理。但它也有一些缺陷:

  • 访问的方式是请求js,所以如果域名不安全,则很容易被恶意代码直接执行并攻击

  • 无法检测是否错误,因为js不支持这样的接口事件,只能超时判断

上面两种方式很容易看出,我们在支持CORS之前,使用的方法只不过是采用img/css/js等不受跨域访问限制的对象,变相拿到了响应数据,但都有缺陷,所以如果没有历史包袱,建议采用XDR或XHR对象来实现跨域访问。

XHR的兼容性

我们可以直接到Can I use这个网站上查询兼容性问题:
AJAX principles and CORS cross-domain methods


The above is the detailed content of AJAX principles and CORS cross-domain methods. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn