Validate input to a POST request? - c#

I have made a controller for a REST POST api endpoint, is as following
public IActionResult POST([FromBody]Person person)
{
....
}
and where Person is defined as
public class Person
{
public string Name {get; set;}
public int Age {get; set;}
}
meaning that the post request is able deserialise an input like this
{
"Name": "Peter",
"Age": 2
}
without any problems...
Problems occurs though when I pass something like
{
"Name": "Peter",
"Age": 2,
"Error": 123123123
}
It still creates an Person which have the first two filled out..
I would like to trigger an error here stating that the input format is wrong.
I am storing the first input - and then use it to compare the next input based on the first one.
If they are the same - nothing should happen.
But since the last input contains a invalid field but the actual instance the input created is completely similar to the first one - I get an error on they are not equal?
I assume the error statement in the JSON is somehow stored in the new instance?
but how do i make sure that the input is being validated before the controller created an instance given the JSON input?

1) to validate your Person model, you could have something like
using System.ComponentModel.DataAnnotations;
public class Person
{
[Required]
[StringLength(250, MinimumLength = 2)]
public string Name { get; set; }
[Required]
[Range(1, 121)]
public int Age { get; set; }
}
and inside controller's POST action you can use if(!ModelState.IsValid){.....}
2) to compare if two Person instances are equal you could override Equals():
public class Person
{
...
...
public override bool Equals(object obj) =>
(obj is Person otherPerson) ? (Name,Age) == (otherPerson.Name,otherPerson.Age): false;
}
here current instance's Name and Age are compared to other instance's Name and Age. It will allow using if ( person.Equals ( someOtherPErsonInstance ) ){....}
3) if you need to make sure extra fields are not present in the POST request, you could add custom implementation IModelBinder, or accept raw data in POST action, then, parse and check.

Related

C# Receiving empty object in controller

I'm trying to get some data from the request body in a POST Controller, but the console shows empty props:
The Post Controller:
[HttpPost("{id}/features")]
public ActionResult<bool> AddFeatureAsync(Guid Id, [FromBody] AddRoleFeatureRequest request)
{
Console.WriteLine(request.Name);
Console.WriteLine(request.Description);
Console.WriteLine(request.Id);
return true;
}
The AddRoleFeatureRequest class:
public class AddRoleFeatureRequest
{
public Guid Id;
public string? Name;
public string? Description;
}
The JSON data from Postman (Using body raw as Json):
{
"name": "Feature ABC",
"description": "description",
"id": "7e12b0ad-2c82-46f0-a69e-8538efb0aa60"
}
What am I doing wrong?
I'm trying to get some data from the request body in a POST
Controller, but the console shows empty props:
Your reason for getting null data on your console or in controller is pretty obvious because you have defined your AddRoleFeatureRequest class field only which doesn't allow to set any value on it. For instance, public string? Name; is a field not property. To set value, you must implement valid setter. Thus, it can be treated as valid property and able to assign value into it.
Solution:
public class AddRoleFeatureRequest
{
public Guid Id { get; set; }
public string? Name { get; set; }
public string? Description { get; set; }
}
Note: Property without getter and setter will always consider as field, it will not allow you to assign value from outside.
Output:
Note: Modifying your class defination would completely resolve your issue. No other changes required.
Your "AddRoleFeatureRequest" class has capitals and your json data does not. This could be the source of your problems.
the attribute names might be the reasons because they ar different then the json keys, retry it while considering letters cases,

Is there an approach in C# WebAPI with DTO's to only update elements that need to change?

In the tutorials I've walked through around creating an API in C#, I've gone through creating an HTTP PUT command for updating records in a table contained in a database.
The examples I've seen, essentially, I create a DTO around the fields that can be updated in that table. For example, I have a class that looks like the following:
public class UpdateTablenameDTO
{
public int ID { get; set; }
public int IsActive { get; set; }
public int IsDeleted { get; set;}
...
I then built a controller and all of the fields in my UpdateTablenameDTO appear as elements expected when I do an update.
What I wanted to know is there a proper approach to not requiring all of the elements in the Update DTO when doing the Update call? When I send my payload to include only ID and IsActive, it complained that it needed the rest of my fields. When I think this through, there could be a situation that a user is sitting on a screen with an old state but with a specific update that they want to send through (i.e. make the record inactive).
I don't necessarily want to update all of the elements, really only the specific changes, which would be the only thing I would want to send, along with the ID for identification. I suppose the way I could do this is to check if the record has changed since the user last viewed it upon updating, but I wanted to make sure I wasn't missing something obvious for this kind of scenario.
You can use Nullable value types to indicate that a property is "optional". The deserializer on the webapi side will keep the property as null when no value is provided. You can define the receiving DTO as follow:
public class UpdateTablenameDTO
{
public int ID { get; set; } // still required
public int? IsActive { get; set; } // now it's optional
public int? IsDeleted { get; set;} // optional as well
}
When you provide the JSON body as {"ID":5, "IsActive": 20} then you get an UpdateTablenameDTO instance as follow:
new UpdateTablenameDTO {
ID = 5,
IsActive = 20,
// IsDeleted = null
}
When you receive such an object, you can simply do a check against null or check the HasValue property to see, if there was a value in the request.
if (data.IsActive.HasValue) {
// do something with "data.IsActive.Value"
}

Determine which fields in JSON have been specified

I'm using WebAPI to to update some entities in the database (.net core 3.0, mvc webapi, efcore, vs2019). I'm having trouble figuring out which properties have actually been sent in JSON request to my webapi endpoint, so I do not update them in the database if they were not sent (i.e. leave them as they are).
Here's my code:
Model:
public class UpdateModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
}
WebAPI method:
[HttpPut("entity/{id}")]
public async Task<SomeResult> Update(int id, [FromBody]UpdateModel model)
{
...
}
Request payload (PUT method, example URL: https://myserver/api/entity/1):
{
FirstName: "John"
}
In case with above JSON, only FirstName would get updated in the DB.
If, for example, this JSON was sent:
{
FirstName: "John",
Address: null
}
That would mean that (besides FirstName gets updated) Address would also be updated to null (which wouldn't be in the first case).
There are actually not just string objects in the Model, they are just used here as an example.
My API gets called and everything (with LastName and Address being set to NULL, of course), I just want to know which fields have actually been sent in a JSON request. Must I check every field for NULL value or is there any other method for checking (more generic)?
Maybe there is some other approach to this kind of problem and I'm just not familiar with it?

JSON Root Node - Different From Class Name

I need to get my JSON output looking like this:
{
"User": {
"Id" : "1",
"FirstName" : "John",
"LastName" : "Doe",
... etc
My first issue is that the class name being serialized here is called Person not User, and I am not able to change that. But the JSON needs User.
Secondly, my Web API method is not returning a root node here at all, what exactly am I doing wrong?
My JSON looks like this:
{"Id":1,"BossId":null,"Title":"CEO","GivenName":"Doe", ... etc
This is so badly formatted that even my Chrome extension to make JSON pretty doesn't recognize this stuff.
Here's my Web API controller to get a user by ID, which is resulting in the above:
[Route("{id:int}")]
public HttpResponseMessage GetPerson(int id) {
Person person = repository.Get(id);
if (person == null) {
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return Request.CreateResponse(HttpStatusCode.OK, person);
}
I am also going to have to map class properties to different names here, which is a separate issue.
For example, the class has GivenName and FamilyName but the JSON needs FirstName and LastName. Is there an attribute I can apply to the property for this?
Any help would be appreciated.
ASP.NET WebApi uses JSON.NET for serialization to json. So you can change name in this way:
class Person
{
[JsonProperty(PropertyName = "LastName")]
public string FamilyName { get; set; }
...
}
EDIT
For add root element look at this answer. I didn't try this but looks nice.
Assuming you are using the Newtonsoft Json.Net, the most popular .Net Json serializer, following modifications are required:
Wrap the Person object inside a wrapper and assign a JsonProperty to it:
[JsonObject]
public class Wrapper
{
[JsonProperty("User")]
public Person Person {get; set;}
}
Now use the same JsonProperty inside the Person class too:
[JsonObject]
public class Person
{
[JsonProperty("FirstName")]
public string GivenName {get; set;}
[JsonProperty("LastName")]
public string FamilyName {get; set;}
... More Properties
}
Now while filling the response.following need to be done:
Wrapper w = new Wrapper();
w.Person = <assign Value>
return Request.CreateResponse(HttpStatusCode.OK, w);
One last thing Json unlike XML doesn't have a concept of a root node, it's nameless, that's why wrapper doesn't come anywhere and it would start from first object marked as User in this case, Json is a like an anonymous type in C#, internally a Key Value pair, since Keys are always string

JSON.NET Abstract / Derived Class Deserialization with WebAPI 2

I'm implementing a Web API 2 service that uses JSON.NET for serialization.
When I try to PUT ( deseralize ) updated json data, the abstract class is not present meaning it didn't know what to do with it so it did nothing. I also tried making the class NOT abstract and just inheriting from it and then each PUT deseralized to the base class rather than the derrived class missing the properties of the derrived class.
Example:
public class People
{
// other attributes removed for demonstration simplicity
public List<Person> People { get;set; }
}
public abstract class Person
{
public string Id {get;set;}
public string Name {get;set;}
}
public class Employee : Person
{
public string Badge {get;set;}
}
public class Customer : Person
{
public string VendorCategory {get;set;}
}
with my web api configured to do typename handling:
public static void Register(HttpConfiguration config)
{
config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling =
TypeNameHandling.Objects;
}
then I PUT the JSON like:
{
people: [{
name: "Larry",
id: "123",
badge: "12345",
$type: "API.Models.Employee, API"
}]
}
to the web api method:
public HttpResponseMessage Put(string id, [FromBody]People value)
{
people.Update(value); // MongoDB Repository method ( not important here )
return Request.CreateResponse(HttpStatusCode.OK);
}
but the output when inspecting value is always:
People == { People: [] }
or if non-abstract:
People == { People: [{ Name: "Larry", Id: "123" }] }
missing the inherrited property. Anyone ran into this problem and come up with anything?
The $type function has to be the first attribute in the object.
In the above example I did:
{
people: [{
name: "Larry",
id: "123",
badge: "12345",
$type: "API.Models.Employee, API"
}]
}
after moving $type to the top like:
{
people: [{
$type: "API.Models.Employee, API",
name: "Larry",
id: "123",
badge: "12345"
}]
}
the serializer was able to deseralize the object to the correct cast. Gotta love that!
I have tried your scenario now and it works fine. But I did notice that you are missing a , (comma) after the id property in your json input.
I figured this out by using the following ModelState validity check in my action which then showed the error in my request payload. This could be useful to you too:
if (!ModelState.IsValid)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, this.ModelState);
}
I know this post is old now and the answer has been marked, but I thought my solution might be helpful....
Try adding the JsonProperty attribute to the properties on your abstract class.
using JTC.Framework.Json;
...
public class People
{
// other attributes removed for demonstration simplicity
public List<Person> People { get;set; }
}
public abstract class Person
{
[JsonProperty()]
public string Id {get;set;}
[JsonProperty()]
public string Name {get;set;}
}
public class Employee : Person
{
public string Badge {get;set;}
}
public class Customer : Person
{
public string VendorCategory {get;set;}
}
JsonSubTypes library allows specifying which subclass of the given class should be used to deserialize into via attributes just like Jackson library in Java does. To be more specific, you can:
Choose a field and specify its value for each subclass, or
Specify fields present only in certain subclass.
I had a very similar issue. What worked for me was to add a default constructor that initializes the objects in your class. Make sure you initialize each object.
In your case, you need to add the constructor to the People class.
public class People
{
public People()
{
People = new List<Person>();
}
public List<Person> People { get;set; }
}
Also, this seems to be an all-or-nothing shot. If you do not initialize any contained objects, none of them will contain values.

Categories

Resources