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):
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
}
