How to Bind a Model that contains an IEnumerables class - asp.net-core

I have a front end calling an axios request to a web api in ASP.NET Core and for some reason my model will not bind. I am sending email data to the web api and trying to bind it to an email and contact model. It just keeps showing the email object as null. My axios request seems to be working okay as it does send the payload but errors out in the API due to the email object being null.
JS
axios({
method: 'post',
url: window.location.origin + '/MainReview/SendEmail',
data: {
From: this.emailFrom,
To: this.emailToModel,
Cc: this.emailCcModel,
Bcc: this.emailBccModel,
Subject: this.emailSubject,
Body: this.emailBody,
},
headers: {
'Content-Type': 'multipart/form-data',
"RequestVerificationToken": document.forms[0].querySelector('input[name="__RequestVerificationToken"]').value,
}
}).then(response => {
}).catch(error => {
console.log(error.response.data.error);
});
Web API
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SendEmail([FromBody] Email email)
{
System.Diagnostics.Debug.WriteLine(email.From);
return Ok(new { Result = email.From });
}
Model:
public class Email
{
public string? From { get; set; }
public IEnumerable<Contact>? To { get; set; }
public IEnumerable<Contact>? Cc { get; set; }
public IEnumerable<Contact>? Bcc { get; set; }
public string? Subject { get; set; }
public string? Body { get; set; }
}
public class Contact
{
public string EmailAddress { get; set; }
}
Json data sent in payload:
{ "From":"emailfrom#example.com", "To":[ { "EMAIL_ADDRESS":"emailTo#example.com" }, { "EMAIL_ADDRESS":"emailCC#example.com" } ], "Cc":[ { "EMAIL_ADDRESS":"emailCC2#example.com" } ], "Bcc":[ ], "Subject":"Temporary Placeholder", "Body":"tessdf" }
I believe my issue is something with the IEnumerable. I will be honest I have stayed away from models but I figure I will start trying to use them more since its best practice. However, this is driving me nuts as I do not see why this would not work.

1.You use [FromBody] in backend and post json data in frontend, the Content-Type should be application/json.
headers: {
'Content-Type': 'application/json', //change here....
"RequestVerificationToken": document.forms[0].querySelector('input[name="__RequestVerificationToken"]').value,
}
2.The posted json data is not correct because the property in Contact model is named EmailAddress, but you post json with name EMAIL_ADDRESS.
data: { "From":"emailfrom#example.com", "To":[ { "EmailAddress":"emailTo#example.com" }, { "EmailAddress":"emailCC#example.com" } ], "Cc":[ { "EmailAddress":"emailCC2#example.com" } ], "Bcc":[ ], "Subject":"Temporary Placeholder", "Body":"tessdf" },

Ensure the structure matches methods parameter class.
Here, we've got an email parameter, which is an object {}. And it also has properties which are a list of objects [{}].
data: {
From: this.emailFrom,
To: [{ EmailAddress: this.emailToModel }],
Cc: [{ EmailAddress: this.emailCcModel }],
Bcc: [{ EmailAddress: this.emailBccModel }],
Subject: this.emailSubject,
Body: this.emailBody,
}
Note: If you were passing more than one parameter you need to specify the parameter name as well, with an extra set of {}.
data: {
email: {
From: this.emailFrom,
To: [{ EmailAddress: this.emailToModel }],
Cc: [{ EmailAddress: this.emailCcModel }],
Bcc: [{ EmailAddress: this.emailBccModel }],
Subject: this.emailSubject,
Body: this.emailBody,
},
parameter2:"XXXXXXXXX"
}

Related

ASP.NET Core AJAX POST not returning error, however, not saving data

I am using a button OnClick event to try and save a record to a database using AJAX in ASP.NET Core. The function is not returning an error, however, the data is not being saved. I am just trying to test with hard coded data first. A record with AdapollingProjectProcessStatusId = 1 exists in the database.
function SendHtmlEditorValueToController(data) {
$.ajax({
type: 'POST',
url: '#Url.Action("AJAXPost", "LiveAdapollingProjectProcessStatus")',
contentType: "application/json",
data: JSON.stringify({ "id": 1, "status": 'test'}),
dataType: 'json',
success: () => {
console.log("value is sent");
},
error: (error) => {
console.log(JSON.stringify(error));
}
});
}
LiveAdapollingProjectProcessStatusController:
[HttpPost]
public JsonResult AJAXPost(int id, string status)
{
LiveAdapollingProjectProcessStatus processstatus = new LiveAdapollingProjectProcessStatus
{
AdapollingProjectProcessStatusId = id,
AdapollingProjectProcessStatus = status
};
//save it in database
return Json(processstatus);
}
LiveAdapollingProjectProcessStatus.cs:
namespace CPSPMO.Models.PMO
{
public partial class LiveAdapollingProjectProcessStatus
{
public int AdapollingProjectProcessStatusId { get; set; }
public string AdapollingProjectProcessStatus { get; set; }
}
}
Please let me know if you are able to help me with this AJAX Post.
Thanks
Not sure how do you store it to the database, but the way you pass parameter to backend by ajax should be like below:
function SendHtmlEditorValueToController(data) {
$.ajax({
type: 'POST',
url: '#Url.Action("AJAXPost", "LiveAdapollingProjectProcessStatus")',
//contentType: "application/json", //remove this...
data:{ "id": 1, "status": 'test'}, //modify here...
dataType: 'json',
success: () => {
console.log("value is sent");
},
error: (error) => {
console.log(JSON.stringify(error));
}
});
}
After reviewing the comments regarding missing code for saving the data in the database, I modified the controller:
[HttpPost]
public JsonResult AJAXPost(int id, string status)
{
LiveAdapollingProjectProcessStatus processstatus = new LiveAdapollingProjectProcessStatus
{
AdapollingProjectProcessStatusId = id,
AdapollingProjectProcessStatus = status
};
//save it in database
var result = _context.LiveAdapollingProjectProcessStatuses.Update(processstatus);
_context.SaveChanges();
return Json(processstatus);
}
It is saving the data to the database now. Thanks for the help

Why is the model binding not working? ASP.NET Core 5.0 RazorPages

ASP.NET Core 5.0 Razor Pages.
When posting the array, the model is not binding - value is showing empty array.
This is a JSON array posted using Ajax -
[
{ "Order":1, "FileName":"bbed5ecf-4133-4681-b0f3-c11366ad3186.jpg" },
{ "Order":2, "FileName":"737e60dc-0631-493d-947d-2f5948d7818c.jpg" },
{ "Order":3, "FileName":"6c76f9d1-44bd-4b80-926e-2ce4307eb30b.jpg"}
]
function UpdateImageOrder() {
$.ajax({
type: "POST",
url: "/property/imagesorter",
dataType: "json",
headers:
{
"RequestVerificationToken": $('input:hidden[name="__RequestVerificationToken"]').val()
},
data: JSON.stringify(newOrderDictionary),
success: function (response) {
}
});
}
RazorPage action Method
public async Task OnPostAsync(ImageOrder[] data)
{
}
public class ImageOrder
{
public int Order { get; set; }
public string FileName { get; set; }
}
The data parameter of the POST should be the object value, not a stringified version of it. Try changing to:
...
data: newOrderDictionary,
...
Assuming newOrderDictionary is an array.
Considering using post to pass data, you need to add contentType:"application/json" in ajax.
$.ajax({
type: "POST",
url: "/?handler",
dataType: "json",
contentType:"application/json",
//...
});
In addition, add [FromBody] in the bakend.
public async Task OnPostAsync([FromBody]ImageOrder[] data)
{
}
Then, it can get the data.
Turns out that this works :
public async Task OnPostAsync(ImageOrder[] order)
{
}
function UpdateImageOrder() {
$.ajax({
type: "POST",
url: "/property/imagesorter",
dataType: "json",
headers:
{
"RequestVerificationToken": $('input:hidden[name="__RequestVerificationToken"]').val()
},
data: {
order: newOrderDictionary
},
success: function (response) {
}
});
}

Send additional info to server in uploading image process

im using filepond 4.25.1 on vue 2.6.11 and everything work without problem until now.
i want to send additional information to my server which is aspnet core 3. i send my request from filepond like below
myServer: {
url: "http://**********/api/CustomerAuth/",
process: {
url: "uploadimages",
method: "POST",
withCredentials: false,
headers: {},
data: {
nationalcode: "1234567890",
typecode:"1"
},
timeout: 7000,
},
load: (source, load) => {
fetch(source)
.then((res) => res.blob())
.then(load);
},
}
and server side
[HttpPost("uploadimages")]
public IActionResult UploadImages()
{
try
{
var file = Request.Form.Files[0];
string folderName = "Upload";
string webRootPath = _hostingEnvironment.WebRootPath;
string newPath = Path.Combine(webRootPath, folderName);
if (!Directory.Exists(newPath))
{
Directory.CreateDirectory(newPath);
}
if (file.Length > 0)
{
string fileName =
ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
string fullPath = Path.Combine(newPath, fileName);
using (var stream = new FileStream(fullPath, FileMode.Create))
{
file.CopyTo(stream);
}
}
return Ok("Upload Successful");
}
catch (System.Exception ex)
{
return NotFound(new { img_upld_error = ex.Message });
}
}
in server side i need to access "nationalcode" and "typecode" which is send as data in process and value of these two parameters always change so its not static value and with interact of user value of this two always change.
I really appreciated if someone give me a some clue or guide me to solve my problem.
FilePond dev here.
data does not exist as a prop on process.
You can add additional FormData parameters with the ondata property. See updated example below:
myServer: {
url: "http://**********/api/CustomerAuth/",
process: {
url: "uploadimages",
method: "POST",
withCredentials: false,
headers: {},
data: {
nationalcode: "1234567890",
typecode:"1"
},
ondata: (formData) => {
formData.append('nationalcode', '1234567890');
formData.append('typecode', '1');
return formData;
}
timeout: 7000,
},
load: (source, load) => {
fetch(source)
.then((res) => res.blob())
.then(load);
},
}
Alternatively you can use the filepond metadata plugin to add metadata to each file (this is automatically sent to the server).
https://pqina.nl/filepond/docs/patterns/plugins/file-metadata/
FilePond.setOptions({
fileMetadataObject: {
'nationalcode': '1234567890',
'typecode': '1'
}
})
You can get file's in model, define your model like this
public class FileWithDataModel
{
public IFormFile File { get; set; }
public string NationalCode { get; set; }
public string TypeCode { get; set; }
}
and controller method will be :
public async Task<IActionResult> UploadFileWithData(FileWithDataModel model)
{
var file = model.File;
//you can save this file...
var nCode = model.NationalCode; //can access data easy
//......
return Ok();
}
Microsoft suggest to use Async method especially for file processing and uploading
here is example of jquery client
var form = new FormData();
form.append("NationalCode", "12345678");
form.append("TypeCode", "1");
form.append("File", fileInput.files[0], "/path/to/file");
var settings = {
"url": "http://**********/api/CustomerAuth/",
"method": "POST",
"timeout": 0,
"headers": {
"Content-Type": "application/x-www-form-urlencoded"
},
"processData": false,
"mimeType": "multipart/form-data",
"contentType": false,
"data": form
};
$.ajax(settings).done(function (response) {
console.log(response);
});

I cannot find a way to update calendar events on mssql. Posting to my razor page does not work (400 error)

Code to update with a custom button in the calendar:
myCustomButton: {
text: 'Save Events',
click: () => {
var allevents = calendar.getEvents();
$.ajax({
type: 'POST',
contentType: 'application/json',
dataType: 'json',
data: allevents,
url: '/Wishes/Individual/Update',
headers: {
'RequestVerificationToken': '#antiforgery.GetAndStoreTokens(HttpContext).RequestToken'
},
success: function (response) {
alert: ('success');
},
failure: function (response) {
alert: ('failure');
}
});
}
},
According to your description and code, it seems that you'd like to post all events within FullCalendar to your Razor Page handler by making AJAX Post Request(s), and then save/update events on database.
To achieve the requirement, you can refer to the following example.
On JavaScript Client
customButtons: {
myCustomButton: {
text: 'Save Events',
click: function () {
var allevents = calendar.getEvents();
var events = [];
$.each(allevents, function (index, event) {
//console.log(event);
// include only expected data (such as title, start and end etc) in json object `newevent`
// instead of all information of calendar event
var newevent = { "title": event.title, "start": event.start, "end": event.end };
events.push(newevent);
});
$.ajax({
type: 'POST',
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify(events),
url: '/Wishes/Individual/Update',
headers: {
'RequestVerificationToken': '#antiforgery.GetAndStoreTokens(HttpContext).RequestToken'
},
success: function (response) {
alert: ('success');
},
failure: function (response) {
alert: ('failure');
}
});
alert('clicked the custom button!');
}
}
},
Razor page handler method
public IActionResult OnPostUpdate([FromBody]List<Event> events)
{
//code logic here
return Content("ok");
}
Custom Event class
public class Event
{
public string title { get; set; }
public string start { get; set; }
public string end { get; set; }
// defind other properties
// such as groupId, url etc based on your requirements
}
Test Result

JSON not mapped to Class

I have a asp.net core 3.0 application with WebPages and I try to send a JSON via ajax to my handler. But the parameter is always null or just filled with the default values. I tried to if the JSON string from the post is parseable and it works when I parse manually.
This is my controller:
public void OnPost([FromBody]FilterModel filter)
{
Console.Write(filter);
}
This is the model class:
public class FilterModel
{
public DateTime TimeRangeFrom { get; set; }
public DateTime TimeRangeTo { get; set; }
public int CustomerId { get; set; }
}
The ajax request:
$.ajax({
url: "/CustomerOverview/Test",
type: "POST",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: JSON.stringify({
CustomerId: customerId,
TimeRangeFrom: picker.startDate,
TimeRangeTo: picker.endDate
}),
beforeSend: function(xhr) {
xhr.setRequestHeader("XSRF-TOKEN",
$('input:hidden[name="__RequestVerificationToken"]').val());
},
success: function(response) {
button.hide();
},
failure: function(response) {
alert(response);
}
});
And the JSON as logged in Chrome Dev Tools:
{CustomerId: "1", TimeRangeFrom: "2019-08-26T22:00:00.000Z", TimeRangeTo: "2019-09-25T21:59:59.999Z"}
Do I have to setup a mapping or something?
FromBody attribute will parse the model the default way which in most cases are sent by the content type application/json from the request body.So if you fill a form and pass the form data to the controller action,you need to convert the JavaScript object into a string with JSON.stringify().
FromForm attribute is for incoming data from a submitted form sent by the content type application/x-www-url-formencoded.
Reference:https://learn.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-3.0#sources
Turns out that it can't convert an "1" to an int. I had to convert the value for customerId to a int first so that I have this JSON:
{
"CustomerId": 1,
"TimeRangeFrom": "2019-08-26T22:00:00.000Z",
"TimeRangeTo": "2019-09-25T21:59:59.999Z"
}