Ajax及跨域
概念
Ajax
Ajax,Asynchronous JavaScript and XML,字面意思:异步的 JavaScript 和 XML,是指一种创建交互式网页应用的网页开发技术。
用于异步地去获取XML作为数据交换的格式,当然,现在的 ajax 并不仅仅局限于XML作为数据交换格式,还可以像纯文本、XML、HTML、JSON 等格式均可。
特点
- 使用脚本操纵HTTP和Web服务器进行数据交换,不会导致页面重载。
- 避免页面重载(这是Web初期的标准做法)的能力使Web应用感觉更像传统的桌面应用。
- Web应用可以使用Ajax技术把用户的交互数据记录到服务器中;也可以开始只显示简单页面,之后按需加载额外的数据和页面组件来提升应用的启动时间。
异步原理
Ajax 的 A 就是 asynchronous 的简写,表示异步。
同步和异步:
同步,按照代码书写的顺序,一个任务一个任务的来执行。
异步,并不是按照代码书写的顺序,通常会结合回调和事件来执行相应代码。
在同步中,如果有一个任务耗时较长,整个的后面任务都需要等待。
在异步中,可以将耗时较长先放起来,执行其他的,其他的执行完毕,回头再执行这个。
同步:提交请求->等待服务器处理->处理完毕返回 阻塞模式。
异步:请求通过事件触发->服务器处理->处理完毕。非阻塞模式。
目的
为什么需要Ajax
首先,我们都了解软件的两种架构形态,C/S(Client/Server)和B/S(Browser/Server)。它们的区别如下:
C/S,在客户端安装了客户端软件之后,整个程序的运行,分担到客户端和服务器端。用户在操作的时候,体验更好,响应速度非常快。
B/S,所务的服务都放在服务器端,通过浏览器使用服务器,用户在操作的时候,体验不太好,响应速度特别慢。
B/S的好处,就是不需要安装客户端,软件维护和更新比较方便,用户体验不好。
C/S的好处,用户体验够好,响应及时,但是软件的维护和更新比较麻烦。
随着互联网的发展,越来越多的C/S应用慢慢 转成的B/S,这也是未来的趋势。但是矛盾很突出,B/S的响应速度慢。需要B/S模式具备C/S模式的快速响应特点。
Ajax的出现就是为了解决这个问题-----异步刷新。
在异步刷新机制,整个页面不用跳转,只需要更新需要变化的地方。
Ajax 能干什么
- 表单交互
- 动态标签页
- 及时编辑
- 地图类应用
- 移动APP
- ...
宗旨:提升页面的访问速度,提高用户体验。
发展历程
2005年2月,Adaptive Path公司的Jesse James Garrett最早提出这个概念。它出现在Garrett的文章“Ajax: A New Approach to Web Applications”中。这篇文章描述了混合使用XHTML、CSS、JavaScript、DOM、XMLHttpRequest进行Web开发将会成为一种新的趋势。
同年,Google在三大产品中使用了Ajax技术:
- Google Suggest
- Google Maps
- Gmail。
实际上,Ajax发展经历了三个时代:
- Ajax前传,使用iframe实现局部刷新技术;
- Ajax正传,传统的Ajax;
- Ajax新传,下一代Ajax,反向Ajax。
早起 Ajax 实现
2005年,没有 Ajax 的概念,但是异步刷新的需要还是非常多。常用的方式就是 iframe。
实现原理
第一,iframe有一个src属性,通过设置src属性,发出请求,发出请求的时候不会跳转;
第二,iframe其实就是指向单独的页面,可以在其中进行任意的操作,可以在iframe中书写js代码,可以去操作父页面。
iframe 的优缺点
优点,实现起来特别简单,兼容性好。
缺点,功能比较弱,在服务端需要写大量的代码。
简单示例:
前台代码:
<body>
<h2>用户注册</h2>
<form action="/reg" method="post">
<p>
<label for="username">用户名:</label>
<input type="text" id="username" name="username">
<span id="msg"></span>
</p>
<p>
<label for="password">密 码:</label>
<input type="password" id="password" name="password">
</p>
<p><input type="submit" value="注册"></p>
</form>
<iframe src="" frameborder="0" width="0" height="0" id="iframe1"></iframe>
<script>
var iframe1 = document.getElementById('iframe1');
var username = document.getElementById('username');
username.addEventListener('blur',function(){
iframe1.src = "/check?username=" + this.value;
});
</script>
</body>
服务器端代码:
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const app = express();
app.get('/',(req,res)=>{
res.sendFile(path.join(__dirname,'login.html'));
});
app.post('/reg',(req,res)=>{
let username = req.body.username;
let password = req.body.password;
// 检测用户名是否可用,典型的做法需要去连接数据库,取出所有用户名,进行查询比较
// TODO
// 这种方式,可以满足需求,但是用户体验极差
// 每次都是填写完所有的表单内容,才能做一些检测
// 整个页面已经发生刷新跳转
});
// 模仿数据库
let users = ['admin','test'];
app.get('/check',(req,res)=>{
let username = req.query.username;
if (inArray(username,users)) {
res.send("<script>window.parent.document.getElementById('msg').innerHTML='对不起,用户名已被占用,请重新注册!'</script>");
} else {
res.send("<script>window.parent.document.getElementById('msg').innerHTML='恭喜,用户名可用!'</script>");
}
});
app.listen(3000,()=>{
console.log('server is listening in port 3000...');
});
// 简单封装一个方法,用于检测一个值是否在数组中存在
function inArray(str,arr){
for(let i=0; i<arr.length; i++){
if(str == arr[i]){
return true;
break;
}
}
return false;
}
完整的Ajax通信流程
1.首先要创建一个 Ajax 对象
var xhr = new XMLHttpRequest();
状态:readyState:
status:
responseText:
2.初始化
状态:readyState:
status:
responseText:
3.调用 open()
方法,开启一个请求,但没有向服务器端发起请求
xhr.open(method, url [,async = true]);
状态:readyState: 1
status:
responseText:
4.调用 send()
方法,正式向服务器端发起请求
get
xhr.send([data=null]);
post
xhr.setRequestHeader(header,value);
header:content-type
表单value:application/x-www-form-urlencoded
文件上传value:multipart/form-data
状态:readyState:2
status:
responseText:
5.当服务器端返回数据,浏览器端接收数据时
状态:readyState:3
status:
responseText:
6.当浏览器端结束请求的时候
xhr.onreadystatechange = function(callback){
if(xhr.readyState == 4){
if( (xhr.status >= 200 && xhr.status < 300) || xhr.status==304 ){
callback(xhr.responseText);
} else {
alert('request was unsuccessful:' + xhr.status);
}
}
}
状态:readyState:4
status:200
responseText:<!DOCTYPE html>响应返回值
XMLHttpRequest 对象
定义
- 通用定义:XMLHttpRequest是一套可以在Javascript、VbScript、Jscript等脚本语言中通过http协议传送或从接收XML及其他数据的一套API。
- 来自MSDN的解释:XMLHttpRequest提供客户端同http服务器通讯的协议。客户端可以通过该对象向http服务器发送请求并使用XML文档对象模型处理回应。
属性
onreadystatechange* 指定当readyState属性改变时的事件处理句柄。只写
readyState 返回当前请求的状态,只读.
responseBody 将回应信息正文以unsigned byte数组形式返回.只读
responseStream 以Ado Stream对象的形式返回响应信息。只读
responseText 将响应信息作为字符串返回.只读
responseXML 将响应信息格式化为Xml Document对象并返回,只读
status 返回当前请求的http状态码.只读
statusText 返回当前请求的响应行状态,只读
方法
abort 取消当前请求
getAllResponseHeaders 获取响应的所有http头
getResponseHeader 从响应信息中获取指定的http头
open 创建一个新的http请求,并指定此请求的方法、URL以及验证信息(用户名/密码)
send 发送请求到http服务器并接收回应
setRequestHeader 单独指定请求的某个http头
创建xhr对象
Microsoft最早把XMLHttpRequest对象引入到IE5中,且在IE5和IE6中它只是一个ActiveX对象。IE7之前的版本不支持非标准的XMLHttpRequest()构造函数,兼容如下:
function createXHR(){
var xhr = null;
try{
xhr = new XMLHttpRequest();
}catch(e1){
try{
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}catch(e2){
try{
xhr = new ActiveXObject("Msxml2.XMLHTTP");
}catch(e3){
throw new Error("XMLHttpRequest is not supported");
}
}
}
return xhr;
}
请求方式
两种请求方式,有所不同。
get 请求
- 数据大小,受限于浏览器,大部分2k的限制,但每个浏览器不一样,chrome 是8k
- 数据形式,查询字符串方式,名值对的形式,中间用
&
链接 - 安全性,不太安全
- 数据的传递通过查询字符串,在url后面用
?
跟上 xhr.send([data=null])
post请求
- 数据大小,原则上没有限制,php.ini中默认上限是8M
- 数据形式,把
form
表单的数据给请求出来以xml形式传递给服务器 - 数据的传递得使用
send
方法,其中数据以键值对的形式来传递 - 安全性,比较安全
- 需要设置请求头信息
- 需要设置请求头部
xhr.setRequestHeader("content-type","application/x-www-form-urlencoded");
xhr.send(Formdata);
post请求注意事项
- 使用setRequestHeader()把传递的数据组织为xml格式
- 调用send()方法时,需要传递数据
- 该方式请求的同时也可以传递get参数信息,使用get方式来接收该信息即可
请求参数序列化
get请求中,如果参数是对象,那么我们需要对它进行转换,编码之后,不需要解码,浏览器会自动解码;
var url = "example.json?" + serialize(formdata);
xhr.open('get', url, true);
xhr.send(null);
post请求中,有特殊符号(=和&)和中文需要编码,encodeURIComponent
方法
xhr.open('post', 'example.json', true);
xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded');
xhr.send(serialize(formdata));
序列化方法:
function serialize(data){
if(!data) return '';
var pairs = [];
for(var name in data){
if(!data.hasOwnProperty(name)) continue;
if(typeof data[name] === 'function') continue;
var value = data[name].toString();
name = encodeURIComponent(name);
value = encodeURIComponent(value);
pairs.push(name + '=' + value);
}
return pairs.join('&');
}
状态说明
0(未初始化)
此阶段确认xhr对象是否创建成功,为调用open方法做好准备,值为0表示对象已经存在,否则浏览器报错
1(载入)
对xhr进行初始化,调用open方法,根据参数完成对象状态的设置,并调用send开始向服务器端发送请求,值为1表示正在向服务器端发送请求
2(载入完成)
接收服务器端的响应数据,但获得的还是服务器响应的原始数据,并不能在客户端使用。值为2表示已经接收完全部响应数据,并为下一阶段对数据进行解析做好准备
3(交互)
解析接收到的服务器端响应数据,根据服务器头部返回的MIME把数据转换成对应格式,为在客户端调用做好准备。3表示正在解析数据
4(完成)
确认全部数据已经解析为客户端可用的格式,解析已经完成。值为4表示数据解析完毕,可以通过xhr对象的属性取得数据
返回数据格式
xml responseXML
和 text responseText
作为文本返回时,返回的是字符串,但有些字符串比较特殊,如html代码、json对象,所以有如下几种形式
- 纯文本字符串,如1、0、yes、no等,直接处理
- 返回html字符串,以innerHTML的方式写回到页面中
- json格式,由js解析
跨域资源访问
同源策略
两个页面拥有相同的协议(protocol)端口(port)和主机(host),那么这两个页面就属于同一个源(origin)
不满足同源策略的资源访问,叫跨域资源访问
注意
如果是协议和端口造成的跨域问题“前台”是无能为力的
在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。
(“URL的首部”指window.location.protocol +window.location.host,也可以理解为“Domains, protocols and ports must match”。)
实现方法
CORS
CORS(Cross-Origin Resource Sharing)跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。
CORS背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。
目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。
对于开发者来说,CORS通信与同源的Ajax通信没有差别,代码完全一样。
浏览器一旦发现Ajax请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
参考阮老师的文章:http://www.ruanyifeng.com/blog/2016/04/cors.html
JSONP
JSONP(JSON with Padding)是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。单向的数据请求。
JSONP的原理与实现思路
- Web页面通过调用外部js文件,可实现跨域请求。扩展一下:但凡有src属性的标签都具有跨域能力,例如
<script><img><iframe>
。 - 跨域服务器动态生成数据并存入js文件(通常json后缀),返回给客户端,供客户端调用;因为对json数据格式支持度都非常好。
- 为了便于客户端使用数据,形成一个非正式传输协议,称为JSONP。该协议重点是允许用户传递一个callback参数给服务器,然后服务器返回数据时 将此callback参数作为函数名包裹住JSON数据,使得客户端可以随意定制自己的函数来自动处理返回数据。
因为通过script标签引入的js是不受同源策略的限制的。所以我们可以通过script标签引入一个js或者是一个其他后缀形式(如php,jsp等)的文件,此文件返回一个js函数的调用
示例如下:(服务器端用node.js)
浏览器端代码:
动态生成script
标签,创建一个callback
,以查询字符串形式跟在src
的url
后边
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONP</title>
</head>
<body>
<button id="btn">JSONP</button>
<script>
var btn = document.getElementById('btn');
function show(data){
console.log(data);
}
btn.onclick = function(){
var url = "http://localhost:4000/test?callback=show";
var script = document.createElement('script');
script.setAttribute('src',url);
document.getElementsByTagName('head')[0].appendChild(script);
}
</script>
</body>
</html>
服务器端代码:
const express = require('express');
const path = require('path');
const app = express();
app.get('/',(req,res)=>{
res.sendFile(path.join(__dirname,'index.html'));
});
app.listen(3000,()=>{
console.log('http server is listening in port 3000');
});
跨域服务器端代码:获取请求url
中的callback
,把callback
和json
数据组合后返回,这里需要注意,node.js返回数据的时候,设置res.type("text/javascript");
,
const express = require('express');
const path = require('path');
const app = express();
app.get('/test',(req,res)=>{
let test = require('./test.json');
const callback = req.query.callback;
res.type("text/javascript");
res.send(callback+'('+JSON.stringify(test)+')');
});
app.listen(4000,()=>{
console.log('http server is listening in port 4000');
});
json数据:
{
"title" : "JSONP",
"time" : "2017-1-1"
}
结果:
图1
图2
优缺点
JSONP的优点:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;
它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;
并且在请求完毕后可以通过调用callback的方式回传结果
JSONP的缺点:它只支持GET请求而不支持POST等其它类型的HTTP请求;
它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题
CORS和JSONP对比
- JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
- 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。
- JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS。
- CORS与JSONP相比,无疑更为先进、方便和可靠。
其他实现方法
- 通过document.domain跨域
- 通过location.hash跨域
- 通过HTML5的postMessage方法跨域
- 通过window.name跨域
- CSST (CSS Text Transformation)
一种用 CSS 跨域传输文本的方案
优点:相比 JSONP 更为安全,不需要执行跨站脚本。
缺点:没有 JSONP 适配广,CSST 依赖支持 CSS3 的浏览器。
原理:通过读取 CSS3 content 属性获取传送内容。通过创建一个 link 请求到 css 文件,然后通过 computedStyle = window.getComputedStyle
获取到指定元素的 style 对象,再通过 computedStyle .content
获取到内容
Ajax及跨域的更多相关文章
- AJAX POST&跨域 解决方案 - CORS
一晃又到新年了,于是开始着手好好整理下自己的文档,顺便把一些自认为有意义的放在博客上,记录成点的点滴. 跨域是我在日常面试中经常会问到的问题,这词在前端界出现的频率不低,主要原因还是 ...
- 解决Ajax不能跨域的方法
1. Ajax不能跨域请求的原因 同源策略(Same Origin Policy),是一种约定,该约定阻止当前脚本获取或者操作另一个域下的内容.所有支持Javascript的浏览器都支持同源策略,也 ...
- AJAX POST&跨域 解决方案 - CORS(转载)
跨域是我在日常面试中经常会问到的问题,这词在前端界出现的频率不低,主要原因还是由于安全限制(同源策略, 即JavaScript或Cookie只能访问同域下的内容),因为我们在日常的项目开发时会不可避免 ...
- jQuery的ajax jsonp跨域请求
了解:ajax.json.jsonp.“跨域”的关系 要弄清楚以上ajax.json.jsonp概念的关系,我觉得弄清楚ajax是“干什么的”,“怎么实现的”,“有什么问题”,“如果解决存在的问题”等 ...
- javascript ajax 脚本跨域调用全解析
javascript ajax 脚本跨域调用全解析 今天终于有点时间研究了一下javsscript ajax 脚本跨域调用的问题,先在网上随便搜了一下找到一些解决的办法,但是都比较复杂.由是转到jqu ...
- JavaScript(10)——Ajax以及跨域处理
Ajax以及跨域处理 哈哈哈,终于写到最后一章了.不过也还没有结束,说,不要为了学习而学习,恩.我是为了好好学习而学习呀.哈哈哈.正在尝试爱上代码,虽然有一丢丢的难,不过,我相信我会的! [Ajax] ...
- ASP.Net WebAPI与Ajax进行跨域数据交互时Cookies数据的传递
前言 最近公司项目进行架构调整,由原来的三层架构改进升级到微服务架构(准确的说是服务化,还没完全做到微的程度,颗粒度没那么细),遵循RESTFull规范,使前后端完全分离,实现大前端思想.由于是初次尝 ...
- 原生JS实现Ajax及Ajax的跨域请求
前 言 如今,从事前端方面的程序猿们,如果,不懂一些前后台的数据交互方面的知识的话,估计都不太好意思说自己是程序猿.当然,如今有着许多的框架,都有相对应的前后台数据交互的方法. ...
- ajax实现跨域访问
ajax跨域访问是一个老生畅谈的问题啦,网上解决方法很多,discuz用的p3p协议,有兴趣的朋友可以了解下,比较常用的是JSONP方法,貌似目前这种方法只支持GET方式,不如POST方式安全. 即使 ...
- Ajax之跨域访问与JSONP
前言 同源策略的限制,使得ajax无法发出跨域请求.在许多情况下,我们需要让ajax支持跨域.以下是其中一种解决方案(JSONP).JSONP解决了跨域数据访问的问题. 在html中,具有src属性的 ...
随机推荐
- 为你的Web程序加个启动画面
.Net开发者一定熟悉下面这个画面: 这就是宇宙第一IDE Visual Studio的启动画面,学名叫Splash Screen(或者Splash Window).同样,Javar们一定对Eclip ...
- 利用bootstrap的carousel.js实现轮播图动画
前期准备: 1.jquery.js. 2.bootstrap的carousel.js. 3.bootstrap.css. 如果大家不知道在哪下载,可以联系小颖,小颖把这些js和css可以发送给你. 一 ...
- Power BI官方视频(3) Power BI Desktop 8月份更新功能概述
Power BI Desktop 8月24日发布了更新版本.现将更新内容翻译整理如下,可以根据后面提供的链接下载最新版本使用. 1.主要功能更新 1.1 数据钻取支持在线版 以前的desktop中进行 ...
- 计算机程序的思维逻辑 (54) - 剖析Collections - 设计模式
上节我们提到,类Collections中大概有两类功能,第一类是对容器接口对象进行操作,第二类是返回一个容器接口对象,上节我们介绍了第一类,本节我们介绍第二类. 第二类方法大概可以分为两组: 接受其他 ...
- $ORACLE_HOME变量值末尾多“/”惹的祸
之前一直误以为$ORACLE_HOME变量的路径中末尾多写一个"/"不会有影响. 今天做实验时碰到一个情景,发现并不是这样. 环境:OEL 5.7 + Oracle 10.2.0. ...
- C#各种同步方法 lock, Monitor,Mutex, Semaphore, Interlocked, ReaderWriterLock,AutoResetEvent, ManualResetEvent
看下组织结构: System.Object System.MarshalByRefObject System.Threading.WaitHandle System.Threading.Mutex S ...
- 解决IE8下不兼容rgba()的解决办法
rgba()是css3的新属性,所以IE8及以下浏览器不兼容,这怎么办呢?终于我找到了解决办法. 解决办法 我们先来解释以下rgba rgba: rgba的含义,r代表red,g代表green,b代表 ...
- jQuery遮罩层登录对话框
用户登录是许多网站必备的功能.有一种方式就是不管在网站的哪个页面,点击登录按钮就会弹出一个遮罩层,显示用户登录的对话框.这用方式比较灵活方便.而现在扫描二维码登录的方式也是很常见,例如QQ.微信.百度 ...
- Maven常用命令
开发中常用的命令: 1. mvn compile 编译源代码2. mvn test-compile 编译测试代码3. mvn test 运行测试4. mvn package 打包,根据pom.xml打 ...
- MySQL: Fabric 搭建 HA
搭建好Fabric之后,就可以在它的基础上创建HA Group. Shard Group.HA+Shard Group等.这里来说明一下如何快速的搭建HA环境. Fabric 192.168.2.23 ...