Are there any safer alternatives to hidden input fields for persisting properties across POST requests?

81 Views Asked by At

I have run into a problem where post requests in my CRUD app result in data loss due to simply forgetting to add a hidden field inside my <form> tags. For example, I have a UserModel that has a DateCreated property which ideally shouldn't be updated after creating the record. But subsequent updates inside my CRUD app led to my UserModel's DateCreated field being set to NULL. This is because there was no <input asp-for="Input.DateCreated" hidden /> tag that preserved the value of DateCreated across POST requests in my Razor Page. I am hesitant to accept that the best way to solve this problem is just to remember to add hidden input tags for every field you don't want to update. To simply remember to do this seems like an unnecessary risk and bad design.

Some sample code:

<form method="post">
    <input asp-for="AspNetUsersId" hidden />
    <input asp-for="Input.DateCreated" hidden />
    <div class="mb-2">
        <label asp-for="Input.Email" class="form-label">Email:</label>
        <input asp-for="Input.Email" type="email" class="form-control form-control-sm" />
        <span asp-validation-for="Input.Email" class="text-danger form-text"></span>
    </div>
    <div class="mb-2">
        <label asp-for="Input.FirstName" class="form-label">First Name:</label>
        <input asp-for="Input.FirstName" class="form-control form-control-sm" />
        <span asp-validation-for="Input.FirstName" class="text-danger form-text"></span>
    </div>
    <div class="mb-2">
        <label asp-for="Input.LastName" class="form-label">Last Name:</label>
        <input asp-for="Input.LastName" class="form-control form-control-sm" />
        <span asp-validation-for="Input.LastName" class="text-danger form-text"></span>
    </div>
</form>

One solution that came to mind is to start using DTOs for updating which would not include the DateCreated field at all, and would just require the Id property to be included in the hidden fields, but then I run into the problem where I have to make subsequent queries to fetch the remainder of my properties for my UserModel and then map them accordingly and pass that to my Update method.

Or, I could write another update method that would accept an UpdateUserDto.

Is using hidden input tags the best way to persist model properties across post requests or is this problem a result of larger architectural issues?

1

There are 1 best solutions below

2
Mike Brind On

The DbContext.Update method is fine where you want all properties to be included in the SQL update command. However, where you only want to enable modification of some properties in the entity, The recommended solution is to attach your entity to the context and then specify which properties might have been modified. That way, only those fields will be included in the generated SQL update command.

context.Attach(myEntity);
context.Entry(myEntity).Property(x => x.Name).IsModfied = true;
context.Entry(myEntity).Property(x => x.Description).IsModfied = true;
await context.SaveChangesAsync();

Including other property values in hidden fields is not a good idea because they can be modified by a user who is familiar with the browser dev tools, for example. Or anyone who can craft an HTTP request.