客户要求 ASP.NET Core API 返回特定格式,怎么办?(续)

需求

【需求】客户就要求 API 的返回值属性名必须是 PascalCase如UserName),但是这些 API 需要同时提供给内部系统使用,默认都是 CamelCase(如 userName)。

【分析】返回的都是 JSON 格式,只是写入属性名的大小写不一样。

问题

ASP.NET Core 3.0 或更高版本中,默认 JSON 格式化程序基于 System.Text.Json,可以配置 Microsoft.AspNetCore.Mvc.JsonOptions.JsonSerializerOptions 实现自定义功能。

比如,设置返回值属性名是 PascalCase 格式:

1
2
3
4
5
6
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()
            .AddJsonOptions(options => 
               options.JsonSerializerOptions.PropertyNamingPolicy = null);
}

但是,这种只能实现固定设置,不能满足不同请求返回不同格式的需求。

这时,我们可以利用 Newtonsoft.Json 实现更灵活的配置。

添加 Newtonsoft.Json 支持

引用 nugetMicrosoft.AspNetCore.Mvc.NewtonsoftJson,并修改 Startup.cs,代码如下:

1
2
3
4
5
6
7
8
9
public void ConfigureServices(IServiceCollection services)
{
    // ...
    
    services.AddControllers().AddNewtonsoftJson(options =>
    {
        options.SerializerSettings.ContractResolver = new MyCustomContractResolver();
    });
} 

使用自定义类 MyCustomContractResolver 格式化 JSON

MyCustomContractResolver 实现

MyCustomContractResolver 实现代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class MyCustomContractResolver : DefaultContractResolver 
{
    private CamelCaseNamingStrategy _camelCase = new CamelCaseNamingStrategy();
    
    public override JsonContract ResolveContract(Type type)
    {
        return CreateContract(type);
    }
    
    protected override string ResolvePropertyName(string propertyName)
    {
        if (GetFormat() == "json2")
        {
            return propertyName;
        }

        return _camelCase.GetPropertyName(propertyName, false);
    }

    private string GetFormat()
    {
        Microsoft.Extensions.Primitives.StringValues headerValues;

        if (AppContext.Current.Request.Headers.TryGetValue("x-format", out headerValues))
        {
            return headerValues.FirstOrDefault();
        }
        
        return "json";
    }
}
  • 默认的 ResolveContract 缓存了指定类型的格式化设置,以加快运行速度,不能满足不同请求对同一类型执行不同的格式化要求。因此,为演示方便,这里去掉了缓存,也可以实现自定义缓存

  • GetFormat 是判断当前请求格式化方式的自定义方法。为演示方便,这里判断的是 x-format Header,你也可以改成其他方式,比如根据当前用户凭证进行判断

  • AppContext.Current 是对当前请求的 HttpContext 的封装

结论

尝试发送请求,运行效果如下图:

使用 x-format Header

不使用 x-format Header

完全满足了要求,只需要客户在每个 API 请求加上 x-format Header 即可。

0%