The Objective of this Tutorial
Let me first explain the objective of this tutorial.
First download the project from the linked downloads, so that you can follow the whole story.
Open the solution explorer and locate the program.cs file. Double click to open it. Scroll to the part that you are seeing here.
Consider this POST type of web api. It receives a Doctor instance, adds it to the database, and then returns the same object in a 201 response.
app.MapPost("/doctor", async (MyApiContext ctx, Doctor doc) => { ctx.Doctors.Add(doc); // success assumed for tutorial purposes await ctx.SaveChangesAsync(); return Results.Created($"/doctor/{doc.Id}", doc); });
Since the entire object is being shuttled between the client and server, we can say that the Doctor object is over-posted in this case.
The objective is to modify this web api so as to prevent over-posting.
For this, the communication has to be through a data transfer object DoctorDTO
that posts a smaller subset of the properties. For a primer on over-posting, please refer a previous tutorial "Over-Posting in WebApi and a Solution with Data Transfer Object".
Video Explanation (see it happen!)
Please watch the following youtube video:
Modifying the WebApi
Now we can start making the modifications.
The first alteration is to change the parameter to a DoctorDTO type, so that just the relevant properties are received into the handler function. Replace the Doctor argument by DoctorDTO this will save us unnecessary conversions of form data because it has to convert each field into a Doctor property whereas when it is a DoctorDTO object it will have to convert only a subset of properties. So in this way it will save us unnecessary conversions of form data.
// program.cs // we are modifying a POST webapi to prevent overposting // this is an extract // see the attached downloads for source code app.MapPost("/doctor", async (MyApiContext ctx, DoctorDTO doctor) => { // create a doctor from DTO // EMail is not for webapi communication // so it can be set on the basis of some // calculation or on the basis of other tables Doctor doc = new Doctor() { Name = doctor.Name, Fees = doctor.Fees, EMail = "assign-any" }; ctx.Doctors.Add(doc); // success assumed for tutorial purposes await ctx.SaveChangesAsync(); // create a DTO from doc and return it return Results.Created($"/doctor/{doctor.Id}", new DoctorDTO(doc)); });
The next is to create a new Doctor in the body of the request handler. The properties on this object are set on the basis of the properties received from the parameter. We are copying the respective properties into our doctor object.
It is possible now to set other properties, such as EMail in this case, on the basis of some synthesis, or on the basis of data in other tables . . . we have a lot of flexibility here, because email is one of the properties that we do not want to participate in web api communication. This property can be now set independently in whatever way you want.
The third is to create a DoctorDTO out of this Doctor so that over-posting doesn't take place.
With these changes the API prevents over-posting.
Running the Project
Now it's time to verify the changes.
Run the project to open the home page. This ensures that the server is running. Take note of the URL alongwith the port.
Open the Postman software and select method as POST, and type a body json string for the new object, and hit the send button!
If you are finding it difficult to understand this Postman, we have already covered this Postman in one of the previous tutorials. You can go and have a look at that.
So once we hit the send button we observe that a db update error is thrown (see the linked video).
This error is because our DOT class DoctorDTO doesn't have a default constructor. A small debugging helped me locate this problem - we missed it in the first tutorials. Let us add it now!
Open the solution explorer and locate the Doctor.cs file, and double click to open it as you see here.
public record DoctorDTO { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; init; } // name public string Name { get; init; } = default!; // fees public int Fees { get; init; } // add this now public DoctorDTO() { } // DTO class public DoctorDTO(Doctor doctor) => (Id, Name, Fees) = (doctor.Id, doctor.Name, doctor.Fees); }
Add the default constructor.
Now again run the application and again open the Postman software as you see here.
Hit the Send button!
We verify that the call succeeds and we receive 201-Created response from the server.
Other API can be modified in the same way. I have attached the completed project in the attached downloads. Thankyou!
Similar Posts
This Blog Post/Article "(C# ASP.NET Core) DTO Object as Argument and Return in a WebApi" by Parveen is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.