JsonRequestBehavior.AllowGet does not work with HttpGet - asp.net

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.

Related

JSON Post Request is not working in ASP.NET5 MVC but is working in ASP.NET4.5 MVC

I have an example which sends a JSON post request to MVC controller. This example works in ASP.NET 4.5 but won’t work in the newest ASP.NET 5 release. Do I miss anything in this example?
I created a model, but I didn’t bind it to database. It will be just the object created in the memory.
public class SalesOrder
{
public int SalesOrderId { get; set; }
public string CustomerName { get; set; }
public string PONumber { get; set; }
}
I use Visual Studio 2015 to create that model based controller and its associated CRUD views. However, in this example, I will only run the “Create” view.
Inside the controller, I hard coded a SalesOrder object list and it only contains an item. I will use it to work with the CRUD views.
private List<SalesOrder> salesOrderList =
new List<SalesOrder> (
new SalesOrder[]
{
new SalesOrder()
{
SalesOrderId = 1,
CustomerName = "David",
PONumber = "123"
}
});
Inside the controller, I also create a new function to process the “Save” request. The request will just change the CustomerName property of the model then bounce back with the JSON result.
[HttpPost]
public JsonResult Save(SalesOrder salesOrderViewModel)
{
salesOrderViewModel.CustomerName = "David returns";
return Json(new { salesOrderViewModel });
}
In the Create.cshtml, I created a button and attach JQuery script to its click event.
<p><button id="saveButton">Save</button></p>
This is the JQueryScript.
$(function () {
var clickFunc = function () {
var salesOrderViewModel = {
"salesOrderViewModel": {
"SalesOrderId": 123,
"CustomerName": "ab",
"PONumber": "2",
}
}
$.ajax({
url: "/SalesOrders/Save/",
type: "POST",
cache: false,
data: JSON.stringify(salesOrderViewModel),
contentType: "application/json; charset=utf-8"
});
}
$('#saveButton').click(clickFunc)
})
I click the "Save" button in Create.cshtml to trigger the post request.
I set the debug break point in the controller and verify the coming post request. In ASP.NET 4.5, the JSON deserialization is working, and it shows all the values.
However, in ASP.NET 5, an empty object is returned.
In ASP.NET 5 case, if press F12 to start the debugger in Microsoft Edge, then it shows the Post request does have the correct values, but for some reasons, they are not passed to MVC controller.
Please see these screen shots:
http://i63.tinypic.com/68vwnb.jpg
Do I miss anything?
Thanks for helping…
I don't think you need to specify the name of the parameter. Try:
var salesOrderViewModel = {
"SalesOrderId": 123,
"CustomerName": "ab",
"PONumber": "2",
}
I think you need to especify fromBody in the Controller.
[HttpPost]
public JsonResult Save([FromBody]SalesOrder salesOrderViewModel)
{
salesOrderViewModel.CustomerName = "David returns";
return Json(new { salesOrderViewModel });
}
Thanks for DCruz22 and Stephen Muecke.
The working version of the script is:
$(function () {
var clickFunc = function () {
var salesOrderViewModel = {
"salesOrderViewModel": {
"SalesOrderId": 0,
"CustomerName": "ab",
"PONumber": "2",
"MessageToClient": "POST to server"
}
}
$.ajax({
url: "/SalesOrders/Save/",
type: "POST",
cache: false,
data: salesOrderViewModel
});
}
$('#saveButton').click(clickFunc)
})
I found the parameter name "salesOrderViewModel" is not a problem. With it, the script is still working.
Actually, this question is a simplified version of my original problem. My originally problem is starting from the knockoutJs. Below is the script doing the data binding. After user clicks the "Save" button, the self.save function() is called and the script will send the post request including the data model to the MVC controller.
SalesOrderViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, {}, self);
self.save = function () {
$.ajax({
url: "/SalesOrders/Save/",
type: "POST",
cache: false,
data: ko.toJSON(self),
success: function (data) {
}
});
}
}
The problem is "ko.toJSON(self)" includes some other information. Below is the request body I captured from the Microsoft Edge debugger.
{"SalesOrderId":0,
"CustomerName":"chiang123",
"PONumber":"123",
"MessageToClient":null,
"__ko_mapping__":{
"ignore":[],
"include":["_destroy"],
"copy":[],
"mappedProperties":{
"SalesOrderId":true,
"CustomerName":true,
"PONumber":true,
"MessageToClient":true}}}
You can see all data starts from "ko_mapping" is KnockoutJs specific. Do I need to manually trim those data in order to make this work? Per information, obviously the same implementation should work in ASP.NET 4.5 but I just haven't try it yet. Many thanks...

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.

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.

Pass JSON to MVC 3 Action

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.

ASP.NET MVC2 JsonResult This request has been blocked

I know the question is very familiar but i can't over it.
This is my Controller Action
public JsonResult AddToCart(int productId, int quantity = 1, int optionValue = 0)
{
AjaxActionResponse res = new AjaxActionResponse();
res.Result = ture;
......
return Json(res, JsonRequestBehavior.AllowGet);
}
and this is my ajax request
$.ajax({
type: "GET",
contentType: "application/json; charset=utf-8",
url: "<%= Url.Action("AddToCart", "CartAjax") %>",
data: ({'productId': productId, 'quantity': quantity, 'optionValue': optionValue}),
dataType: "json",
success: function (d) {
if ($.isEmptyObject(d)) {
return;
}
if (!d.Result) {
alert(d.ErrorMessage[0].ErrorMessage);
}
else {
$("#myCartBox").dialog("open");
}
return;
}
});
when i run the ajax request known error pops up
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.
I tried to making AddToCart action [HttpPost] acceptable but at this time: parameters never arrived the method and missing argument error returned from the request (500 int. serv error)
I can run only with get method but request has been blocked at this time :)
Am i missing something? Or what is the right way for MVC2 Ajax request. WebForms was very successfully about calling methods from JavaScript but i couldn't do that on MVC.
Any Idea?
Have you tried using POST using this method signature?
[HttpPost]
public ActionResult AddToCart(FormCollection form)
Or using databinding:
public class CartItem {
public int productId {get; set;}
public int quantity {get; set;}
public int optionValue {get; set;}
}
Then:
public ActionResult AddToCart(CartItem c)
Unfortunately I don't have a good answer, but I've solved some of my own problems this way (rather than figure out how to get the parameters passed nicely using routes).
I don't know for certain that this is your fundamental issue, but you shouldn't set the content-type to text/html. That isn't what you're sending or what MVC expects. Omit that parameter altogether, and let jQuery set it to application/x-www-form-urlencoded, which is appropriate.

Resources