(C# ASP.NET Core) Over-Posting in WebApi and a Solution with Data Transfer Object

We have been working with a model class consisting of three properties - id, Name and Fees. All the three properties have participated and have been exposed in json communication through every Web Api that we have discussed and written in the past tutorials of this chapter. This is called over-posting. But what if there were a property such as an email address that we didn't want exposed? This tutorial presents a solution through the concept of a Data Transfer Object.
(Rev. 19-Mar-2024)

Categories | About |     |  

Parveen,

What is over-posting

Let us briefly discuss about over-posting.

We know model classes are exchanged as parameters and response objects. The ASPNET Core Engine takes care of the translation of a C# type to a json string, and vice-versa. Every public property of a model is exposed in this communication. The entire object is shuttled between a client and a server, even if a small subset is relevant. This is called over-posting.

Over-posting might be un-desirable for various reasons. One of them is bandwidth, and more importantly, it could be security.

Video Explanation (see it happen!)

Please watch the following youtube video:

Solution to the problem of over-posting

One solution is to use a shadow model class that contains just the properties that we want exposed. Such a class is called a Data Transfer Object.

Consider a model class such as this one. This class is the same Doctor class that we have been working with, except a new property for EMail, that we want to hide from every WebApi communication.

// Models -> Doctor.cs 

// model class 
public class Doctor
{

  [Key]
  [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
  public int Id { get; set; }

  // name 
  public string Name { get; set; } = default!;

  public int Fees { get; set; }

  // field to be hidden from web-api 
  public String? EMail { get; set; } = default!;

}

The solution is to create another record class called DoctorDTO that contains all properties of Doctor except EMail. Notice that we have used the record keyword to emphasise the fact that this is just a read-only type meant for data exchange. If you are not familiar with this keyword, then you can equally use the class keyword.

// Models -> Doctor.cs 
public record DoctorDTO
{

  [Key]
  [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
  public int Id { get; init; }

  // name 
  public string Name { get; init; } = default!;

  public int Fees { get; init; }

  // conversion from Doctor to a DoctorDTO 
  public DoctorDTO(Doctor doctor)
    => (Id, Name, Fees) = (doctor.Id, doctor.Name, doctor.Fees);

}

This record also contains a constructor that is used for translation of a Doctor to a DoctorDTO.

Suggested Rules

I have written a few useful rules for using a DTO class.

The first is that wherever your API takes a parameter of type Doctor, replace that parameter by DoctorDTO because a client will communicate only through the properties contained in a DTO. So it will be more meaningful to use a DTO. And inside the body of the function, the DoctorDTO can be converted to a Doctor by copying the respective properties. More on this in the coming tutorials.

The second is that wherever your API returns a type of Doctor, replace that by a DoctorDTO, by using its constructor.

And thirdly, wherever your API returns a list of Doctor objects, replace that by a list of DoctorDTO objects by using a projection function such as Select: await ctx.Doctors.Select(x => new DoctorDTO(x)).ToListAsync();

// returning a list of DTO 
await ctx.Doctors.Select(x => new DoctorDTO(x)).ToListAsync();

We shall modify our WebApi in the next few tutorials. Thanks!


This Blog Post/Article "(C# ASP.NET Core) Over-Posting in WebApi and a Solution with Data Transfer Object" by Parveen is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.