发生管道破裂时如何处理 AJAX 响应?
How to handle AJAX response when broken pipe happens?
我有一个 (Spring Boot 1.5) REST 服务器处理文件上传 (multipart/form-data
) 和一个使用 jQuery 文件上传的 JS 客户端。在大多数情况下它工作正常但是当我想上传一个更大的文件(大约 4MB)时,服务器发现它超出了限制并发回一个包含错误消息的响应。
然而,服务器似乎停止读取请求(这当然是正确的),这导致客户端的管道损坏。在这种情况下,不会处理响应。使用 data.jqXHR
的以下内容调用 fail
回调(data.response
未定义):
{"readyState":0,"responseText":"","status":0,"statusText":"error"}
使用curl
调用时,结果为:
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
curl: (55) Send failure: Broken pipe
{"files":[{"size":3863407,"error":"upload_uploadSizeExceeded"}]}
所以返回了一个响应,但它似乎被 JS 客户端忽略了。是否有任何选项可以让 jQuery 处理响应,即使只发送了部分请求?
顺便说一句:更奇怪的是,我在服务器日志中看到请求重复了几次这样的情况,可能是JS中的一种重试机制?
你在 application.properties 中尝试过 adding/tweaking 这个吗?
spring.servlet.multipart.max-file-size=1MB # Max file size. Values can use the suffixes "MB" or "KB" to indicate megabytes or kilobytes, respectively.
spring.servlet.multipart.max-request-size=10MB # Max request size. Values can use the suffixes "MB" or "KB" to indicate megabytes or kilobytes, respectively.
我不知道你所说的如何处理失败是什么意思,因为 fail
回调是在客户端执行的(从技术上讲,这已经是你处理 AJAX 失败响应的地方) .我认为可以公平地说,您应该在 fail
代码块中向您的用户显示错误 modal/popup。
您是否尝试过 jQuery $.post 来处理失败响应?
$.post('server.php', {deviceId: id})
.done( function(msg) { ... } )
.fail( function(xhr, textStatus, errorThrown) {
console.log(xhr.responseText);
});
Is there any option to make jQuery handle the response even if the
request is sent only partially?
简答
不,浏览器使用 XMLHttpRequest 和 Fetch API,根据规范,这被视为 网络错误,并且网络错误有意为空。
CURL 不根据 XMLHttpRequest 规范处理响应。
长答案
模拟服务器
读取请求的ReadableStream,中途取消:
const http = require('http');
const server = http.createServer((req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Content-Type', 'text/plain');
//Close Request ReadableStream Prematurely
//to simulate close pipe on exceed file size
if (req.url === '/data' && req.method === 'POST') {
console.log('simulating...');
let i = 0;
req.on('data', chunk => {
if (i++ === 10)
req.destroy('broken pipe');
});
}
res.end('fooby\n');
}).listen(8080);
客户端测试
方法一:XMLHttpRequests
我仔细检查了每个事件,没有显示 已发送 字节。如果我们确实有一个 Sent bytes 值,它应该在 loaded
中,我们可以知道请求是否中途取消以处理这种没有响应的情况:
let req = new XMLHttpRequest();
req.open('POST', 'http://localhost:8080/data');
req.onloadstart = function (event) {
console.log(event);
};
req.onprogress = function (event) {
console.log(event);
};
req.onabort = function (event) {
console.log(event);
};
req.onerror = function (event) {
console.log(event);
};
req.onload = function (event) {
console.log(event);
};
req.ontimeout = function (event) {
console.log(event);
};
req.onloadend = function (event) {
console.log(event);
};
req.onreadystatechange = function (event) {
console.log(event);
};
req.send(new ArrayBuffer(100000000));
很遗憾,没有。
The read-only XMLHttpRequest.status property returns the numerical
status code of the response of the XMLHttpRequest. status will be an
unsigned short. Before the request is complete, the value of status
will be 0. It is worth noting that browsers report a status of 0 in
case of XMLHttpRequest errors too.
来自XMLHttpRequest specification:
A response whose type is "error" is known as a network error.
A network error is a response whose status is always 0, status message
is always the empty byte sequence, header list is always empty, body
is always null, and trailer is always empty.
方法二:获取API
我希望拦截一个低级别的 ReadableStream 并得到一些东西,但不幸的是,在网络错误时没有调用 resolve 回调:
fetch('http://localhost:8080/data', {
method: 'POST',
body: new ArrayBuffer(100000000),
mode: 'cors'
}).then(resp => {
console.log(resp);
//hopefully we can get readable stream here
//...nope, networkerrors do not trigger resolve
}).catch(error => {
console.log(error);//TypeError: "NetworkError when attempting to fetch resource."
}).then(retry => {
console.log(retry);
});
A fetch() promise rejects with a TypeError when a network error is
encountered, although this usually means a permissions issue or
similar. An accurate check for a successful fetch() would include
checking that the promise resolved, then checking that the Response.ok
property has a value of true. An HTTP status of 404 does not
constitute a network error.
浏览器不会将此视为 HTTP 错误,而是网络错误,因此不会转发与用户代码相关的任何 HTTP。
结论
XHR 和 Fetch 规范声明网络错误作为空响应处理。
我有一个 (Spring Boot 1.5) REST 服务器处理文件上传 (multipart/form-data
) 和一个使用 jQuery 文件上传的 JS 客户端。在大多数情况下它工作正常但是当我想上传一个更大的文件(大约 4MB)时,服务器发现它超出了限制并发回一个包含错误消息的响应。
然而,服务器似乎停止读取请求(这当然是正确的),这导致客户端的管道损坏。在这种情况下,不会处理响应。使用 data.jqXHR
的以下内容调用 fail
回调(data.response
未定义):
{"readyState":0,"responseText":"","status":0,"statusText":"error"}
使用curl
调用时,结果为:
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
curl: (55) Send failure: Broken pipe
{"files":[{"size":3863407,"error":"upload_uploadSizeExceeded"}]}
所以返回了一个响应,但它似乎被 JS 客户端忽略了。是否有任何选项可以让 jQuery 处理响应,即使只发送了部分请求?
顺便说一句:更奇怪的是,我在服务器日志中看到请求重复了几次这样的情况,可能是JS中的一种重试机制?
你在 application.properties 中尝试过 adding/tweaking 这个吗?
spring.servlet.multipart.max-file-size=1MB # Max file size. Values can use the suffixes "MB" or "KB" to indicate megabytes or kilobytes, respectively.
spring.servlet.multipart.max-request-size=10MB # Max request size. Values can use the suffixes "MB" or "KB" to indicate megabytes or kilobytes, respectively.
我不知道你所说的如何处理失败是什么意思,因为 fail
回调是在客户端执行的(从技术上讲,这已经是你处理 AJAX 失败响应的地方) .我认为可以公平地说,您应该在 fail
代码块中向您的用户显示错误 modal/popup。
您是否尝试过 jQuery $.post 来处理失败响应?
$.post('server.php', {deviceId: id})
.done( function(msg) { ... } )
.fail( function(xhr, textStatus, errorThrown) {
console.log(xhr.responseText);
});
Is there any option to make jQuery handle the response even if the request is sent only partially?
简答
不,浏览器使用 XMLHttpRequest 和 Fetch API,根据规范,这被视为 网络错误,并且网络错误有意为空。
CURL 不根据 XMLHttpRequest 规范处理响应。
长答案
模拟服务器
读取请求的ReadableStream,中途取消:
const http = require('http');
const server = http.createServer((req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Content-Type', 'text/plain');
//Close Request ReadableStream Prematurely
//to simulate close pipe on exceed file size
if (req.url === '/data' && req.method === 'POST') {
console.log('simulating...');
let i = 0;
req.on('data', chunk => {
if (i++ === 10)
req.destroy('broken pipe');
});
}
res.end('fooby\n');
}).listen(8080);
客户端测试
方法一:XMLHttpRequests
我仔细检查了每个事件,没有显示 已发送 字节。如果我们确实有一个 Sent bytes 值,它应该在 loaded
中,我们可以知道请求是否中途取消以处理这种没有响应的情况:
let req = new XMLHttpRequest();
req.open('POST', 'http://localhost:8080/data');
req.onloadstart = function (event) {
console.log(event);
};
req.onprogress = function (event) {
console.log(event);
};
req.onabort = function (event) {
console.log(event);
};
req.onerror = function (event) {
console.log(event);
};
req.onload = function (event) {
console.log(event);
};
req.ontimeout = function (event) {
console.log(event);
};
req.onloadend = function (event) {
console.log(event);
};
req.onreadystatechange = function (event) {
console.log(event);
};
req.send(new ArrayBuffer(100000000));
很遗憾,没有。
The read-only XMLHttpRequest.status property returns the numerical status code of the response of the XMLHttpRequest. status will be an unsigned short. Before the request is complete, the value of status will be 0. It is worth noting that browsers report a status of 0 in case of XMLHttpRequest errors too.
来自XMLHttpRequest specification:
A response whose type is "error" is known as a network error.
A network error is a response whose status is always 0, status message is always the empty byte sequence, header list is always empty, body is always null, and trailer is always empty.
方法二:获取API
我希望拦截一个低级别的 ReadableStream 并得到一些东西,但不幸的是,在网络错误时没有调用 resolve 回调:
fetch('http://localhost:8080/data', {
method: 'POST',
body: new ArrayBuffer(100000000),
mode: 'cors'
}).then(resp => {
console.log(resp);
//hopefully we can get readable stream here
//...nope, networkerrors do not trigger resolve
}).catch(error => {
console.log(error);//TypeError: "NetworkError when attempting to fetch resource."
}).then(retry => {
console.log(retry);
});
A fetch() promise rejects with a TypeError when a network error is encountered, although this usually means a permissions issue or similar. An accurate check for a successful fetch() would include checking that the promise resolved, then checking that the Response.ok property has a value of true. An HTTP status of 404 does not constitute a network error.
浏览器不会将此视为 HTTP 错误,而是网络错误,因此不会转发与用户代码相关的任何 HTTP。
结论
XHR 和 Fetch 规范声明网络错误作为空响应处理。