Как обрабатывать ответ AJAX, когда происходит сломанная труба?

у меня есть (Spring Boot 1.5) сервер REST, обрабатывающий загрузку файлов (multipart/form-data) и клиент JS с использованием jQuery fileupload. В большинстве случаев он работает нормально, но когда я хочу загрузить больший файл (около 4 МБ), сервер выясняет, что он выше предела и отправляет ответ, включая сообщение об ошибке.

однако кажется, что сервер перестает читать запрос (что, конечно, правильно), что приводит к сломанной трубе на стороне клиента. В этом случае ответ не обрабатываются. Этот fail обратный вызов вызывается со следующим содержимым data.jqXHR (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 обрабатывать ответ, даже если запрос отправляется только частично?

BTW: еще более странно, я вижу, что запрос повторяется несколько раз в журнале сервера в таком случае, возможно, вид механизма повторной попытки в JS?

3 ответов


есть ли возможность сделать jQuery обрабатывать ответ, даже если запрос отправляется только частично?

Короткий Ответ:

нет, браузеры используют 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);

Клиент Тесты

Метод 1: XMLHttpRequests

я тщательно проверил каждое событие, и нет никаких признаков отправлено байт. Если бы у нас было значение отправленных байтов, которое должно быть в 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));

к сожалению, ничего.

XMLHttpRequest только для чтения.свойство Status возвращает численное код состояния ответа XMLHttpRequest. статус будет беззнаковое короткое. До завершения запроса значение status будет 0. стоит отметить, что браузеры сообщают о состоянии 0 в случай ошибок XMLHttpRequest тоже.

С спецификация XMLHttpRequest:

ответ которого тип "ошибка" известен как сетевая ошибка.

сетевая ошибка-это ответ, состояние которого всегда равно 0, сообщение о состоянии всегда пустая последовательность байтов, список заголовков всегда пуст, тело всегда null, и трейлер всегда пуст.

Метод 2: Fetch 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);
});

обещание fetch() отклоняется с помощью TypeError, когда сетевая ошибка встречались, хотя это обычно означает проблему с разрешениями или подобный. Точная проверка для успешного fetch () будет включать проверка того, что обещание выполнено, затем проверка ответа.ладно свойство имеет значение true. Состояние HTTP 404 не представляют собой сетевая ошибка.

Fetch Документация

Принести Спецификация

браузеры не рассматривают это как ошибку HTTP, а сетевую ошибку, и поэтому не пересылают ничего HTTP, связанного с кодом пользователя.

вывод

спецификация XHR и Fetch утверждает, что сетевые ошибки обрабатываются как пустые ответы.


вы пытались добавить / настроить это в своем приложении.свойства?

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). Я думаю, справедливо сказать, что вы должны просто показать пользователю ошибку modal/popup в fail код блока.


вы пробовали jQuery $.после обработки не ответ?

$.post('server.php', {deviceId: id})
.done( function(msg) { ... } )
.fail( function(xhr, textStatus, errorThrown) {
    console.log(xhr.responseText);
});