Резюме

С идентичным кодом (на основном сайте asp.net) в разных браузерах при переходе на локальную версию страницы IIS XMLHttpRequest обнаруживает какую-то ошибку, вызывает ее событие ошибки и устанавливает статус в ноль, несмотря на успешное выполнение прочитать данные (сеть показывает 200 OK) и сохранить их в responseText.

На производственном / промежуточном веб-сервере код каждый раз работает нормально.

Файлы с открытым текстом, похоже, работают нормально и локально по какой-то причине (строго говоря, они обрабатываются более ранним промежуточным программным обеспечением), но сгенерированный контент (HTML и JSON) с нормальными заголовками вызывает ошибку.

Мой вопрос: почему он выдает эту ошибку сейчас, а не раньше?

Ответ ниже ...

Полный исходный текст

До недавнего времени (2-3 дня назад) вызовы AJAX с использованием jQuery $ .get () отлично работали на моей машине разработки и в промежуточных / производственных средах.

Со вчерашнего дня (когда я заметил) вызовы AJAX теперь не работают на локальной версии сайта и отлично работают в производственной версии.

Я пробовал в Firefox, Edge и Opera - у всех был одинаковый результат. Я сгенерировал максимально урезанный код, который мог и все еще могу выдавать тот же результат ошибки, что почти полностью исключает мой код.

Я проверил примечания к выпуску Firefox и Edge и не нашел ничего подходящего для меня.

Localhost использует самозаверяющий тестовый сертификат, для которого есть исключение в firefox. Промежуточный сайт имеет настоящий сертификат, подписанный третьей стороной.

Полный код страницы:

<html>
  <head>
    <style>@media print {#ghostery-purple-box {display:none !important}}</style>
  </head>
  <body>
    <h1>hi</h1>
    <script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
  </body>
</html>

Затем я запускаю этот javascript в окне консоли каждого браузера:

$.ajax({url:'/Test',error:function(a,b,c){alert(b)}, success:function(a){alert(a);}});

Простите за форматирование, единственная строка предназначена для упрощения копирования / вставки и тестирования.

Вывод в каждой конфигурации, о которой я могу думать, - это запускается часть alert(b) и b равно "ошибке".

Единственное различие в двух конфигурациях - это URL:

https://localhost:44300/Test

Против

https://[productionfqdn]:444/Test

И заголовки возвращаются с веб-серверов, локально это IIS Express, а удаленно - IIS - оба поддерживаются Kestrel как часть сайта ASP.NET Core, хотя я исключил что-либо из этого как возможную часть проблемы ( не буду вдаваться в подробности, сколько ушло на возврат к предыдущей версии кода, прежде чем я понял, что это не имеет ничего общего с моей базой кода).

IIS Express: (разработчик)

HTTP/2.0 200 OK
content-type: text/html; charset=utf-8
server: Kestrel
x-sourcefiles: [redacted]
x-powered-by: ASP.NET
date: Tue, 26 Mar 2019 23:05:06 GMT
X-Firefox-Spdy: h2

IIS: (постановка)

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: text/html; charset=utf-8
Server: Kestrel
Strict-Transport-Security: max-age=2592000
Date: Tue, 26 Mar 2019 23:04:47 GMT

Результат вызовов на сайт localhost успешен, в devtools в FireFox я вижу, что ожидаемые данные были возвращены, в этом случае те же данные, которые были получены из / Test для запуска теста в первую очередь.

Результат тех же вызовов на промежуточном сайте создает предупреждение с текстом html, как и ожидалось.

Я ожидаю, что эти две среды дадут одинаковый результат, а именно возврат html-текста.

Любые идеи? Я сбит с толку!

Изменить 2019-03-28 Я попытался из контекста той же страницы сделать то же самое с обычным XMLHttpRquest и получил следующее:

var x = new XMLHttpRequest();
undefined
x.open('GET', '/Test')
undefined
x.send(null)
undefined
x
XMLHttpRequest { onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, responseURL: "", status: 0, statusText: "", responseType: "",
response: "<h1>hi</h1>\r\n<script type=\"text/javascript\" src=\"https://code.jquery.com/jquery-3.3.1.min.js\"></script>"

Где можно четко увидеть ожидаемый ответ от веб-сервера в поле ответа объекта. (разрыв строки для ясности)

То же самое, прямо как изображение из инструментов разработчика браузера:

Firefox javascript console

Редактировать 2 28 марта 2019 г.

(кроме того, вкладка сети показывает, что данные возвращаются нормально)

Итак, поскольку последнее изменение, казалось, указывало на то, что jQuery делает что-то глупое, я решил запустить отладчик и посмотреть, что происходит в jQuery. В текущей версии (3.3.1) я сломал строку 9300, которая находится после того, как запрос был завершен и после того, как jQuery его оценил. Я обнаружил, что функция done имеет значения, которые различаются между моими локальными (dev) и удаленными (stg) веб-серверами:

Местный

Local web server values

Удаленный

Remote web server values

Основная проблема заключается в том, что локальная версия jQuery запускает done() с status = 0, тогда как удаленная версия запускает done() с status = 200, что имеет смысл.

В обеих версиях поле состояния локальной переменной response - «успех». В строке 9247 jQuery оценивает переменную status, переданную в done(), и поскольку 0 не передается, для isSuccess устанавливается значение false.

Однако в сети показано:

enter image description here

Я продолжу копать, но буду следить за любопытными отзывами :)

Изменить 3

Итак, done() вызывается из строки 9538:

callback = function( type ) {
                    return function() {
                        if ( callback ) {
                            callback = errorCallback = xhr.onload =
                                xhr.onerror = xhr.onabort = xhr.ontimeout =
                                    xhr.onreadystatechange = null;

                            if ( type === "abort" ) {
                                xhr.abort();
                            } else if ( type === "error" ) {

                                // Support: IE <=9 only
                                // On a manual native abort, IE9 throws
                                // errors on any property access that is not readyState
                                if ( typeof xhr.status !== "number" ) {
                                    complete( 0, "error" );
                                } else {
                                    complete(

                                        // File: protocol always yields status 0; see #8605, #14207
                                        xhr.status,
                                        xhr.statusText
                                    );
                                }

Строка перед if ( typeof xhr.status !== "number" ) { проверяет xhr.status, который является 0, а не фактическим кодом состояния, несмотря на то, что определенно возвращается 200. Он объясняет, откуда приходит сообщение "ошибка"! В done() он проверяет этот код статуса:

isSuccess = status >= 200 && status < 300 || status === 304;

Так откуда это 0 ?!

jQuery handling XHR completion

Изменить 4

В конечном итоге проблема, похоже, в том, что XMLHttpRequest дает мне статус 0 с localhost, и этого не произошло до нескольких дней назад. Супер расстраивает:

var x = new XMLHttpRequest(); x.open('GET', 'https://localhost:44300/Test'); x.send(null);
undefined
x
XMLHttpRequest { onreadystatechange: null, readyState: 1, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, responseURL: "", 
status: 0, statusText: "", responseType: "", response: "" }

var x = new XMLHttpRequest(); x.open('GET', 'https://127.0.0.1:44300/Test'); x.send(null);
undefined
x
XMLHttpRequest { onreadystatechange: null, readyState: 1, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, responseURL: "", 
status: 0, statusText: "", responseType: "", response: "" }

Изменить 5

Итак, снова попытка свести его к минимуму требует сделать ошибку. Я немного покопался в jquery и заметил, что XHR вызывает обратный вызов ошибки JQ (назначенный на xhr.onerror), поэтому я сделал то же самое:

var x = new XMLHttpRequest()
XMLHttpRequest { onreadystatechange: null, readyState: 0, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, responseURL: "", status: 0, statusText: "", responseType: "", response: "" }

x.onerror = function () {console.log('error happened')};
function onerror()

x.open('GET','/Test')
undefined
x.send(null)
undefined
error happened
x
XMLHttpRequest { onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, responseURL: "", status: 0, statusText: "", responseType: "", response: "<h1>hi</h1>\r\n<script type=\"text/javascript\" src=\"https://code.jquery.com/jquery-3.3.1.js\"></script>" }

Это привело к тому, что в предупреждении появилось сообщение «Произошла ошибка», поэтому я могу с некоторой уверенностью сказать, что это не jQuery, а что-то странное, вызывающее локальную ошибку.

Изменить 6

Изменил указанное выше на x.onerror = function (e) {console.log('error happened'); console.log(e)};, чтобы выдать ошибку, и получил следующее:

error
  ​bubbles: false
  ​cancelBubble: false
  ​cancelable: false
  ​composed: false
  ​currentTarget: null
  ​defaultPrevented: false
  ​eventPhase: 0
  ​explicitOriginalTarget: XMLHttpRequest { readyState: 4, timeout: 0, withCredentials: false, … }
​  isTrusted: true
  ​lengthComputable: false
  ​loaded: 0
  ​originalTarget: XMLHttpRequest { readyState: 4, timeout: 0, withCredentials: false, … }
  ​returnValue: true
  ​srcElement: XMLHttpRequest { readyState: 4, timeout: 0, withCredentials: false, … }
  ​target: XMLHttpRequest { readyState: 4, timeout: 0, withCredentials: false, … }
  ​timeStamp: 594573
  ​total: 0
  type: "error"

Что не проливает много света.

Изменить 7

Модификация моей тестовой страницы на более полную, чтобы я мог лучше тестировать:

<script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script type="text/javascript">
    var x = new XMLHttpRequest();
    $(document).ready(function () {
        x.onerror = function (e) { console.log('error happened'); console.log(e); $('#err').text(e); };
        x.onreadystatechange = function () {
            if (x.readyState == 4) {
                $('#test').text(x.responseText);
            }
        };
        //x.open('GET', '/test.txt');
        x.open('GET', '/Search/Server?term=[redacted]');
        x.send(null);
    });
</script>
<h1>hi</h1>

<div style="border: 1px solid black">
    <pre id="test"></pre>
</div>

<div style="border: 1px solid red">
    <pre id="err"></pre>
</div>

Когда я прошу /test.txt, все работает нормально, status = 200 без ошибок

Когда я прошу /Test, который представляет собой именно этот код, который я поместил выше, status = 0, возникает ошибка (без полезной информации о том, почему)

Когда я прошу /Search/Server?term=[redacted], который возвращает правильно сформированный json, status = 0, возникает ошибка (без полезной информации)

Заголовки AJAX (рабочие)

HTTP/2.0 304 Not Modified
content-type: text/plain
last-modified: Thu, 28 Mar 2019 06:20:18 GMT
accept-ranges: bytes
etag: "1d4e52e505b3d27"
server: Kestrel
x-sourcefiles: [redacted]
x-powered-by: ASP.NET
date: Thu, 28 Mar 2019 06:47:09 GMT
X-Firefox-Spdy: h2

Заголовки AJAX (не работают)

HTTP/2.0 200 OK
content-type: text/html; charset=utf-8
server: Kestrel
x-sourcefiles: [redacted]
x-powered-by: ASP.NET
date: Thu, 28 Mar 2019 06:46:18 GMT
X-Firefox-Spdy: h2

HTTP/2.0 200 OK
content-type: application/json; charset=utf-8
server: Kestrel
x-sourcefiles: [redacted]
x-powered-by: ASP.NET
date: Thu, 28 Mar 2019 06:37:41 GMT
X-Firefox-Spdy: h2

Может ли это быть charset в заголовке content-type ?!

Возможно, стоит повторить, все эти вызовы с точным кодом отлично работают в производственной / промежуточной среде - одни и те же браузеры, такая же аутентификация (не то чтобы она актуальна после того, как это произошло), те же относительные URL-адреса и т. Д. Единственная разница в том, что prd / stg - это IIS и имеет реальный сертификат, а local - это IIS express и использует самоподписанное с исключением. Это я могу придумать.

1
Kruft 27 Мар 2019 в 02:31

1 ответ

Лучший ответ

Оказывается, проблема заключалась в настраиваемом промежуточном программном обеспечении asp.net, которое проверяет определенное условие, а затем позволяет конвейеру продолжить работу. Я предполагаю, что все, что он делал в процессе разработки, приводило к прерыванию запроса или имело какое-то качество, которое мешало его работе. По сути, код проблемы был;

if (env.IsDevelopment())
{
    // Unimportant code here
    await _next(context);
    await Task.CompletedTask; // <<-- this line made it go wrong
}

Замена проблемной строки на «возврат» устранила проблему.

if (env.IsDevelopment())
{
    // Unimportant code here
    await _next(context);
    return;
}

Есть больше работы над оператором if, который следует пропустить, если IsDevelopment() == true, однако я считаю, что не завершил это промежуточное ПО должным образом в первом блоке здесь.

Я не понимаю, почему это дало качественно иной ответ на XMLHttpRequest. Я не могу найти ничего, что указывало бы на плохой ответ.

Тот факт, что статические файлы поступали нормально через AJAX, указывает на то, что, поскольку это более раннее промежуточное программное обеспечение, вероятно, проблема была в моем настраиваемом промежуточном программном обеспечении, которое появилось позже в конвейере.

0
Kruft 28 Мар 2019 в 23:03