(C# ASP.NET Core) DTO Object as Argument and Return in a WebApi

This tutorial explains how to modify a POST type of WebApi so that it doesn't over-post data. A POST WebApi receives a data object as a parameter, and then adds it to a database, and finally it returns an HTTP 201 Created response alongwith the json for the newly added object. We have already covered this in one of the previous tutorials and for a primer on over-posting, please refer a previous tutorial "Over-Posting in WebApi and a Solution with Data Transfer Object".
(Rev. 19-Mar-2024)

Categories | About |     |  

Parveen,

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!


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.