Has anyone found a good way to use data annotations to prevent specifc properties from being updated in a json patch doc.
Model:
public class Entity
{
[DoNotAllowPatchUpdate]
public string Id { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public string Action { get; set; }
}
Logic:
var patchDoc = new JsonPatchDocument<Entity>();
patchDoc.Replace(o => o.Name, "Foo");
//Prevent this from being applied
patchDoc.Replace(o => o.Id, "213");
patchDoc.ApplyTo(Entity);
The logic code is just an example of what the patch doc could look like coming from the client just generating in C# for quick testing purposes
I wrote an extension method for JsonPatchDocument; here's an abbreviated version:
public static void Sanitize<T>(this Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T> document) where T : class
{
for (int i = document.Operations.Count - 1; i >= 0; i--)
{
string pathPropertyName = document.Operations[i].path.Split("/", StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
if (typeof(T).GetProperties().Where(p => p.IsDefined(typeof(DoNotPatchAttribute), true) && string.Equals(p.Name, pathPropertyName, StringComparison.CurrentCultureIgnoreCase)).Any())
{
// remove
document.Operations.RemoveAt(i);
//todo: log removal
}
}
}
Add a minimal attribute:
[AttributeUsage(AttributeTargets.Property)]
public class DoNotPatchAttribute : Attribute
Apply the attribute to your class properties:
public class SomeEntity
{
[DoNotPatch]
public int SomeNonModifiableProperty { get; set; }
public string SomeModifiableProperty { get; set; }
}
Then you can call it before applying the transformation:
patchData.Sanitize<SomeEntity>();
SomeEntity entity = new SomeEntity();
patchData.ApplyTo(entity);
You could create your own Attribute
. Something like :
DoNotAllowPatchUpdate:Attribute{}
public class Entity
{
[DoNotAllowPatchUpdate]
public string Id { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public string Action { get; set; }
}
And then check for it like:
var notAllowedProperties = typeof(Entity).GetProperties()
.Where(x => Attribute.IsDefined(x, typeof(DoNotAllowPatchUpdate)))
.Select(x => x.Name).ToList();
now before you update them you can check notAllowedProperties
.
Although the question specifically asked about using annotations to restrict updates via JsonPatchDocuments, I thought adding another approach may be helpful to some.
I usually create an update-specific model which only has the fields that I want to allow to be updated. Then it's impossible to update the Id, for example:
public class UpdateEntityModel
{
public string Name { get; set; }
public string Status { get; set; }
public string Action { get; set; }
}
My controller/function receives a parameter of type JsonPatchDocument<UpdateEntityModel>
. I fetch the required entity from the database, map its properties to my update model, apply the patch to the update model and validate the result. Then map this back to the entity to persist the changes in the database.
/* Fetch entity from db */
var updateEntityModel = MapEntityToUpdateModel(entity);
jsonPatchDocument.ApplyTo(updateEntityModel);
ValidateModel(updateEntityModel); // Using FluentValidation validator
MapUpdateModelBackToEntity(entity, updateEntityModel);
/* Persist entity in db */
I use FluentValidation, AbstractValidator<UpdateEntityModel>
, to specifically validate the update models.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With