Model is null in API controller after being submitted from Angular frontend - c#

I have a Angular with .Net Core project, it's very simple in nature, the app just lists people also allows for someone to add a person.
However when I submit a new person the model in the controller is always null.
Here is my Angular-Post method:
public async onSubmit(value: any, valid: boolean, isLive: boolean) {
const model = {
...value,
};
console.log(JSON.stringify(model));
try {
const header = new HttpHeaders()
.set('Content-type', 'application/json');
var success = this.httpClient.post(this.baseUrl + 'person', JSON.stringify(model), { headers: header }).toPromise();
if (success) {
console.log('wooo');
}
} catch (error) {
console.log(error);
}
}
Logging the model to the console shows me the following data:
{"Firstname":"john","Lastname":"Doe","Gender":"2","DateOfBirth":"29/09/1955"}
However in the API controller I see the following:
Can anyone recommend or suggest why this comes through as null? I've tried removing the JSON.stringify but the issue persists.
Hitting the end point via Postman as suggested in the comments the model is populated with the data. Below is the Postman body:
{
"Firstname": "frefrefre",
"Lastname": "dewdewdew",
"Gender": 1,
"DateOfBirth": "2019-01-06T17:16:40"
}
Seems submitting from the Angular frontend sets the Gender variable to a string, and the DateOfBirth to an incorrect format..

You shouldn't need the [FromBody] unless you are also passing URL data in the same call, the issue would appear to be something wrong with the server interpretation of the content type/headers.
Try making the header collection exactly the same as it is in the Postman test.
Also it might be worth simplifying the code a little to trace the issue:
const headers = { 'content-type': 'application/json'}
const body=JSON.stringify(model);
console.log(body)
var success = this.httpClient.post(this.baseURL + 'person', body,{'headers':headers})

Related

Angular/C# null body sent for POST

I've trying to do a POST in Angular, which makes a call to my C# backend. One API call works fine, but the other one doesn't exactly. I'm having a hard time figuring out what's going on, but I see that when I open the Network window in my browser's DevTools, the request payload has the JSON populated just fine. But in C#/the backend, it receives a null object, and I get a 200 code/null response from the call.
I've got the following code in Angular:
item.service.ts
private readonly _api = '...'
private postOptions = { headers: { 'Content-Type': 'application/json; charset=utf-8' }};
public addItem(formGroup: formGroup, isInactive: boolean): Observable<Item> {
let api = ``;
// These require different API calls depending on the flag
if (isInactive)
api = `${this._api}/AddInactiveItem`;
else
api = `${this._api}/AddItem`; // This one is the one having issues
const body = ItemPost.parse(formGroup.value);
return this.http.post<Item>(api, body, this.postOptions).pipe(
this.handle(
"POST successful",
"Error with POST"
)
);
}
item-post.ts
export class ItemPost {
Name: string;
Inactive: string;
...
public static parse(obj: any): ItemPost {
return !obj ? undefined : {
Name: obj.name,
Inactive: obj.inactive,
...
};
}
}
My backend/POST code is in C#. Both of my POST methods are built the exact same, but with different SQL calls.
[HttpPost]
public JsonResult AddInactiveItem([FromBody] ItemBody item)
{
if (!ModelState.IsValid)
return Json(null);
// Do SQL call for POST here, return JSON
}
[HttpPost]
public JsonResult AddItem([FromBody] ItemBody item) // This is where I have a breakpoint and it's passing in null
{
if (!ModelState.IsValid)
return Json(null); // And this is where I find myself
// Do SQL call for POST here, return JSON
}
JSON Payload (sorry, unable to get a screenshot but this is what I'm seeing):
{"Name":"Test", ..., "Inactive":"N"}
I think you are having problem with:
`$(this.api}/AddInactiveItem`
The right syntax for template strings is:
`${this.api}/AddInactiveItem`
I figured it out...and I feel dumb. Really dumb.
An int variable in Item was considered an optional int/number in the front-end for when an Item was considered active (not inactive), but in the backend, ItemBody didn't reflect that and was considered as just an int instead of int?. I had to dig through my ModelState errors through the debugger and it hinted this, but it's late at night and my mind didn't process it.
Gotta make sure all of the variable types are reflected properly in the Body object.

why do I have to match the parameter name to get json data from ajax call in MVC4 web app?

I just want to know why it's necessary for .NET to match parameter name with the JSON object's key name?
Quick code preview here...
var json = {
"service": "COMMON",
"method": "MENU_SUBLIST",
"UID": "1000007",
"ULID": "stackoverflow",
"UNM": "queston",
"SITE": "1",
"DEPT": "2",
"LANG": "ko",
"MENUID": "0000",
"STEPMENU": "",
"ACTIONNAME": ""
}
Okay, Let's call an action in a controller through Ajax.
$.ajax({
type: "POST",
url: "DATACRUD.json",
data: JSON.stringify(json),
contentType: "application/json; charset=utf-8",
dataType: "json",
async: false, //_async,
success: function (result) {
}
});
And my c# action code here..
[HttpPost]
public ActionResult DATACRUD(string jsondata)
{
return Json(new{ fromMVC = jsondata});
}
// Just example.
jsondata is here null because I didn't match the key name.
For DATACRUD to get the JSON data, I have to do like this.
{ jsondata : {
"service":"COMMON",
"method":"MENU_SUBLIST",
"UID":"1000007",
"ULID":"stackoverflow",
"UNM":"queston",
"SITE":"1",
"DEPT":"2",
"LANG":"ko",
"MENUID":"0000",
"STEPMENU":"",
"ACTIONNAME":""
}
}
Here question No.1 Why do I have to match the key name with the param name?
It just does? there's gotta be a reason, and I want to know why.
And what I want to do is...
{
"service":"COMMON",
"method":"MENU_SUBLIST",
"UID":"1000007",
"ULID":"stackoverflow",
"UNM":"queston",
"SITE":"1",
"DEPT":"2",
"LANG":"ko",
"MENUID":"0000",
"STEPMENU":"",
"ACTIONNAME":""
}
to pass this JSON data into the action, DATACRUD I specified above
I want DATACRUD action to take the JSON data and consume it whatever the key name is.
There's another answer for this. The answer is to create a model for JSON data and receive it as a model type, and get the model as string.
But defining models cannot be possible in my apps. It could cause a hundred of model creation.
So receiving the JSON data after making a model is the last thing I need.
In this case, how am I supposed to do?
No key name matching is allowed.
No generating model is allowed.
No third party framework is allowed.
I think the possible answers narrow down to a few....
What I have to do?
The MVC routing engine dictates that the parameter names must match, as that is how it knows what to populate since everything comes through as strings to the server. The MVC plumbing will be searching through the query portion of the URL, and even searching fields in a form on a POST to populate all of your parameters.
Having a hundred models is not that bad for a complex project. However, it can be a pain if you have to go back and retrofit your entire application.
No matter what you do, you'll need to make sure that your JavaScript variable names match those of your Action method parameters, which shouldn't be a problem since you're writing both sides.
Base on post MVC controller : get JSON object from HTTP body?
You action should be:
[HttpPost]
public ActionResult DATACRUD()
{
Stream req = Request.InputStream;
req.Seek(0, System.IO.SeekOrigin.Begin);
string json = new StreamReader(req).ReadToEnd();
return Json(new { fromMVC = json });
}

No action was found that matches the request URI

I have attempted to modify one of my api controller to allow for the creation of multiple reservations by allowing one of the parameters to be passed in as a pipe delimited string. The method and class can be seen here:
public class ReservationsController : ApiController
{
public HttpResponseMessage PostReservation(string eRaiderUserName, string SpaceNumbers)
{
char[] delimiter = { '|' };
string[] spaces = SpaceNumbers.Split(delimiter);
bool saved = true;
foreach(string space in spaces)
{
var reservation = new Reservation { eRaiderUserName=eRaiderUserName, SpaceNumber=Convert.ToInt32(space) };
if (true)
{
reservation.Game = db.Games.FirstOrDefault(g => g.ID == AppSettings.CurrentGameID);
db.Reservations.Add(reservation);
db.SaveChanges();
//HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, reservation);
//response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = reservation.ID }));
//return response;
}
else
{
saved = false;
//return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
}
db.SaveChanges();
if (saved)
{
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = 1 }));
return response;
} else
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
}
}
I have a form that posts what I think should be the right information, but I keep getting this error:
{"$id":"1","Message":"No HTTP resource was found that matches the request URI 'http://localhost:58463/api/Reservations'.","MessageDetail":"No action was found on the controller 'Reservations' that matches the request."}
The (modified) save method in the api is still definitely a work in progress. But what is keeping this from finding the web api controller? Here is the firebug output:
As pointed out, the problem is that a POST action can only transfer the data posted in the body to a single object (for technical reasons).
That means that you can get data from the route data, from the querystring, and from the body, with the following limitations:
data from querystring or route data must be single values (i.e. they cannnot be classes), in any number
you can have only one parameter of the action with data coming from the request body, but this can be a complex class
you can make any combination of this, i.e. a single or complex param coming from the body, and any number of single parameters coming from the route data or the querystring.
So, the most generic way to solve your problem (i.e. that can be easyly applied to other classes where you need to pass complex data, even more complex than this case) is this:
First, make a class which has properties for all the needed data,in your case:
public class ReservationData
{
public string eRaiderUserName { get; set; }
public string SpaceNumbers { get; set; }
}
Second, use this class as the type of the received parameter in your action:
public HttpResponseMessage PostReservation(ReservationData reservationData)
With this code the formatter can map all the data in the request body to a single parameter in the action. You can use JSON or formdata formats, like the generated by jQuery.
NOTE: the property names must case-sensitively match the name of the posted parameters.
This is because you send x-www-form-urlencoded data to controller, to handle this data you must use [FromBody] before parameter like
public HttpResponseMessage Post([FromBody] string name) { ... }
but this approach has a lot of limitation:
1) There can be only one parameter marked [FromBody] attribute (it can be complex type)
2) The data must be encoded as =value not as key=value .
You can read it here description and how make it work here example .
If it possible i recommend you to send Json data to controller, without this limitation.
Web API has limited support to map POST form variables to simple parameters of a Web API method. Web API does not deal with multiple posted content values, you can only post a single content value to a Web API Action method.
public HttpResponseMessage PostReservation(string eRaiderUserName, string SpaceNumbers)
{ //...}
and you are trying to call using jQuery:
$.ajax({ url: 'api/reservations', type: 'POST', data: { ... }, dataType: 'json', success: function (data) {alert(data);} });
Unfortunately, Web API can’t handle this request and you’ll get error. But if you pass parameters using query string, It’ll work:
$.ajax({ url: 'api/reservations?eRaiderUserName=2012&SpaceNumbers=test', type: 'POST', dataType: 'json', success: function (data) { alert(data); } });
But it’s not good solution and not applicable for complex objects. So here are the different ways to do it.
Using Model Binding (Preferred)
Using Custom Parameter Binding
FormDataCollection
Query String

Unable to send JSON to ASMX as anything other than Dictionary<>

I'm writing an ASP.NET web application that transmits JSON between the client and the server. I have nearly everything complete, except that I cannot seem to transmit from the client the JSON to the ASMX and have it interpreted as anything but a Dictionary<string>
On the server-side, I have
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
[ScriptService]
public class WebService1: System.Web.Services.WebService
{
[WebMethod(EnableSession = true)]
public bool SaveExpression(ExpressionStructure Expression) {
return true;
}
}
On the client-side, I am sending the data using $.ajax(), where expressionObject.expression is an object of .NET class ExpressionStructure received earlier by the client:
var dataSubmit = { Expression: expressionObject.expression };
$.ajax({
url: "WebService.asmx/SaveExpression",
contentType: "application/json; charset=utf-8",
dataType: "json",
type: 'POST',
data: JSON.stringify(dataSubmit),
success: function (msg) {
alert(msg.d);
},
error: function (obj, status, msg) { ajaxServerError(obj, status, msg); }
});
When the SaveExpression method accepts Object, I get a dictionary of strings. When I use ExpressionStructure, I get a 500 Internal Server Error.
The expression being sent is an unmodified version of what is received from this MVC3 controller method.
public ActionResult Expression(int ExpressionID) {
ExpressionStructure es = GetExpressionFromDatabase(ExpressionID);
return new JsonResult {
Data = new {
expression = es,
view = this.RenderPartialView("_Expression_Statement", es)
},
JsonRequestBehavior = System.Web.Mvc.JsonRequestBehavior.AllowGet
};
}
The expression itself looks like this:
{
"Expression":
{
"Name":"Status Desc",
"Type":0,
"Statement":
{
"FormulaItem":
{
"Type":"Replace",
"Parameters":
[
{
"Type":"Char",
"Value":
{
"Value":"[Status]"
},
"Source":"picker"
},
{
"Type":"input",
"Value":
{
"Value":"0,1,2;Home,Driving,Away"
},
"Source":"inputBox"
}
]
}
}
}
}
I've tried changing the ASMX method to take string, ExpressionStructure but both return a 500 Internal Server Error. I've also tried modifying the JSON coming from the client, doing various wrappings (including wrapping the outermost "Expression" with square braces).
What critical element am I missing that is preventing the ASMX from correctly taking the JSON and getting the ExpressionStructure? Optionally, how can I get a string either directly or from the Dictionary so I can perform the JSON convertion manually (which I'd rather not do)?
After building a small test case separated from the main project, I was able to get better information about why the server was returning 500 Internal Server Error. As it happens, the web app was catching a deserialization error and attempting to redirect the request to the error handler page. Since it wasn't an actual page request, the redirect threw an error, which forced a redirect to the error handler page....until too many redirects kicked out an error.
The underlying problem was in the deserializing of the JSON to a rather complicated (collection of) classes on the .NET side. The classes, perfectly decorated with XML attribute decorators, used abstract classes when there was a choice in the schema (ie one element or another). Without similar JSON decorators, the deserialization didn't know how to deserialize the JSON to which class.

AJAX error in ASP.NET c#

I am very new to Ajax and ASP.NET MVC. I have a function, that returns back to AJAX and I need to handle the error situation. When everything works fine, then the code is okay. My question is how to handle the error part. Here is what I have:
To return success I have:
var data = new { success = false };
return Json(data, JsonRequestBehavior.AllowGet);
And I need to know what to return when there is an exception or error??
This is my query:
function DoMailPDF() {
$("#submitMail").attr("disabled", true);
var personid = $("#personid").val();
var unitid = $("#unitid").val();
var url = "#(Url.Action("SendEmail", "Report"))";
$.ajax({
url: url,
data: { person: personid , unit:unitid},
success: function () {
// $('input[name=MailSent]').attr('checked', true);
$("#submitMail").removeAttr("disabled");
alert("Email sent!");
},
error: function () {
alert("Email not sent!");
}
});
}
It never comes to the error function. How do I make it go to the error? Any tips and suggestions are most welcome.
You can access your json response object by writing:
$.ajax({
url: url,
data: { person: personid , unit:unitid},
dataType: 'json',
success: function (response) {
if (response.success == false) {
// Error handling
} else {
// Success handling
}
},
error: function () {
alert("Email not sent!");
}
});
As Nick Bork already explained in a comment, the error/success status of a response is determined by the Http status code that is sent down in the header. You can still go the suggested way and inspect the response object and the success property but it is clearly not the proper way when you already have a more powerful and long proven mechanism (the HTTP protocol itself).
.NET will use HTTP 200 (OK) when everything goes according to the code but you can change this behaviour in the Controller by accessing the Reponse object like this, for example:
Response.StatusCode = 500; // or any of the other HTTP "failure" status codes
Any status code in the 4xx or 5xx category will trigger the error() handler specified in the $.ajax(...) call. From there you can of course also inspect the proper status code, the response details and every properties of the XHR object to provide a more meaningful user experience.
HTTP status codes are pretty much set in stone and are not likely to change, that's why they are in my opinion definitely preferrable to a custom made solution...
PS: For a list of HTTP status codes, wikipedia is your friend.

Categories

Resources