JavaScript 网络请求与远程资源(一)-------XHR与CORS

Ajax技术使得网页在不刷新的情况下与服务器交互数据,关键在于XMLHttpRequest对象。FetchAPI作为其现代替代,提供了Promise支持和服务线程。文章详细介绍了XHR的使用步骤,包括GET和POST请求的发送,以及HTTP头部的设置。同时,讨论了跨源资源共享(CORS)机制,包括预检请求和凭据请求,以及替代性的跨源技术如JSONP。
摘要由CSDN通过智能技术生成

2005年,Ajax(Asynchronous JavaScript+XML)技术的出现,发送服务器请求额外数据而不刷新页面。

Ajax被推到历史舞台上的关键技术是XMLHttpRequest对象。Ajax(异步 js+xml)主要是可以实现在不刷新页面的情况下从服务器中,获取数据,并通过DOM修改网页.

XHR对象的API被普遍认为比较难用,而Fetch API自从诞生以后就迅速成为了XHR更现代替代标准。Fetch API支持期约promise和服务线程service worker。

XMLHttpRequest 对象

所有现代浏览器都通过XMLHttpResquest构造函数原生支持XHR(XMLHttpRequest)对象。

let xhr = new XMLHttpRequest();

 

1.1 使用XHR

1.首先调用open()方法,
接收三个参数:请求类型,请求URL,表示请求是否异步的布尔值

调用open方法不会实际发送请求,只是为发送做准备
注意:只能访问同源URL,域名相同、端口相同、协议相同

xhr.open('get','1.php',false)

2.然后发送定义好的请求,必须调用send()方法

接收一个参数:作为请求体发送的数据,如果不需要发送请求体,则必须传null

调用send之后,请求就会发送到服务器。

xhr.send(null)

因为这个请求是同步的,所以JS代码会等待服务器响应之后在继续执行,收到响应后,XHR对象的以下属性会被填充上数据:

responseText:响应体返回的文本

responseXML:包含响应数据的XML DOM文档

status:响应的HTTP状态

statusText:响应的HTTP状态描

3.收到响应后,第一步检测status属性以确保响应成功(status>200&&status<300),这时候,responseText或responseXML属性上就会有内容.如果status==304,就说明资源未修改,是从浏览器缓冲中取得的.

if(xhr.status>200&&xhr.status<300||xhr.status==304){
    alert(xhr.responseText)
}else{
    alert('error')
}

XHR对象有一个readyState属性,表示当前处在请求/响应过程的哪个阶段

0:未初始化,尚未调用open方法

1:已打开,已调用open方法

2:已发送,已调用send方法

3:接收中,已经接收到部分响应

4:完成。已经接收到所有响应,可以使用了。

4.每次readyState从一个值变为另一个值,都会触发readystatechange事件

let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() { // 为了保证跨浏览器兼容,onreadystatechange需要在open之前赋值
  if(xhr.readyState == 4) { // 已经接收到所有响应
    if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
      alert(xhr.responseText);
    }else {
      alert('Request was unsuccessful:' + xhr.status);
    }
  }
};
xhr.open('get', 'example.php', true);
xhr.send(null);

5.在收到响应之前如果想取消异步请求,可以调用abort()方法

xhr.abort()

中断请求后,应该取消对XHR对象的引用,由于内存问题,不推荐重用XHR对象.

1.2 HTTP头部

每个HTTP请求和响应都会携带一些头部字段,默认情况下,XHR请求会发送以下头部字段:

Accept 浏览器可以处理的内容类型

Accept-Charset 浏览器可以显示的字符集

Accept-Encoding 浏览器可以处理的压缩编码类型

Accept-Language 浏览器使用的语言

Connection 浏览器与服务器的连接类型

Cookie 页面中设置的cookie

Host 发送请求的页面所在的域

Referer 发送请求的页面URI

User-Agent 浏览器的用户代理字符串

如果需要发送额外的请求头部,可以使用setRequsetHeader()方法,它接受俩参数:头部名称的名称和值。必须在open之后send之前调用它。

xhr.open('get', 'example.php', true);
xhr.setRequsetHeader('MyHeader', 'MyValue');
xhr.send(null);

getResponseHeader():从XHR对象获取响应头部

let myHeader = xhr.getResponseHeader("MyHeader");

getAllResponseHeaders():返回包含所有响应头部的字符串

let allHeaders = xhr.getAllResponseHeaders();

1.3 GET 请求

用于向服务器中查询某些信息.必要时,需要在GET请求的url后面添加查询字符串参数.查询字符串必须正确编写在URL后面,然后再传入open()方法.

查询字符串中的每个名和值都必须使用encodeURIComponent()编码.

let xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if (xhr.readyState==4){
            if (xhr.status>=200&&xhr.status<=300||xhr.status==304){
                alert(xhr.responseText);
            }else{
                alert(xhr.status+xhr.statusText)
            }
        }
    }
    let url='./GET.php'
    url+='?'+encodeURIComponent('name')+'='+encodeURIComponent('wta')
    xhr.open('GET',url,true);
    xhr.send()

1.4 POST请求

POST请求用于向服务器发送应该保存的数据.

let xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if (xhr.readyState==4){
            if (xhr.status>=200&&xhr.status<=300||xhr.status==304){
                alert(xhr.responseText);
            }else{
                alert(xhr.status+xhr.statusText)
            }
        }
    }
    let url='./GET.php'
    data=encodeURIComponent('name')+'='+encodeURIComponent('wta')
    xhr.open('POST',url,true);
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
    let form =document.forms[0];

1.5 XMLHttpRequest Level 2

1.5.1 FormData类型

用于表单序列化,有一个添加数据的方法,append(name,value)接收两个参数:键和值.

let data=new FormData()

data.append('name','wta')
//使用FormData,不再需要给XHR对象显性设置任何请求头部.
//即不需要xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
xhr.open('post','example.php',true);
let form = document.getElementById('user-info');
xhr.send(new FormData(form));

1.5.2 timeout属性(超时)

XHR对象新增了一个timeout属性,用于表示发送请求后等待多少毫秒,如果响应不成功就中断请求。

let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() { // 为了保证跨浏览器兼容,onreadystatechange需要在open之前赋值
  if(xhr.readyState == 4) { // 已经接收到所有响应
    try {
      if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
        alert(xhr.responseText);
      }else {
        alert('Request was unsuccessful:' + xhr.status);
      }
    }catch (ex) {}
  }
};
xhr.open('get', 'example.php', true);
xhr.timeout = 1000;
xhr.ontimeout = function() {
  alert('Request did not return in a second.')
}
xhr.send(null);

注意:在超时后访问status属性会发生错误,可以把检查status属性的代码封装在try、catch语句中

1.5.3 overrideMimeType()方法

Firefox首先引入了overrideMimeType方法用于重写XHR响应的MIME类型。

假设服务器实际上发送了XML数据,但响应头设置的MIME类型是text/plain。结果就会导致数据是XML,但responseXML属性值是null。此时调用overrideMimeType方法可以保证将响应成XML而不是纯文本来处理

xhr.overrideMimeType('text/xml');

注意: 必须在send之前调用overrideMimeType。

2. 进度事件

XHR关于进展的6个事件:

loadstart:在接收到响应 的第一个字节时触发.

progress:在接收响应期间反复触发.

error:在请求出错时触发.

abort:在调用abort()终止连接时触发.

load: 在成功接收完响应时触发.

loadend: 在通信接收完成时,且在error ,load,abort事件之后触发.

每个请求都会首先触发loadstart事件,之后是一个或多个的progress事件,接着是error/abort/load事件,最后以loadend事件结束.

2.1 load事件

Firefox在实现XHR的时候,增加了一个load事件替代readystatechange事件。

load事件在响应接收完成后立即触发,就不用检查readystatechange属性了

let xhr = new XMLHttpRequest();
xhr.onload = function() {
  if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
    alert(xhr.responseText);
  }else {
    alert('Request was unsuccessful:' + xhr.status);
  }
}
xhr.open('get', 'example.php', true);
xhr.send(null);

2.2 progress事件

Mozilla在XHR对象上创建了progress事件,在浏览器接收数据期间,这个事件会反复触发

onprogress事件处理程序会接受event对象,其target属性是XHR对象,还有三个属性:

lengthComputable:一个布尔值,表示进度信息是否可用

position:接收到的字节数

totalSize:是响应的Content-Length头部定义的总字节数

必须在open之前添加onprogress事件处理程序

通过progress可以给用户提供进度条.

let div=document.getElementsByTagName("div")[0];
let div1=document.getElementsByTagName("div")[1];
let xhr=new XMLHttpRequest();
xhr.onload=function() {
    if (xhr.status>=200&&xhr.status<=300||xhr.status==304){
        div.innerHTML='请求成功,数据为'+xhr.responseText
    }else{
        div.innerHTML='请求失败'
    }
}
xhr.onloadstart=function() {
        div.innerHTML='开始请求'
    }
xhr.onprogress=function(event, progress){
        if(event.lengthComputable){
            console.log(event.position+'/'+event.totalSize)
        }else{
            div1.innerHTML='进度条'
        }
    }
xhr.open('GET','./progress.txt',false)
xhr.send()

3. 跨源资源共享

 

通过XHR进行Ajax通信的一个主要限制是跨源安全策略。默认情况下XHR只能访问与发起请求的页面在同一个域内的资源。

跨源资源共享(CORS,Cross-Origin Resource Sharing)定义了浏览器与服务器如何实现跨源通信。

对于简单的请求(1.GET、POST 请求2.没有自定义头部,3.而且请求体是text/plain类型),这样的请求在发送时会有一个额外的头部Origin。Origin头部包含发送请求的页面的源(协议、域名、端口):

Origin: http://www.baidu.com

如果服务器决定响应请求,那么应该发送Access-Control-Allow-Origin头部,包含相同的源。或者资源是公开的,那么就包含*:

Access-Control-Allow-Origin: *

如果没有这个头部,或者有但源不匹配,则表明不会响应浏览器请求。否则,服务器就会处理这个请求。(无论请求是否响应都不会包含cookie信息)

跨域XHR对象允许访问status、statusText属性,也允许同步请求。

跨域XHR对象施加的一些限制:

1.不能使用setRequestHeader()设置自定义头部

2.不能发送请求cookie

3.getAllResponseHeaders()方法始终返回空字符串

3.1 预检请求

CORS通过一种预检请求的服务器验证机制。允许使用自定义头部、除GET、POST之外的方法,以及不同请求体内容类型。

在要发送涉及上述某种高级选项的请求中,会向服务器发送一个‘预检’请求。

这个请求使用OPTIONS方法发送并包含以下头部:

Origin:与简单请求相同

Access-Control-Request-Method

Access-Control-Request-Headers

在这个请求发送后,服务器可以确定是否允许这种类型的请求。服务器会通过在响应中发送如下头部与浏览器沟通这些信息:

Access-Control-Allow-Origin:与简单请求相同

Access-Control-Allow-Methods:允许的方法

Access-Control-Allow-Headers:服务器允许的头部

Access-Control-Max-Age:缓存预检请求的秒数

预检请求返回后,结果会按响应指定的时间缓存一段时间,换句话说,只有第一次发送这种类型的请求时才会多发送一次额外的HTTP请求.

3.2 凭据请求

默认请求下,跨源请求不提供凭据(cookie、HTTP认证和客户端SSL证书)

可以通过将withCredentials属性设置为true来表明请求会发送凭据,如果服务器允许带凭据的请求,那么可以在响应中包含如下HTTP头部:

Access-Control-Allow-Credentials: true

如果发送了凭据请求而服务器返回的响应中没有这个头部,则浏览器不会把响应交给javascript.服务器也可以在预检请求中发送这个http头部,以表明这个源允许发送凭证请求.

4. 代替性跨源技术

4.1 图片探测

图片探测:因为所有页面都可以跨域加载图片而不必担心限制,所以我们可以动态的创建图片,然后通过他们的onload和onerror事件处理程序得到何时收到响应.

let img = new Image();
img.onload = img.onerror = function() {
  alert('Done!');
};
img.src = 'http://www.baidu.com/test?name="lyb"'

注意:图片探测只能发送GET请求,无法获取服务器响应的内容。只能利用图片探测实现浏览器与服务器单向通信的原因。

4.2 JSONP

function handleResponse(response) {
  console.log(`you're at ip address ${response.ip}, which is in ${response.city}, ${response.region_name}`);
}

let script = document.createElement('script');
script.src = 'http://www.baidu.com/json/?callback=handleResponse';
document.body.insertBefore(script, document.body.firstChild);
  • JSONP是从不同的域拉取可执行代码。如果这个域不可信,则可能在响应中加入恶意内容。
  • 不好确认JSONP请求是否失败。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值