Запрос JS Ajax выдает ошибку на localhost, но не на веб-сервере

#javascript #jquery #asp.net #ajax

#javascript #jquery #asp.net #ajax

Вопрос:

Краткие сведения

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

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

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

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

Ответ ниже…

Полное оригинальное сообщение

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

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

Я пробовал в 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 Основной сайт — хотя я исключил любую из возможных причин проблемы (я не буду вдаваться в то, сколько времени ушло на возврат к предыдущей версии кода, прежде чем я понял, что это не имеет ничего общего с моей кодовой базой).

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 Я попытался из контекста той же страницы сделать то же самое с помощью ванильного запроса xmlhttprи получил следующее:

 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>rn<script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>"
  

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

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

Консоль javascript Firefox

Редактировать 2 2019-03-28

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

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

Местные новости

Значения локального веб-сервера

Удаленный

Значения удаленного веб-сервера

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

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

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

введите описание изображения здесь

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

Редактировать 3

So 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 amp;amp; status < 300 || status === 304;

Итак, откуда берется это 0?!

jQuery обрабатывает завершение XHR

Редактировать 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>rn<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: falsecancelBubble: falsecancelable: falsecomposed: falsecurrentTarget: nulldefaultPrevented: falseeventPhase: 0explicitOriginalTarget: XMLHttpRequest { readyState: 4, timeout: 0, withCredentials: false, … }
​  isTrusted: truelengthComputable: falseloaded: 0originalTarget: XMLHttpRequest { readyState: 4, timeout: 0, withCredentials: false, … }
  ​returnValue: truesrcElement: XMLHttpRequest { readyState: 4, timeout: 0, withCredentials: false, … }
  ​target: XMLHttpRequest { readyState: 4, timeout: 0, withCredentials: false, … }
  ​timeStamp: 594573total: 0
  type: "error"
  

Which doesn’t shed a great deal of light.

Edit 7

Modifying my test page to be more comprehensive to allow me to do better testing:

 <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. Может ли это быть связано с CORS? У меня был тот же симптом, когда я возился с переменными, связанными с CORS на моем локальном компьютере, то есть включал или отключал расширение Chrome CORS.

2. Я так не думаю. Источник тот же. Когда я выполняю тот же запрос ajax вручную и указываю полное доменное имя (скажем, промежуточного сайта с моего локального компьютера), я получаю соответствующие ошибки CORS: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://[fqdn]:444/Test. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).[Learn More] В противном случае он не выдал никаких выходных данных с ошибкой ни в одном из 3 браузеров, которые я упомянул.

3. Все эти браузеры включают в себя сетевую консоль, где вы можете видеть запросы и ответы. Есть ли там какие-либо подсказки? Кроме того, в вашем error обработчике вы должны записать результат c

4. В моей правке я добавил значение c . c является пустой строкой. Кроме того, я добавил ванильный XHR, который показывает, что данные возвращаются нормально. Собираюсь добавить еще одно редактирование с дополнительной информацией 🙂

Ответ №1:

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

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

замена проблемной строки на ‘return’ устранила проблему.

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

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

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

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