更改单个 ASP.NET Core 控制器的 JSON 序列化设置

Posted

技术标签:

【中文标题】更改单个 ASP.NET Core 控制器的 JSON 序列化设置【英文标题】:Change the JSON serialization settings of a single ASP.NET Core controller 【发布时间】:2018-10-02 09:51:14 【问题描述】:

我有两个控制器控制器:ControllerAControllerB。每个控制器的基类是Controller

ControllerA 需要以默认格式(camelCase)返回 JSON。 ControllerB 需要以不同的 JSON 格式返回数据:snake_case。

如何在 ASP.NET Core 3.x 和 2.1 中实现这一点?

我已经尝试过startup

services
    .AddMvc()
    .AddJsonOptions(options =>
    
        options.SerializerSettings.Converters.Add(new StringEnumConverter());
        options.SerializerSettings.ContractResolver = new DefaultContractResolver()
        
            NamingStrategy = new SnakeCaseNamingStrategy()
        ;
    )
    .AddControllersAsServices();

但这会改变所有控制器的序列化,而不仅仅是ControllerB。如何为 1 个控制器配置或注释此功能?

【问题讨论】:

【参考方案1】:

ASP.NET Core 3.0+

您可以通过Action Filter 和Output Formatter 的组合来实现此目的。

3.0+ 的情况看起来有些不同,其中 3.0+ 的默认 JSON 格式化程序基于 System.Text.Json。在撰写本文时,这些don't have built-in support for a snake-case naming strategy。

但是,如果您使用 3.0+ 的 Json.NET(详细信息在 docs 中),则上面的 SnakeCaseAttribute 仍然可行,但需要进行一些更改:

    JsonOutputFormatter 现在是 NewtonsoftJsonOutputFormatterNewtonsoftJsonOutputFormatter 构造函数需要MvcOptions 的参数。

代码如下:

public class SnakeCaseAttribute : ActionFilterAttribute

    public override void OnActionExecuted(ActionExecutedContext ctx)
    
        if (ctx.Result is ObjectResult objectResult)
        
            objectResult.Formatters.Add(new NewtonsoftJsonOutputFormatter(
                new JsonSerializerSettings
                
                    ContractResolver = new DefaultContractResolver
                    
                        NamingStrategy = new SnakeCaseNamingStrategy()
                    
                ,
                ctx.HttpContext.RequestServices.GetRequiredService<ArrayPool<char>>(),
                ctx.HttpContext.RequestServices.GetRequiredService<IOptions<MvcOptions>>().Value));
        
    

ASP.NET Core 2.x

您可以通过Action Filter 和Output Formatter 的组合来实现此目的。下面是动作过滤器的示例:

public class SnakeCaseAttribute : ActionFilterAttribute

    public override void OnActionExecuted(ActionExecutedContext ctx)
    
        if (ctx.Result is ObjectResult objectResult)
        
            objectResult.Formatters.Add(new JsonOutputFormatter(
                new JsonSerializerSettings
                
                    ContractResolver = new DefaultContractResolver
                    
                        NamingStrategy = new SnakeCaseNamingStrategy()
                    
                ,
                ctx.HttpContext.RequestServices.GetRequiredService<ArrayPool<char>>()));
        
    

使用OnActionExecuted,代码在相应操作之后运行,并首先检查结果是否为ObjectResult(由于继承,这也适用于OkObjectResult)。如果是ObjectResult,过滤器只需添加JsonOutputFormatter 的自定义版本,它将使用SnakeCaseNamingStrategy 序列化属性。 JsonOutputFormatter 构造函数中的第二个参数是从 DI 容器中获取的。

要使用此过滤器,只需将其应用于相关控制器:

[SnakeCase]
public class ControllerB : Controller  

注意:例如,您可能希望在某个地方提前创建 JsonOutputFormatter/NewtonsoftJsonOutputFormatter - 我在示例中没有走那么远,因为这是手头问题的次要问题。

【讨论】:

【参考方案2】:

最终创建了我在端点上使用的这个方法:

           
    // needed to get the same date and property formatting 
    // as the Search Service:
    var settings = new JsonSerializerSettings
    
        ContractResolver = new DefaultContractResolver()
        
            NamingStrategy = new SnakeCaseNamingStrategy()
        ,
        DateFormatString = "yyyy-MM-ddTHH:mm:ss.fffZ"
    ;

    return Json(result, settings);

【讨论】:

【参考方案3】:

不需要操作过滤器等​​。只需在控制器中覆盖 Json() 即可。

public class MyController : Controller

    public override JsonResult Json(object data)
    
        return base.Json(data, new JsonSerializerSettings 
            // set whataever default options you want
        );
    

【讨论】:

以上是关于更改单个 ASP.NET Core 控制器的 JSON 序列化设置的主要内容,如果未能解决你的问题,请参考以下文章

在 ASP.NET Core 中更改控制器路由

ASP.NET Core 依赖注入单个聚合类而不是多个单独的构造函数注入

如何更改 ASP.NET Core API 中的默认控制器和操作?

在控制器中更改 ASP.NET Core 2.0 标识连接字符串

ASP.NET Core 更改 AccessDenied 路由

ASP .NET Core 更改默认 ApiController BadResponse