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

ASP.NET Core API 提供给到外部系统使用时,在某些情况下,可能需要根据这些外部系统的要求来格式化数据。

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

怎么办?

虽然可以为外部系统重新做一套 API,但是代价太大!能不能从自定义 API 的返回值上想办法?

在微软官方文档上找到一篇设置 ASP.NET Core Web API 中响应数据的格式,介绍了响应格式 URL 映射。

使用 FormatFilter 可以根据请求路径的路由映射将相应映射到相应的格式上。

希望达到的效果:

1
2
3
4
5
6
7
[ApiController]
[Route("[controller]")]
[FormatFilter]
public class WeatherForecastController : ControllerBase
{
    [HttpGet("{formatstyle?}")]
    public IEnumerable<WeatherForecast> Get()
路由 响应格式
WeatherForecast 默认格式 CamelCase
WeatherForecast?formatstyle=pascalcase PascalCase

代码实现

首先,需要让 API 支持自定义格式,返回 MediaType 还是 application/json

1
2
3
4
5
6
7
8
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.FormatterMappings.SetMediaTypeMappingForFormat
            ("pascalcase", MediaTypeHeaderValue.Parse("application/json"));
    });
}

接着,我们需要替换掉默认的 JsonOutputFomatter

1
2
3
4
5
6
7
8
for (int i = 0; i < options.OutputFormatters.Count; i++)
{
    if (options.OutputFormatters[i] is Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter jsonOutputFormatter)
    {
        options.OutputFormatters[i] = new CustomJsonOutputFormatter(jsonOutputFormatter.SerializerOptions);
        break;
    }
} 

CustomJsonOutputFormatter 的代码如下:

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
public class CustomJsonOutputFormatter : SystemTextJsonOutputFormatter
{
    private readonly SystemTextJsonOutputFormatter _pascalCaseFormater;

    public const string PascalCaseStyle = "pascalcase";
    public const string FormatterStyleParameter = "formatstyle";

    public CustomJsonOutputFormatter(JsonSerializerOptions jsonSerializerOptions)
        : base(jsonSerializerOptions)
    {
        _pascalCaseFormater = new SystemTextJsonOutputFormatter(new JsonSerializerOptions(jsonSerializerOptions)
        {
            PropertyNamingPolicy = null
        });
    }

    public override Task WriteAsync(OutputFormatterWriteContext context)
    {
        if (GetFormat(context) == PascalCaseStyle)
        {
            return _pascalCaseFormater.WriteAsync(context);
        }

        return base.WriteAsync(context);
    }

    private string? GetFormat(OutputFormatterCanWriteContext context)
    {
        if (context.HttpContext.Request.RouteValues.TryGetValue(FormatterStyleParameter, out var obj))
        {
            var routeValue = Convert.ToString(obj, CultureInfo.InvariantCulture);
            return string.IsNullOrEmpty(routeValue) ? null : routeValue;
        }

        var query = context.HttpContext.Request.Query[FormatterStyleParameter];

        if (query.Count > 0)
        {
            return query.ToString();
        }

        return "json";
    }
}

如果 formatstylepascalcase,就用 pascalCaseFormater 处理,否则使用默认处理。

小插曲: 需要设置 pascalCaseFormaterJsonSerializerOptions.PropertyNamingPolicy 属性,指定用于将对象的属性名称转换为其他格式(例如 PascalCase)的策略,本来还准备实现一个 PascalCasePolicy。但是转念一想,C# 本身的 Property 名不就是 PascalCase 的吗?!直接将 PropertyNamingPolicy 设为 null 不处理就行了。

结论

完全满足了要求,只需要客户在每个 API 请求 URL 上加上 ?formatstyle=pascalcase 即可。

0%