Passing parameter as Dictionary<string,string[]> with jquery getJSON method - javascript

I am trying to pass multiple parameters using $.getJSON function. To do this I have created a DTO class that defined as below.
public class MyDTO
{
public string IncludeProperties {get; set;}
public Dictionary<string,string[]> Predicate {get; set;}
}
Here is my web api functon:
[HttpGet]
[Route("FindAll")]
public IEnumerable <Hotel> FindAll([FromUri] MyDTO predicateDTO) {
return DataStore.FindAll <Hotel> (predicateDTO.IncludeProperties, PredicateHelper.ConvertStringToLambda <Hotel> (predicateDTO.Predicate));
}
jquery:
var uri = '/api/Hotel/FindAll';
var predicate = {
"HotelName": ["HOtel", "==", ""],
"PaymentStatus": ["True", "==", "AND"]
}
var data = {
IncludeProperties: ["HotelDetails", "HotelDetails.HotelMainPhotos"],
Predicate: predicate
}
busyIndicatorVisibility(true);
$('#ModalOtel .modal-body').find(".media").remove();
$.getJSON(uri, {
predicateDTO: data
}).done( //TODO something)
When I call my function IncludeProperties has items but Predicate property is null.
How can I pass Dictionary<string,string[]> as parameter?

I'll try to show you an idea to start with the solution.
You will need to update your MyDTO Model like this:
public class Predicate
{
public string[] HotelName { get; set; } // In javascript can hold: ["HOtel", "==", ""]
public string[] PaymentStatus { get; set; }
}
public class MyDTO
{
public string[] IncludeProperties { get; set; }
public Predicate predicate { get; set; }
}
In your jQuery function, you will need to replace your $.getJSON for $.ajax(), like this:
var predicate = {
"HotelName": ["HOtel", "==", ""],
"PaymentStatus": ["True", "==", "AND"]
};
var data = {
IncludeProperties: ["HotelDetails", "HotelDetails.HotelMainPhotos"],
Predicate: predicate
};
$.ajax(
{
url: uri,
type: "POST",
data: JSON.stringify(data), // To convert javascript objects in json strings.
dataType: "json",
contentType: "application/json; charset=utf-8", // To send json objects to the web api controller.
beforeSend: function ()
{
console.log("Wait a minute...");
},
success: function (response)
{
console.log(response);
}
});
The $.ajax() function will send your json data with this format:
{
"IncludeProperties": [
"HotelDetails",
"HotelDetails.HotelMainPhotos"
],
"Predicate": {
"HotelName": [
"HOtel",
"==",
""
],
"PaymentStatus": [
"True",
"==",
"AND"
]
}
}
In your web api controller, try to change to this:
public string FindAll(MyDTO predicateDTO) // This contains all model with submodels.
{
string result = "";
// You can have access to predicateDTO instance with the current data;
// predicateDTO.predicate.HotelName...
return result;
}
I hope this helps you.

I have changed my method.
[HttpGet]
[Route("FindAll")]
public IEnumerable<Hotel> FindAll(string[] IncludeProperties, string Predicate)
{
Dictionary<string,string[]> predicateAsDictionary = JsonConvert.DeserializeObject<Dictionary<string, string[]>>(predicateString)
return DataStore.FindAll<Hotel>(IncludeProperties, predicateAsDictionary);
}
getJson method does not convert var predicate object that is defined in jquery to Dictionary However when I send this object using JSON.stringify(predicate) I can deserialize that string to a Dictionary

Related

Is there a way to add array elements to a formData object, so net core [FromForm] will deserialize them correctly?

So, I'm using the feth api to do posts, and it's generally working out really well. But I am having one issue, say I have the following two models:
public class Contact
{
public int Id { get; set; }
public string Name { get; set; }
public List<Phone> Phones { get; set; }
}
public class Phone
{
public string Number { get; set; }
public string Model { get; set; }
}
Now, I want to create a formData object from the html form, which adds the strings and ints just fine. but when I do:
var data = new formData(document.querySelector('form'));
let arr = [{
Number: "12345678"
},
{
Number: "87654321"
}
];
data.append('Phones', JSON.stringify(arr));
//OR
data.append('Phones', JSON.stringify(arr[0]));
data.append('Phones', JSON.stringify(arr[1]));
//OR
data.append('Phones[]', JSON.stringify(arr[0]));
data.append('Phones[]', JSON.stringify(arr[1]));
//OR
data.append('Phones[0]', JSON.stringify(arr[0]));
data.append('Phones[1]', JSON.stringify(arr[1]));
and then post to something like this:
[HttpPost]
public ActionResult SaveContact([FromForm]Contact contact)
{
...
}
The object is bound as its supposed to, with the correct values for Id, Name, But Phones contains nothing. Does anyone have some input here, its driving me nuts. Thanks!
Try to change your code to the following form:
let arr = ["123456","7895555"];
formData.append('Phones[0].Number', arr[0]);
formData.append('Phones[1].Number', arr[1]);
Result:
Array type property with complex objects
const fd = new FormData();
const phones = [
{ number: '123', model: 'model x' },
{ number: '456', model: 'model y' },
];
for (var i = 0; i < phones.length; i++) {
fd.append(`phones[][${i}][number]`, phones[i].number);
fd.append(`phones[][${i}][model]`, phones[i].model);
}
Starting from the great solution posted by Yiyi You (very thanks for your post, it helped me a lot) I would recommend also the following solution for my problem that was slightly different.
Javascript code:
var formData = new FormData();
let arr = ["123456","7895555"];
formData.append('Phones[0]', arr[0]);
formData.append('Phones[1]', arr[1]);
$.ajax({
type: "POST",
url: "/Contacts/SaveContacts",
data: formData,
processData: false,
contentType: false,
success: function(result) {
console.log(result);
},
error: function(xhr, resp, text) {
console.log(xhr, resp, text);
}
});
C# code:
[Route("[controller]")]
public class ContactsController : Controller
{
public ContactsController()
{
}
public class PhonesList
{
public Dictionary<int, string> Phones{ get; set; }
}
public async Task<JsonResult> SaveContacts([FromForm] PhonesList phonesList)
{
}
}
If you like, you can cycle with the forEach method in an array
arr.forEach((obj,index) => formData.append(`listName[${index}]`,obj.Number))
for ES5 >=

ASP.Net Core 3 Posting JSON array to controller

I´m having issues passing an array of JSON objects to an MVC controller in ASP.Net core 3. The JSON object is built from a CSV file and passed through an AJAX call. The correct action is called but the object received is always null.
JS:
async function updateData(){
var obj = await getData();
$.ajax({
type: "POST",
contentType: "application/json",
url: "/admin/metric/Update",
data: obj,
dataType: 'json',
success: function (data) {
console.log("SUCCESS : ", data);
},
error: function (e) {
console.log("ERROR : ", e);
}
});
}
async function getData(){
var metric = {};
var metrics = [];
var response = await fetch('https://bloburi.net/source/file.csv');
var data = await response.text();
var table = data.split('\n').slice(1);
table.forEach(row => {
var columns = row.split(',');
metric["FirstName"] = columns[0];
metric["LastName"] = columns[1];
metric["Email"] = columns[2];
metric["AverageHandleTime"] = columns[3];
metric["AverageFollowUp"] = columns[4];
metric["AverageHold"] = columns[5];
metric["Date"] = columns[6];
metrics.push(JSON.stringify(metric));
});
console.log(metrics);
return metrics;
}
Models:
public class MetricBase
{
[Required]
public string Date { get; set; }
public double AverageHandleTime { get; set; }
public double AverageHold { get; set; }
public double AverageFollowUp { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class MetricModel
{
public IEnumerable<MetricBase> MetricBase { get; set; }
}
Controller:
[HttpPost]
public IActionResult Update([FromBody]MetricModel obj)
{
return Json(new { obj });
}
I think the issue may be on how I'm deffining the class that is receiving the JSON, but I've tried multiple things like deffining MetricBase as a List<> or straight up as an array in MetricModel, but it doesn't seem to work.
Any help is appreciated!
You adding the stringify item inside array via code
metrics.push(JSON.stringify(metric));
instead of stringify the whole array. Change the line to
metrics.push(metric);
and data: obj, to
data: JSON.stringify(obj),.
With the mentioned change, the $.ajax sends the whole array. Also change the action to
public IActionResult Update([FromBody]IEnumerable<MetricBase> obj)

Javascript Windows.Location pass complex JSON object

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-complex-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?
You cannot pass complex 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.

Pass an object to MVC View using json and Ajax

Hi I want to pass an object to my view and work with it in JavaScript. I've created this model
namespace WebApplication1.Models
{
public class OpenlayerLayer
{
public OpenlayerLayer(string Layers,string name,string url,string style,Boolean isqueryable,string projection,string maxEx)
{
LAYERS = Layers;
Name = name;
URL = url;
STYLES = style;
queryable = isqueryable;
this.projection = projection;
maxExtent = maxEx;
}
public string LAYERS { get; set; }
public string Name { get; set; }
public string URL { get; set; }
public string STYLES { get; set; }
public Boolean queryable { get; set; }
public string projection { get; set; }
public string maxExtent { get; set; }
}
}
and I used this method in controller
public string getLayerlist()
{
OpenlayerLayer ol = new OpenlayerLayer("Test: GParcel", "Parcels", "http://127.0.0.1:8080/geoserver/test/wms", "", true, "EPSG:900913", "5725524.327803587, 3835467.5859751734, 5729564.058979791, 3841404.792330884");
var json = JsonConvert.SerializeObject(ol);
return json;
}
well, I used this in my view
$(selector).getJSON('getLayerlist', data, loadopenlayers(data, status))
this is 'loadopenlayers' method
function loadopenlayers(result,status) {
alert("Seems works fine");
var layer = JSON && JSON.parse(result) || $.parseJSON(result);
var wms2 = new OpenLayers.Layer.WMS(
layer.name, layer.URL,
{
LAYERS: layer.LAYERS,
STYLES: layer.STYLES,
format: format,
},
{
buffer: 0,
displayOutsideMaxExtent: true,
isBaseLayer: true,
projection: new OpenLayers.Projection(layer.projection),
minResolution: null,
maxResolution: 48,
numZoomLevels: 32,
maxScale: null,
minScale: 1271428,
maxExtent: new OpenLayers.Bounds(layer.maxExtent),
minExtent: null,
displayProjection: new OpenLayers.Projection("EPSG:4326"),
}
);
}
so It must work and then call loadopenlayers in JavaScript code,right?
Now I have a problem how do I work with result of getLayerlist in loadopenlayers
Is my method right way to communicate between models and JavaScript in view?
In fact I have a large number of JavaScript codes which must be customized using model parameters and I want a consistence and standard method to do it
Thanks very much
Better use jquery ajax call e.g:
$.ajax({
type: "POST",
url: '/ControllerName/getLayerlist',
// data: data,send data to controller from here
dataType: 'json',
//processData: false,
success: function (msg) {
//your json data will be available in msg
//here you can also call loadopenplayers() method
},error: function (error) {
console.log(error.responseText);
}
});
Whereas your controller method will look like
[HttpPost]
public JsonResult getLayerlist(string data)
{
OpenlayerLayer ol = new OpenlayerLayer("Test: GParcel", "Parcels", "http://127.0.0.1:8080/geoserver/test/wms", "", true, "EPSG:900913", "5725524.327803587, 3835467.5859751734, 5729564.058979791, 3841404.792330884");
return Json(ol , JsonRequestBehavior.AllowGet);//automatically converts to Json
}
function loadopenlayers(result){
var newResult = JSON.stringify(result);
//Now newResult is a json
}
You can call through ajax
$.ajax({
url: '/ControllerName/getLayerlist',
type: "POST",
cache:false,
success: function (data) {
//You can get your result data here
console.log("Data :"+JSON.stringify(data));
loadopenlayers(data);
},error: function (error) {
console.log(error.responseText);
}
});
function loadopenlayers(data){
var newResult = JSON.stringify(data);//This is your JSON result
}

Pass complex object to MVC api is null

I have a service call that gets a filtered set of historical records from our database.
I am having some trouble getting my filter to make it to my main mvc call.
[AcceptVerbs("POST")]
public History History( HistoryFilters filters){......}
config.Routes.MapHttpRoute(
name: "DefaultActionRoute",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new {id = RouteParameter.Optional}
);
amplify.request.define('post', 'ajax', {
url: '/api/{controller}/{action}',
dataType: 'json',
type: 'POST',
cache: cache
});
//History Filter js
{
DateRange: { Start: ..., End: ...},
BetStatus: "unresolved",
TransactionTypes: "bets"
}
Every time I make my call filters is filled with null values. The JS filter structure matches that of my HistoryFilters class in C#. If I JSON.stringify my filters and change my Api call to string then i get that but it would be nice if it could convert it for me.
Can anyone tell me what i'm missing?
Edit
var amp = function(id, data) {
return $.Deferred(function(dfd) {
amplify.request({
resourceId: id,
data: data,
success: dfd.resolve,
error: dfd.reject
});
}).promise();
};
History: function (filters) {
return amp('post', { controller: 'user', action: 'history', '':filters});
}
public class HistoryFilters
{
public DateFilter DateRange { get; set; }
public string BetStatus { get; set; }
public string TransactionTypes { get; set; }
}
public class DateFilter
{
public string Start { get; set; }
public string End { get; set; }
}
It is my understanding that AmplifyJS has an issue with setting contentType and therefore can not make this type of call. I removed the amplifyJS call for now and am using just jquery.

Categories