当前位置: 首页 > news >正文

JavaScript网络请求( XMLHttpRequest 对象,进度事件, 跨源资源共享)

一、 XMLHttpRequest 对象

IE5 是第一个引入 XHR 对象的浏览器。这个对象是通过 ActiveX 对象实现并包含在 MSXML 库中
的。为此, XHR 对象的 3 个版本在浏览器中分别被暴露为 MSXML2.XMLHttp MSXML2.XMLHttp.3.0 和 MXSML2.XMLHttp.6.0
所有现代浏览器都通过 XMLHttpRequest 构造函数原生支持 XHR 对象:
let xhr = new XMLHttpRequest();
1 使用 XHR
使用 XHR 对象首先要调用 open() 方法,这个方法接收 3 个参数:请求类型( "get" "post" 等)、
请求 URL ,以及表示请求是否异步的布尔值。下面是一个例子:
xhr.open("get", "example.php", false);
这行代码就可以向 example.php 发送一个同步的 GET 请求。关于这行代码需要说明几点。首先,这 里的 URL 是相对于代码所在页面的,当然也可以使用绝对 URL 。其次,调用 open() 不会实际发送请求,只是为发送请求做好准备。
注意 只能访问同源 URL ,也就是域名相同、端口相同、协议相同。如果请求的 URL 与发送请求的页面在任何方面有所不同,则会抛出安全错误。
要发送定义好的请求,必须像下面这样调用 send() 方法:
xhr.open("get", "example.txt", false);
xhr.send(null);
send() 方法接收一个参数,是作为请求体发送的数据。如果不需要发送请求体,则必须传 null
因为这个参数在某些浏览器中是必需的。调用 send() 之后,请求就会发送到服务器。
因为这个请求是同步的,所以 JavaScript 代码会等待服务器响应之后再继续执行。收到响应后, XHR 对象的以下属性会被填充上数据。
responseText :作为响应体返回的文本。
responseXML :如果响应的内容类型是 "text/xml" "application/xml" ,那就是包含响应 数据的 XML DOM 文档。
status :响应的 HTTP 状态。
statusText :响应的 HTTP 状态描述。
收到响应后,第一步要检查 status 属性以确保响应成功返回。一般来说, HTTP 状态码为 2 xx
示成功。此时, responseText responseXML (如果内容类型正确)属性中会有内容。如果 HTTP 状态码是 304 ,则表示资源未修改过,是从浏览器缓存中直接拿取的。当然这也意味着响应有效。为确 保收到正确的响应,应该检查这些状态,如下所示:
xhr.open("get", "example.txt", false);
xhr.send(null);
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {alert(xhr.responseText);
} else {alert("Request was unsuccessful: " + xhr.status);
}
以上代码可能显示服务器返回的内容,也可能显示错误消息,取决于 HTTP 响应的状态码。为确定 下一步该执行什么操作,最好检查 status 而不是 statusText 属性,因为后者已经被证明在跨浏览器 的情况下不可靠。无论是什么响应内容类型,responseText 属性始终会保存响应体,而 responseXML 则对于非 XML 数据是 null
虽然可以像前面的例子一样发送同步请求,但多数情况下最好使用异步请求,这样可以不阻塞
JavaScript 代码继续执行。 XHR 对象有一个 readyState 属性,表示当前处在请求 / 响应过程的哪个阶段。
这个属性有如下可能的值。
0 :未初始化( Uninitialized )。尚未调用 open() 方法。
1 :已打开( Open )。已调用 open() 方法,尚未调用 send() 方法。
2 :已发送( Sent )。已调用 send() 方法,尚未收到响应。
3 :接收中( Receiving )。已经收到部分响应。
4 :完成( Complete )。已经收到所有响应,可以使用了。
每次 readyState 从一个值变成另一个值,都会触发 readystatechange 事件。可以借此机会检
readyState 的值。一般来说,我们唯一关心的 readyState 值是 4 ,表示数据已就绪。为保证跨浏 览器兼容,onreadystatechange 事件处理程序应该在调用 open() 之前赋值。来看下面的例子:
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("Request was unsuccessful: " + xhr.status);}}
};
xhr.open("get", "example.txt", true);
xhr.send(null);
以上代码使用 DOM Level 0 风格为 XHR 对象添加了事件处理程序,因为并不是所有浏览器都支持
DOM Level 2 风格。与其他事件处理程序不同, onreadystatechange 事件处理程序不会收到 event 对象。在事件处理程序中,必须使用 XHR 对象本身来确定接下来该做什么。
注意 由于 onreadystatechange 事件处理程序的作用域问题,这个例子在 onreadystatechange 事件处理程序中使用了 xhr 对象而不是 this 对象。使用 this 可能导致功能失败或导致错误,取决于用户使用的是什么浏览器。因此还是使用保存 XHR 对象的 变量更保险一些。
在收到响应之前如果想取消异步请求,可以调用 abort() 方法:
xhr.abort();

调用这个方法后,XHR 对象会停止触发事件,并阻止访问这个对象上任何与响应相关的属性。中

断请求后,应该取消对 XHR 对象的引用。由于内存问题,不推荐重用 XHR 对象。
2 HTTP 头部
每个 HTTP 请求和响应都会携带一些头部字段,这些字段可能对开发者有用。 XHR 对象会通过一
些方法暴露与请求和响应相关的头部字段。 默认情况下,XHR 请求会发送以下头部字段。
Accept :浏览器可以处理的内容类型。
Accept-Charset :浏览器可以显示的字符集。
Accept-Encoding :浏览器可以处理的压缩编码类型。
Accept-Language :浏览器使用的语言。
Connection :浏览器与服务器的连接类型。
Cookie :页面中设置的 Cookie
Host :发送请求的页面所在的域。
Referer :发送请求的页面的 URI 。注意,这个字段在 HTTP 规范中就拼错了,所以考虑到兼容
性也必须将错就错。(正确的拼写应该是 Referrer 。)
User-Agent :浏览器的用户代理字符串。
虽然不同浏览器发送的确切头部字段可能各不相同,但这些通常都是会发送的。如果需要发送额外
的请求头部,可以使用 setRequestHeader() 方法。这个方法接收两个参数:头部字段的名称和值。
为保证请求头部被发送,必须在 open() 之后、 send() 之前调用 setRequestHeader() ,如下面的例
子所示:
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("Request was unsuccessful: " + xhr.status);}}
};
xhr.open("get", "example.php", true);
xhr.setRequestHeader("MyHeader", "MyValue");
xhr.send(null);

服务器通过读取自定义头部可以确定适当的操作。自定义头部一定要区别于浏览器正常发送的头部, 否则可能影响服务器正常响应。有些浏览器允许重写默认头部,有些浏览器则不允许。

可以使用 getResponseHeader() 方法从 XHR 对象获取响应头部,只要传入要获取头部的名称即
可。如果想取得所有响应头部,可以使用 getAllResponseHeaders() 方法,这个方法会返回包含所
有响应头部的字符串。下面是调用这两个方法的例子:
let myHeader = xhr.getResponseHeader("MyHeader");
let allHeaders xhr.getAllResponseHeaders();

服务器可以使用头部向浏览器传递额外的结构化数据。getAllResponseHeaders()方法通常返回

类似如下的字符串:
Date: Sun, 14 Nov 2004 18:04:03 GMT
Server: Apache/1.3.29 (Unix)
Vary: Accept
X-Powered-By: PHP/4.3.8
Connection: close
Content-Type: text/html; charset=iso-8859-1
通过解析以上头部字段的输出,就可以知道服务器发送的所有头部,而不需要单独去检查了。
3 GET 请求
最常用的请求方法是 GET 请求,用于向服务器查询某些信息。必要时,需要在 GET 请求的 URL
后面添加查询字符串参数。对 XHR 而言,查询字符串必须正确编码后添加到 URL 后面,然后再传给 open()方法。
发送 GET 请求最常见的一个错误是查询字符串格式不对。查询字符串中的每个名和值都必须使用
encodeURIComponent() 编码,所有名 / 值对必须以和号( & )分隔,如下面的例子所示:
xhr.open("get", "example.php?name1=value1&name2=value2", true);

可以使用以下函数将查询字符串参数添加到现有的 URL 末尾:

function addURLParam(url, name, value) {url += (url.indexOf("?") == -1 ? "?" : "&");url += encodeURIComponent(name) + "=" + encodeURIComponent(value);return url;
}

这里定义了一个 addURLParam()函数,它接收 3 个参数:要添加查询字符串的 URL、查询参数和

参数值。首先,这个函数会检查 URL 中是否已经包含问号(以确定是否已经存在其他参数)。如果没有, 则加上一个问号;否则就加上一个和号。然后,分别对参数名和参数值进行编码,并添加到 URL 末尾。
最后一步是返回更新后的 URL
可以使用这个函数构建请求 URL ,如下面的例子所示:
let url = "example.php";
// 添加参数
url = addURLParam(url, "name", "Nicholas");
url = addURLParam(url, "book", "Professional JavaScript");
// 初始化请求
xhr.open("get", url, false);

这里使用 addURLParam()函数可以保证通过 XHR 发送请求的 URL 格式正确。

4 POST 请求
第二个最常用的请求是 POST 请求,用于向服务器发送应该保存的数据。每个 POST 请求都应该在 请求体中携带提交的数据,而 GET 请求则不然。 POST 请求的请求体可以包含非常多的数据,而且数据 可以是任意格式。要初始化 POST 请求, open() 方法的第一个参数要传 "post" ,比如:
xhr.open("post", "example.php", true);

接下来就是要给 send()方法传入要发送的数据。因为 XHR 最初主要设计用于发送 XML,所以可

以传入序列化之后的 XML DOM 文档作为请求体。当然,也可以传入任意字符串。
默认情况下,对服务器而言, POST 请求与提交表单是不一样的。服务器逻辑需要读取原始 POST
数据才能取得浏览器发送的数据。不过,可以使用 XHR 模拟表单提交。为此,第一步需要把 Content-Type 头部设置为 "application/x-www-formurlencoded" ,这是提交表单时使用的内容类型。第二 步是创建对应格式的字符串。POST 数据此时使用与查询字符串相同的格式。如果网页中确实有一个表 单需要序列化并通过 XHR 发送到服务器,则可以使用 serialize() 函数来创建相应的字符 串,如下所示:
function submitData() {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("Request was unsuccessful: " + xhr.status);}}};xhr.open("post", "postexample.php", true);xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");let form = document.getElementById("user-info");xhr.send(serialize(form));
}

在这个函数中,来自 ID "user-info"的表单中的数据被序列化之后发送给了服务器。PHP 文件

postexample.php 随后可以通过 $_POST 取得 POST 的数据。比如:
<?php
header("Content-Type: text/plain");
echo <<<EOF
Name: {$_POST['user-name']}
Email: {$_POST['user-email']}
EOF;
?>
假如没有发送 Content-Type 头部, PHP 的全局 $_POST 变量中就不会包含数据,而需要通过
$HTTP_RAW_POST_DATA 来获取。
注意 POST 请求相比 GET 请求要占用更多资源。从性能方面说,发送相同数量的数据,
GET 请求比 POST 请求要快两倍。
5 XMLHttpRequest Level 2
XHR 对象作为事实标准的迅速流行,也促使 W3C 为规范这一行为而制定了正式标准。
XMLHttpRequest Level 1 只是把已经存在的 XHR 对象的实现细节明确了一下。 XMLHttpRequest Level 2 又进一步发展了 XHR 对象。并非所有浏览器都实现了 XMLHttpRequest Level 2 的所有部分,但所有浏 览器都实现了其中部分功能。
1. FormData 类型
现代 Web 应用程序中经常需要对表单数据进行序列化,因此 XMLHttpRequest Level 2 新增了
FormData 类型。 FormData 类型便于表单序列化,也便于创建与表单类似格式的数据然后通过 XHR 发送。下面的代码创建了一个 FormData 对象,并填充了一些数据:
let data = new FormData();
data.append("name", "Nicholas");

append()方法接收两个参数:键和值,相当于表单字段名称和该字段的值。可以像这样添加任意

多个键 / 值对数据。此外,通过直接给 FormData 构造函数传入一个表单元素,也可以将表单中的数据 作为键/ 值对填充进去:
let data = new FormData(document.forms[0]);
//有了 FormData 实例,可以像下面这样直接传给 XHR 对象的 send()方法:
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("Request was unsuccessful: " + xhr.status);}}
};
xhr.open("post", "postexample.php", true);
let form = document.getElementById("user-info");
xhr.send(new FormData(form));

使用 FormData 的另一个方便之处是不再需要给 XHR 对象显式设置任何请求头部了。XHR 对象能

够识别作为 FormData 实例传入的数据类型并自动配置相应的头部。
2. 超时
IE8 XHR 对象增加了一个 timeout 属性,用于表示发送请求后等待多少毫秒,如果响应不成功
就中断请求。之后所有浏览器都在自己的 XHR 实现中增加了这个属性。在给 timeout 属性设置了一个 时间且在该时间过后没有收到响应时,XHR 对象就会触发 timeout 事件,调用 ontimeout 事件处理 程序。这个特性后来也被添加到了 XMLHttpRequest Level 2 规范。下面看一个例子:
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {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) {// 假设由 ontimeout 处理}}
};
xhr.open("get", "timeout.php", true);
xhr.timeout = 1000; // 设置 1 秒超时
xhr.ontimeout = function() {alert("Request did not return in a second.");
};
xhr.send(null);

这个例子演示了使用 timeout 设置超时。给 timeout 设置 1000 毫秒意味着,如果请求没有在 1

秒钟内返回则会中断。此时则会触发 ontimeout 事件处理程序, readyState 仍然会变成 4 ,因此也
会调用 onreadystatechange 事件处理程序。不过,如果在超时之后访问 status 属性则会发生错误。
为做好防护,可以把检查 status 属性的代码封装在 try / catch 语句中。
3. overrideMimeType()方法
Firefox 首先引入了 overrideMimeType() 方法用于重写 XHR 响应的 MIME 类型。这个特性后来
也被添加到了 XMLHttpRequest Level 2 。因为响应返回的 MIME 类型决定了 XHR 对象如何处理响应, 所以如果有办法覆盖服务器返回的类型,那么是有帮助的。
假设服务器实际发送了 XML 数据,但响应头设置的 MIME 类型是 text/plain 。结果就会导致虽
然数据是 XML ,但 responseXML 属性值是 null 。此时调用 overrideMimeType() 可以保证将响应
当成 XML 而不是纯文本来处理:
let xhr = new XMLHttpRequest();
xhr.open("get", "text.php", true);
xhr.overrideMimeType("text/xml");
xhr.send(null);

这个例子强制让 XHR 把响应当成 XML 而不是纯文本来处理。为了正确覆盖响应的 MIME 类型,

必须在调用 send() 之前调用 overrideMimeType()

二、 进度事件

Progress Events W3C 的工作草案,定义了客户端 - 服务器端通信。这些事件最初只针对 XHR ,现 在也推广到了其他类似的 API 。有以下 6 个进度相关的事件。
loadstart :在接收到响应的第一个字节时触发。
progress :在接收响应期间反复触发。
error :在请求出错时触发。
abort :在调用 abort() 终止连接时触发。
load :在成功接收完响应时触发。
loadend :在通信完成时,且在 error abort load 之后触发。
每次请求都会首先触发 loadstart 事件,之后是一个或多个 progress 事件,接着是 error abort
load 中的一个,最后以 loadend 事件结束。
这些事件大部分都很好理解,但其中有两个需要说明一下。
1 load 事件
Firefox 最初在实现 XHR 的时候,曾致力于简化交互模式。最终,增加了一个 load 事件用于替代
readystatechange 事件。 load 事件在响应接收完成后立即触发,这样就不用检查 readyState 属性
了。 onload 事件处理程序会收到一个 event 对象,其 target 属性设置为 XHR 实例,在这个实例上
可以访问所有 XHR 对象属性和方法。不过,并不是所有浏览器都实现了这个事件的 event 对象。考虑 到跨浏览器兼容,还是需要像下面这样使用 XHR 对象变量:
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", "altevents.php", true);
xhr.send(null); 

只要是从服务器收到响应,无论状态码是什么,都会触发 load 事件。这意味着还需要检查 status

属性才能确定数据是否有效。 Firefox Opera Chrome Safari 都支持 load 事件。
2 progress 事件
Mozilla XHR 对象上另一个创新是 progress 事件,在浏览器接收数据期间,这个事件会反复触
发。每次触发时, onprogress 事件处理程序都会收到 event 对象,其 target 属性是 XHR 对象,且
包含 3 个额外属性: lengthComputable position totalSize 。其中, lengthComputable
一个布尔值,表示进度信息是否可用; position 是接收到的字节数; totalSize 是响应的 Content
Length 头部定义的总字节数。有了这些信息,就可以给用户提供进度条了。以下代码演示了如何向用 户展示进度:
let xhr = new XMLHttpRequest();
xhr.onload = function(event) {if ((xhr.status >= 200 && xhr.status < 300) ||xhr.status == 304) {alert(xhr.responseText);} else {alert("Request was unsuccessful: " + xhr.status);}
};
xhr.onprogress = function(event) {let divStatus = document.getElementById("status");if (event.lengthComputable) {divStatus.innerHTML = "Received " + event.position + " of " + event.totalSize                 +"bytes";}
};
xhr.open("get", "altevents.php", true);
xhr.send(null);

为了保证正确执行,必须在调用 open()之前添加 onprogress 事件处理程序。在前面的例子中,

每次触发 progress 事件都会更新 HTML 元素中的信息。假设响应有 Content-Length 头部,就可以 利用这些信息计算出已经收到响应的百分比。

三、 跨源资源共享

通过 XHR 进行 Ajax 通信的一个主要限制是跨源安全策略。默认情况下, XHR 只能访问与发起请
求的页面在同一个域内的资源。这个安全限制可以防止某些恶意行为。不过,浏览器也需要支持合法跨 源访问的能力。
跨源资源共享( CORS Cross-Origin Resource Sharing )定义了浏览器与服务器如何实现跨源通信。
CORS 背后的基本思路就是使用自定义的 HTTP 头部允许浏览器和服务器相互了解,以确实请求或响应 应该成功还是失败。
对于简单的请求,比如 GET POST 请求,没有自定义头部,而且请求体是 text/plain 类型,
这样的请求在发送时会有一个额外的头部叫 Origin Origin 头部包含发送请求的页面的源(协议、
域名和端口),以便服务器确定是否为其提供响应。下面是 Origin 头部的一个示例:
Origin: http://www.nczonline.net

如果服务器决定响应请求,那么应该发送 Access-Control-Allow-Origin 头部,包含相同的源;

或者如果资源是公开的,那么就包含 "*" 。比如:
Access-Control-Allow-Origin: http://www.nczonline.net

如果没有这个头部,或者有但源不匹配,则表明不会响应浏览器请求。否则,服务器就会处理这个

请求。注意,无论请求还是响应都不会包含 cookie 信息。
现代浏览器通过 XMLHttpRequest 对象原生支持 CORS 。在尝试访问不同源的资源时,这个行为
会被自动触发。要向不同域的源发送请求,可以使用标准 XHR 对象并给 open() 方法传入一个绝对 URL , 比如:
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("Request was unsuccessful: " + xhr.status);}}
};
xhr.open("get", "http://www.somewhere-else.com/page/", true);
xhr.send(null);

跨域 XHR 对象允许访问 status statusText 属性,也允许同步请求。出于安全考虑,跨域 XHR

对象也施加了一些额外限制。
不能使用 setRequestHeader() 设置自定义头部。
不能发送和接收 cookie
getAllResponseHeaders() 方法始终返回空字符串。
因为无论同域还是跨域请求都使用同一个接口,所以最好在访问本地资源时使用相对 URL ,在访问 远程资源时使用绝对 URL 。这样可以更明确地区分使用场景,同时避免出现访问本地资源时出现头部或 cookie 信息访问受限的问题。
1 预检请求
CORS 通过一种叫 预检请求 preflighted request )的服务器验证机制,允许使用自定义头部、除 GET 和 POST 之外的方法,以及不同请求体内容类型。在要发送涉及上述某种高级选项的请求时,会先向服 务器发送一个“预检”请求。这个请求使用 OPTIONS 方法发送并包含以下头部。
Origin :与简单请求相同。
Access-Control-Request-Method :请求希望使用的方法。
Access-Control-Request-Headers :(可选)要使用的逗号分隔的自定义头部列表。
下面是一个假设的 POST 请求,包含自定义的 NCZ 头部:
Origin: http://www.nczonline.net
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://www.nczonline.net
Access-Control-Allow-Methods: POST, GET
Access-Control-Allow-Headers: NCZ
Access-Control-Max-Age: 1728000

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

请求时才会多发送一次额外的 HTTP 请求。
2 凭据请求
默认情况下,跨源请求不提供凭据( cookie HTTP 认证和客户端 SSL 证书)。可以通过将
withCredentials 属性设置为 true 来表明请求会发送凭据。如果服务器允许带凭据的请求,那么可
以在响应中包含如下 HTTP 头部:
Access-Control-Allow-Credentials: true

如果发送了凭据请求而服务器返回的响应中没有这个头部,则浏览器不会把响应交给 JavaScript

responseText 是空字符串, status 0 onerror() 被调用)。注意,服务器也可以在预检请求的
响应中发送这个 HTTP 头部,以表明这个源允许发送凭据请求。

四、替代性跨源技术

CORS 出现之前,实现跨源 Ajax 通信是有点麻烦的。开发者需要依赖能够执行跨源请求的 DOM 特 性,在不使用 XHR 对象情况下发送某种类型的请求。虽然 CORS 目前已经得到广泛支持,但这些技术 仍然没有过时,因为它们不需要修改服务器。
1 图片探测
图片探测是利用 <img> 标签实现跨域通信的最早的一种技术。任何页面都可以跨域加载图片而不
必担心限制,因此这也是在线广告跟踪的主要方式。可以动态创建图片,然后通过它们的 onload 和 onerror 事件处理程序得知何时收到响应。
这种动态创建图片的技术经常用于 图片探测 image pings )。图片探测是与服务器之间简单、跨域、 单向的通信。数据通过查询字符串发送,响应可以随意设置,不过一般是位图图片或值为 204 的状态码。
浏览器通过图片探测拿不到任何数据,但可以通过监听 onload onerror 事件知道什么时候能接收 到响应。下面看一个例子:
let img = new Image();
img.onload = img.onerror = function() {alert("Done!");
};
img.src = "http://www.example.com/test?name=Nicholas"; 

这个例子创建了一个新的 Image 实例,然后为它的 onload onerror 事件处理程序添加了同一

个函数。这样可以确保请求完成时无论什么响应都会收到通知。设置完 src 属性之后请求就开始了,这 个例子向服务器发送了一个 name 值。
图片探测频繁用于跟踪用户在页面上的点击操作或动态显示广告。当然,图片探测的缺点是只能发
GET 请求和无法获取服务器响应的内容。这也是只能利用图片探测实现浏览器与服务器单向通信的 原因。
2 JSONP
JSONP 是“ JSON with padding ”的简写,是在 Web 服务上流行的一种 JSON 变体。 JSONP 看起来 跟 JSON 一样,只是会被包在一个函数调用里,比如:
callback({ "name": "Nicholas" });

JSONP 格式包含两个部分:回调和数据。回调是在页面接收到响应之后应该调用的函数,通常回调 函数的名称是通过请求来动态指定的。而数据就是作为参数传给回调函数的 JSON 数据。下面是一个典 型的 JSONP 请求:

http://freegeoip.net/json/?callback=handleResponse

这个 JSONP 请求的 URL 是一个地理位置服务。JSONP 服务通常支持以查询字符串形式指定回调函 数的名称。比如这个例子就把回调函数的名字指定为 handleResponse()

JSONP 调用是通过动态创建 <script> 元素并为 src 属性指定跨域 URL 实现的。此时的 <script>
<img> 元素类似,能够不受限制地从其他域加载资源。因为 JSONP 是有效的 JavaScript ,所以 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://freegeoip.net/json/?callback=handleResponse";
document.body.insertBefore(script, document.body.firstChild);

这个例子会显示从地理位置服务获取的 IP 地址及位置信息。

JSONP 由于其简单易用,在开发者中非常流行。相比于图片探测,使用 JSONP 可以直接访问响应, 实现浏览器与服务器的双向通信。不过 JSONP 也有一些缺点。
首先, JSONP 是从不同的域拉取可执行代码。如果这个域并不可信,则可能在响应中加入恶意内容。 此时除了完全删除 JSONP 没有其他办法。在使用不受控的 Web 服务时,一定要保证是可以信任的。
第二个缺点是不好确定 JSONP 请求是否失败。虽然 HTML5 规定了 <script> 元素的 onerror 事件
处理程序,但还没有被任何浏览器实现。为此,开发者经常使用计时器来决定是否放弃等待响应。这种 方式并不准确,毕竟不同用户的网络连接速度和带宽是不一样的。

小结

Ajax 是无须刷新当前页面即可从服务器获取数据的一个方法,具有如下特点。
Ajax 迅速流行的中心对象是 XMLHttpRequest XHR )。
这个对象最早由微软发明,并在 IE5 中作为通过 JavaScript 从服务器获取 XML 数据的一种手段。
之后, Firefox Safari Chrome Opera 都复刻了相同的实现。 W3C 随后将 XHR 行为写入 Web 标准。
虽然不同浏览器的实现有些差异,但 XHR 对象的基本使用在所有浏览器中相对是规范的,因此
可以放心地在 Web 应用程序中使用。
XHR 的一个主要限制是同源策略,即通信只能在相同域名、相同端口和相同协议的前提下完成。
访问超出这些限制之外的资源会导致安全错误,除非使用了正式的跨域方案。这个方案叫作跨源资源共 享(CORS Cross-Origin Resource Sharing ), XHR 对象原生支持 CORS 。图片探测和 JSONP 是另外两种 跨域通信技术,但没有 CORS 可靠。

相关文章:

JavaScript网络请求( XMLHttpRequest 对象,进度事件, 跨源资源共享)

一、 XMLHttpRequest 对象 IE5 是第一个引入 XHR 对象的浏览器。这个对象是通过 ActiveX 对象实现并包含在 MSXML 库中 的。为此&#xff0c; XHR 对象的 3 个版本在浏览器中分别被暴露为 MSXML2.XMLHttp 、 MSXML2.XMLHttp.3.0 和 MXSML2.XMLHttp.6.0 。 所有现代…...

Android Studio、JDK、AGP、Gradle、kotlin-gradle-plugin 兼容性问题

文章目录 问题&#xff1a;解决办法&#xff1a;gradle与 java的版本兼容AGP与Gradle的版本兼容kotlin 与 jvm 的版本兼容KGP、Gradle、AGP兼容关系kotlin 与 java 的编译版本配置 问题&#xff1a; 你从githb上clone了一个项目&#xff0c;本地跑的时候&#xff0c;各种报错。…...

滑动窗口(定长窗口)

题目一&#xff1a;找到字符串中所有字母异位词 438. 找到字符串中所有字母异位词 - 力扣&#xff08;LeetCode&#xff09; ​ ​ 分析 异位次&#xff0c;就是通过排序之后可以跟哪个单词一样&#xff0c;例如 hello 跟 lleho 这是定长窗口&#xff0c; 窗口维护在p的…...

Vue Web开发(九)

1. 用户管理 1.1. user页面下的diolog表单 本节课完成user页面下的diolog表单&#xff0c;表单里的元素都是动态渲染&#xff0c;以一个CommonForm组件的形式放入user页面。 运用Element ui Form表单。 1.1.1. CommonForm组件 在src/components下新建CommonForm.vue组件&am…...

go语言zero框架下的日志记录的sdk实战案例

在 Go 语言中&#xff0c;构建一个日志 SDK 是常见的开发任务&#xff0c;尤其是当你希望将日志记录集中管理时。一个好的日志 SDK 可以帮助你规范化日志记录的方式&#xff0c;并将日志存储到不同的地方&#xff08;例如&#xff1a;控制台、文件、数据库、远程日志服务等&…...

【Unity基础】Unity中拖拽3D物体的过程分析和实现方法

我们先来分析一下Unity中拖拽物体的过程&#xff1a; 第一步&#xff1a;先检测拖拽的输入方式&#xff0c;可以鼠标&#xff0c;触摸&#xff0c;可以用InputManager输入&#xff0c;也可以 使用InputSystem输入 第二步&#xff1a;获取触碰点的位置&#xff0c;并计算offse…...

深入了解Spring

目录 Spring基础 什么是Spring框架&#xff1f; Spring 包含的模块有哪些&#xff1f; Core Container AOP Data Access/Integration Spring Web Messaging Spring Test Spring,Spring MVC,Spring Boot 之间什么关系? Spring基础 什么是Spring框架&#xff1f; Sp…...

vue3修改elementui-plus的默认样式的几种方法

#创作灵感 今天写vue的前端项目&#xff0c;因为需要去修改elementui-plus中drawer的默认样式&#xff0c;所以刚好将修改步骤记录下来。 一共提供了三种方法&#xff0c;但亲测第二种最好用。 使用第二种是可以无视自己的代码中是否定义了该盒子&#xff0c;因为有时候盒子的…...

免费开源!推荐一款网页版数据库管理工具!

免费开源&#xff01;推荐一款网页版数据库管理工具&#xff01; DBGate 是一个开源的数据库管理工具&#xff0c;DBGate 的最大特点是可以 Web 访问&#xff01;&#xff0c;轻松实现一台机器部署&#xff0c;所有人使用&#xff01; 无论是 MySQL、PostgreSQL、SQLite 还是…...

Ubuntu22.04切换gcc版本教程

在编译安装程序的时候,由于gcc版本过高,导致编译无法通过,需要降低gcc版本。 一、安装gcc版本 根据自己的需求安装gcc版本。 sudo apt update sudo apt install gcc-10 g++-10二、切换gcc版本 sudo update-alternatives --install /usr/bin/gcc gcc...

OpenCV相机标定与3D重建(24)计算两个二维点集之间的最佳仿射变换矩阵(2x3)函数estimateAffine2D()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 计算两个二维点集之间的最优仿射变换&#xff0c;它计算 [ x y ] [ a 11 a 12 a 21 a 22 ] [ X Y ] [ b 1 b 2 ] \begin{bmatrix} x\\ y\\ \en…...

并发修改导致MVCC脏写问题

并发修改导致MVCC脏写问题 一、概要 1.1 业务场景 数据库表结构设计&#xff1a; 一个主档数据&#xff0c;通过一个字段&#xff0c;逗号分隔的方式去关联其他明细信息的id。 如主档数据A&#xff0c;有3条明细数据与A关联&#xff0c;其id分别是1,2,3&#xff0c;那么其存…...

webSokect安卓和web适配的Bug 适用实时语音场景

❌BUG&#xff1a; Failed to construct WebSocket: The URLs scheme must be either ws or wss. https is not allowed.WebSocket 协议在创建连接时&#xff0c;规定其 URL 的协议头&#xff08;scheme&#xff09;必须是 ws&#xff08;用于非加密的普通 WebSocket 连接&…...

Linux系列之如何更换Centos yum源?

环境 Centos7Xshell7 问题描述 最近安装了一个虚拟机&#xff0c;准备用来学习&#xff0c;不过使用yum命令安装一些软件&#xff0c;不过使用这个命令时候&#xff0c;提示 Cannot find a valid baseurl for repo: base/7/x86_64&#xff0c;Could not retrieve mirrorlis…...

微知-C语言如何指定弱符号?(#define WEAK __attribute__((weak)); WEAK int foo(void))

背景 在C语言中&#xff0c;弱符号是一种特殊的符号&#xff0c;它可以被重定义&#xff0c;但在链接时&#xff0c;只会使用被重定义的符号。 在某些情况下&#xff0c;我们需要在多个文件中定义相同的函数&#xff0c;但只使用其中一个函数。本文将介绍如何在C语言中实现弱符…...

三极管功能

1 三极管的结构 2 三极管开关电路设计注意事项 1 三极管进入饱和状态 电机&#xff1a;500毫安 2 判断三级什么状态&#xff1a;电压法 3 判断三级什么状态&#xff1a;电流法 4 求IB的电阻 5 当三极管用作开关时&#xff0c;通常N型三极管控制负载的gnd端&#xff0c;P型…...

Python的3D可视化库【vedo】2-5 (plotter模块) 坐标转换、场景导出、添加控件

文章目录 4 Plotter类的方法4.7 屏幕和场景中的坐标点转换4.7.1 屏幕坐标转为世界坐标4.7.2 世界坐标转为屏幕坐标4.7.3 屏幕坐标取颜色 4.8 导出4.8.1 导出2D图片4.8.2 导出3D文件 4.9 添加控件4.9.1 添加内嵌子窗口4.9.2 添加选择区4.9.3 添加比例尺4.9.4 为对象添加弹出提示…...

定时/延时任务-万字解析Spring定时任务原理

文章目录 1. 概要2. EnableScheduling 注解3. Scheduled 注解4. postProcessAfterInitialization 解析4.1 createRunnable 5. 任务 Task 和子类6. ScheduledTaskRegistrar6.1 添加任务的逻辑6.2 调度器初始化6.3 调用时机 7. taskScheduler 类型7.1 ConcurrentTaskScheduler7.2…...

Android上传到Minio本地存储

Minio简介 MinIO是一个对象存储解决方案&#xff0c;它提供了与Amazon Web Services S3兼容的API&#xff0c;并支持所有核心S3功能。 MinIO有能力在任何地方部署 - 公有云或私有云&#xff0c;裸金属基础设施&#xff0c;编排环境&#xff0c;以及边缘基础设施。author: https…...

海量数据-Vastbase G100数据库安装

海量数据-Vastbase G100数据库安装 文章目录 海量数据-Vastbase G100数据库安装前期准备防火墙配置方案一&#xff1a;关闭防火墙方案二&#xff1a;开放数据库端口 SELINUX配置时间同步IPC参数配置 单机安装设置主机名创建数据库安装用户和目录(可选)修改资源限制 字符安装&am…...

Python | 对于DataFrame中所有行数据比较的几种方法

在数据分析中&#xff0c;比较DataFrame数据框中的行是一项基本操作&#xff0c;可应用于多种场景&#xff0c;包括&#xff1a; 查找重复项&#xff1a;标识所有相似或包含相同数据的行。相似性检查&#xff1a;确定某些选定因子的不相似行的相似程度。成对分析&#xff1a;非…...

React:闭包陷阱产生和解决

在 React 中&#xff0c;闭包陷阱是一个常见的问题&#xff0c;尤其是在处理异步操作、事件处理器、或是定时器时。理解闭包的工作原理以及它在 React 中如何与状态和渲染交互&#xff0c;可以帮助你避免陷入一些常见的错误。 一、闭包陷阱的产生 1、什么是闭包陷阱&#xff1…...

图的遍历|深度优先搜索|广度优先搜索(C)

图的基本操作 图的基本操作是独立于图的存储结构的。而对于不同的存储方式&#xff0c;操作算法的具体实现会有着不同的性能。在设计具体算法的实现时&#xff0c;应考虑采用何种存储方式的算法效率会更高。 图的基本操作主要包括&#xff08;仅抽象地考虑&#xff0c;所以忽略…...

Pytest-Bdd vs Behave:选择最适合的 Python BDD 框架

Pytest-Bdd vs Behave&#xff1a;选择最适合的 Python BDD 框架 Pytest BDD vs Behave&#xff1a;选择最适合的 Python BDD 框架BDD 介绍Python BDD 框架列表Python BehavePytest BDDPytest BDD vs Behave&#xff1a;关键区别Pytest BDD vs Behave&#xff1a;最佳应用场景结…...

python文字转语音

一、安装对应的包 pip install pyttsx3 二、代码实现 import pyttsx3# 初始化tts引擎 engine pyttsx3.init()# 设置要转换的文本 text "你好"# 设置语速&#xff0c;范围从-1到1&#xff0c;1是正常速度&#xff0c;-1是最慢速度 rate engine.getProperty(rate…...

【安全研究】某黑产网站后台滲透与逆向分析

文章目录 x01. 前言x02. 分析 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python与爬虫领域研究与…...

XSLT 编辑 XML

XSLT 编辑 XML 介绍 XSLT&#xff08;可扩展样式表语言转换&#xff09;是一种用于转换XML文档的语言。它允许开发人员将XML数据转换为其他格式&#xff0c;如HTML、PDF或纯文本。XSLT通过使用XPath查询来定位XML文档中的元素&#xff0c;并对这些元素应用转换规则。在本教程…...

计算机网络信息系统安全问题及解决策略

目 录 摘 要 前 言 一、计算机网络信息系统研究现状及安全技术 &#xff08;一&#xff09;计算机网络信息系统研究现状 &#xff08;二&#xff09;计算机网络信息系统全技术概述 二、计算机网络信息系统安全问题 &#xff08;一&#xff09;环境危害引发的安全问…...

112.【C语言】数据结构之排序(详解插入排序)

目录 1.排序定义 2.插入排序 "插入"的含义 代码 函数框架 函数设计思路 以升序为例,分析插入的三种可能 单趟排序代码 优化后 将单趟排序代码嵌入到循环中 错误代码 两种改法 运行结果 时间复杂度 1.排序定义 使一串记录,按照其中的某个或某些关键字的…...

洞察:OpenAI 全球宕机,企业应该如何应对 LLM 的不稳定性?

北京时间12月12日上午&#xff0c;OpenAI证实其聊天机器人ChatGPT正经历全球范围的宕机&#xff0c;ChatGPT、Sora及API受到影响。 OpenAI 更新事故报告称&#xff0c;已查明宕机原因&#xff0c;正努力以最快速度恢复正常服务&#xff0c;并对宕机表示歉意。 此次 OpenAI 故障…...

Git Bash Here 中文显示乱码的处理方法

在使用"open Git Bash Here"时&#xff0c;遇到中文显示乱码问题。 原因&#xff1a;通常是由于编码设置不正确导致的。 open Git Bash Here —>鼠标右击空白处&#xff0c;点击「选项」|或「Options」 在「文本」或 「Text」选项卡中&#xff0c;找到"local…...

【python因果库实战6】LaLonde 数据集

目录 LaLonde 数据集 数据 收入指示变量 教育年限的因子化 变量选择 模型 估计因果效应 未经调整的估计 LaLonde 数据集 经济学家长期以来假设培训项目可以改善参与者的劳动力市场前景。为了测试&#xff08;或证明&#xff09;这一点&#xff0c;国家支持性工作示范项…...

和Ente交流

今日去清华大学深圳国际研究生院能源环境大楼与研三师弟交流。 交流内容&#xff1a; 今年年初3月份的时候去实习&#xff0c;刚刚开始字节远程实习&#xff08;海投&#xff09;&#xff0c;然后远程了一个月&#xff0c;让去线下&#xff0c;然后就去线下待了一个月&#x…...

imx6ull qt多页面控制系统(正点原子imx系列驱动开发)

开题答辩完了也考完了四六级&#xff0c;赶紧来更新一下一个月前留下的坑吧 QAQ首先&#xff0c;因为毕业设计需要用到这些知识所以就从网络上找了一个智能车机系统&#xff0c;借鉴了一下大佬的项目思路&#xff0c;缝缝补补一个月终于完成了这一内容。 在这里先感谢从两位大佬…...

[C++]运算符重载

一、 什么是运算符重载&#xff1f; 运算符重载是 C 中的一种功能&#xff0c;它允许用户定义的类或数据类型重新定义或扩展运算符的行为&#xff0c;使运算符能够作用于用户定义的对象。 二、 通俗解释 在 C 中&#xff0c;运算符&#xff08;如 , -, *, 等&#xff09;默认…...

C++基础

01引用的本质 int a 10;/**引用本质是指针常量&#xff0c;指针指向不可更改,因此引用一旦初始化就不可以更改*自动转换为int* const ref&a;*/int &ref a;/*内部发现是引用&#xff0c;自动转换为*ref20;*/ref20; 02函数高级 2.1默认参数 #include <iostream&g…...

鸿蒙项目云捐助第七讲鸿蒙App应用的首页推荐模块布局的实现

鸿蒙项目云捐助第七讲鸿蒙App应用的首页推荐模块布局的实现 最后设置首页的推荐模块&#xff0c;参考模板如下图所示。 一、首页热门推荐模块的实现 对于热门推荐模块&#xff0c;先有上面的小标题栏&#xff0c;这里的标题栏也有一个小图标&#xff0c;首先从“百度图库”中…...

【网络安全设备系列】1、防火墙

0x00 前言 最近由于工作原因&#xff0c;需要详细如今各类网络安全设备&#xff0c;所以开了此系列文章&#xff0c;希望通过对每个网络安全设备进行整理总结&#xff0c;来详细了解各类网络安全设备作用功能以及实现原理、部署配置方法等。 0x01 定义&#xff1a;防火墙指的…...

C# 备份文件夹

C# 备份目标文件夹 方法1&#xff1a;通过 递归 或者 迭代 结合 C# 方法 参数说明&#xff1a; sourceFolder&#xff1a;源文件夹路径destinationFolder&#xff1a;目标路径excludeNames&#xff1a;源文件夹中不需备份的文件或文件夹路径哈希表errorLog&#xff1a;输出错…...

【sizeof】各种数据类型所占空间大小

各种数据类型所占空间大小 文章目录 前言 一、sizeof是什么&#xff1f; 二、使用步骤 1.整型 2.字符型 总结 前言 ‌sizeof在C语言中是一个运算符&#xff0c;用于获取数据类型或变量在内存中所占的字节数。‌它可以在编译时计算数据类型或变量的内存大小&#xff0c;而…...

水仙花数(流程图,NS流程图)

题目&#xff1a;打印出所有的100-999之间的"水仙花数"&#xff0c;并画出流程图和NS流程图。所谓"水仙花数"是指一个三位数&#xff0c;其各位数字立方和等于该数本身。例如&#xff1a;153是一个"水仙花数"&#xff0c;因为1531的三次方&#…...

wireshark捕获过滤和操作方式详解

大家觉得有用记得关注和点赞&#xff0c;谢谢。 一、Wireshark介绍 Wireshark&#xff08;前身是Ethereal&#xff09;是一个网络封包分析软件&#xff0c;目前是全球使用最广泛的开源抓包软件&#xff0c;别名小鲨鱼或者鲨鱼鳍。 网络封包分析软件的功能是截取网卡进出的网络…...

ChatGPT Search开放:实时多模态搜索新体验

点击访问 chatTools 免费体验GPT最新模型&#xff0c;包括o1推理模型、GPT4o、Claude、Gemini等模型&#xff01; ChatGPT Search&#xff1a;功能亮点解析 本次更新的ChatGPT Search带来了多项令人瞩目的功能&#xff0c;使其在搜索引擎市场中更具竞争力。 1. 高级语音模式&…...

【docker】docker swarm常用命令以及电商平台构建案例

1. 初始化Swarm集群 用于初始化一个Swarm集群&#xff0c;并将当前节点设置为Manager节点。 docker swarm init 用法&#xff1a; docker swarm init --advertise-addr <Manager节点IP>示例&#xff1a; docker swarm init --advertise-addr 192.168.1.100这会将当前节…...

3D计算机视觉概述

3D计算机视觉 3D计算机视觉概述 像机标定 文章目录 3D计算机视觉前言一、人类视觉二、计算机视觉2.1 计算机视觉的研究目的2.2 计算机视觉的研究任务2.3 计算机视觉的研究方法2.4 视觉计算理论2.5 马尔框架中计算机视觉表达的四个层次2.5.1 图像&#xff08;像素表达&#xff…...

23. 合并 K 个升序链表(java)

题目描述&#xff1a; 给你一个链表数组&#xff0c;每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中&#xff0c;返回合并后的链表。 示例 1&#xff1a; 输入&#xff1a;lists [[1,4,5],[1,3,4],[2,6]] 输出&#xff1a;[1,1,2,3,4,4,5,6] 解释&#xff…...

与乐鑫相约 CES 2025|创新技术引领物联网与嵌入式未来

2025 国际消费电子产品展览会 (International Consumer Electronics Show, CES) 将于 2025 年 1 月 7 至 10 日在美国拉斯维加斯盛大开幕。作为全球规模最大、水准最高&#xff0c;且影响力最广的消费电子类科技盛会&#xff0c;CES 每年都吸引着全球行业领袖、开发者和技术爱好…...

MIPS指令集(一)基本操作

目录 计算机硬件的操作数 存储器操作数 常数或立即数操作数 有符号数和无符号数 指令的格式 逻辑操作 决策指令 循环 计算机硬件的操作数 先从一条C语句入手 a b c; 将其翻译为MIPS add a, b, c 其中a&#xff0c;b&#xff0c;c就是这条指令的操作数。表示将b与c…...

半导体数据分析(二):徒手玩转STDF格式文件 -- 码农切入半导体系列

一、概述 在上一篇文章中&#xff0c;我们一起学习了STDF格式的文件&#xff0c;知道了这是半导体测试数据的标准格式文件。也解释了为什么码农掌握了STDF文件之后&#xff0c;好比掌握了切入半导体行业的金钥匙。 从今天开始&#xff0c;我们一起来一步步地学习如何解构、熟…...

在window环境下安装openssl生成钥私、证书和签名,nodejs利用express实现ssl的https访问和测试

在生成我们自己的 SSL 证书之前&#xff0c;让我们创建一个简单的 Express应用程序。 要创建一个新的 Express 项目&#xff0c;让我们创建一个名为node-ssl -server 的目录&#xff0c;用终端cmd中进入node-ssl-server目录。 cd node-ssl-server 然后初始化一个新的 npm 项目…...