Json array is null when received in controller - arrays

function viewReports(firstDate, lastDate) {
var selected = $('#ReportSelected').find(":selected");
var controller = "PortalReports";
var method = "GetReport";
var urlAjax = $("#basePath").val() + controller + "/" + method;
var companydropdown = $('#ReportSelected :selected').data("companydropdown");
var agencydropdown = $('#ReportSelected :selected').data("agencydropdown");
var userdropdown = $('#ReportSelected :selected').data("userdropdown");
var data =
{
reportSelected: selected.text(),
firstDate: firstDate,
lastDate: lastDate,
companydropdown: companydropdown,
agencydropdown: agencydropdown,
userdropdown: userdropdown
};
/*var data =
[{
"reportSelected": selected.text(),
"firstDate": firstDate,
"lastDate": lastDate,
"companydropdown": companydropdown,
"agencydropdown": agencydropdown,
"userdropdown": userdropdown
}];*/
var answer = JSON.stringify({ data });
$.ajax({
traditional: true,
data: JSON.stringify({ data }),
url: urlAjax,
success: function (response) {
loadReport(response);
},
error: function (ob, errStr) {
alert("An error occured. Please try again.");
}
});
//Mvc
public JsonResult GetReport(JArray data)
{
var persons = data.Select(x => x.ToObject<InputJson>());
JArray data is always null irrespective of how many ways I add square brackets of remove quotation marks etc, what am I doing wrong!!!
Prefer simple Object returned in array for readability as I might need to add to it.

Since you are sending a complex data structure(array), you should specify the contentType property when making ajax call. Specify the value of contentType property as "application/json"
//Values hard coded, you may replace with real values from your form.
var dataArray = [{
reportSelected:'201501',
firstDate: '11/12/2010',
lastDate: '12/12/2010',
companydropdown: 4,
agencydropdown: 6,
userdropdown: 16,
}];
var urlAjax = "/Home/GetReport"; // Hard coded for demo. Read further..
$.ajax({
type: "POST",
contentType: "application/json",
data: JSON.stringify(dataArray),
url: urlAjax,
success: function (response) {
console.log(response);
},
error: function (ob, errStr) {
alert("An error occured. Please try again.");
}
});
I suggest you create a view model /DTO to represent the data you are sending and use that in your action method.
public class ReportRequest
{
public string reportSelected { get; set; }
public DateTime firstDate { get; set; }
public int companydropdown { get; set; }
}
[HttpPost]
public JsonResult GetReport(IEnumerable<ReportRequest> data)
{
//do something with data
// to do : Return something
}
In the example, I hardcoded the value of urlAjax variable. You may consider using the html helper methods to generate the correct relative url path to the action method(s) as explained in this post.

Related

Ng-file-upload: Sending array to Web Api

Using angularjs 1.3 and C# .net core web api
I have a ng-file-upload which being used to upload file. When the upload method is called I want to pass in an extra array of some data to the upload method which is then received by the method at my web api. Here is my ng-file-upload
factory.upload = function (file, myArray) {
var url = '{0}upload'.format(apiPath)
return Upload.upload({
url: url,
arrayKey: '',
data: { file: file, myArray: myArray}
}).then(function (res) {
return res;
});
};
Below is my webapi:
[HttpPost("upload")]
public async Task<IActionResult> FileUpload(IFormFile file, List<string> myArray)
{
//code
}
And finally here is the array which I am trying to pass along with upload to my webapi:
[
{
id: "1",
name: "steve"
},
{
id: "2",
name: "adam"
}
]
The issue is in my webapi, the myArray parameter which is to accept the array from the UI is always null. I searched online and its mentioned to add
arrayKey: ''
But still doesnt works. Any inputs?
---Updated---
I created a string array as :
var cars = ["steve", "adam", "ronnie"];
And updated my api as:
List<ArrayItem> myArray
The above code works. So looks like there is issue with array being passed.
I am passing the following array by creating like this:
var myArray= [];
for (var i = 0; i < myJson.length; i++) {
myArray.push({
id: myJson[i].id,
name: myJson[i].name,
});
}
The result of above as seen in console:
Array(2)
0:{id: "1", name: "steve"}
1:{id: "2", name: "adam"}
Whats missing here?
For passing object array, you need to define the list object to accept the parameters.
ArrayItem with Id/Name properties.
public class ArrayItem
{
public int Id { get; set; }
public string Name { get; set; }
}
Change action
public async Task<IActionResult> FileUpload(IFormFile file, List<ArrayItem> myArray)
{
return Ok();
}
Update
You need to remove arrayKey: '[]', try code below:
app.service('crudService', function ($http, Upload) {
var baseUrl = 'http://localhost:50829';
this.uploadFile = function (file, array) {
return Upload.upload({
url: baseUrl + "/api/books/file/upload",
//arrayKey: '[]',
data: { file: file, array: array },
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}).then(function (res) {
return res;
}, function (err) {
throw err;
});
};
});

Byte array and JSON in [FromBody]

I am trying pass an object which consists of different data type. I am always getting null value for orderDetails in Web API.
However if do this,
purchaseOrder.Attachments = null,
in the client then orderDetails is no longer null and I have other informations like "SendEmail" and PurchaseOrderNumber.
It looks I might not be correctly set the parameter in the client (angular 2).
However testing the same Web Api method from Console app works fine and I am not getting a null value.
Do I need to separate the JSON data and byte array?
regards,
-Alan-
Models
public class Attachments
{
public int AttachmentId { get; set; }
public string FileName { get; set ;}
public byte[] FileData { get; set ;}
}
public class UpdatePurchaseOrderViewModel
{
public bool SendEmail { get; set; }
public int PurchaseOrderNumber { get; set; }
public Attachments Attachments { get; set;
}
Here is my Web API put method definition
[HttpPut("AddPurchaseOrderNumber/{purchaseOrderId}")]
public StatusCodeResult AddPurchaseOrderNumber(int purchaseOrderId, [FromBody] UpdatePurchaseOrderViewModel orderDetails)
{
try
{
var status = _service.AddPurchaseOrderNumber(purchaseOrderId, orderDetails);
if (status == 200)
_unitOfWorkAsync.SaveChanges();
else return StatusCode(status);//No Data
}
catch
{
return StatusCode(400); // Bad Request
}
return StatusCode(200);//OK
}
Typescript snippet
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Accept','application/json');
let options = new RequestOptions({ headers: headers });
var body = JSON.stringify(
purchaseOrder
);
var uri = 'http://localhost:33907/api/purchaseorder/addpurchaseordernumber/' + purchaseOrderId;
return this._http.put(uri, body , options)
.map((response: Response) => {
let data = response.json();
if (data) {
return true;
}
else {
return false;
}
})
Update
The orderDetails is created as below
let file = Observable.create((observer) => {
let fr = new FileReader();
let data = new Blob([this.attachment]);
fr.readAsArrayBuffer(data);
fr.onloadend = () => {
observer.next(fr.result);
observer.complete();
};
fr.onerror = (err) => {
observer.error(err);
}
fr.onabort = () => {
observer.error("aborted");
}
});
file.map((fileData) => {
//build the attachment object which will be sent to Web API
let attachment: Attachments = {
AttachmentId: '0',
FileName: this.form.controls["attachmentName"].value,
FileData: fileData
}
//build the purchase order object
let order: UpdatePurchaseOrder = {
SendEmail: true,
PurchaseOrderNumber:this.form.controls["purchaseOrderNumber"].value * 1, //for casting purpose
Attachments: attachment
}
console.log("Loading completed");
return order;
})
When sending objects that have byte arrays as a property back and forth between a client to a WebAPI endpoint, I typically use a DTO that stores the property to explicitly define it as a Base64 string. On the server side I map the DTO to my entity by converting the Base64 string to / from the byte array for server side operations and storing in the database.
The serializer will do something like this automatically but the format passed from JavaScript may not match what the WebAPI JSON serializer is expecting (which is why it's working from your C# Console App).
You didn't include how you are creating the purchaseOrder object in your JavaScript so I can't comment on how that object is being setup - which may be where your issue is.

How to pass class object in angularjs function

I have a class
public class Customer
{
private int _Id;
public int Id
{
get { return _Id; }
set { _Id = value; }
}
private String _Name;
public String Name
{
get { return _Name; }
set { _Name = value; }
}
private String _Address;
public String Address
{
get { return _Address; }
set { _Address = value; }
}
}
I need to save data using this class. I need to know how to call this class in data function
$scope.Save = function () {
var httpreq = {
method: 'POST',
url: 'ajs.aspx/Save',
headers: {
'Content-Type': 'application/json; charset=utf-8',
'dataType': 'json'
},
data: { //** Here How to call assign object value for my customer class **// }
}
$http(httpreq).success(function (response) {
alert("Saved successfully.");
})
};
How to create class object and assign value for my customer class in this data section.
In my web method
[System.Web.Services.WebMethod()]
public static void Save(Customer objcsr)
{
using (SqlConnection con = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["DBConnection"].ConnectionString))
{
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = con;
cmd.CommandText = "insert into customer (Name, Address) values (#Name, #Address);";
cmd.Parameters.AddWithValue("#Name", objcsr.Name);
cmd.Parameters.AddWithValue("#Address", objcsr.Address);
con.Open();
cmd.ExecuteNonQuery();
con.Close();
}
}
}
You need pass the json format data with the class properties, Try this below code
Var objcsr={Id:"1",Name:"Donald Trump",Address:"Alien Planet"}
$scope.Save = function () {
var httpreq = {
method: 'POST',
url: 'ajs.aspx/Save',
headers: {
'Content-Type': 'application/json; charset=utf-8',
'dataType': 'json'
},
data: objcsr
}
$http(httpreq).success(function (response) {
alert("Saved successfully.");
})
};
When you are sending data from client to server, according to the best practices, the data should be sent either in the form of XML or JSON.
Hence, the JSON of your class object could be something like
var obj = {
"id" : "YOUR ID",
"name" : "YOUR NAME",
"address" : "YOUR ADDRESS"
}
Moreover, according to the best practices, the http requests should be made using a factory or service.
So your factory should be something like
angular.module("YOUR_MODULE").factory('YOUR_SERVICE',function($http,$q){
var obj = {};
obj.saveObject = function(data){
var defer = $q.defer();
$http.post("YOUR URL",data).then(function(response){
defer.resolve(response);
},function(error){
defer.reject(error);
});
return defer.promise;
}
return obj;
});
And thus you can make a controller call as
$scope.Save = function (data) {
YOUR_SERVICE.saveObject(data);
}
and that can be called like
var obj = {
"id" : "YOUR ID",
"name" : "YOUR NAME",
"address" : "YOUR ADDRESS"
}
$scope.Save(obj);
If your getting it from form elements then you should be using it like
$scope.Save = function () {
var customerObject={
"Id": $scope.customerId,
"Name": $scope.customerName,
"Address":$scope.customerAddress
};
var httpreq = {
method: 'POST',
url: 'ajs.aspx/Save',
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
data: { customerObject }
}
$http(httpreq).success(function (response) {
alert("Saved successfully.");
})
};
Also Note that, the objectName and their respective properties that your are passing should match with that of the objectName in webmethod. For example
Your properties are
your corresponding json object must have the same properties
Id: $scope.Id
Name: $scope.Name
Address: $scope.Address

How to send a string value to a function (ng-click)

I've got a very simple questions;
Here's my HTML;
<button ng-click="projectTypeController.deleteProjectType(pt.Code)">X</button>
And my function in my controller:
self.deleteProjectType = function (projectTypeCode) {
$http.post('http://localhost:49165/Service1.svc/projecttypes/delete/', projectTypeCode)
.then(getProjectTypes)
.then(function (response) {
});
};
Code in my webservice:
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "ProjectTypes/Delete/", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
void DeleteProjectType(string projectTypeCode);
This works perfectly fine as long as 'pt.Code' is numeric, but as soon as it's a string it results in a HTTP 400 Bad Request. So I think i'm doing something wrong with the deleteProjectType(pt.Code) Part.
I tried to look for examples where they pass a string into a method, but i cannot find anything...
the pt.Code = a scope variable by the way, also adding data in the form of object inside a form element works fine, so passing strings in an object is not a problem, just passing a single string, instead of an integer to a method doesn't seem to work
You need to format the parameter as a valid json.
To do that you can use JSON.stringify().
To implementet this in the method
self.deleteProjectType = function (projectTypeCode) {
$http.post('http://localhost:49165/Service1.svc/projecttypes/delete/', JSON.stringify(projectTypeCode))
.then(getProjectTypes)
.then(function (response) {
});
};
I prefere to use objects instead of just sending a value as a string.
If you at any time need to extend the body then you only need to add more fields to the objects and not modify the function call or the service method.
Here's an example of how you could do that
self.deleteProjectType = function (projectTypeCode) {
var data = { code: projectTypeCode};
$http.post('http://localhost:49165/Service1.svc/projecttypes/delete/', JSON.stringify(data))
.then(getProjectTypes)
.then(function (response) {
});
};
Create the class server side
Public class ProjectTypeDTO
{
public string code { get; set; }
}
Add the class as the parameter in the service method and you are gtg
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "ProjectTypes/Delete/", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
void DeleteProjectType(ProjectTypeDTO pt);

Why are numbers sent from the web UI arriving in my MVC app as 'null;?

I am sending a jQuery Ajax request as such:
$scope.geo.setSuburbs2 = function(areaId) {
var idData = JSON.stringify({ 'areaId': 3, 'agentId': 1 });
$.ajax({
url: "/Area/SuburbsJson",
method: "POST",
data: idData,
dataType: "ajax"
}).then(function(idData) {
alert("Done!");
},
function(data, status) {
alert("Error: " + status);
});
};
This is supposed to fetch suburbs in ID AreaId serviced by an agent with ID agentId. The suburbs returned are key-value pairs in an array on the viewmodel. When I receive the model at the controller, all the keys are still correct in the array, but all numeric values are null.
When I send a data option from the $.ajax call that looks like:
{ 'areaId': 3, 'agentId': 1 }
I get the same size array of key-values on the web app, in OnActionExecuting in the controller, the above array item looks like:
{ 'areaId': null, 'agentId': null }
I strongly suspect a serialization problem on the browser, because the null values should not yet have arrived at my JsonDotNet custom (from the web) serializer. I normally only use that to improve serialization in POST resonses, when I send data back to the UI.
Is this some known problem, or do I have to pore through all the settings, a long list, on the JsonDotNet serializer? If it is a known problem, what should I do about it?
The viewmodel is List<CheckListItemModel>, where:
public class CheckListItemModel
{
public int Id { get; set; }
public string Label { get; set; }
public bool IsChecked { get; set; }
}
I don't use serialized viewmodels to talk between the controllers and the UI, I just pass a UI Angular controller function a filter ID for a list, and it returns the serialized list, with all numeric values set to null.
The action that sends the correct data to the UI is:
public JsonResult SuburbsJson(int areaId, int agentId)
{
var allBurbs = _areaClient.GetSuburbs(areaId).ToList();
var agentBurbIds = _agentClient.GetAgentSuburbs(agentId).Select(s => s.SuburbId).ToList();
var model = new List<CheckListItemModel>();
foreach (var burb in allBurbs)
{
model.Add(new CheckListItemModel { Id = burb.SuburbId, Label = burb.SuburbName, IsChecked = agentBurbIds.Contains(burb.SuburbId) });
}
return Json(model, JsonRequestBehavior.DenyGet);
}
It's a question of Id and IsChecked. And item is already checked if its Id is in a list of Agents associated with that SuburbId.
The JSON.stringify() method converts a JavaScript value to a JSON
string,
Recheck your payload construction. you need to stringify actual JavaScript object like...
$scope.geo.setSuburbs2 = function(areaId) {
var value = { areaId: 3, agentId: 1 }; //Object to be sent to controller
var idData = JSON.stringify(value);
$.ajax({
url: "/Area/SuburbsJson",
type: "POST",
contentType: 'application/json',
dataType: 'json',
data: idData
}).then(function(idData) {
alert("Done!");
},
function(data, status) {
alert("Error: " + status);
});
};
by removing the single quotes on the keys. Also note the changes to how the request was made to the controller.

Resources