More Ajax / MVC weirdness
I've been puzzling over some odd behaviour with my MVC2 project for the last few nights.
I have an MVC action result that accepts a project ID an开发者_JS百科d a complex Json object, looking like this:
[HttpPost]
public JsonResult AddStory(int projectid, Story story)
{
try
{
Project prj = repository.Single(p => p.ID == projectid);
//prj.Stories.Add(story);
//repository.SaveChanges();
return Json(new { Result = story });
}
catch (Exception ex)
{
ErrorSignal.FromCurrentContext().Raise(ex);
return Json(new { Result = 0 });
}
}
My jQuery code for sending the complex object looks like this so far (complex object has more properties than this, but am trying only these two for debugging reasons):
$.ajax({
url: '/Project/1/AddStory',
data: { Summary: myStory.Summary, Size: myStory.Size },
dataType: 'json',
processData: false,
traditional: true,
type: 'POST'
});
My problem is that no matter how I post this object; even if I send it to a different controller and action, the request never seems to hit the server and the page is automatically redirected to the following URL:
http://localhost:57932/Project/1/Board?story.Summary=Test+description&story.Size=8&story.Priority=2&story.Deadline=08%2F31%2F2010&story-owner=http%3A%2F%2Ftestaccount.myopenid.com
Firebug's console shows an error in jQuery.min.js with the right headers, but no POST or RESPONSE values.
I've tried looking at the traffic in Fiddler and I can see that the request headers appear well-formed:
Accept application/json, text/javascript, /
And there is definitely an object being sent in the query string. So what am I missing? I'm sure there has to be a simple reason why things are getting so borked.
Edit:
Routes (from Global.asax) are the following plus the default mapped route:
routes.MapRoute(
"Project",
"Project/{projectid}/{action}/{id}",
new { controller = "Project", action = "Index", id = "" });
Turns out the solution is simple. The ajax call in this current shape simply doesn't send any POST data due to processData = false
setting. Change the jQuery to this:
$.ajax({
url: '/Project/1/AddStory',
data: { Summary: myStory.Summary, Size: myStory.Size },
dataType: 'json',
traditional: true,
type: 'POST'
});
The jQuery API doco is a bit obtuse:
processData
Default: trueBy 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.
My interpretation is that it it's false, $.ajax
will not convert the data object as name value parameters. Not sure what it's used for, but setting it to false seems like a bad idea :)
I don't have your full source code, so it is difficult to say why you have request redirection from '/Project/1/AddStory' to the
http://localhost:57932/Project/1/Board?story.Summary=Test+description&...
T tried to reproduce your problem. I created a small MVC 2 aplication with the action AddStory
in the controller Project
which looks like yours:
[HttpPost]
public JsonResult AddStory (int projectid, Story story) {
return Json (new {
Result = story,
myNewProjectid = projectid,
myNewSummary = story.Summary + ". " + "Bla bla",
myNewSize = story.Size + 20
});
}
where Story
class I declared just like folowing:
public class Story {
public string Summary { get; set; }
public int Size { get; set; }
}
I inserted the route like you posted and added following jQuery
sctipt in the Index
view of the Home
controller:
jQuery(document).ready(function () {
var myStory = { Summary: 'Test description', Size: 8 };
$.ajax({
url: '/Project/1/AddStory',
data: { Summary: myStory.Summary, Size: myStory.Size },
dataType: 'json',
success: function (data, textStatus, xhr) {
alert('myNewSummary="' + data.myNewSummary +
'", Result.Summary="' + data.Result.Summary +
'", Result.Size=' + data.Result.Size);
},
error: function (xhr, textStatus, errorThrown) {
alert("error");
},
type: 'POST'
});
});
The code work without any problem and produce the message box with the text myNewSummary="Test description. Bla bla", Result.Summary="Test description", Result.Size=8
how expected.
You can continue to use traditional: true
if you receive problems with posting more complex data structures. The current test don't need the usage of traditional: true
.
To make you easy for you to compare your code with my working eample I placed full Visual Studio 2010 project under http://www.ok-soft-gmbh.com/ForStackOverflow/MvcApplicationJson.zip. I hope this helps you quickly to find th error in your code and fix it.
i'd suggest you use $.post
in stead
var jsonbox = { Summary: myStory.Summary, Size: myStory.Size }
$.post("/Project/1/AddStory", jsonbox, function doneit(data) {
if (data.succes == true) {
//do something like return a message 'saved, all ok'
} else {
/give an error.
}
}, "json");
i always use this style of action
[HttpPost]
public ActionResult SomeAction(int id, FormCollection collection) {
try {
//some code
} catch (Exception e) {
return Json(new { succes = false, error = "An error occured. Details: " + e.Message });
}
return Json(new { succes = true });
}
$.post
works good if you dont need anything fancy just a post, a variable to send and a function to execute when you get the result.
Ugh. PEBKAC issue.
Turns out the problem was because I'd previously had my input fields inside a set of <form>
tags and jQuery was dutifully submitting my content - which doesn't need the projectId explicitly passed since that is part of the query string and is handled by MVC - and then, upon seeing the form, was actioning a "submit" call, which redirected back to the same page.
Sorry for wasting everyone's time. There was some really useful advice and a lot of practical ideas to troubleshoot my problem with.
精彩评论