Pass JSON to MVC 3 Action - asp.net

I am trying to submit JSON to a MVC action. What I want is to take the JSON object and then access it's data. The number of JSON fields will vary each time so I need a solution that will handle all cases.
This is my POST to my action, address could have 3 fields or 20 it will vary on each post.
Update: I'll go into a little more detail. I'm trying to use the LinkedIn API, I'll be sent a JSON which will look like the JSON at the end of this page : link. I need to create an Action which will accept this JSON which will vary for every person.
var address =
{
Address: "123 rd",
City: "Far Away",
State: "Over There"
};
$.ajaxSetup({ cache: false });
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "/Account/GetDetails/",
data: JSON.stringify(address),
dataType: "json",
success: function () {
alert("Success from JS");
}
});
This is my action in MVC, I need to be apply to take whatever JSON object is passed and access its fields.
[HttpPost]
public ActionResult GetDetails(object address)
{
//address object comes in as null
#ViewBag.Successs = true;
return View();
}

I don't believe nobody saying this so I will try. Have your code like this
public class Personal
{
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
//other properties here
}
[HttpPost]
public ActionResult GetDetails(Personal address)
{
//Should be able to get it.
#ViewBag.Successs = true;
return View();
}
In general, you can add those possible properties into the class Personal (or whatever you can name it). But according to linkedin API, you will need a tool to generate the data class due to its complexity. I think xsd.exe can help if you can get xsd file for that (or you can even generate xsd file from xml)

Remove data: JSON.stringify(address) with data: address
Action method
[HttpPost]
public ActionResult GetDetails(string Address, string City, string State, string PropName)
{
//access variable here
}
As you have said your data object may contain 20 props, to avoid creating 20 parameters, you can use formscollection like below
[HttpPost]
public ActionResult GetDetails(FormCollection address)
{
string city= address["city"] ;
string anotherPro=address["prop"];
}

Not sure if this will work (never done this myself), but you could try the following signature:
public ActionResult LinkedIn(dynamic address)
I'm actually quite interested myself to see what will happen then. Or, as suggested in the comment by Kristof Claes, use a FormCollection.
Second, when things like this happen, always check whether the browser actually sends the data you expected to the server. IE9 and Chrome support this out of the box, otherwise you can use a tool like Fiddler.
EDIT: Just tried for myself with a dynamic parameter and that doesn't work.. The runtime type of the parameter is object so everything you submitted is lost. You'd better
use FormCollection.

I believe you can use a FormCollection for this.
public ActionResult LinkedIn(FormCollection address)
{
var street = address["street"];
...
return View();
}

Might just be a typo, but you are calling GetDetails ActionResult, but yet your code is LinkedIn ?

You need to wrap your JSON object in the name of parameter you are expecting in your action method. Something like this:
var data = { address: { address: '123 Test Way', city: 'Parts Unknown', state: 'TX' } };
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "/Account/GetDetails/",
data: JSON.stringify(data),
dataType: "json",
success: function () {
alert("Success from JS");
}
Then in your action method, do this:
public ActionResult GetDetails(dynamic address) {}

My take is all the answers are sort of right.
As your don't know the number of things you're sending, use formcollection on the server. But also remove the stringfy from the ajax call. These means the data will be sent using www-encoding.
If you wnat to send n address objects, change the mvc action parameter to an array of address objects and use stringfy.

This is how I do it. I have a MyProject.Model.Entities and I serialize them in by using [ParamSerializationFilter] attribute on the given action method.
Full code here: https://gist.github.com/3b18a58922fdd8d5a963
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!Enum.GetNames(typeof(AllowedMethods)).Any(n => n == filterContext.HttpContext.Request.HttpMethod))
throw new InvalidOperationException("Invalid Request: HttpMethod");
foreach (var param in filterContext.ActionDescriptor.GetParameters())
{
if (ModelTypes.Contains(param.ParameterType))
{
if ((filterContext.HttpContext.Request.ContentType ?? string.Empty) == ("application/json"))
{
filterContext.ActionParameters[param.ParameterName] =
JsonSerializer.DeserializeFromStream(param.ParameterType, filterContext.HttpContext.Request.InputStream);
}
else if (filterContext.HttpContext.Request.ContentType.Contains("xml"))
{
filterContext.ActionParameters[param.ParameterName] =
XmlSerializer.DeserializeFromStream(param.ParameterType, filterContext.HttpContext.Request.InputStream);
}
}
}
}

You're doing too much work! You can pass the JSON Literal directly to the server and access the elements via the action method parameters, like this:
var address =
{
Address: "123 rd",
City: "Far Away",
State: "Over There"
};
$.ajaxSetup({ cache: false });
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "/Account/GetDetails/",
data: address,
dataType: "json",
success: function () {
alert("Success from JS");
}
});
[HttpPost]
public ActionResult LinkedIn(string Address, string City, string State)
{
// now you have everything you want as params
#ViewBag.Successs = true;
return View();
}
Note: This will only work if your action params are named the exact same as your JSON Literal attributes.

Related

JsonRequestBehavior.AllowGet does not work with HttpGet

I've read MSDN documentation of JsonRequestBehavior.AllowGet and many answers here at SO. I experimented and I am still confused.
I have the following action method. It works fine if I use POST method in my ajax call. It fails with status 404 (Resource not found) if I use GET method in my ajax call. So, the question is what exactly does the JsonRequestBehavior.AllowGet enum do in this Json method? The MSDN documentation says: AllowGet HTTP GET requests from the client are allowed. (https://msdn.microsoft.com/en-us/library/system.web.mvc.jsonrequestbehavior(v=vs.118).aspx), but then why does it fail when I use GET method in my ajax call? Changing the attribute from HttpPost to HttpGet does not help, it fails with either POST or GET method.
[HttpPost]
public JsonResult Create(Model m)
{
m.Ssn = "123-45-8999";
m.FirstName = "Aron";
m.LastName = "Henderson";
m.Id = 1000;
return Json(m, JsonRequestBehavior.AllowGet);
}
public class Model
{
public int Id { get; set; }
public string Ssn { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Here is my jQuery ajax call:
$(function () {
console.log("hola");
$("button").on("click", function () {
$.ajax({
method: "POST", //Try changing this to GET and see.
url: "Home/Create",
data: { Id: 123, Ssn: "585-78-9981", FirstName: "John", LastName: "Smith" }
})
.done(function (msg) {
alert("Data Saved: " + msg);
});
});
})
A 404 (Resource not found) means the method is not found (and has nothing to do with JsonRequestBehavior).
Change your ajax to use
$.ajax({
url: "/Home/Create", // note leading forward slash
....
or better, use url: '#Url.Action("Create", "Home")', to correctly generate your url.
404's are due to the attributes, nothing to do with "AllowGet" on the JSON.
You need one or the other [HttpVERB] attributes... not both attributes.
This would work if it is your scenario.
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
You should check out this well documented post
AllowGet will simply allow that JSON response to work over the GET scenario without exception. If you do not, you will see this message:
This request has been blocked because sensitive information could be
disclosed to third party web sites when this is used in a GET request.
To allow GET requests, set JsonRequestBehavior to AllowGet.

AJAX Passing multiple parameter to WebApi

AJAX request:
$.ajax({
url: url,
dataType: 'json',
type: 'Post',
data: {token:"4", feed:{"id":0,"message":"Hello World","userId":4} }
});
Server Side Web API:
[HttpPost]
public HttpResponseMessage Post(string token, Feed feed)
{
/* Some code */
return new HttpResponseMessage(HttpStatusCode.Created);
}
Error Code 404: {"message":"No HTTP resource was found that matches
the request URI 'localhost:8080/api/feed'.","messageDetail":"No action
was found on the controller 'Feed' that matches the request."}
Why I am getting this error and Why I am not able POST multiple parameters to my API?
Start by writing a view model:
public class MyViewModel
{
public string Token { get; set; }
public Feed Feed { get; set; }
}
that your controller action will take as parameter:
[HttpPost]
public HttpResponseMessage Post(MyViewModel model)
{
/* Some code */
return new HttpResponseMessage(HttpStatusCode.Created);
}
and finally adapt your jQuery call to send it as JSON:
$.ajax({
url: url,
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
token: '4',
feed: {
id: 0,
message: 'Hello World',
userId: 4
}
})
});
Important things to note for the AJAX call:
setting the request contentType to application/json
wrapping the data in a JSON.stringify function to effectively convert the javascript object to a JSON string
removed the useless dataType: 'json' parameter. jQuery will automatically use the Content-Type response header sent by the server to deduce how to parse the result passed to the success callback.
Try this server-side (from memory you can only have a single FromBody parameter so it needs to contain all the incoming properties):
public class TokenAndFeed
{
public String token {get; set;}
public Feed feed {get; set;}
}
public HttpResponseMessage Post([FromBody]TokenAndFeed tokenAndFeed)
{
/* Some code */
return new HttpResponseMessage(HttpStatusCode.Created);
}
I had a similar problem recently and here's some info on how I solved it. I think the problem is to do with how WebApi handles parameters. You can read a bit about it here and here but essentially there are two ways to post parameters, in the body or in the uri. The body can only contain one parameter, but it can be a complex parameter, whereas the uri can contain any number of parameters (up to the uri character limit) but they must be simple. When jquery does a POST ajax call it tries to pass all data parameters in the body, which does not work in your case since the body can only have one parameter.
In code terms I think you need something like this:
var token = "4";
var feed = {Id:0, Message:"Hello World", UserId:4};
$.ajax({
url: "/api/Feed/?token=" + token,
dataType: 'json',
type: 'Post',
data: JSON.stringify(feed)
});
Hope that helps.
Can you post your Feed class, just to make sure the properies match up.
var data = {
token: "4",
feed: {Id:0,Message:"Hello World",UserId:4}
}
$.ajax({
url: "/api/Feed/",
dataType: 'json',
type: 'Post',
data: JSON.stringify(data)
});
Try with this one. You have to get json object data from body. Read Request's input streem and map it to your data model.
public class TokenAndFeed
{
public string Token { get; set; }
public Feed Feed { get; set; }
}
[HttpPost]
public HttpResponseMessage Post()
{
System.IO.Stream str;
String jsonContents;
Int32 strLen, strRead;
str = HttpContext.Current.Request.InputStream;
strLen = Convert.ToInt32(str.Length);
byte[] byteArray = new byte[strLen];
strRead = str.Read(byteArray, 0, strLen);
jsonContents = Encoding.UTF8.GetString(byteArray);
TokenAndFeed tAndf = JsonConvert.DeserializeObject<TokenAndFeed>(jsonContents);
// some code here
return new HttpResponseMessage(HttpStatusCode.Created);
}
hope this will help.

Pass two parameters from view to controller

I'm working with Visual Studio 2010 and ASP.NET MVC4 view engine razor.
I like to pass two parameters (object, string) from view to controller.
In the view, i have this:
var persona = {};
function ModeloPersona() {
persona.CI = $('#CI').val();
persona.Nombre = $('#Nombre').val();
persona.Apellidop = $('#Apellidop').val();
persona.Apellidom = $('#Apellidom').val();
persona.Direccion = $('#Direccion').val();
persona.Sexo = $('#Sexo').val();
persona.Cumple = $('#Cumple').val();
}
function Grabar(modo) {
ModeloPersona();
$.ajax({
url: '#Url.Action("Grabar", "Home")',
type: 'POST',
data: {
Persona: JSON.stringify(persona),
Modo: modo
},
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function () {
}
});
}
and my controller I have this:
public ActionResult Grabar(Persona mPersona, string modo){
if (ModelState.IsValid){
if (modo == "2"){
}
else{
}
}
return View();
}
the problem is the following, the object mPersona is null but value of modo is correct.
Why mPersona is null? what is the problem, please help me with this
Regards
Ricardo
see the post params you are sending using the ajax
data: {
Persona: JSON.stringify(persona),
Modo: modo
}
MVC's default model binding follows the convention that is based on the parameter names, if you want the default model binder to work you have to specify the identical sending and receiving parameters in your case the receiving params in the ActionResult should look like
public ActionResult Grabar(Persona Persona, string Modo){...}
Changing the name of the parameter to persona should work:
public ActionResult Grabar(Persona persona, string modo){
The names of the parameters to the action method have to match the names of the objects in the JSON data for the model binding to work.

Why am I not able to pass a JSON object to a WCF web service?

I built a fairly complex app using the older "ASMX" web service implementation and I am migrating to WCF. There is one thing that is just driving me crazy - it should be easy but my AJAX calls error out no matter how I try to structure this call. This worked fine with ASMX calls but not with WCF.
Here is the AJAX call:
var ProfileData = new Object();
ProfileData.SUID = SUID;
ProfileData.FirstName = $("#FirstName").val();
ProfileData.LastName = $("#LastName").val();
ProfileData.Birthdate = new Date($("#Birthdate").val());
var DTO = {'ProfileData': ProfileData };
$.ajax({
type: "POST",
url: "AllianceService.svc/SaveBasicProfile",
data: JSON.stringify(DTO),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
UpdateTips($("#BasicProfileValidate"), "Success! Your data has been updated.");
}
},
error: function (xhr, ajaxOptions, thrownError, request, error) {
alert('Error Saving Basic Profile Data');
}
});
Here is the declaration of the type on the C# / Server side:
[DataContract]
public class BasicFolderData
{
[DataMember]
public string SUID { get; set; }
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
[DataMember]
public DateTime Birthdate { get; set; }
}
And here is the definition of the service:
[OperationContract]
public int SaveBasicProfile(BasicFolderData ProfileData)
{
... do stuff
}
Now, if I break out all of the datamembers and use them as parameters, I can get this to work with a simple
data: JSON.stringify(ProfileData)
But the object is actually much longer than this and I'd like to know how to pass objects.
Also, I have tried:
data: JSON.stringify({"ProfileData": ProfileData }),
and
data: JSON.stringify('{"ProfileData":' + ProfileData + '}'),
and
data: '{"ProfileData":' + JSON.stringify(ProfileData) + '}',
but all to no avail...I get the error message on all of them.
If I use:
data: JSON.stringify(ProfileData),
then, oddly enough, the request makes it to the server but the ProfileData parameter is null. I suspect that the problem is my datacontract but don't know where to start. Also, I have a lot of functions with scalar parameters that work just fine - it is only when I attempt to pass objects that I fail. Any help would be greatly appreciated!!
Following DonAndre's suggestion, I have found that simply changing the parameter to a scalar type will make the call succeed. However, the parameter is always null.
WCF wraps the JSON with a d if you have enableWebScript set to true in your config on the server. Take a look here: JsonConvert.DeserializeObject and "d" wrapper in WCF
Ok - the problem was trivial: the "BasicFolderClass" had a DateTime field (Birthdate) in it.
When JSON.stringify is called on the DTO object, it turns the DateTime field in the javascript side into a string before sending it up. The mismatch on that field between the JSON string being presented and the DateTime being expected is what led to the error.

jQuery and parsing JSON in the callback

I'm using jQuery to call an asmx and return some data. I'm making the call like this
function getRequestInfo(event) {
var id = $('#<%= RequestDaysId.ClientID %>').val();
var formattedId = "{'id': '115'}";
$.ajax({
type: "Post",
url: "services/VacationServices.asmx/GetVacationInfo",
data: "{'id': '" + id + "'}",
dataType: "json",
contentType: "application/json; charset=utf-8",
processdata: true,
success: function(data) {
$('#<%=Note.ClientID %>').val(data.Note);
$('.pendingrequestinfo').show().fadeIn(2000);
},
error: function(result, errortype, exceptionobject) {
$('.failureMessage').fadeIn(2000).fadeOut(2000);
}
})
};
Everything seems to be working fine, I set a break point in my success function and inspect the data object and see this.
"{"Note":"this is a note","dayInfo":[{"ShortDate":"3/4/2010","DayType":"Vacation","HalfDay":""},{"ShortDate":"3/5/2010","DayType":"Vacation","HalfDay":""}]}"
The problem comes when I try to get the values out of the JSON. If I do something like data.Note, I get undefined back.
It's late, It's Saturday and I've been at it all day, I sure would like a push in the right direction when it comes to parsing though my JSON.
EDIT:
I'm using Asp.net and JavaScriptSerializer.Serialize() to create the JSON. When I set a break point and inspect the 'data' object it looks to have a property d that contains the string that should be JSON.
ANOTHER EDIT:
If I do something like this in my success
$('#<%=Note.ClientID %>').val(data.d.[0]);
I get the { opening curly brace. I guess i'm getting a string instead of JSON, but it seems to go against what the jquery api states about the return value when the datatype is set to JSON.
Thanks guys.
Jim
First make sure that the JSON string exists in the "d" variable in the response returned in the success callback. Next, in order to get the JSON object you will need to convert the string into the JSON. You can use the eval function or the JQuery built in function to convert string to JSON. I like the jquery-json plug in to convert string into JSON representation.
Your code will look something like this:
var jsonObject = eval('(' + data.d + ')');
Now, you can use jsonObject.Note or any other property.
With jquery-json plugin you can do the following:
var note = $.evalJSON(data.d).Note;
That's not a valid JSON format. Remove the doublequotes at beginning and end to make it fullworthy JSON.
This one is so silly...sorry guys. When returning data from the asmx there is no need to serialize it into JSON
I have the following class that I'm populating and returing from my web method
public class VacationInfo
{
public string Note { get; set; }
public List<DayInfo> dayInfo { get; set; }
public VacationInfo(string note, List<DayInfo> dayinfo)
{
this.Note = note;
this.dayInfo = dayinfo;
}
public class DayInfo
{
public string ShortDate { get; set; }
public string DayType { get; set; }
public string HalfDay { get; set; }
public DayInfo(string shortdate, string daytype, string halfday)
{
this.ShortDate = shortdate;
this.DayType = daytype;
this.HalfDay = halfday;
}
}
}
as long as your web service is decorated with
[System.Web.Script.Services.ScriptService]
your object will be serialized and returned as JSON at no charge to you. :)
then I'm able to do this
data.d.Note
in my call back.
Thanks for the help guys.
Credit where credit is due
Here is how I process. Note the dataFilter: part - this makes it work with either newer or older asp.net stuff.
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
data: objectSaveData,
dataFilter: function(data)
{
var msg;
if (typeof (JSON) !== 'undefined' &&
typeof (JSON.parse) === 'function')
msg = JSON.parse(data);
else
msg = eval('(' + data + ')');
if (msg.hasOwnProperty('d'))
return msg.d;
else
return msg;
},
url: "/mywebservice.asmx/myMethodName",
success: function(msg)
{
//do stuff
},
failure: function(msg)
{
//handlefail
}
});

Resources