HTTP POST data not going from AJAX to C# MVC 5 Backend - javascript

As the title states, I'm trying to send data from a Javascript frontend to my C# MVC 5 backend, and it doesn't seem to make it. I've run through it with the debugger in Visual Studio and the data I'm trying to pass always ends up null.
Here is my front end code:
var file = $("#my-file").files[0];
file = file[0];
var formData = new FormData();
var MyData = {
Name: "Form 133",
Attachments: {
Type: "Check"
}
};
formData.append('file', file);
$.ajax({
type: "POST",
url: '/NMISProduct/Index',
contentType: false,
processData: false,
data: { formData: formData, MyData: MyData },
success: function (result) {
console.log(result);
},
error: function (xhr, status, p3, p4) {
var err = "Error " + " " + status + " " + p3 + " " + p4;
if (xhr.responseText && xhr.responseText[0] == "{")
err = JSON.parse(xhr.responseText).Message;
console.log(err);
}
});
Backend code:
[HttpPost]
public async Task<JsonResult> Index(MyData MyData)
{
Debug.WriteLine(Request.Params["MyData"]);
Debug.WriteLine(MyData.Name);
try
{
foreach (string file in Request.Files)
{
var fileContent = Request.Files[file];
if (fileContent != null && fileContent.ContentLength > 0)
{
// get a stream
var stream = fileContent.InputStream;
// and optionally write the file to disk
var fileName = fileContent.FileName;
var path = Path.Combine(Server.MapPath("~/App_Data/"), fileName);
using (var fileStream = System.IO.File.Create(path))
{
stream.CopyTo(fileStream);
}
}
}
}
catch (Exception)
{
return Json("Upload failed");
}
return Json("File uploaded successfully");
}
public class MyData
{
public string Name { get; set; }
public Attachment Attachments { get; set; }
}
public class Attachment
{
public string Type { get; set; }
}
As I stated, on the Backend MyData always ends up being null. What am I doing wrong?

jQuery docs state:
processData (default: true)
Type: Boolean
By default, data passed in to the data option as an object (technically, anything other than a string) will be processed and transformed into a query string, fitting to the default content-type "application/x-www-form-urlencoded". If you want to send a DOMDocument, or other non-processed data, set this option to false.
The problem is that you are trying to send MyData as an object while setting processData to false. Which will result in the data not being processed before being send. The data property should contain a query string of the form key1=value1&key2=value2, or an object of the form {key1: 'value1', key2: 'value2'}, the latter will be converted into a query string before being send unless you are setting processData to false.
So if you want to send your formData along with the MyData you will need to append the MyData to the formData before sending it like so.
formData.append('Name', 'Form 133');
formData.append('Type', 'Check');
And then in the $ajax call add the FormData to the data property like so.
data: formData,

contentType setting to false you just told him to not sending any data in the header so basically the tag [HttpPost] become useless.
processData setting to false you told him not to process has and object so rendering MyData useless again by setting to true if i remember correctly it mean process anything beside string has a object.
$.ajax({
url: "#Url.Action("Your METHOD", "Your Controller")",
type: "POST",
contentType: "application/json",
data: JSON.stringify({ MyData: MyData}),
success: function(response) {
response ? alert("Process") : alert("Boom");
}
});

Related

Sending multiple JSON lists and image in FormData to C#

I'm trying to send two lists of JSON, a DateTime, int, string, and file to a C# controller via JQuery AJAX POST request.
All data sends fine except the two object arrays, they send nothing.
I've tried changing them from a list of objects to a list of strings to convert from there, but the array is received as one long string, making it nonconvertible via JsonConvert in Newtonsoft.Json.
I've tried logging the formdata objects in order to check their validity, and they seem fine in console. I'm not entirely sure where I've messed up.
Here is my JS:
var formData = new FormData();
formData.append("assignedUsers", JSON.stringify(assignedUsers));
formData.append("ccUsers", JSON.stringify(ccUsers));
formData.append("dueDate", $("#DueDate").val());
formData.append("goalClassID", parseInt(goalClassID));
formData.append("goalDescription", $("#GoalDescription").val());
formData.append("file", document.getElementById("GoalFile").files[0]);
for (var pair of formData.entries()) {
console.log(pair[0] + ', ' + pair[1]);
}
$.ajax({
url: api + "main/CreateGoal",
type: "POST",
data: formData,
cache: false,
dataType: "json",
processData: false,
contentType: false,
success: function (result) {
if (result) {
toastr.success("Goal successfully created");
} else {
toastr.error("Goal creation failed.");
}
}
})
This is my C# Controller:
public bool CreateGoal([FromForm]List<User>AssignedUsers, [FromForm]List<User>CCUsers, [FromForm]DateTime DueDate, [FromForm]int GoalClassID, [FromForm]string GoalDescription, [FromForm]IFormFile File = null)
This is User class
public class User : Base
{
public string UUID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public int Permission { get; set; }
public string FullName { get { return FirstName + " " + LastName; } }
}
Both object arrays are empty because you are posting them as json, but all the data is posted as form-data and [FromForm] expects input to be form-data of special format. Everything is working fine for primitive values because they added to FormData like this
formData.append(key, value)
as expected. But when you need to pass an array of objects it must be of the following format
formData.append("obj[0].Field1", field1Val)
formData.append("obj[0].Field2", field2Val)
formData.append("obj[0].Field3", field3Val)
...
formData.append("obj[1].Field1", field1Val)
formData.append("obj[1].Field2", field2Val)
formData.append("obj[1].Field3", field3Val)
So you need to update your code to something like this
var formData = new FormData();
formData.append("assignedUsers[0].uuid", assignedUsers[0].uuid);
formData.append("assignedUsers[0].firstName", assignedUsers[0].firstName);
...
formData.append("assignedUsers[1].uuid", assignedUsers[1].uuid);
formData.append("assignedUsers[1].firstName", assignedUsers[1].firstName);
...
formData.append("dueDate", $("#DueDate").val());
formData.append("goalClassID", parseInt(goalClassID));
formData.append("goalDescription", $("#GoalDescription").val());
formData.append("file", document.getElementById("GoalFile").files[0]);
Well, since this is not very good way to write code you would like to create a function doing it for you. The final solution is
function addUsers(formdata, users, name) {
for (var i = 0; i < users.length; i++) {
addSingleUser(formdata, users[i], name + "[" + i + "]");
}
}
function addSingleUser(formdata, user, name) {
for (var key in user) {
formdata.append(name + "." + key, user[key]);
}
}
var formData = new FormData();
formData.append("dueDate", $("#DueDate").val());
formData.append("goalClassID", parseInt(goalClassID));
formData.append("goalDescription", $("#GoalDescription").val());
formData.append("file", document.getElementById("GoalFile").files[0]);
addUsers(formData, assignedUsers, "assignedUsers")
addUsers(formData, ccUsers, "ccUsers")
$.ajax({
url: "/Home/CreateGoal",
type: "POST",
data: formData,
cache: false,
dataType: "json",
processData: false,
contentType: false,
success: function (result) {
if (result) {
toastr.success("Goal successfully created");
} else {
toastr.error("Goal creation failed.");
}
}
})
So due to the way the ajax call needs to handle files it does not also process the models in the array as json objects. So, I made a change where the first method will return the created object and the second method will then accept the file.
C# Controller
[HttpPost("CreateGoal")]
public Goal CreateGoal(List<User> AssignedUsers, List<User>CCUsers, DateTime DueDate, int GoalClassID, string GoalDescription)
{
try
{
//Code to save new goal model
return goal;
}
catch (Exception e)
{
return null;
}
}
[HttpPost("CreateGoalFile")]
public bool CreateGoalFile([FromForm] int GoalID, [FromForm]IFormFile File)
{
try
{
//Code to save file and make database relation
return true;
}
catch (Exception)
{
return false;
}
}
Javascript solution
$.post(api + "main/CreateGoal",
{ AssignedUsers: assignedUsers, CCUsers: ccUsers, DueDate: $("#DueDate").val(), GoalClassID: parseInt(goalClassID), GoalDescription: $("#GoalDescription").val() },
function (result) {
if (document.getElementById("GoalFile").files[0] != null) {
var formData = new FormData();
formData.append("goalID", result.id);
formData.append("file", document.getElementById("GoalFile").files[0]);
$.ajax({
url: api + "main/CreateGoalFile",
type: "POST",
data: formData,
cache: false,
dataType: "json",
processData: false,
contentType: false,
success: function (result) {
if (result) {
toastr.success("File uploaded sucessfully created");
} else {
toastr.error("File uploaded failed.");
}
}
})
}
toastr.success("Goal successfully created");
}
)

Pass an array as parameter to a controller from an ajax call in JavaScript

I'm working on an ASP.NET MVC 4 website and I've got some troubles with a functionality. I explain, I've to select entities displayed in a table with their linked checkbox :
Screenshot of my table where each row has a checkbox with the same Id as the entity
Console showing updates in the array
Inside my script I have been abled to store each checked Id's checkbox in an array and remove those if the checkbox is unchecked. But I can't pass the array to my controller's function to delete each selected entity in the database.
I used $.ajax() from jquery to send through a POST request the array (as JSON) but I always get 500 error :
JSON primitive invalid
Null reference
Here's my function in my script (I don't know if my array's format is valid) :
var sendDocsToDelete = function (docsArray) {
$.ajax({
type: 'POST',
url: 'Main/DeleteDocuments',
data: JSON.stringify(docsArray),
contentType: 'application/json; charset=utf-8',
datatype: 'json',
success: function (result) {
alert('Success ' + result.d);
},
error: function (result) {
alert('Fail ' + result.d);
}
});
}
Then, the POST call the following function in my controller :
[Authorize]
[WebMethod]
public void DeleteDocuments(string docsToDelete)
{
int id;
string[] arrayDocs = JsonConvert.DeserializeObject<string[]>(docsToDelete);
foreach (string docId in arrayDocs)
{
int.TryParse(docId, out id);
dal.DeleteDocument(id); // dal = DataAccessLayer is the class which interacts with the database by executing queries (select, delete, update...)
}
}
Update 2
[Authorize]
public ActionResult DeleteDocuments(int[] docsToDelete)
{
try{
foreach (string docId in arrayDocs)
{
int.TryParse(docId, out id);
dal.DeleteDocument(id); // dal = DataAccessLayer is the class which interacts with the database by executing queries (select, delete, update...)
}
return Json("Success");
}
catch
{
return Json("Error");
}
}
var sendDocsToDelete = function (docsArray) {
$.ajax({
type: 'POST',
url: 'Main/DeleteDocuments',
data: docsArray,
contentType: 'application/json; charset=utf-8',
datatype: 'json',
success: function (result) {
alert('Success ' + result.d);
},
error: function (result) {
alert('Fail ' + result.d);
}
});
}
Any ideas about this issue ? I hoped I was clear enough. Do not hesitate if you need more details.
If you are passing an integer array properly from $.ajax (i.e. your docsArray should be having value like [15,18,25,30,42,49]) then you should try :
[Authorize]
public ActionResult DeleteDocuments(int[] docsArray)
{
//int id;
//string[] arrayDocs = JsonConvert.DeserializeObject<string[]>(docsToDelete);
try {
foreach (int docId in docsArray)
{
//int.TryParse(docId, out id);
dal.DeleteDocument(docId); // dal = DataAccessLayer is the class which interacts with the database by executing queries (select, delete, update...)
}
return "Success ";
}
catch {
return "Error";
}
}
Update :
Your javascript code should be :
var sendDocsToDelete = function (docsArray) {
$.ajax({
type: 'POST',
url: 'Main/DeleteDocuments',
data: JSON.stringify(docsArray),
contentType: 'application/json; charset=utf-8',
datatype: 'json',
success: function (result) {
alert('Success ');
},
error: function (result) {
alert('Fail ');
}
});
}
Maybe the datatype in the JSON array is not a string? (This could happen if you have an array in the form of [45,64,34,6], or a mixed one like [345,"wef4"]).
To make sure something is a string in Javascript you can do this: var string = "".concat(otherVar);
Try changing your ajax data to something like this..
data : JSON.stringify({'docsToDelete':docsArray}),
Make these changes to your code.
In Jquery
data: docsArray, no need to stringify the array
In Controller
[Authorize] //remove [WebMethod]
public ActionResult DeleteDocuments(string[] docsToDelete) //Add ActionResult, Change parameter to accept array
{
int id;
string[] arrayDocs = docsToDelete; //no need of deserilization
foreach (string docId in arrayDocs)
{
int.TryParse(docId, out id);
dal.DeleteDocument(id); // dal = DataAccessLayer is the class which interacts with the database by executing queries (select, delete, update...)
}
return Json(id); //return id back to ajax call...
}

JQuery/Ajax & Spring Rest Multi-part form submit

I am quite new to JQuery and I was trying to do some asynchronous multipart form uploading. The form consist of few data fields and a file type. I have set up the server side code (Spring) like this:
#RequestMapping(method = RequestMethod.POST)
public #ResponseBody
Upload multipleSave(MultipartHttpServletRequest request)
{
Upload upload = new Upload();
Iterator<String> iterator = request.getFileNames();
while (iterator.hasNext())
{
MultipartFile file = request.getFile(iterator.next());
try
{
System.out.println(MessageFormat.format("File Length: {0}", Arrays.toString(file.getBytes())));
System.out.println("File Type: " + file.getContentType());
upload.setContent(file.getBytes());
upload.setDocId(id++);
upload.setError(null);
upload.setName(file.getName());
upload.setSize(file.getSize());
fileList.put(upload.getDocId().toString(), upload);
} catch (Exception e)
{
System.out.println("Error occurred: " + e);
upload.setError("500: Something went wrong!");
}
}
return upload;
}
and client side code like this:
function processFileUpload()
{
console.log("fileupload clicked");
var formData = new FormData();
formData.append("file", files[0]);
$.ajax({dataType: 'json',
url: "/SpringJqueryFileUpload/upload",
data: formData,
type: "POST",
enctype: 'multipart/form-data',
processData: false,
contentType: false,
success: function (result) {
alert('success' + JSON.stringify(result));
},
error: function (result) {
alert('error' + JSON.stringify(result));
}
});
}
When I do submit, the server responds with this:
java.lang.IllegalArgumentException: No converter found for return value of type: class com.upload.model.Upload
I am wondering with error. Could I be missing something here??
I would try changing your annotation to:
#RequestMapping(method = RequestMethod.POST, produces=MediaType.APPLICATION_JSON_VALUE)
And make sure you have Jackson (which Spring uses for JSON serialization) properly on your path. Also, make sure your Upload class is serializable, e.g. is not private or anything like that. If it is just a normal Java bean type class it should be fine.
Lastly, if that doesn't work you can turn on Spring debug logs with something like:
log4j.category.org.springframework.web=ALL
in your log4j.properties file.

Why ModelBinding don't work with FormData but works with RequestPayload?

I have been working with Web API and found an interesting observation that I am not able to understand.
controller:
public class UserController: ApiController
{
public void Post(MyViewModel data)
{
//data is null here if pass in FormData but available if its sent through Request Payload
}
}
viewModel
public class MyViewModel{
public long SenderId { get; set; }
public string MessageText { get; set; }
public long[] Receivers { get; set; }
}
JS that is not working
var usr = {};
usr.SenderId = "10";
usr.MessageText = "test message";
usr.Receivers = new Array();
usr.Receivers.push("4");
usr.Receivers.push("5");
usr.Receivers.push("6");
$.ajax(
{
url: '/api/User',
type: 'POST',
data: JSON.stringify(usr),
success: function(response) { debugger; },
error: function(error) {debugger;}
});
JS that is working
var usr = {};
usr.SenderId = "10";
usr.MessageText = "test message";
usr.Receivers = new Array();
usr.Receivers.push("4");
usr.Receivers.push("5");
usr.Receivers.push("6");
$.post( "/api/User", usr)
.done(function( data ) {
debugger;
});
So, if I pass on $.ajax with lots of other configuration like type, contentType, accept etc, it still don't bind model correctly but in case of $.post it works.
Can anybody explain WHY?
Try looking at what gets POSTed when you try it with $.ajax (e.g. with Fiddler of F12 tools of your choice). It can very well be that jQuery passes the data as URL-encoded string rather that as JSON literal.
To fix the issue try specifying dataType together with contentType parameter. Also, I don't think you need JSON.stringify, just pass the JSON literal you're creating:
$.ajax({
data: usr,
dataType: 'json',
contentType: 'application/json',
/* The rest of your configuration. */
});
Here's the TypeScript method that we use in one of our projects (ko.toJSON returns a string representing a JSON literal passed as a method parameter):
public static callApi(url: string, type?: string, data?: any): RSVP.Promise {
return new RSVP.Promise((resolve, reject) => {
$.ajax('/api/' + url, {
type: type || 'get',
data: data != null ? ko.toJSON(data) : null,
dataType: 'json',
contentType: 'application/json; charset=utf-8',
success: () => {
resolve.apply(this, arguments);
},
error: () => {
reject.apply(this, arguments);
}
});
});
}
Hope this helps.

building, sending, and deserializing a json object that contains arrays

I'm reaaallly new at JSON, but here's what I got. I needed to create an object that contains an array/list and a couple flat fields. For example:
var names= new Array();
names[0] = "Christy";
names[1] = "Jeremy";
var obj = {
names: names,
age: "21+",
comment: "friends"
};
I then stringify it and attempt to send it to a pagemethod via AJAX:
var jsonData = JSON.stringify(obj);
sendData(obj);
And then the send:
function sendData(jsonData) {
$.ajax({
type: "POST",
url: "Default.aspx/TestArray",
data: jsonData,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
alert('win');
},
error: function (a, b, ex) {
alert('fail');
}
});
}
so all together:
$(document).ready(function () {
$("#btnSubmit").click(function (e) {
e.preventDefault();
var names = new Array();
names[0] = "Christy";
names[1] = "Jeremy";
var obj = {
names: names,
age: "21+",
comment: "friends"
};
var jsonData = JSON.stringify(obj);
sendData(jsonData);
});
function sendData(jsonData) {
$.ajax({
type: "POST",
url: "Default.aspx/TestArray",
data: jsonData,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
alert(msg.d);
},
error: function (a, b, ex) {
alert("oops: " + ex);
}
});
}
});
I'm not sure if I'm doing this right. It doesn't even get to the webmethod, rather it goes straight to the error function. But just for the sake of conversation, this is what I have in the codebehind:
[WebMethod()]
public static string TestArray(string guids)
{
Comment cmt = (Comment)JsonConvert.DeserializeObject(guids, typeof(Comment));
return "Winner";
}
And of course class im trying to deserialize into:
public class Comment
{
public List<string> names { get; set; }
public string age { get; set; }
public string comment { get; set; }
}
According to the signature of your web method:
public static string TestArray(string guids)
you must send a single string argument whereas you are sending an entire complex JSON object which doesn't match. So:
var jsonData = JSON.stringify({ guids: 'foo bar' });
Now if you wanted to send a complex structure use:
public static string TestArray(Comment comment)
and then:
var names = new Array();
names[0] = "Christy";
names[1] = "Jeremy";
var obj = {
names: names,
age: "21+",
comment: "friends"
};
var jsonData = JSON.stringify({ comment: obj });
Also inside your web method don't do any JSON serialization/deserializations. That's infrastructure stuff that's handled for you by the framework. So to recap:
[WebMethod]
public static string TestArray(Comment comment)
{
return "Winner";
}
and then:
var names = new Array();
names[0] = "Christy";
names[1] = "Jeremy";
var obj = {
names: names,
age: "21+",
comment: "friends"
};
var jsonData = JSON.stringify({ comment: obj });
$.ajax({
type: "POST",
url: "Default.aspx/TestArray",
data: jsonData,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
// Notice the .d property. That's ASP.NET PageMethods
// serialize the response.
alert(msg.d);
},
error: function (a, b, ex) {
alert('fail');
}
});
Also in order to be able to easily debug such problems in the future I would very strongly recommend you using a javascript debugging tool such as FireBug which shows you any potential js errors that you might have as well as all network traffic including AJAX requests.
You're json data object does not have to be Stringified. JQuery automatically creates a json object for it:
var jsonData = JSON.stringify(obj);
sendData(jsonData);
Can become:
sendData(obj);
Also, in code behind you use JsonConvert from the JSON.Net library, .NET also has a (somewhat limited) JSON parser called JavaScriptSerializer. That way you can use something like:
public static string TestArray(string guids)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
// Deserialize
Comment cmt = serializer.Deserialize<Comment>(guids);
}

Categories