Backslash '\' is causing error when passing over query string

Viewed 38

I'm working on a solution to export a grid to excel on the server.

We are encoding the filters that go across the server via query string.

Here is that code that fails:

function encodeFilter(filter) {
    const operators = [">", "<", "=", "<>", ">=", "<=", "!"];
    let filterString = JSON.stringify(filter);
    return parseResult = JSON.parse(filterString, (key, val) => {
        if (typeof val === "string" && !operators.includes(val)) {
            if (val === null) {
                return val;
            }

            return encodeURIComponent(val);
        }
        return val;
    });
}

When we have a backslash in the field value as a filter it fails with this error:

Newtonsoft.Json.JsonReaderException: Unterminated string. Expected delimiter: ". Path '[1]', line 1, position 30.
     at Newtonsoft.Json.JsonTextReader.ReadStringIntoBuffer(Char quote)     
     at Newtonsoft.Json.JsonTextReader.ParseValue()
     at Newtonsoft.Json.JsonTextReader.Read()
     at Newtonsoft.Json.JsonReader.ReadForType(JsonContract contract, Boolean hasConverter)
     at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, String id)
     at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
     at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
     at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
     at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
     at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
     at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
     at DevExtreme.AspNet.Data.Helpers.DataSourceLoadOptionsParser.Parse(DataSourceLoadOptionsBase loadOptions, Func`2 valueSource)
     at DevExtreme.AspNet.Mvc.DataSourceLoadOptionsHttpBinder.BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
     at System.Web.Http.ModelBinding.ModelBinderParameterBinding.ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
     at System.Web.Http.Controllers.HttpActionBinding.<ExecuteBindingAsyncCore>d__12.MoveNext()
  --- End of stack trace from previous location where exception was thrown ---
     at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
     at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
     at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__5.MoveNext()
  --- End of stack trace from previous location where exception was thrown ---
     at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
     at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
     at System.Web.Http.Controllers.ExceptionFilterResult.<ExecuteAsync>d__6.MoveNext()

Here is an image with what is going across the server that is failing (kinda hard to see):

enter image description here

If I update the encodeFilter call and do not encode the \, it works perfect.

Why is encoding a backslash causing Newtonsoft.Json.JsonReaderException: Unterminated string. Expected delimiter: ". error?

This code works perfect:

function encodeFilter(filter) {
    const operators = [">", "<", "=", "<>", ">=", "<=", "!"];
    let filterString = JSON.stringify(filter);
    return parseResult = JSON.parse(filterString, (key, val) => {
        if (typeof val === "string" && !operators.includes(val)) {
            if (val === null) {
                return val;
            }

            if (val === "\\") {
                return "\\"
            }

            return encodeURIComponent(val);
        }
        return val;
    });
}

Here is the JavaScript ajax call and Server call:

    const url = BuildSafeURL(`api/Grid/ExportGridData${paramValues.LoadOptionsParams}`, null);
    $.ajax(url, {
        contentType: "application/json",
        data: JSON.stringify(paramValues),
        method: "POST",
        dataType: "json"
    }).done(function (data) {
        if (data == "") {
            hideLoadingPanelExcelServerExport();
            swal({
                type: 'info',
                title: 'No data to export',
                width: "auto"
            });
            return;
        }

        const workbook = new ExcelJS.Workbook();
        workbook.addWorksheet('Smart Grid Data');
        workbook.xlsx.writeBuffer().then(function () {
            hideLoadingPanelExcelServerExport();
            saveAs(new Blob([Base64ToBytes(data)], { type: 'application/octet-stream' }), exportFileName);
        });
    })
    .fail(function (jqXHR, textStatus, errorThrown) {
        **// THIS IS WHERE IT FAILS**
        console.log(jqXHR);
        swal({
            type: 'error',
            title: 'Error exporting to Excel.',
            width: "auto"
        });
    })
    .always(function () {
        if (gridInstance) {
            prepareForExportOnServer(gridInstance, false).then(() => {
                gridInstance.endUpdate();
            })
        }

        hideLoadingPanelExcelServerExport();
    });

        [HttpPost]
        public async Task<HttpResponseMessage> ExportGridData(DataSourceLoadOptions loadOptions, [FromBody] object paramValues)
        {
            // NEVER GETS TO THIS CALL
        }
0 Answers
Related