ASP.NET 核心中的 SnakeCaseNamingStrategy 和 JsonPatch

SnakeCaseNamingStrategy and JsonPatch in ASP.NET Core

有没有办法在使用 ApsNetCore.JsonPatch (2.1.1) 包?

我 运行 遇到路径未解决的问题,因为我的模型中的属性是 PascalCase,但 JsonPatch 中的路径是 SnakeCase。

在这种情况下,我必须将 JsonPatchDocument 上的 ContractResolver 设置为 Startup.cs 文件中 Default/Globally 注册的 ContractResolver。

它有效,但我必须为我要实施的每个补丁路线执行此操作。

启动配置:

public IServiceProvider ConfigureServices(IServiceCollection services)
{
  services
    .AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver
    {
      NamingStrategy = new SnakeCaseNamingStrategy()
    })
}

控制器:

[HttpPatch("{id}"]
[Consumes(MediaTypeNames.Application.Json)]
public async Task<IActionResult> Patch(string id,
    [FromBody] JsonPatchDocument<Entity> patchEntity)
{
    ...
    patchEntity.ContractResolver = new DefaultContractResolver
    {
        NamingStrategy = new SnakeCaseNamingStrategy()
    };
    patchEntity.ApplyTo(entity);
    ...

似乎没有简单的方法来影响在创建 JsonPatchDocument<T> 的实例时使用的 ContractResolver。此 class 的实例由 TypedJsonPatchDocumentConverter 创建,如以下代码片段所示:

var container = Activator.CreateInstance(
    objectType,
    targetOperations,
    new DefaultContractResolver());

这里很明显 DefaultContractResolver 在创建 JsonPatchDocument<T> 的实例时被硬编码为第二个参数。

在使用 ASP.NET 核心 MVC 时处理此问题的一个选项是使用 Action Filter,它允许对传递到操作中的任何参数进行更改。这是一个基本示例:

public class ExampleActionFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext ctx)
    {
        // Find a single argument we can treat as IJsonPatchDocument.
        var jsonPatchDocumentActionArgument = ctx.ActionArguments.SingleOrDefault(
            x => typeof(IJsonPatchDocument).IsAssignableFrom(x.Value.GetType()));

        // Here, jsonPatchDocumentActionArgument.Value will be null if none was found.
        var jsonPatchDocument = jsonPatchDocumentActionArgument.Value as IJsonPatchDocument;

        if (jsonPatchDocument != null)
        {            
            jsonPatchDocument.ContractResolver = new DefaultContractResolver
            {
                NamingStrategy = new SnakeCaseNamingStrategy()
            };
        }
    }
}

此处传入的ActionExecutingContextclass包含一个ActionArguments属性,在本例中用于尝试查找[=20=类型的参数].如果找到,我们会相应地覆盖 ContractResolver

为了使用这个新的动作过滤器,您可以将它添加到控制器、动作或全局注册。下面是全局注册的方法(其他选项有很多答案,所以我不会在这里深入探讨):

services.AddMvc(options =>
{
    options.Filters.Add(new ExampleActionFilterAttribute());
});