I am working on an Asp Core Project and trying to Ajax post a filepicker blob Javascript Object back to a action to upload the image to Amazon S3. I have tried several different ways and haven't been able to get this to work. It appears to be a model binding issue.
This functionality worked when I was only posting the image url to the action with a string parameter, but I need the filename also.
Issue: model object in action is null
Ajax Code:
function pickit() {
//Start the filepicker
filepicker.pick(
{
//cropRatio: 4 / 3,
////cropMin: [200, 100],
//cropForce: true,
mimetype: 'image/*',
//services: ['CONVERT', 'COMPUTER', 'FACEBOOK', 'FLICKR', 'DROPBOX', 'INSTAGRAM', 'BOX', 'EVERNOTE', 'GMAIL', 'PICASA', 'IMAGE_SEARCH', 'FTP', 'GOOGLE_DRIVE', 'SKYDRIVE', 'URL', 'WEBCAM', 'CLOUDDRIVE', 'IMGUR', 'CLOUDAPP'],
conversions: ['crop', 'rotate', 'filter', 'scale']
},
function (Blob) {
$("#imagespinner").removeClass("invisible")
$.ajax({
type: "POST",
url: '#Url.Action("CreateImage","Photos")',
contentType: 'application/json',
dataType: "json",
data: JSON.stringify(Blob),
success: function (data, textStatus, jqXHR) {
$("#ImageURL").val(data);
var img = document.getElementById("imgImageURL");
img.src = data;
$("#imagespinner").addClass("invisible")
},
error: function (jqXHR, textStatus, error) {
alertify.alert(jqXHR.responseText);
$("#imagespinner").addClass("invisible")
}
});
setTimeout(function () { $('#image-loading-message').fadeOut() }, 500);
},
function (FPError) {
},
function (FPProgress) {
}
);
};
Controller Action:
[HttpPost]
public async Task<JsonResult> CreateImage(FilePickerViewModel model)
{
if (!String.IsNullOrWhiteSpace(model.url))
{
//Code to Upload to S3
return Json("ImageURL");
else
{
Response.StatusCode = 400;
return Json("Url is needed");
}
}
ViewModel:
public class FilePickerViewModel
{
public Int32 id { get; set; }
public String url { get; set; }
public String filename { get; set; }
public Int32 size { get; set; }
public String client { get; set; }
public String mimetype { get; set; }
public Boolean isWriteable { get; set; }
}
Thank you for any help
I figured out the issue, the controller action needs to have the declaration of [FromBody] since this is coming from an ajax post
Related
I have 2 tables, one is a one to one or one to zero relationship:
public class ComponentText
{
[Key]
public int ComponentTextId { get; set; }
public string ComponentContent { get; set; }
public ComponentTextSection ComponentTextSection { get; set; }
}
public class ComponentTextSection
{
[Key]
public int ComponentTextId { get; set; }
public string SectionTitle { get; set; }
public ComponentText ComponentText { get; set; }
}
I can add row fine using usual .net core posting using a form, here is code which does this:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var newComponentText = new ComponentText();
if (await TryUpdateModelAsync<ComponentText>(
newComponentText,
"ComponentText",
i => i.ComponentTextSection, i => i.ComponentContent))
{
_context.ComponentText.Add(newComponentText);
await _context.SaveChangesAsync();
}
return RedirectToPage("./Index");
}
I need to update though via ajax, but I am having issues updating the SectionTitle using this current way. It adds the data to the ComponentText table fine but not the ComponentTextSection table. Here is my ajax code:
function saveWindow() {
var ComponentText = { "ComponentText.ComponentTextSection.SectionTitle": $("#ComponentText_ComponentTextSection_SectionTitle").val(),
"ComponentTextId": $("#ComponentText_ComponentTextId").val(),
"ComponentContent": $("#ComponentText_ComponentContent").val()};
$.ajax({
type: 'PUT',
contentType: 'application/json; charset=utf-8',
headers: {
'RequestVerificationToken': '#AntiForgery.GetAndStoreTokens(HttpContext).RequestToken'
},
data: JSON.stringify(ComponentText),
url: '#Url.Page("Edit", "demo4")',
success: function (result) {
closeWindow();
}
});
}
It must be to do with ComponentText.ComponentTextSection.SectionTitle but I have been trying lots of things to get this to work but failing. Does anyone know how to pass a one to one related data via ajax?
Thanks
For the one-to-one model, if you want to use ajax to transfer the complex nested model to the page, you need to create the same as the ComponentText model structure in saveWindow function of ComponentText variable.
<script>
function saveWindow() {
var ComponentText = {
"ComponentTextSection":
{
"SectionTitle": $("#ComponentText_ComponentTextSection_SectionTitle").val(),
},
"ComponentTextId": 1,
"ComponentContent": $("#ComponentText_ComponentContent").val()
};
$.ajax({
type: 'PUT',
contentType: 'application/json; charset=utf-8',
headers: {
'RequestVerificationToken':
'#AntiForgery.GetAndStoreTokens(HttpContext).RequestToken'
},
data: JSON.stringify(ComponentText),
url: '#Url.Page("Edit", "demo4")',
success: function (result) {
closeWindow();
}
});
}
</script>
Here is the test result:
I can not send a Model that I am manually creating to my controller. When I send the request, it's coming up with empty properties. There is something wrong that is hindering the conversion. Does anyone know how to help me?
var operadoraChamadas = {
Id: 0,
Descricao: 'rssrrssr',
PadraoSistema: true
};
var requestData = { operadoraChamadasViewModel: operadoraChamadas}
$.ajax({
url: "/pessoa-gerenciar/changeFormaContato",
type: "POST",
data: JSON.stringify(requestData),
contentType: "application/json",
dataType: "json",
success: function (result) {
alert('ok');
},
error: function () {
alert("Oops! Algo deu errado.");
console.log(requestData);
}
});
[HttpPost]
[Route("pessoa-gerenciar/changeFormaContato")]
public IActionResult changeFormaContato(OperadoraChamadaViewModel operadoraChamadaViewModel)
{
//ViewBag.indice_new = indice;
//return PartialView("~/Views/Pessoa/PessoaContato/_PessoaContatoAdd.cshtml", _pessoaContatoAppService.CreateNew(pessoaNatureza, formaContatoId));
return null;
}
ViewModel:
public class OperadoraChamadaViewModel
{
[Key]
[DisplayName("ID")]
public int Id { get; set; }
[Required(ErrorMessage = "A Descrição é obrigatória")]
[MaxLength(50)]
[DisplayName("Descricao")]
public string Descricao { get; set; }
[DisplayName("Padrão do Sistema")]
public bool PadraoSistema { get; set; }
}
ASP.NET Core requires to add [FromBody] attribute to parameter to parse application/json content
[HttpPost]
[Route("pessoa-gerenciar/changeFormaContato")]
public IActionResult changeFormaContato([FromBody] OperadoraChamadaViewModel operadoraChamadaViewModel)
I have this function in my view:
function EditButtonClick(e1) {
var urlString = '#Url.Action( "Create", "Group")';
var groupItem = {
GroupCode: e1.getAttribute("data-GroupCode"),
GroupType: e1.getAttribute("data-GroupType"),
Description: e1.getAttribute("data-Description"),
InternalNotes: e1.getAttribute("data-InternalNotes"),
StatusCode: e1.getAttribute("data-StatusCode"),
Edit: "true"
};
$.ajax({
type: 'POST',
url: urlString,
data: { '': groupItem },
dataType: 'json'
}).fail(function () {
alert('Edit process failed');
});
}
My view model looks like this:
[Serializable]
public class GroupItem : ApplicationModel, IValidatableObject
{
public GroupItem() { }
[DisplayName("Group Code")]
public int GroupCode { get; set; }
public string GroupTypeDescription { get; set; }
[DisplayName("Group Type")]
public int GroupType { get; set; }
[DisplayName("Group Name")]
public string Description { get; set; }
[DisplayName("Internal Notes")]
public string InternalNotes { get; set; }
public string PartialInternalNotes { get; set; }
public string Status { get; set; }
[DisplayName("Status Code")]
public int StatusCode { get; set; }
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy HH:mm:ss}")]
public DateTime EnterTime { get; set; }
public string UserId { get; set; }
public string ActionType { get; set; }
public bool Edit { get; set; }
and, finally, my action looks like this:
[HttpPost]
public ActionResult Create(GroupItem groupItem)
{
if (ModelState.IsValid)
{
_groupService.SaveGroup(groupItem);
return RedirectToAction("List", "Group", new { showAll = false });
}
ViewBag.GroupTypeList = _MasterDataService.GetCodeMasterList((int)Constants.CodeType.GroupType);
ViewBag.StatusList = _MasterDataService.GetCodeMasterList((int)Constants.CodeType.GroupStatus);
if (groupItem.GroupCode > 0)groupItem.Edit = true;
return this.RazorView(groupItem);
}
Now, I put a break point in the view just before the ajax call, and another in the controller at the top of the action. The properties that I have set up in my groupItem object in the view are all populated as expected, according to an inspection just before the ajax call. However, at the breakpoint in the action, all the properties in the GroupItem argument are default values.
In other code in another view, I call this very same Action method via a form submit. In that case, all the properties are populated as expected. What am I missing in my ajax call?
You need to use JSON.stringify to first serialize your object to JSON, and then specify the contentType so your server understands it's JSON data.
So you complete AJAX function call would become,
$.ajax({
type: 'POST',
url: urlString,
data: JSON.stringify(groupItem),
contentType: "application/json",
dataType: 'json'
}).fail(function () {
alert('Edit process failed');
});
try this:
$.ajax({
type: 'POST',
url: urlString,
data: { 'groupItem': groupItem },
dataType: 'json'
}).fail(function () {
alert('Edit process failed');
});
I am trying to pass a simple JSON object to a controller using MVC3 and JSON. The object gets passed but I loose all the properties. I see all the properties in firebug on the request but am not sure why I am loosing them on the server. Do all the properties of the object need to be set in order for the mapping to work? I am using MVC3 so the binding should be build in. What am I missing?
Class:
[Serializable]
public class StoryNote
{
public int Id { get; set; }
public string Note { get; set; }
public Nullable<int> StoryCardId { get; set; }
public string CreatedBy { get; set; }
public Nullable<System.DateTime> CreateDate { get; set; }
public virtual StoryCard StoryCard { get; set; }
}
JSON:
$(document).ready(function () {
$('#newNote').click(function (e) {
e.preventDefault();
var storynote = {
StoryNote: {
Note: $('#Note').val(),
StoryCardId: $('#StoryCard_Id').val(),
CreatedBy: 'Xyz', }
};
$.ajax({
url: '#Url.Action("PostNote")',
type: 'POST',
data: JSON.stringify(storynote),
dataType: 'json',
contentType: 'application/json; charset=utf-8',
success: function (data) {
$('#previousNotes').append(data.Note + '<br/>' + data.CreatedBy + '<br/><hr/>');
},
});
});
});
Controller:
[HttpPost]
public JsonResult PostNote(StoryNote newStoryNote)
{
StoryNote newNote = new StoryNote { Note = newStoryNote.Note, CreatedBy = newStoryNote.CreatedBy, StoryCardId = newStoryNote.StoryCardId, CreateDate = DateTime.Now };
db.StoryNotes.Add(newStoryNote);
return Json(newStoryNote, JsonRequestBehavior.AllowGet);
}
You have a name mismatch in your code - parameter is named "StoryNote" in Javascript code and "newStoryNote" in Controller. Those names should be equal. I believe if you change
var storynote = {
StoryNote: {
Note: $('#Note').val(),
StoryCardId: $('#StoryCard_Id').val(),
CreatedBy: 'Xyz', }
};
to
var storynote = {
newStoryNote: {
Note: $('#Note').val(),
StoryCardId: $('#StoryCard_Id').val(),
CreatedBy: 'Xyz', }
};
then your code should work.
In server side I have following class
public class EditorContext
{
public Module Module { get; set; }
public Holder Holder { get; set; }
}
public class Module
{
public string CodeName { get; set; }
public string Content { get; set; }
}
public class Holder
{
public int Id { get; set; }
public int Type { get; set; }
}
public class EditorController : Controller
{
[HttpPost]
public ActionResult AddModule(EditorContext context)
{
return Json(new { });
}
}
From the client I send request like this
var data =
{
Module:
{
CodeName: 1,
Content: 2
},
Holder:
{
Type: 3,
Id: 4
}
};
$.ajax({
type: "POST",
url: 'Editor/AddModule',
data: JSON.stringify(data),
async: false,
success: function () {
},
error: function (xhr, status, error) {
throw new Error();
}
});
1 - Fiddler shows that he sent {"Module":{"CodeName":1,"Content":2},"Holder":{"Type":3,"Id":4}}, but in server Request.Form = %7b%22Module%22%3a%7b%22CodeName%22%3a1%2c%22Content%22%3a2%7d%2c%22Holder%22%3a%7b%22Type%22%3a3%2c%22Id%22%3a4%7d%7d, WHY?
2 - If instead of "JSON.stringify(data)" I use "postify" like in here, so EditorController.AddModule gets already filled EditorContext. This postify change the data to "Model.CodeName=1&Model.Content=2&Holder.Type=3&Holder.Id=4". So, why in this way EditorContext is filled automatically by default binder and in (1) it doesn't?
Thank you
You need to tell the binder that your data is JSON, otherwise it has no idea it should be parsing it as such. Add the correct content type header to your request:
$.ajax({
type: "POST",
url: 'Editor/AddModule',
data: JSON.stringify(data),
contentType: "application/json",
async: false,
success: function () {
},
error: function (xhr, status, error) {
throw new Error();
}
});