I'm following along this tutorial to create a Web API using C#, and I have across an issue. The GET handler is working fine but the POST handler seems to be having issues parsing the body. Whenever I make a GET request, I get a valid response back (photos below), but if I try a POST of PUT, I get a 415 HTTP error.
This is my model:
public class Pet
{
public long Id { get; set; }
public string? Name {get; set;}
public string? Type {get; set;}
public string? Breed {get; set;}
public string? Description {get; set;}
public string? BirthDate {get; set;}
public string? ImageUrl {get; set;}
public long? Adopter {get; set;} = null;
public bool Available {get; set;} = false;
}
This is the corresponding controller:
public class PetsController : ControllerBase {
...
// The handler for POST
[HttpPost]
public async Task<ActionResult<Pet>> PostPet([FromBody]Pet pet)
{
_context.Pet.Add(pet);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetPet), new { id = pet.Id }, pet);
}
...
}
Here is a GET request response.
And here is a POST request response.
This is the payload I'm sending.
Complete JSON response (for POST).
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "00-298784fa1ee54a98afe19a0db7410d23-6cef112f5fc292b1-00",
"errors":{
"$":[
"'-' is invalid within a number, immediately after a sign character ('+' or '-'). Expected a digit ('0'-'9'). Path: $ | LineNumber: 0 | BytePositionInLine: 1."
],
"pet":[
"The pet field is required."
]
}
}
Two ways to resolve this:
Change [FromBody] to [FromForm] in your controllers method signature:
Or, don't use a form, and instead use a POST body in JSON.
For example:
{
"name": "allan",
"type": "cat",
"breed": "husky",
"description": "a nice doggy",
"birthDate": "12/12/2009",
"imageUrl": "dog.png"
}
This should bind the values properly:
Related
I have something like these:
BaseDTO
public record BaseDTO
{
public virtual int Id { get; protected init; }
public DateTime Timestamp { get; protected init; }
}
NotificationDTO
public record NotificationDTO(string Code, string Name, string Description, NotificationType Type) : BaseDTO;
NotificationsController
[HttpPost]
[ProducesResponseType((int)HttpStatusCode.OK)]
public async Task<IActionResult> Post([FromBody] NotificationDTO notificationDTO)
{
await _notificationsService.AddNotification(notificationDTO);
return Ok();
}
When I open SwaggerURL only members of NotificationsDTO can be set in "Try it out" section, members of BaseDTO are missing. Even if I add them by hand like setting the Id to 1 in Swagger, if I put a breakpoint in controller, Id it's 0, so Swagger doesn't modify it.
How can I see the base entity members in Swagger?
Edit:
It seems the object binding is not done properly, I have tried to send this in the request body with Postman:
{
"type": 0,
"id": 1,
"timestamp": "2022-05-13T09:22:18.429Z",
"code": "string",
"name": "string",
"description": "string"
}
And in the controller I've got this:
{
"NotificationDTO"{
Id = 0,
"Timestamp = 1/1/0001 12":"00":00 AM,
"Code = string",
"Name = string",
"Description = string",
"Type = Email"
}
}
If I change the access modifiers of the properties of base entity from public virtual int Id { get; protected init; } to public virtual int Id { get; init; } then the objects are binded properly and I can see the members of the base entity in Swagger.
I'm trying to build an alternative of ModelState as when I return this from api like return BadRequest(ModelState) it doesn't have much information other than somethings gone wrong with the request. So I have decided to build an alternative which will eventually look like this:
{
"code": 1000,
"message": "Invalid request",
"fields": [
{
"name": "Password",
"message": "Password does not meet complexity requirements."
}
]
}
In order to accomplish this, I have the following models in my api project:
ErrorResponse.cs:
public class ErrorResponse
{
public string Code { get; set; }
public string Message { get; set; }
public Dictionary<string, Field> Fields { get; set; }
}
Field.cs:
public class Field
{
public string Name { get; set; }
public string Message { get; set; }
}
Thanks to using FluentResults I can determine the error from the service layer within my controller like so:
if (result.HasError<PasswordError>())
{
}
However I've realised that I now need to return the ErrorResponse but each Field has a name and the only way to know the name of the field is through the controller using:
nameof(RegisterRequest.Password)
Is there a way to do this in a clean generic way without using magic strings?
I have the following JSON data:
{
"count": 2,
"data" : {
"items" : [
{
"id" : "1",
"letterheadline" : "This is a test",
"message" : "testing.. testing..",
"dateEntered" : "2018-01-01 18:00"
},
{
"id" : "2",
"letterheadline" : "Message two",
"message" : "testing.. testing.. testing..",
"dateEntered" : "2018-02-01 18:00"
},
]
}
}
I am trying to parse it into my own object that uses different values, i.e:
public class Message
{
public string title {get; set;}
public string body {get; set;}
public DateTime entryDate {get; set;}
}
public class Messages
{
public int itemCount {get; set;}
public List<Message> messages {get; set}
}
I am using
Messages messages = new JavaScriptSerializer().Deserialize<Messages>(result);
I have tried to use the following:
[JsonProperty("letterheadline")] (for example)
But I am still getting an error saying that it cannot be converted.
Is this because the JSON data itself is too deep to parse? Therefore would I need to create a new property Data inside my object that contains a list of Messages?
I've been stuck on this for awhile. I have a JSON response sending me keys that include periods. For example: "cost_center.code"
How can I get this into my object? I'm not getting any errors but the value is just coming in as null and isn't being deserialized into my class.
Here's my classes:
public class Result
{
public string company { get; set; }
public string first_name { get; set; }
public string email { get; set; }
public string employee_id { get; set; }
public string last_name { get; set; }
[DeserializeAs(Name="cost_center.code")]
public string cost_center { get; set; }
}
public class RootObject
{
public List<Result> result { get; set; }
}
Here's the JSON response:
{
"result": [
{
"company": "My Company",
"first_name": "First",
"email": "example#fakeaddress.com",
"employee_id": "123456789",
"last_name": "Last",
"cost_center.code": "12345"
}
]
}
I execute with:
var response = client.Execute<List<RootObject>>(request);
// this returns null
Console.WriteLine(response.Data[0].result[0].cost_center);
// all other values return fine ex:
Console.WriteLine(response.Data[0].result[0].company);
I've tried both with and without the DeserializeAs. I'm not sure its even working. Am I using this property incorrectly? Is it a container issue with the List?
Edited and accepted the answer below to use JsonProperty. For others who may come along this was the solution.
Added JSON.net nuget.
using Newtonsoft.Json;
Set the JsonProperty as described:
[JsonProperty("cost_center.code")]
Changed my execute to:
var response = client.Execute(request);
Then deserialized it like this:
var jsonResponse = JsonConvert.DeserializeObject<RootObject>(response.Content);
Afterwards I can access the value:
Console.WriteLine(jsonResponse.result[0].CostCenter
Do the following with properties having period in their names :
[JsonProperty("cost_center.code")]
public string CostCenter{ get; set; }
It should work
If you want to user RestSharp natively or couldn't get the Newtonsoft.Json.JsonSerializer support to work (which I couldn't), they just added support for proper deserialization of properties with dots in their names as of 106.1.0.
See my response here: Accessing properties with a dot in their name
Hi I have a function that will expose to the RestAPI. I need to pass in a JSON string to be a defined class. I use the following class for JSON format:
public class Registrant : Guest
{
public int RegistrantID { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public int EventID { get; set; }
public bool IsPrimary { get; set; }
}
And I have the following function in my controller:
public HttpResponseMessage Create(string id, [FromBody]Registrant registrant)
However When I pass in the JSON string the same structure, it could not deserialize correctly.
Is there anything wrong with it?
PS: my JSON string in request body:
{
"RegistrantID":"0",
"Email": "abc#abc.com",
"Phone": "123456789",
"EventID": "8",
"IsPrimary": "true",
"CustomerId": "12345678",
"FirstName": "abc",
"LastName": "def"
}
Update:
The problem is resolved by choosing Content-Type as Application/Json
Also, I took out the quotes on int and bool params it works fine.
Finally the api call look like this in project:
public HttpResponseMessage Create([FromUri]string id, [FromBody]Registrant registrant)