I've the following object which I am passing into an MVC Controller:
this.JsonData = {
"__RequestVerificationToken": $('input[name=__RequestVerificationToken]').val(),
"searchMode": {
"mode": Number(mode.val()),
"pageSize": Number(pagesize.val()) || 5, "pageNumber": Number(pagenumber.val()) || 1,
"sortField": sortfield.val() || "Ref",
"sortDirection": sortdirection.val() || "desc"
},
"searchData": {
"Compare": Number(StdComparison.val()),
"SearchTextFrom": searchText.val(),
"SearchTextTo": searchTextTo.val()
}
This works ok, but I recent requirement has arisen whereby I am looking to encode this object for use with the javascript function Window.location
Suggestions I have used:
how-to-pass-plex-json-object-in-url-using-javascript
window.location + "?SearchCriteria=" + JSON.Stringify(this.JsonData);
Creates the following request:
Controller/Action?SearchCriteria={
"__RequestVerificationToken": "tokenvalue",
"searchMode": {
"mode": 2,
"pageSize": 5,
"pageNumber": 1,
"sortField": "Ref",
"sortDirection": "desc"
},
"searchData": {
"Compare": 1,
"SearchTextFrom": "From A",
"SearchTextTo": "To Z"
}
}
Whereas
window.location + "?SearchCriteria=" + this.JsonData;
Produced the following:
Controller/Action?SearchCriteria=[object%20Object]
With a page not found error on both of the above.
UPDATE:
I have moved forward in the quest for an answer.
OK as per request from helpers, I have included more source code.
I have Three Classes.
public class MainSearch
{
public MainSearch()
{
SearchData searchData = new SearchData();
SearchMode searchMode = new SearchMode();
}
public SearchData searchData { get; set; }
public SearchMode searchMode { get; set; }
public int? page { get; set; }
public object ToPagedListParameters(int pagenumber)
{
searchMode.pageNumber = pagenumber;
return page;
}
public IList<string> ValidationErrorMessages { get; set; }
}
public class SearchData
{
// Fields used for the ticket number search
public int? ticketNumberCompare { get; set; }
public string ticketSearchTextFrom { get; set; }
public string ticketSearchTextTo { get; set; }
}
public class SearchMode
{
public int? mode { get; set; }
public int? pageNumber { get; set; }
public int? pageSize { get; set; }
public string sortDirection { get; set; }
public string sortField { get; set; }
public string userURN { get; set; }
public string __RequestVerificationToken { get; set; }
}
These classes hold the criteria used for searching (SearchData has been truncated)
The following is my controller code:
[HttpGet]
public ActionResult DownloadFileCSV(MainSearch search)
{
string fileName = Server.MapPath("~/Content/Pdf/") + "somefile.pdf";
byte[] fileContents = System.IO.File.ReadAllBytes(fileName);
return File(fileContents, "application/pdf", "result.pdf");
}
And finally, the Ajax call that is made from the cshtml file.
$("#DownloadAttachmentCSV").click(function () {
$.ajax(
{
url: '@Url.Action("DownloadFileCSV", "Home")',
contentType: 'application/json; charset=utf-8',
datatype: 'json',
data: JsonData,
type: "GET",
success: function () {
window.location = '@Url.Action("DownloadFileCSV", "Home")' + '?' + JsonData;
},
error: function (xhr, ajaxOptions, thrownError) {
alert(xhr.status);
alert(thrownError);
}
});
});
Oddly enough, the code above does actually work and the file is downloaded, but here is the problem. The JSON data is not populating the MainSearch variable.
So far the only way I can get the JSON data to populate the c# classes is to change the method to a POST.
Must this be the case?
I've the following object which I am passing into an MVC Controller:
this.JsonData = {
"__RequestVerificationToken": $('input[name=__RequestVerificationToken]').val(),
"searchMode": {
"mode": Number(mode.val()),
"pageSize": Number(pagesize.val()) || 5, "pageNumber": Number(pagenumber.val()) || 1,
"sortField": sortfield.val() || "Ref",
"sortDirection": sortdirection.val() || "desc"
},
"searchData": {
"Compare": Number(StdComparison.val()),
"SearchTextFrom": searchText.val(),
"SearchTextTo": searchTextTo.val()
}
This works ok, but I recent requirement has arisen whereby I am looking to encode this object for use with the javascript function Window.location
Suggestions I have used:
how-to-pass-plex-json-object-in-url-using-javascript
window.location + "?SearchCriteria=" + JSON.Stringify(this.JsonData);
Creates the following request:
Controller/Action?SearchCriteria={
"__RequestVerificationToken": "tokenvalue",
"searchMode": {
"mode": 2,
"pageSize": 5,
"pageNumber": 1,
"sortField": "Ref",
"sortDirection": "desc"
},
"searchData": {
"Compare": 1,
"SearchTextFrom": "From A",
"SearchTextTo": "To Z"
}
}
Whereas
window.location + "?SearchCriteria=" + this.JsonData;
Produced the following:
Controller/Action?SearchCriteria=[object%20Object]
With a page not found error on both of the above.
UPDATE:
I have moved forward in the quest for an answer.
OK as per request from helpers, I have included more source code.
I have Three Classes.
public class MainSearch
{
public MainSearch()
{
SearchData searchData = new SearchData();
SearchMode searchMode = new SearchMode();
}
public SearchData searchData { get; set; }
public SearchMode searchMode { get; set; }
public int? page { get; set; }
public object ToPagedListParameters(int pagenumber)
{
searchMode.pageNumber = pagenumber;
return page;
}
public IList<string> ValidationErrorMessages { get; set; }
}
public class SearchData
{
// Fields used for the ticket number search
public int? ticketNumberCompare { get; set; }
public string ticketSearchTextFrom { get; set; }
public string ticketSearchTextTo { get; set; }
}
public class SearchMode
{
public int? mode { get; set; }
public int? pageNumber { get; set; }
public int? pageSize { get; set; }
public string sortDirection { get; set; }
public string sortField { get; set; }
public string userURN { get; set; }
public string __RequestVerificationToken { get; set; }
}
These classes hold the criteria used for searching (SearchData has been truncated)
The following is my controller code:
[HttpGet]
public ActionResult DownloadFileCSV(MainSearch search)
{
string fileName = Server.MapPath("~/Content/Pdf/") + "somefile.pdf";
byte[] fileContents = System.IO.File.ReadAllBytes(fileName);
return File(fileContents, "application/pdf", "result.pdf");
}
And finally, the Ajax call that is made from the cshtml file.
$("#DownloadAttachmentCSV").click(function () {
$.ajax(
{
url: '@Url.Action("DownloadFileCSV", "Home")',
contentType: 'application/json; charset=utf-8',
datatype: 'json',
data: JsonData,
type: "GET",
success: function () {
window.location = '@Url.Action("DownloadFileCSV", "Home")' + '?' + JsonData;
},
error: function (xhr, ajaxOptions, thrownError) {
alert(xhr.status);
alert(thrownError);
}
});
});
Oddly enough, the code above does actually work and the file is downloaded, but here is the problem. The JSON data is not populating the MainSearch variable.
So far the only way I can get the JSON data to populate the c# classes is to change the method to a POST.
Must this be the case?
Share Improve this question edited May 23, 2017 at 11:46 CommunityBot 11 silver badge asked Feb 21, 2017 at 10:31 gilesrpagilesrpa 1,0611 gold badge15 silver badges41 bronze badges 8- You can pass a plex javascript object to a GET method. What is the signature of the method your calling? – user3559349 Commented Feb 21, 2017 at 10:35
- It is using HttpGet on the MVC controller action – gilesrpa Commented Feb 21, 2017 at 10:54
- Yes I know - again what is the signature of the method (and if its a model, show that model) – user3559349 Commented Feb 21, 2017 at 11:00
- The signature is: public FileResult MethodName(SearchCriteria data) – gilesrpa Commented Feb 21, 2017 at 11:01
- the model is: public class SearchCriteria { public SearchMode searchMode {get;set;} public SearchData searchData {get;set;}} – gilesrpa Commented Feb 21, 2017 at 11:03
2 Answers
Reset to default 2You cannot pass plex javascript objects to a GET method like that. A GET has not body and to bind to a model, your query string name/value pairs must match the properties of the object your binding to. For example to bind to the mode
property of the SearchMode
property of MainSearch
, you query string would need to include
....&SearchMode.mode=2....
Change the code your using to generate the javascript object to
var data = {
'searchMode.mode': mode.val(),
'searchMode.pageSize': pagesize.val() || 5,
'searchMode.pageNumber': pagenumber.val() || 1,
'searchMode.sortField': sortfield.val() || "Ref",
'searchMode.sortDirection': sortdirection.val() || "desc",
'searchData.SearchTextFrom': StdComparison.val(),
'searchData.Compare': searchText.val(),
'searchData.SearchTextTo': searchTextTo.val(),
}
and the ajax code to
$.ajax(
{
url: '@Url.Action("DownloadFileCSV", "Home")' + '?' + $.param(data),
type: "GET",
success: function () {
Note also the following
- There is no point using
Number(..)
to convert the values to a number - its all sent across the wire as text - A GET has no body so setting the ajax
contentType
option is pointless - Your method does not return json so
datatype: 'json'
does not make sense. - Passing the antiforgery token to a GET method is not necessary
Having said that, its unclear what your wanting to do here. The code in your MainSearch search()
method never uses any values of the model. And it returns a FileResult
which cannot work with an ajax call (but it can work with window.location
, it which case the code in the success callback would need to be
var baseUrl = '@Url.Action("DownloadFileCSV", "Home")';
var queryString = $.param(data);
window.location = baseUrl + '?' + queryString;
but then its unclear why you making 2 calls to the method - the ajax call, followed by the redirect (the ajax call does nothing at all).
Anything you use as a query parameter should be properly URI encoded, especially with something like JSON:
window.location + "?SearchCriteria=" + encodeURIComponent(JSON.stringify(this.JsonData));
I'm not sure that the model binder is designed to convert JSON in a GET request into a class instance, but the attempts you've made above are definitely not the way to go.