Ajax

说在前面:

传统方法的缺点:

传统的web交互是用户触发一个http请求服务器,然后服务器收到之后,在做出响应到用户,并且返回一个新的页面,每当服务器处理客户端提交的请求时,客户都只能空闲等待,并且哪怕只是一次很小的交互、只需从服务器端得到很简单的一个数据,都要返回一个完整的HTML页,而用户每次都要浪费时间和带宽去重新读取整个页面。这个做法浪费了许多带宽,由于每次应用的交互都需要向服务器发送请求,应用的响应时间就依赖于服务器的响应时间。这导致了用户界面的响应比本地应用慢得多。

什么是ajax?

ajax的出现,刚好解决了传统方法的缺陷。AJAX 是一种用于创建快速动态网页的技术。通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

一、 原生AJAX

AJAX:“Asynchronous Javascript And XML”(异步Javascript和XML),是指一种创建交互式应用的网页开发技术。

注意
① AJAX不是新的编程语言,而是一种使用现有标准的新方法
② AJAX最大的有点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容
③ AJAX不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。
1. XMLHttpRequest对象:用于在后台与服务器交换数据。意味着可以在不重新加载整个网页的情况下,对网页某部分进行更新。目前所有浏览器都支持XMLHttpRequest
五步使用XMLHttpRequest

① 创建XMLHttpRequest对象:var obj = new XMLHttpRequest()

② 使用open方法设置请求方式和地址以及相关交互信息:obj.open(method,url,asyncFlag,user,name,callback)。其中methodpost/get两种方法,asyncFlag为是否异步(默认为true,即默认为异步,若写false,浏览器不会报错,会报警告)。username有时用来确认身份。callback可用来接收服务器返回的内容

③ 设置发送的数据,开始和服务器端交互:obj.send(data)。若为get方法,则数据在url后面,每个参数用&连接,且数据和地址之间用?连接(这里注意,使用GET请求经常会发生的一个错误,就是查询字符串的格式有问题。查询字符串中每个参数的名称和值都必须使用encodeURIComponent()进行编码,然后才能放到URL的末尾)。若为post方法,数据作为send方法的参数。

④ 注册事件:onreadystatechange

属性描述
onreadystatuschange存储函数(或函数名),每当readyState属性改变时,就会调用该函数
readyState存储XMLHttpRequest的状态(0-4。0:请求未初始化。1:服务器连接已建立。2:请求已接收 3:请求处理中。4:请求已完成,且相应已就绪
status200:“ok”。404:未找到页面

⑤ 服务器响应:obj.responseText/obj.responseXML

属性描述
responseText获得字符串形式的相应数据
responseXML获得xml形式的相应数据
response
HTTP头部信息

默认情况下,在发送XHR请求的同时,还会发送下列头部信息

字段解释
Accept浏览器能够处理的内容类型
Accept-Charset浏览器能够显示的字符集
Accept-Encoding浏览器能够处理的压缩编码
Accept-Language浏览器当前设置的语言
Connection浏览器与服务器之间连接的类型
Cookie当前页面设置的任何Cookie
Host发出请求的页面所在域
Referer发出请求的页面的URL
User-Agent浏览器的用户代理字符串

通过XHR对象的setRequestHeader方法可以设置自定义的请求头部信息。这个方法接受两个参数:头部字段的名称和头部字段的值。要成功发送请求头部信息,必须调用oepn()方法之后调用send()方法之前调用setRequestHeader。服务器在接收到这种自定义的头部信息之后,可以执行相应的后续操作(自定义头部字段最好不要和默认字段名称相同)

调用XHR对象的getResponseHeader()方法并传入头部字段名称,可以取得相应的响应头部信息。而调用getAllResponseHeaders()方法则可以取得一个包含所有头部信息的长字符串。

二、跨域源资源共享

默认条件下,XHR对象只能访问与包含它的页面位于同一个域中的资源,这种安全策略可以预防某些恶意行为,但是,实现合理的跨域请求是必要的。

1. CORSCross-Origin Resource Sharing

思想:使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。

CORSCross-Origin Resource Sharing)请求可以分成两种:
  • 简单请求
  • 复杂请求
简单请求

一个简单的请求大致如下:

HTTP方法是下列之一

  • HEAD
  • GET
  • POST

HTTP头包含

  • Accept

  • Accept-Language

  • Content-Language

  • Last-Event-ID

  • Content-Type,但仅能是下列之一

    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

任何不符合上述要求的(请求方法不是HEADGET以及POST其中之一,自定义头部或Content-Type不为上述三个之一),均可以看做 复杂请求。浏览器对简单请求和复杂请求的处理方式是有区别的。

简单请求说明:

例如:一个简单的GET/POST发送的请求,在发送该请求时,需要给它额外添加一个Origin头部,其中包含请求页面的源信息(协议、域名和端口),以便服务器根据这个头部信息来决定是否给予响应。
一个头部示例:

Accept: `*/*`
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: keep-alive
Host: localhost:3000
Origin: http://localhost
Referer: http://localhost/
User-Agent: Mozilla/5.0 (Macintosh; ....... 

无论服务器是否任务该请求可以接受,都会正常地返回一个HTTP回应。但是如果服务器认为这个请求可以接受(即请求页面的源信息在可允许范围内),返回的响应中会多出以下几个头信息字段(不一定每次都多如下几个字段,不需要的字段可以删除),其中在Access-Control-Allow-Origin头部中回发相同的源信息(也可以发送通配符):

Access-Control-Allow-Origin: *//或http://localhost
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

上面的头信息之中,有三个与CORS请求相关的字段,都以Access-Control-开头。

  • Access-Control-Allow-Origin:该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。
  • Access-Control-Allow-Credentials:该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可(在下面会讲到)。
  • Access-Control-Expose-Headers:该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。

如果服务器认为这个请求不可以接受,则不会返回这个头部(Access-Control-Allow-Origin)或者返回的这个头部内容与源信息不匹配,浏览器便会驳回请求页面的请求,即提示报错,错误可以被XMLHttpRequest的onerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。在这里需要注意的是:默认情况下发送请求过程中不能发送和接收cookie的,若需要,请继续向下看~

在这里可以这样理解:对于CORS的简单请求,当页面发送了请求后,会经过浏览器的一层拦截过滤,默认情况下发送到服务端实际上是无cookie以及无其他不可发送的信息的。当服务器方进行请求响应时,实际上也经过了浏览器的一层拦截过滤,当浏览器检查响应头Access-Control-Allow-Origin字段发现页面请求可被接受则将响应内容作为请求的响应内容,否则报错且无响应内容。(自己的理解,如有错误麻烦指出~)
复杂请求

对于复杂请求,浏览器跟服务器之间会有一次预检请求,而简单请求时没有的。

复杂请求说明:

CORS通过Preflighted Requests的透明服务器验证机制支持开发人员使用自定义的头部、GETPOST之外的方法,以及不同类型的主题内容。在进行复杂请求时,会向服务器发送一个Preflight请求,这种请求使用OPTIONS方法,发送下列头部。

Origin:与简单请求相同
Access-Control-Request-Method:请求自身使用的方法
Access-Control-Request-Headers:(可选)自定义的头部信息,多个头部以逗号分隔

示例:
页面请求头部信息:

Origin:http://localhost
Access-Control-Request-Method:POST
Access-Control-Request-Headers:NCZ

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

浏览器返回的头部信息模板

Access-Control-Allow-Origin:与简单请求相同
Access-Control-Allow-Methods:允许的方法,多个方法以逗号分隔
Access-Control-Allow-Headers:允许的头部,多个头部以逗号分隔
Access-Control-Max-Age:应该将该预请求缓存多长时间(以秒表示)

对于上例中浏览器返回的头部信息

Access-Control-Allow-Origin:http://localhost
Access-Control-Allow-Methods:POST,GET
Access-Control-Allow-Headers:NCZ
Access-Control-Max-Age:1728000

Preflight请求结束后,结果将按照响应中指定的时间缓存起来。

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

CORS跨域发送凭证(cookieHTTP认证以及客户端SSL证明等)

需要满足以下三个条件:

  • open XMLHttpRequest后,设置withCredentialstrue即可让该跨域请求携带 凭证
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.withCredentials = true;
xhr.send();
  • 需要在目标服务器接受跨域发送的凭证,否则会被浏览器的同源策略挡住:服务器同时设置Access-Control-Allow-Credentials响应头为true, 即可允许跨域请求携带凭证。
  • 跨域发送 凭证还要求Access-Control-Allow-Origin不允许使用通配符。 事实上不仅不允许通配符,而且只能指定单一域名,否则,浏览器还是会挡住跨域请求。

理解:

  • 实际上,当没有设置withCredentials时,在请求中不会发送凭证。如果设置了withCredentials但后端的响应头的Access-Control-Allow-Origin不为true,实际上凭证发送到了服务端且可以获取到相关内容,但由于响应头的Access-Control-Allow-Origin不为true,故浏览器会当作不可被服务端接受的请求,则会直接报错,即页面无法得到响应信息。
  • 在复杂请求中,我们可以在客户端设置withCredentialstrue,服务器可以在Preflight响应中设置Access-Control-Allow-Origintrue,即标识允许客户端发送带凭证的请求
2. 图像Ping

可以利用img标签的跨域特性完成跨域请求。动态创建图像经常用于图像Ping。图像Ping是与服务器进行简单、单向的跨域通信的一种方式。请求的数据是通过查询字符串形式发送的,而响应可以是任意内容,但通常是像素图或204响应。通过图像Ping,浏览器无法得到任何具体的数据,但通过侦听loaderror事件,它能知道响应是什么时候接收到的。

图像Ping最常用于跟踪用户点击页面或动态广告曝光次数。但它有两个缺点:① 只能发送GET请求 ② 无法访问服务器的相应文本。故其只能用于浏览器与服务期间的单向请求

3. JSONP

思想:利用<script>元素的可跨域特性完成跨域请求

  1. 在页面上引入不同域的js脚本文件是可以的。jsonp正是利用这个特性来实现的。
  2. 通过script标签引入一个js文件,这个js文件载入成功后会执行我们在url参数中指定的回调函数(实际上是请求了远程的script文件,并服务端返回了执行该回调函数的js语句),并且会把我们需要的json数据作为参数传入。所以jsonp是需要服务器端的页面进行相应的配合的。
  3. 在使用jsonp跨域时必须要在发送请求前定义回调函数,否则找不到回调函数

eg:有个a.html页面,它里面的代码需要利用ajax获取一个不同域上的json数据,假设这个json数据地址是http://example.com/data.php,那么a.html中的代码就可以这样:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四个方案:
方案一:
  1. 服务端: 将要返回的数据,填充进一条字符串格式的js语句中,组成一条正确的可执行的js语句再返回。
  2. 客户端: 添加<script src="服务器端地址">
    结果: script可跨域请求到服务器返回的js语句,并在客户端立刻执行。
    问题: 服务端返回的js语句是写死的。
方案二:
  1. 服务端: 返回一条自定义函数的调用语句,要求客户端必须执行指定名称的函数
  2. 客户端: 添加<script src="服务器端地址">并提前定义一个与服务端同名的函数,函数有一个参数可接受服务器端的数据, 函数内可执行任意操作
    问题: 函数名是写死的,极容易发生冲突。
方案三:
  1. 服务端:接受一个函数名参数,将客户端发来的函数名参数拼接到js语句的开头(即返回执行函数名参数对应的函数的js语句
  2. 客户端:<script src=“地址?参数名=本地函数名”
    问题: script是写死的。只能在页面加载时请求一次
方案四:
  1. 服务端:接受一个函数名参数,将客户端发来的函数名参数拼接到js语句的开头(即返回执行函数名参数对应的函数的js语句
  2. 客户端: 动态创建script元素,script只要被加到页面上,就立刻自动请求
    问题: script不断累积
    解决: 在回调函数中自动删除script

参考文章:
CORS 跨域发送 Cookie
CORS——一种新的跨域解决方案
从原理分析CORS——我们到底是怎么跨域的
跨域资源共享 CORS 详解
HTTP访问控制(CORS)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值