You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core中如何用数据注解阻止JSON Patch更新特定属性?

在.NET Core中通过数据注解阻止JSON Patch更新特定属性的可行方案

当然有完美的解决方案!我们可以通过自定义数据注解+Action过滤器的组合来实现这个需求,既符合你用数据注解标记禁止更新属性的要求,又能优雅地拦截并过滤非法的Patch操作。下面一步步给你实现:

1. 定义自定义禁止Patch的注解

首先我们创建一个专属的属性注解,用来标记哪些属性不允许通过JSON Patch更新:

using System;

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class DoNotAllowPatchUpdateAttribute : Attribute
{
    // 注解不需要额外逻辑,仅作为标记用
}

2. 标记实体类中的禁止更新属性

把这个注解应用到你的Entity类对应的属性上,比如示例中的Id:

public class Entity 
{ 
    [DoNotAllowPatchUpdate] 
    public string Id { get; set; } 
    public string Name { get; set; } 
    public string Status { get; set; } 
    public string Action { get; set; } 
}

3. 创建自定义Action过滤器拦截非法Patch操作

接下来我们写一个Action过滤器,它会在Patch请求到达业务逻辑前,自动检查并移除那些针对被标记属性的操作:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.JsonPatch;
using System.Reflection;
using System.Text.Json.Serialization;

public class ValidatePatchAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // 从Action参数中获取JSON Patch文档
        if (context.ActionArguments.TryGetValue("patchDoc", out var patchDocObj) && 
            patchDocObj is JsonPatchDocument<Entity> patchDoc)
        {
            // 获取所有被标记为禁止Patch的属性对应的JSON路径
            var forbiddenPaths = typeof(Entity)
                .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(prop => prop.GetCustomAttribute<DoNotAllowPatchUpdateAttribute>() != null)
                .Select(prop => 
                {
                    // 兼容JsonPropertyName注解的自定义命名(比如驼峰命名)
                    var jsonNameAttr = prop.GetCustomAttribute<JsonPropertyNameAttribute>();
                    var propertyName = jsonNameAttr?.Name ?? prop.Name;
                    return $"/{propertyName.ToLowerInvariant()}";
                })
                .ToList();

            // 筛选出需要移除的非法操作
            var invalidOperations = patchDoc.Operations
                .Where(op => forbiddenPaths.Contains(op.Path.ToLowerInvariant()))
                .ToList();

            // 移除非法操作
            foreach (var op in invalidOperations)
            {
                patchDoc.Operations.Remove(op);
            }
        }

        base.OnActionExecuting(context);
    }
}

4. 在Controller中应用过滤器

把这个过滤器加到你的Patch接口上,就能自动生效了:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.JsonPatch;

[ApiController]
[Route("api/entities")]
public class EntitiesController : ControllerBase
{
    [HttpPatch("{id}")]
    [ValidatePatch] // 应用过滤器
    public IActionResult PatchEntity(string id, [FromBody] JsonPatchDocument<Entity> patchDoc)
    {
        // 模拟从数据库获取实体
        var targetEntity = new Entity 
        { 
            Id = id, 
            Name = "OriginalName", 
            Status = "Active", 
            Action = "None" 
        };

        // 应用Patch(此时非法操作已经被移除)
        patchDoc.ApplyTo(targetEntity);

        // 这里添加保存到数据库的逻辑...

        return Ok(targetEntity);
    }
}

测试验证

用你提供的示例代码测试:

var patchDoc = new JsonPatchDocument<Entity>();
patchDoc.Replace(o => o.Name, "Foo"); // 这个操作会正常生效
patchDoc.Replace(o => o.Id, "213");   // 这个操作会被过滤器自动移除

// 调用接口后,实体的Name会变成"Foo",Id保持原来的值不变

额外优化(通用化过滤器)

如果你的项目中有多个实体需要这个功能,可以把过滤器改成泛型版本,适配任意实体类型:

public class ValidatePatchAttribute<TEntity> : ActionFilterAttribute where TEntity : class
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (context.ActionArguments.TryGetValue("patchDoc", out var patchDocObj) && 
            patchDocObj is JsonPatchDocument<TEntity> patchDoc)
        {
            var forbiddenPaths = typeof(TEntity)
                .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(prop => prop.GetCustomAttribute<DoNotAllowPatchUpdateAttribute>() != null)
                .Select(prop => 
                {
                    var jsonNameAttr = prop.GetCustomAttribute<JsonPropertyNameAttribute>();
                    var propertyName = jsonNameAttr?.Name ?? prop.Name;
                    return $"/{propertyName.ToLowerInvariant()}";
                })
                .ToList();

            var invalidOperations = patchDoc.Operations
                .Where(op => forbiddenPaths.Contains(op.Path.ToLowerInvariant()))
                .ToList();

            foreach (var op in invalidOperations)
            {
                patchDoc.Operations.Remove(op);
            }
        }

        base.OnActionExecuting(context);
    }
}

使用时只需要指定实体类型:

[HttpPatch("{id}")]
[ValidatePatch<Entity>]
public IActionResult PatchEntity(string id, [FromBody] JsonPatchDocument<Entity> patchDoc)
{
    // ... 业务逻辑
}

内容的提问来源于stack exchange,提问作者Hizzy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:11:34