I am using jQuery to consume my action.
$.ajax({ url: '',
data: 'json',
type: 'post',
dataType: options.dataType,
async: false,
success: function (obj) { returnValue = obj; }
});
now....
if I return this one...
var test1 = Json(new { Message = "Any message here.", Status="True" });
the return on my Ajax call is JUST fine..
BUT.. if i return this one(List of users from EF)
var test2 = Json(IoC.Resolve<IUserService>().GetUsers());
I will get undefined/500 (Internal Server Error).
Now the question is how to properly return a JSON from an object so that jQuery ajax can read it properly?
Thanks
Lee,
Have never seen the usage:
var test2 = Json(IoC.Resolve<IUserService>().GetUsers());
before and am not going to comment. However, you MAY get away with merely adding a .ToList() to the end of the statament, i.e.:
var test2 = Json(IoC.Resolve<IUserService>().GetUsers().ToList());
The reason for the issue is due to the fact that you haven't yet enumerated the object out before attempting to populate the json object, therefore you experience all sorts of latency and object reference problems. By adding ToList() you mitigate these problems as the object is fully enumerated before it hits the Json() method.
it might just work, ... or blow right up :)
public ActionResult MethodName(){
var returnList = IoC.Resolve<IUserService>().GetUsers().ToList();
retun JSON(returnList,JSONRequestBehaviour.AllowGet);
}
jQuery Function
success: function (obj) {
alert(obj.d.ListProperty);
// you can access all the properties you have in the list.
}
Related
I have a really baffling issue with a customer site. We developed a C# MVC 4.0 application that's running in a subdirectory from another ASP.net application. We use a lot of Ajax calls from JQuery to controllers. Recently there's been an issue where a particular method on a controller has started returning "405 method not allowed" when doing a POST. This method is no different from any of the numerous other Ajax methods which are fine.
Edited to provide code:
Here's the offending function:
JavaScript:
function populateCitiesLike(cityTerm, fnInitialCityNames) {
var serviceBase = 'ProjectCities/';
var cityData = { term: cityTerm };
$.ajax({
type: "POST",
data: JSON.stringify(cityData),
url: serviceBase + "GetCitiesThatStartWith/",
contentType: "application/json",
success: function (result) {
$("#cityCheckboxes").empty();
if (result.length === 0) {
return;
}
addCityCheckboxes(result);
if (fnInitialCityNames != null)
fnInitialCityNames();
},
error: function () {
alert("We have an error");
}
});
}
c# controller :
[AjaxOnly,HttpPost]
public ActionResult GetCitiesThatStartWith(string term)
{
List<string> dbCities = null;
List<Cities> cityList = new List<Cities>();
dbCities = _reposProject.GetCitiesThatStartWith(term);
cityList = GetJsonFormatForCityList(dbCities);
// return Json(result);
return Json(cityList, JsonRequestBehavior.AllowGet);
}
I copied the entire web application and created a new subdirectory just to see what would happen. So for example the current application is running under main\A directory and now the cloned application is running under main\B. the method GetCitiesThatStartWith running under main\A that returns a 405 but the same method under main\B works. However there is one specific method called GetCitiesFromRegion that is always failing on both. That particular method used to work.
I don't think it's a code issue because why would one work and the other not. Resetting IIS does not work either. I can add test methods to the controller and call them from a test Ajax page and sometimes they fail and sometimes not. Once they fail with that particular method name I can no longer make it work. It's almost as if IIS remembers that the method failed and is caching the error.
updated
After spending more time with it I have discovered 2 issues. One issue is that the controllers constructor was throwing an exception because it was not authenticated at that point. I have resolved that issue.
The other issue which is baffling is that I could not get GetCitiesThatStartWith to work and a few other methods. I renamed them by appending V2 to the end of the method name and now they work.
Why would renaming a method on a controller make it work? I suspect that once the method gets an error and it stops working then I have to rename the method. Something about throwing an exception in the controller can be fatal to your method name apparently.
I think this will help you:-
I have changed the method name to CitiesThatStartWith by default it will be mapped with Get request because you are using GetCitiesThatStartWith.
Controller code
[AjaxOnly,HttpPost]
public ActionResult CitiesThatStartWith(string term)
{
List<string> dbCities = null;
List<Cities> cityList = new List<Cities>();
dbCities = _reposProject.GetCitiesThatStartWith(term);
cityList = GetJsonFormatForCityList(dbCities);
// return Json(result);
return Json(cityList, JsonRequestBehavior.AllowGet);
}
Javascript code
function populateCitiesLike(cityTerm, fnInitialCityNames) {
var serviceBase = 'ProjectCities/';
var cityData = { term: cityTerm };
$.ajax({
type: "POST",
data: JSON.stringify(cityData),
url: serviceBase + "CitiesThatStartWith/",
contentType: "application/json",
success: function (result) {
$("#cityCheckboxes").empty();
if (result.length === 0) {
return;
}
addCityCheckboxes(result);
if (fnInitialCityNames != null)
fnInitialCityNames();
},
error: function () {
alert("We have an error");
}
});
}
I can execute a query in breeze from the server (using EF) which returns a load of boostrap data thus:
em.executeQuery(_lookupsQuery).then(function (data) {
_lookups = data.results;
console.log(_lookups[0].currentUserId);
This returns currentUserId which is a guid. I then store em using local storage for querying locally later:
_lookups = [{
currentUserId: em.executeQueryLocally(_lookupsQuery.toType(breeze.DataType.String))
}];
However this does not work as it requires an entity type e.g:
em.executeQueryLocally(_lookupsQuery.toType(em.metadataStore.getEntityType("Measure")))
Since currentUserId is a guid I am not sure which type to cast the query to. I have tried to make an entity type on the client just for this but it does not seem to work. Any help on solving this would be appreciated.
Edit:
After a suggestion, I modified lookups:
[HttpGet]
public async Task<object> Lookups()
{
var currentUser = await UserManager.FindById(Guid.Parse(User.Identity.GetUserId()));
var companyId = currentUser.CompanyId.Value;
return new
{
currentUser = new
{
Id = currentUser.Id
}
};
}
When querying remotely using:
em.executeQuery(_lookupsQuery).then(function (data) {
_lookups = data.results;
console.log(_lookups[0].currentUser);
I get:
Object { id="f2dceb4b-29e7-4533-99e2-2052dc39143a"}
I set up the new entity type:
metadataStore.addEntityType({
shortName: "CurrentUser",
dataProperties: {
id: { dataType: "String", isPartOfKey: true }
}
});
but when I query locally:
_lookups = [{
currentUser: em.executeQueryLocally(_lookupsQuery.toType(em.metadataStore.getEntityType("CurrentUser"))) }];
console.log(_lookups[0].currentUser);
this returns []
What am I doing wrong?
There are a few ways you can handle it. I am only going to touch on the two most basic methods that I personally use and hopefully one sparks your interest.
-1. Create an entityType in your metadataStore for user-type information.
metadataStore.addEntityType({
shortName: "User",
dataProperties: {
userId: { dataType: "String", isPartOfKey: true },
userName: { dataType: "String" }
}
});
This will add an entity type that you can serialize your return results to. It is important to note here that if you change anything about that user without accepting changes locally it will try to save it next time you call saveChanges() so make sure you handle those situations if applicable.
Of course that isn't the only option. You can most certainly just grab that user id from the query results without Breeze ever knowing about it or what it is for.
-2. POJO
function user(data) {
var self = this;
self.UserId = data.userId;
self.UserName = data.userName;
}
// After your query returns data
query.execute().then(userReturned);
function userReturned(data) {
// data is the returned object, which contains
// an httpResponse which is what breeze returns
// as the raw response
new user(data.httpResponse.data);
}
This method basically just grabs the httpResponse when the promise returns and uses it to create a Plain Old JavaScript Object without Breeze knowing about it. Of course for this to work you need to examine your data object that is returned and find what you are looking for and serialize that.
Edit
Your query locally should look like this -
query = breeze.entityQuery().from("Whatever").toType("CurrentUser").executeLocally();
em.executeQueryLocally(query);
I want to build a client for my REST-Service using Knockout.js.
I have a lot of Repositorys i want to access through different urls - so i came up with this solution using the Revealing-Prototype-Pattern.
My problem: I can not find out how to map the ItemsProperty with my "data" i receive from my service.
var Repository = function (url) {
this.Url = url;
this.Items = ko.observableArray([]);
this.PendingItems = ko.observableArray([]);
};
Repository.prototype = function () {
var
getAllItems = function () {
var self = this;
$.getJSON(self.Url, function (data) {
// data=[{"Id":1,"Name":"Thomas","LastName":"Deutsch"},{"Id":2,"Name":"Julia","LastName":"Baumeistör"}]
ko.mapping.fromJS(data, self.Items);
});
},
...
// i call it like this:
customerRepository = new Repository('http://localhost:9200/Customer');
customerRepository.getAllItems();
I think the problem is in this: ko.mapping.fromJS(data, self.Items); but i can not find the right way to do it.
Question: what am i doing wrong? i have found an example - and they are doing the same i think: http://jsfiddle.net/jearles/CGh9b/
I believe that the two argument version of fromJS is only used for objects that were previously mapped, i.e they had an implicit empty mapping options object. Since your mapping is the first time it has been run, it needs to provider that empty options object like so.
ko.mapping.fromJS(data, {}, self.Items);
http://jsfiddle.net/madcapnmckay/j7Qxh/1/
Hope this helps.
I have the following controller action:
public ActionResult Details(string pk)
{
IEnumerable<ContentDetail> model = null;
try
{
model = _content.Details(pk);
if (model.Count() > 0)
{
return PartialView(getView(pk) + "Details", model);
}
}
catch (Exception e)
{
log(e);
}
return Content("No records found");
}
I call this with the following routine:
$.ajax({
cache: false,
url: "/Administration/" + table + "s/Details",
data: { pk: partitionKey },
dataType: 'html',
success: function (responseText) {
$('#detailData').html(responseText);
$(".updatable")
.change(function (e) {
var type = $(this).attr('id').split('_')[0];
updateField(table, $(this), type);
});
$('.dialogLink')
.click(function () {
dialogClick(this);
return false;
});
},
error: function (ajaxContext) {
ajaxOnFailure(ajaxContext)
}
});
What I notice is that sometimes when I put a break point on the first line of the controller action then it does not seem to stop at the breakpoint. Is it possible the results are being cached by MVC and how could I stop this from happening while I debug?
Check for any variation of OutputCache being set on the controller itself or, if present, a base controller all your controllers may inherit from.
I've also noticed IE likes to cache things and found the only way to alleviate this is doing to the developer window and clearing the cache (or ctrl+shift+del)
Try adding cache:false to your .ajax call and see if that has any bearing (jQuery will append a timestamp variable making each call unique). Nevermind, just noticed you have it included. Must be getting late here--sure sign it's bed time when I miss things like this.
Load up fiddler and see if the request is even coming back from the browser. Ideally you'll want I write out no cache headers through one of the many methods mentioned here
Disable browser cache for entire ASP.NET website
I have a complex JSON object which is sent to the View without any issues (as shown below) but I cannot work out how Serialize this data back to a .NET object when it is passed back to the controller through an AJAX call. Details of the various parts are below.
var ObjectA = {
"Name": 1,
"Starting": new Date(1221644506800),
"Timeline": [
{
"StartTime": new Date(1221644506800),
"GoesFor": 200
}
,
{
"StartTime": new Date(1221644506800),
"GoesFor": 100
}
]
};
I am not sure how this object can be passed to a Controller Method, I have this method below where the Timelines object mirrors the above JS object using Properties.
public JsonResult Save(Timelines person)
The jQuery I am using is:
var encoded = $.toJSON(SessionSchedule);
$.ajax({
url: "/Timeline/Save",
type: "POST",
dataType: 'json',
data: encoded,
contentType: "application/json; charset=utf-8",
beforeSend: function() { $("#saveStatus").html("Saving").show(); },
success: function(result) {
alert(result.Result);
$("#saveStatus").html(result.Result).show();
}
});
I have seen this question which is similar, but not quite the same as I am not using a forms to manipulate the data.
How to pass complex type using json to ASP.NET MVC controller
I have also seen references to using a 'JsonFilter' to manually deserialize the JSON, but was wondering if there is a way to do it nativly though ASP.NET MVC? Or what are the best practices for passing data in this way?
Edit:
This method should no longer be needed with the arrival of MVC 3, as it will be handled automatically - http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx
You can use this ObjectFilter:
public class ObjectFilter : ActionFilterAttribute {
public string Param { get; set; }
public Type RootType { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext) {
if ((filterContext.HttpContext.Request.ContentType ?? string.Empty).Contains("application/json")) {
object o =
new DataContractJsonSerializer(RootType).ReadObject(filterContext.HttpContext.Request.InputStream);
filterContext.ActionParameters[Param] = o;
}
}
}
You can then apply it to your controller methods like so:
[ObjectFilter(Param = "postdata", RootType = typeof(ObjectToSerializeTo))]
public JsonResult ControllerMethod(ObjectToSerializeTo postdata) { ... }
So basically, if the content type of the post is "application/json" this will spring into action and will map the values to the object of type you specify.
You say "I am not using a forms to manipulate the data." But you are doing a POST. Therefore, you are, in fact, using a form, even if it's empty.
$.ajax's dataType tells jQuery what type the server will return, not what you are passing. POST can only pass a form. jQuery will convert data to key/value pairs and pass it as a query string. From the docs:
Data to be sent to the server. It is
converted to a query string, if not
already a string. It's appended to the
url for GET-requests. See processData
option to prevent this automatic
processing. Object must be Key/Value
pairs. If value is an Array, jQuery
serializes multiple values with same
key i.e. {foo:["bar1", "bar2"]}
becomes '&foo=bar1&foo=bar2'.
Therefore:
You aren't passing JSON to the server. You're passing JSON to jQuery.
Model binding happens in the same way it happens in any other case.
A different take with a simple jQuery plugin
Even though answers to this question are long overdue, but I'm still posting a nice solution that I came with some time ago and makes it really simple to send complex JSON to Asp.net MVC controller actions so they are model bound to whatever strong type parameters.
This plugin supports dates just as well, so they get converted to their DateTime counterpart without a problem.
You can find all the details in my blog post where I examine the problem and provide code necessary to accomplish this.
All you have to do is to use this plugin on the client side. An Ajax request would look like this:
$.ajax({
type: "POST",
url: "SomeURL",
data: $.toDictionary(yourComplexJSONobject),
success: function() { ... },
error: function() { ... }
});
But this is just part of the whole problem. Now we are able to post complex JSON back to server, but since it will be model bound to a complex type that may have validation attributes on properties things may fail at that point. I've got a solution for it as well. My solution takes advantage of jQuery Ajax functionality where results can be successful or erroneous (just as shown in the upper code). So when validation would fail, error function would get called as it's supposed to be.
There is the JavaScriptSerializer class you can use too. That will let you deserialize the json to a .NET object. There's a generic Deserialize<T>, though you will need the .NET object to have a similar signature as the javascript one. Additionally there is also a DeserializeObject method that just makes a plain object. You can then use reflection to get at the properties you need.
If your controller takes a FormCollection, and you didn't add anything else to the data the json should be in form[0]:
public ActionResult Save(FormCollection forms) {
string json = forms[0];
// do your thing here.
}
This answer is a follow up to DaRKoN_'s answer that utilized the object filter:
[ObjectFilter(Param = "postdata", RootType = typeof(ObjectToSerializeTo))]
public JsonResult ControllerMethod(ObjectToSerializeTo postdata) { ... }
I was having a problem figuring out how to send multiple parameters to an action method and have one of them be the json object and the other be a plain string. I'm new to MVC and I had just forgotten that I already solved this problem with non-ajaxed views.
What I would do if I needed, say, two different objects on a view. I would create a ViewModel class. So say I needed the person object and the address object, I would do the following:
public class SomeViewModel()
{
public Person Person { get; set; }
public Address Address { get; set; }
}
Then I would bind the view to SomeViewModel. You can do the same thing with JSON.
[ObjectFilter(Param = "jsonViewModel", RootType = typeof(JsonViewModel))] // Don't forget to add the object filter class in DaRKoN_'s answer.
public JsonResult doJsonStuff(JsonViewModel jsonViewModel)
{
Person p = jsonViewModel.Person;
Address a = jsonViewModel.Address;
// Do stuff
jsonViewModel.Person = p;
jsonViewModel.Address = a;
return Json(jsonViewModel);
}
Then in the view you can use a simple call with JQuery like this:
var json = {
Person: { Name: "John Doe", Sex: "Male", Age: 23 },
Address: { Street: "123 fk st.", City: "Redmond", State: "Washington" }
};
$.ajax({
url: 'home/doJsonStuff',
type: 'POST',
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify(json), //You'll need to reference json2.js
success: function (response)
{
var person = response.Person;
var address = response.Address;
}
});
in response to Dan's comment above:
I am using this method to implement
the same thing, but for some reason I
am getting an exception on the
ReadObject method: "Expecting element
'root' from namespace ''.. Encountered
'None' with name '', namespace ''."
Any ideas why? – Dan Appleyard Apr 6
'10 at 17:57
I had the same problem (MVC 3 build 3.0.11209.0), and the post below solved it for me. Basically the json serializer is trying to read a stream which is not at the beginning, so repositioning the stream to 0 'fixed' it...
http://nali.org/asp-net-mvc-expecting-element-root-from-namespace-encountered-none-with-name-namespace/