当 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";
}
}
|
如果 formatstyle
是 pascalcase
,就用 pascalCaseFormater
处理,否则使用默认处理。
小插曲: 需要设置 pascalCaseFormater
的 JsonSerializerOptions.PropertyNamingPolicy
属性,指定用于将对象的属性名称转换为其他格式(例如 PascalCase
)的策略,本来还准备实现一个 PascalCasePolicy
。但是转念一想,C#
本身的 Property
名不就是 PascalCase
的吗?!直接将 PropertyNamingPolicy
设为 null
不处理就行了。
结论


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