Почему мой пост ajax усечен?

Я только что обновил мою службу mvc, чтобы включить большую ошибку и протоколирование. Я получил эту точную ошибку несколько раз. Но не может реплицироваться.

Unterminated string. Expected delimiter: ". Path 'Breadcrumbs[18].Params', line 1, position 59740. at Newtonsoft.Json.JsonTextReader.ReadStringIntoBuffer(Char quote) at 

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

Мои запросы ajax обычно выглядят следующим образом:

$.ajax(myURL("SendBreadcrumbs"), {
            type: "POST",
            cache: false,
            data: { telemetry: telemetry.JSONify(), userID: currentUser, isMyVI: isMyVI }
        })

В этих случаях идентификаторы userID и isMyVI (boolean) не существовали и строка телеметрии усекается.

Метод JSONify выглядит следующим образом:

self.JSONify = function () {
    var data = ko.mapping.toJSON(self, TelemetryMapping);
    return data;
}

Это сериализатор нокаута.

Сторона сервера:

public void SendBreadcrumbs(string telemetry, string userID = null, bool isMyVI = true)
{
    MyServiceAudit audit;
    Guid UserID;
    if (Guid.TryParse(userID, out UserID))
        audit = InsertAudit(UserID);
    else
        audit = InsertAudit(null);
    try
    {
        Models.Telemetry data = JsonConvert.DeserializeObject<Models.Telemetry>(telemetry);
        Controllers.Telemetry.UpdateTelemetry(data, isMyVI);
    }
    catch (Exception ex)
    {
         MyServiceAuditDB.FailAudit(audit.ID, ex.Message + " " + ex.StackTrace);
    }
}

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

Единственное отличие, которое у меня есть в моем ajax, - глобальный тайм-аут 3 минут.

Является ли это так же просто, как специальные символы, которые не обрабатываются на стороне клиента или ограничения на стороне сервера, или данные настолько велики, что они становятся фрагментированными, а серверная сторона не знает, ждать следующего фрагмента?

Ответы

Ответ 1

С помощью нескольких комментариев, ответов и, наконец, получивших живые неудачные данные, я смог это разрешить.

Оказалось, что пользователь не только использовал специальные символы, но и некоторые из статических данных, отправленных вниз, включали это (GIGO - не мог поверить в состояние некоторых наших данных).

Клиентское решение:

Функция encodeURIComponent() кодирует компонент URI. Эта функция кодирует специальные символы. Кроме того, он кодирует следующие символы:,/?: @и = + $#

$.ajax(myURL("SendBreadcrumbs"), {
        type: "POST",
        cache: false,
        data: { telemetry: encodeURIComponent(telemetry.JSONify()), userID: currentUser, isMyVI: isMyVI }
    })

Решение на стороне сервера:

Преобразует строку в ее неэкранированное представление. Uri.UnescapeDataString()

public void SendBreadcrumbs(string telemetry, string userID = null, bool isMyVI = true)
{
    MyServiceAudit audit;
    Guid UserID;
    if (Guid.TryParse(userID, out UserID))
        audit = InsertAudit(UserID);
    else
        audit = InsertAudit(null);
    try
    {
        Models.Telemetry data = JsonConvert.DeserializeObject<Models.Telemetry>(Uri.UnescapeDataString(telemetry));
        Controllers.Telemetry.UpdateTelemetry(data, isMyVI);
    }
    catch (Exception ex)
    {
         MyServiceAuditDB.FailAudit(audit.ID, ex.Message + " " + ex.StackTrace);
    }
}

Конфигурация веб-приложения:

У меня был пользователь, получивший 500 ошибок, из-за попытки отправить 20 писем. Я обновил конфигурацию, чтобы включить максимальную длину запроса (в килобайтах, пример - 1 ГБ) и максимальную длину содержимого (в байтах, пример - 1 ГБ). Письма не возникли. Не мог в это поверить!

<appSettings>
    <add key="aspnet:MaxJsonDeserializerMembers" value="150000" />
</appSettings>
<system.web>
    <httpRuntime targetFramework="4.5" maxQueryStringLength="32768" maxUrlLength="65536" maxRequestLength="1048576" />
</system.web>
<system.webServer>
    <security>
        <requestFiltering>
            <requestLimits maxQueryString="32768" maxAllowedContentLength="1073741824"  />
        </requestFiltering>
    </security>
</system.webServer>
<system.web.extensions>
    <scripting>
        <webServices>
            <jsonSerialization maxJsonLength="2147483647" />
        </webServices>
    </scripting>
</system.web.extensions>

Ответ 2

Я предлагаю вам использовать скрипач для получения приблизительного представления о том, какие данные действительно отправлены. Несомненно, проблема де-сериализации и ваш код выглядит полностью. Размер данных не должен быть проблемой здесь, получить некоторое представление данных, и вы должны быть в порядке.

Также проверьте, правильно ли вернули данные в формате JSONify(), не избегая последовательности символов.

Ответ 3

Попробуйте использовать encodeURIComponent для содержимого, введенного пользователем.

Ответ 4

Вы не укажете, какую версию jQuery вы используете, но когда мы предполагаем стабильную версию, мы можем быть уверены, что необходимая кодировка и экранирование в нижней строке $.ajax() безупречны.

Если причиной была длина запроса, мы увидели бы ошибку HTTP на стороне клиента и не вызывали бы серверную функцию ( "максимальная длина запроса превышена" ). Это подтверждается вашим наблюдением, что запросы с более длинными строками могут преуспеть, в то время как более короткие могут выйти из строя.

Я бы предложил два действия:

1) Чтобы уменьшить фрагментацию, расслоение и возможные вводящие в заблуждение диагнозы, не используйте JSONify только телеметрию. Постройте объект из телеметрии, userID и isMyVi, затем JSONify, что в целом (это означает, что, если возможно, включите метаданные в TelemetryMapping)

2) Проверьте значение telemetry.JSONify() во время выполнения на стороне клиента. Если вы обнаружите, что это усечение вызвано этим, сосредоточьтесь на версии и целостности нокаутаJS или замените. Как отмечает @Destrif в своем комментарии, fooobar.com/info/386880/... предполагает, что он не может быть частью задач knockoutJS, чтобы избежать двойных кавычек, обратных косых черт и косых черт -

который может быть таким же простым, как перенос вызова в другой escape():

self.JSONify = function () {
    var data = escape(ko.mapping.toJSON(self, TelemetryMapping));
    return data;
}