Search code examples
c#.net-corejson-patch

JSON Patch Validation .Net Core


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


Solution

  • 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);