I am developing an Azure Web app with a database. I've a model and controller for the database. I'm trying to Post data on the database, but have some trouble understanding why does this code send a 404 error when sending data from the Web-Client to the controller.
Here is how I send the data in AngularJS (parameter is a Json string):
$http({
method: 'post',
url: serviceBasePath + "/api/suscribe",
data: parameter,
headers: { 'Content-Type': 'application/json' }
}).then(function (response) {
userService.SetCurrentUser(response.data);
defer.resolve(response.data);
}, function (error) {
defer.reject(error.data);
})
On the controller side, I get 404 if the controller is :
[HttpPost]
[Route("api/suscribe")]
public IHttpActionResult PostGTW_Utilisateur(String JsonString)
{
//
}
But if I leave the model as a parameter, the 404 error is gone :
[HttpPost]
[Route("api/suscribe")]
public IHttpActionResult PostGTW_Utilisateur(User u)
{
//
}
Json object class :
public class JsonSuscribeModel
{
public Utilisateur user { get; set; }
public string guid { get; set; }
public string password2 { get; set; }
}
You miss the endpoint since it does not know what JsonString is. You sent a JSON but in the controller model, you told it to listen for string. Open up Chrome (or other) dev tools and see EXACTLY what you are sending with the request.
Here is another tip:
$http({
method: 'post',
url: serviceBasePath + "/api/suscribe",
data: parameter,
headers: { 'Content-Type': 'application/json' }
}).then(function (response) {
userService.SetCurrentUser(response.data);
defer.resolve(response.data);
}, function (error) {
defer.reject(error.data);
})
I have seen the misuse of promises SO many times. Why would you use $http (which is itself a promise) and then process it in service and return ANOTHER promise? You can simply return $http (and resolve its promises in a controller. You are gonna have to resolve this new promises you are returning anyway, so why have an extra step.
return $http({
method: 'post',
url: serviceBasePath + "/api/suscribe",
data: parameter,
headers: { 'Content-Type': 'application/json' }
}).then(function (response) {
userService.SetCurrentUser(response.data);
return response.data;
}, function (error) {
return error.data;
})
This way you return entire $http and you instruct it what to return in success and error. Since itself it's a promise it will behave same as before.
Thanks for the tips.
The solution was to use the JsonModel I had as a parameter :
public IHttpActionResult PostGTW_Utilisateur(JsonSuscribeModel JsonModel)
Related
Have followed a number of tutorials on the net but still getting the same result. Microsofts documentation is so in-depth considering all I want to is in essence send one variable to a controller.
My client side code looks like this:
var obj = { send: 'test' };
$.ajax({
url: '/Countries/GetVotes',
type: 'POST',
contentType: "application/json",
data: JSON.stringify(obj),
success: function (data) {
if (data === null) {
// TODO
} else {
console.log(data);
}
},
error: function (error) {
console.log(error);
}
});
Then the controller would look like this:
[HttpPost]
public async Task<JsonResult> GetVotes(string send)
{
try
{
var votes = ctx.Candidates.Where(x => x.Country == send)
.OrderByDescending(x => x.Votes)
.ToList();
return Json(votes);
}
catch (Exception ex)
{
logger.Error(ex);
}
return Json("foo");
}
All standard and used to work in .NET framework 4.7.2, anyone whose done any MVC would be very familiar with this approach.
I've made the decision to try using .net core 3.1. Am hitting all the right breakpoints, the problem I have is the string called "send" is sent to my controller as null.
Anybody know the simplest way to get this working?
What you're passing in is a JSON object
{ send: "test" }
What you're expecting in the request body in the controller is just a string. You could try creating a request object that represents the data structure you're expecting:
public class Request {
public string send { get; set; }
}
Then you can update your controller method to
public async Task<JsonResult> GetVotes([FromBody]Request request)
And further access the send property through request.send.
you don't neeed stringify when you are sending string already. And remove content type too. Default would be ok.
So try this
$.ajax({
url: '/Countries/GetVotes',
type: 'POST',
data: obj,
success: function (data) {
if (data === null) {
// TODO
} else {
console.log(data);
}
},
error: function (error) {
console.log(error);
}
});
I'm using Ajax to make model and send it to the controller.
Here is Model:
public class PersonDto{
public Guid Id { get; set; }
public int PersonAge { get; set; }
public string PersonName { get; set; }
public DateTime? DateCreated { get; set; }
}
Here is Controller:
[Route("EditPerson")]
[HttpPost]
public async Task<IActionResult> EditPerson(PersonDto offer) {
//Save to Entity FW
}
Here is Ajax:
var data = {
Id: $('#personModal #personModalTitle').text(),
PersonAge: $('#personModal #personId').val(),
PersonName: $('#personModal #personName').val()
};
var dataJson = JSON.stringify(data);
console.log(dataJson);
$.ajax({
type: 'POST',
url: 'Persons/EditPerson',
data: dataJson,
contentType: "application/json",
success: function (result) {
},
error: function (error) {
Alert("Error Saving offer changes!");
}
});
Here is Console Log (Json), everything looks great:
{"Id":"96f2ae80-45cc-4a6c-abe0-230c2cbd3043","PersonAge":"5","PersonName":"John"}
When I Debug PersonsController I see that my model is never populated, is not null, just no data in it.
I tried Adding DateCreated to Model in Ajax function, I tried parsing Age to int.
I tried adding [FromBody] annotation to PersonDto in Action, then I getting NULL to my model.
I don't know where I can make error here.
For JSON encoded body , just add a [FromBody] :
[Route("EditPerson")]
[HttpPost]
public async Task<IActionResult> EditPerson([FromBody] PersonDto offer)
{
return new JsonResult(offer);
}
Update :
Another way to do that is use a [ApiController] to decorate the controller class .
If there's no [ApiController] , you should add a [FromBody] to enforce the Model Binder to read from body .
Whether a camelCased or pascalCased payload or not important . It makes no difference .
Camel Case :
POST https://localhost:44372/api/values/EditPerson HTTP/1.1
Content-Type : application/json
{"id":"96f2ae80-45cc-4a6c-abe0-230c2cbd3043","personAge":"5","personName":"John"}
Pascal Case :
POST https://localhost:44372/api/values/EditPerson HTTP/1.1
Content-Type : application/json
{"Id":"96f2ae80-45cc-4a6c-abe0-230c2cbd3043","PersonAge":"5","PersonName":"John"}
Can you try to get your data as a JObject:
[FromBody] JObject offer
[Route("EditPerson")]
[HttpPost]
public async Task<IActionResult> EditPerson([FromBody] JObject offer) {
string Id = (string)form.SelectToken("Id")
string PersonAge= (string)form.SelectToken("PersonAge")
string PersonName= (string)form.SelectToken("PersonName")
}
As I can see from your code you are not sending variable, the one you expect, "offer".
When doing AJAX call and sending data, data parameters should be named as those you are expecting.
You should send data in this format:
{
offer: { PersonAge: 10, PersonName: 'Senad' }
}
Your call should look like this
var data = {
Id: $('#personModal #personModalTitle').text(),
PersonAge: $('#personModal #personId').val(),
PersonName: $('#personModal #personName').val()
};
$.ajax({
type: 'POST',
url: 'Persons/EditPerson',
data: { offer: data }, //no need to serialize it
dataType: 'json',
success: function (result) {
},
error: function (error) {
Alert("Error Saving offer changes!");
}
});
I hope this is helpful.
I post data using fetch like this in my client js scripts
fetch('/myarea/mycontroller/myaction', {
method: 'post',
body: JSON.stringify({ name: namevalue, address: addressvalue })
})
.then(function (response) {
if (response.status !== 200) {
console.log('fetch returned not ok' + response.status);
}
response.json().then(function (data) {
console.log('fetch returned ok');
console.log(data);
});
})
.catch(function (err) {
console.log(`error: ${err}`);
});
}, false);
Then on my controller
[HttpPost]
public async Task<IActionResult> MyAction(string name, string address)
{
// Error, name and address is null here, shouldn't be!
// more code here ...
}
My controller action is being called correctly, and I am able to debug inside it, but no data being passed. What could be wrong here? Thanks
The controller action is expecting query parameters (/myaction?name=myname&address=myaddress). That's the default.
You're sending them in the body.
You can change the javascript to send them as query parameters. (see here: https://github.com/github/fetch/issues/256)
Or you can tell the controller action to take the values from the body:
[HttpPost]
public async Task<IActionResult> MyAction([FromBody] Person person)
{
var myName = person.Name;
}
public class Person
{
public string Name {get; set; }
public string Address {get; set; }
}
The [FromBody] attribute kicked in for me only after I defined the header that it is an "application/json" type:
fetch('api/Profile/Update', {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ description: "Some text here" })
});
And this is how the controller action looks like:
[HttpPost("[action]")]
public async Task<IActionResult> Update([FromBody] ProfileUpdateModel profileUpdateModel)
{
//Do some stuff here...
return RedirectToAction("/", "HomeController");
}
And the description property now receives the value from the request. Hope this gives final clarity to somebody tackling with the issue.
Or if you had <form>...</form> you could use
fetch('/url', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: $('#editContactModal form').serialize()
// body like "id=1&clientid=3&name=me&phones=123&email=#mail.com"
})
without [FromBody] !!! (just with regular action method in controller)
Currently I can to create models regularly (by going SettingProfile/Create)
But when I try to use AJAX to send data wrapped in settingProfile JS object it returns HTTP 500 Internal Server Error error, I believe problem is in the data type. What would be correct way of calling Create method in AJAX?
My code:
Model:
public class SettingProfile
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Name { get; set; }
public long? UserId { get; set; }
public string Url { get; set; }
}
View (JS):
function saveSettingProfile() {
var name = prompt("Please enter profile name", "");
var url = $("form").serialize(); // Not AJAX url, its a variable in model
var settingProfile = {
Name: name,
Url: url
};
jQuery.ajax({
url: "#Url.Action("Create", "SettingProfile")",
contentType: "application/json; charset=utf-8",
dataType: "json",
method: "POST",
data: settingProfile
}).done(function (response) {
alert("Profile saved successfully");
}).fail(function () {
alert("Could not save profile");
});
}
Controller:
[HttpPost]
//[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Name,Url")] SettingProfile settingProfile)
{
settingProfile.UserId = 8; // TODO: get user id from session
if (ModelState.IsValid)
{
db.SettingProfiles.Add(settingProfile);
db.SaveChanges();
return Json(new { success = true });
}
return Json(new { success = false });
}
500 error means, your server code is crashing. It could be of many reasons. One thing i noticed (which might cause a 500 error) is the way you are sending data.
You specified the contentType as "application/json" .But you are sending the javascript object as it is. So your request payload will be sent as something like
Name=SomeNameValue&Url=SomeUrlValue And with your current server code, the model binder cannot map it to an object of SettingProfile.
The solution is to send the stringified version of your javascript object. You may use the JSON.stringify method to do so.
jQuery.ajax({
url: "#Url.Action("Create", "SettingProfile")",
contentType: "application/json; charset=utf-8",
method: "POST",
data: JSON.stringify(settingProfile)
}).done(function (response) {
alert("Profile saved successfully");
};
Now your client code will send a request payload like
{"Name":"SomeName","Url":"SomeUrl"}
and model binder will be able to map it properly.
If it is simple data, you can simply not mention a custom contentType and send the js object as it is. jQuery will use the default contentType value which is "application/x-www-form-urlencoded" and model binding will work.
So the below code will work fine.
jQuery.ajax({
url: "#Url.Action("Create", "SettingProfile")",
method: "POST",
data:settingProfile
})
You may also check the browser's network tab and see the response coming back from the server for that ajax call. If an exception happens, you can see the details there.
I have a class in my web project:
public class MyClass
{
public int? Param1 { get; set; }
public int? Param2 { get; set; }
}
which is a parameter in my controller method:
public ActionResult TheControllerMethod(MyClass myParam)
{
//etc.
}
If I call the method using POST, the model binding works automatically (I use angular on the js side, which likely doesn't matter):
$http({
method: "post",
url: controllerRoot + "TheControllerMethod",
data: {
myParam: myParam
}
}).success(function (data) {
callback(data);
}).error(function () {
alert("Error getting my stuff.");
});
If I use a GET, the parameter is always null in the controller.
$http({
method: "get",
url: controllerRoot + "TheControllerMethod",
params: {
myParam: myParam
}
}).success(function (data) {
callback(data);
}).error(function () {
alert("Error getting my stuff.");
});
Does complex model binding using the default model binder only work for POSTs, or is there something I can do to make this work with a GET?
The answer is Yes. The difference between GET and POST requests is that a POST body can have a content type so they can be interpreted correctly on the server side as XML, or Json, so on; for GET, all you have is just a querystring.
With ASP.NET MVC you can indeed bind your model on a GET request, as long as you have the same query string parameter names as of the property names of your Model class. Example from this answer:
public class ViewModel
{
public string Name { set;get;}
public string Loc{ set;get;}
}
You can do a Get request like this
MyAction?Name=jon&Loc=America
and MVC will automatically bind your model:
[HttpGet]
public ViewResult MyAction(ViewModel model)
{
// Do stuff
return View("ViewName", model);
}
Why are you calling the property "data" in the POST, and "params" in the GET? Both should be called "data".
$http({
method: "get",
url: controllerRoot + "TheControllerMethod",
data: {
myParam: myParam
}
}).success(function (data) {
callback(data);
}).error(function () {
alert("Error getting my stuff.");
});