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.
Related
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})
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
This seems so simple I must be over-thinking it.
TL;DR;
How can I modify the code below to return the json object contained in the string rather than a string that happens to contain json?
public ActionResult Test()
{
var json_string = "{ success: \"true\" }";
return Json(json_string, JsonRequestBehavior.AllowGet);
}
This code returns a string literal containing the json:
"{ success: "true" }"
However, I'd like it to return the json contained in the string:
{ success: "true" }
Slightly longer version
I'm trying to quickly prototype some external api calls and just want to pass those results through my "api" as a fake response for now. The json object is non-trivial - something on the order of 10,000 "lines" or 90KB. I don't want to make a strongly typed object(s) for all the contents of this one json response just so I can run it through a deserializer - so that is out.
So the basic logic in my controller is:
Call externall api
Store string result of web request into a var (see json_string above)
Output those results as json (not a string) using the JsonResult producing method Json()
Any help is greatly appreciated... mind is melting.
The whole point of the Json() helper method is to serialize as JSON.
If you want to return raw content, do that directly:
return Content(jsonString, "application/json");
public ActionResult Test()
{
return Json(new { success = true }, JsonRequestBehavior.AllowGet);
}
I currently have a WCF Data service live at www.mywebsite.com. It is a basic service that looks like this:
namespace MyWeb
{
[JSONPSupportBehavior]
public class MyDataService : DataService<MyEntities>
{
public static void InitializeService(IDataServiceConfiguration config)
{
config.UseVerboseErrors = true;
config.SetEntitySetAccessRule("Entities", EntitySetRights.AllRead);
ServiceOperationRights.All);
}
}
}
Currently, we have live clients out in the wild that make requests by posting ajax calls such as these:
$.ajax({
url: serverAddress + "MyDataService.svc/Entities?$top=20&$filter=IsConfirmed%20eq%20null&$format=json&$callback=?",
headers: {
version: "1.0",
platform: "a platform"
},
timeout: 12000,
dataType: 'jsonp',
cache: false,
context: document.body
})
This works as expected, returning a javascript object containing the required objects in the Entities table.
However, we would like to add some intelligence on the server side that limits what results can be returned from this query. To that end, I have attempted to implement a Query Interceptor in the aforementioned MyDataService class:
[QueryInterceptor("Entities")]
public IQueryable<Entity> OnQueryFares(IQueryable<Entity> query)
{
return from e in query
where DataCheck(e)
select e;
}
With the intended logic being that the service will now only return table entries for which DataCheck(e) evaluates to true. This function appears to work. However, when testing with the client, I get the following error:
Web Console(4448): Uncaught SyntaxError: Unexpected token < at
http://www.mywebsite.com/MyDataService.svc/Entities?$top=20&$filter=IsConfirmed%20eq%20null&$format=json&$callback=jQuery17207441281890496612_1340223164872&_=1340223166622:1
This particular error has led me to guess that, for some reason, the returned data from the Query Inspector that I implemented is coming in XML, rather than coming in JSON like the query did before I implemented the interceptor.
I haven't been able to find any instructions on this. How can I enforce a JSON response behavior in a Query Interceptor?
See this for usage of query interceptors: http://msdn.microsoft.com/en-us/library/dd744842.aspx
I'm surprised the above even starts the service (I suspect it doesn't and you get back an error payload and thus fail to read it, you can try to confirm with for example Fiddler).
Query interceptor returns a predicate (Expression) which is added into the query before it executes. So you don't get to return a new query, just modify the existing one.
In the sample above, just modify it like this:
[QueryInterceptor("Entities")]
public Expression<Func<Entity,bool>> OnQueryFares()
{
return e => DataCheck(e);
}
Here is an expanded version
[QueryInterceptor("Entities")]
public Expression<Func<Entity,bool>> OnQueryFares()
{
// Assuming e has two properties Name and Age.
return e => e.Name=="John" && e.Age=23 ;
}
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.