ASP.NET 核心 API JSON 每个请求的序列化器设置
ASP.NET Core API JSON serializersettings per request
基于请求中的某些值(header 或 url),我想更改我的 DTO objects 的序列化。
为什么?好吧,我已经将 [JsonProperty("A")]
应用于我的 DTO,但取决于客户端(网站或移动应用程序)是否要使用 属性。
我从
开始
services
.AddMvc()
.AddJsonOptions(opt =>
{
#if DEBUG
opt.SerializerSettings.ContractResolver = new NoJsonPropertyNameContractResolver();
#endif
}
所以在调试时我得到 JSON 和 属性 全名。我使用 JsonProperty
属性来缩短响应 JSON,这适用于反序列化回相同 DTO 的移动应用程序 (Xamarin)。
但现在我有一个网站使用相同的 API 通过 jQuery 获取数据,但在那里我想处理 DTO 的完整 属性 名称,而不是给定的名称在 JsonProperty
属性中。
网站和WebApi在同一台服务器上,所以响应大一点没问题。
我开始使用中间件 class 对客户 header 值做出反应,这很有效,但现在我不知道如何访问 JSON SerializerSettings。在网上搜索但找不到它。
在搜索时,我已经阅读了有关 InputFormatters 和 OutputFormatters 以及内容协商的内容,但我不知道我必须走哪个方向。
我不想使用不同的设置部署相同的 API 两次。
如果有帮助,我可以更改 routesconfig 之类的东西。
更新
不仅 JSON 响应必须以两种不同的方式序列化,反序列化也必须以两种不同的方式完成。
这里有两个选项:
1。手动格式化
您通过 services.AddMvc().AddJsonOptions()
设置的选项已在 DI 中注册,您可以将其注入到您的控制器和服务中:
public HomeController(IOptions<MvcJsonOptions> optionsAccessor)
{
JsonSerializerSettings jsonSettings = optionsAccessor.Value.SerializerSettings;
}
要按请求覆盖这些序列化设置,您可以使用 Json
方法或创建 JsonResult
实例:
public IActionResult Get()
{
return Json(data, new JsonSerializerSettings());
return new JsonResult(data, new JsonSerializerSettings());
}
2。结果过滤器替换 JSON output
public class ModifyResultFilter : IAsyncResultFilter
{
public ModifyResultFilter(IOptions<MvcJsonOptions> optionsAccessor)
{
_globalSettings = optionsAccessor.Value.SerializerSettings;
}
public async Task OnResultExecutionAsync(
ResultExecutingContext context,
ResultExecutionDelegate next)
{
var originResult = context.Result as JsonResult;
context.Result = new JsonResult(originResult.Value, customSettings);
await next();
}
}
在 action/controller 上使用:
[ServiceFilter(typeof(ModifyResultFilter ))]
public IActionResult Index() {}
或按照 documentation 中所述创建自定义属性:
[ModifyResultAttribute]
public IActionResult Index() {}
不要忘记在 DI 中注册过滤器。
感谢您的评论和回答。我找到了输入和输出格式化程序的解决方案。感谢 http://rovani.net/Explicit-Model-Constructor/ 为我指明了正确的方向。
我创建了自己的输入和输出格式化程序,它们继承自 JsonInputFormatter
以保持尽可能多的功能相同。
在构造函数中,我设置了支持的媒体类型(使用了一些看起来像 JSON 的现有媒体类型)。
还必须覆盖 CreateJsonSerializer
以将 ContractResolver
设置为所需的(可以实现单例)。
必须这样做,因为在构造函数中更改 serializerSettings
会更改所有 input/outputformatters 的序列化程序设置,这意味着默认的 JSON 格式化程序也将使用新的合同解析器。
同样这样做意味着您可以通过 AddMvc().AddJsonOption()
设置一些默认的 JSON 选项
示例inputformatter,outputformatter使用相同的原理:
static MediaTypeHeaderValue protoMediaType = MediaTypeHeaderValue.Parse("application/jsonfull");
public JsonFullInputFormatter(ILogger logger, JsonSerializerSettings serializerSettings, ArrayPool<char> charPool, ObjectPoolProvider objectPoolProvider)
: base(logger, serializerSettings, charPool, objectPoolProvider)
{
this.SupportedMediaTypes.Clear();
this.SupportedMediaTypes.Add(protoMediaType);
}
protected override JsonSerializer CreateJsonSerializer()
{
var serializer = base.CreateJsonSerializer();
serializer.ContractResolver = new NoJsonPropertyNameContractResolver();
return serializer;
}
根据上面提到的 URL 设置 class:
public class YourMvcOptionsSetup : IConfigureOptions<MvcOptions>
{
private readonly ILoggerFactory _loggerFactory;
private readonly JsonSerializerSettings _jsonSerializerSettings;
private readonly ArrayPool<char> _charPool;
private readonly ObjectPoolProvider _objectPoolProvider;
public YourMvcOptionsSetup(ILoggerFactory loggerFactory, IOptions<MvcJsonOptions> jsonOptions, ArrayPool<char> charPool, ObjectPoolProvider objectPoolProvider)
{
//Validate parameters and set fields
}
public void Configure(MvcOptions options)
{
var jsonFullInputFormatter = new JsonFullInputFormatter(
_loggerFactory.CreateLogger<JsonFullInputFormatter>(),
_jsonSerializerSettings,
_charPool,
_objectPoolProvider
);
options.InputFormatters.Add(jsonFullInputFormatter);
options.OutputFormatters.Add(new JsonFullOutputFormatter(
_jsonSerializerSettings,
_charPool
));
}
然后是注册它的扩展方法:
public static class MvcBuilderExtensions
{
public static IMvcBuilder AddJsonFullFormatters(this IMvcBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
ServiceDescriptor descriptor = ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, YourMvcOptionsSetup>();
builder.Services.TryAddEnumerable(descriptor);
return builder;
}
}
在ConfigureServices
中调用它:
services.AddMvc(config =>
{
config.RespectBrowserAcceptHeader = true; // To use the JsonFullFormatters if clients asks about it via Accept Header
})
.AddJsonFullFormatters() //Add our own JSON Formatters
.AddJsonOptions(opt =>
{
//Set up some default options all JSON formatters must use (if any)
});
现在我们的 Xamarin 应用程序可以访问 webapi 并接收 JSON 以及通过 JsonProperty
属性设置的(短)属性 名称。
在网站中,我们可以通过添加 Accept(获取调用)和 ContentType(post/put 调用)header 来获取完整的 JSON 属性 名称。我们通过 jQuery 的 $.ajaxSetup(
做了一次。
$.ajaxSetup({
contentType: "application/jsonfull; charset=utf-8",
headers: { 'Accept': 'application/jsonfull' }
});
基于请求中的某些值(header 或 url),我想更改我的 DTO objects 的序列化。
为什么?好吧,我已经将 [JsonProperty("A")]
应用于我的 DTO,但取决于客户端(网站或移动应用程序)是否要使用 属性。
我从
services
.AddMvc()
.AddJsonOptions(opt =>
{
#if DEBUG
opt.SerializerSettings.ContractResolver = new NoJsonPropertyNameContractResolver();
#endif
}
所以在调试时我得到 JSON 和 属性 全名。我使用 JsonProperty
属性来缩短响应 JSON,这适用于反序列化回相同 DTO 的移动应用程序 (Xamarin)。
但现在我有一个网站使用相同的 API 通过 jQuery 获取数据,但在那里我想处理 DTO 的完整 属性 名称,而不是给定的名称在 JsonProperty
属性中。
网站和WebApi在同一台服务器上,所以响应大一点没问题。
我开始使用中间件 class 对客户 header 值做出反应,这很有效,但现在我不知道如何访问 JSON SerializerSettings。在网上搜索但找不到它。
在搜索时,我已经阅读了有关 InputFormatters 和 OutputFormatters 以及内容协商的内容,但我不知道我必须走哪个方向。
我不想使用不同的设置部署相同的 API 两次。
如果有帮助,我可以更改 routesconfig 之类的东西。
更新
不仅 JSON 响应必须以两种不同的方式序列化,反序列化也必须以两种不同的方式完成。
这里有两个选项:
1。手动格式化
您通过 services.AddMvc().AddJsonOptions()
设置的选项已在 DI 中注册,您可以将其注入到您的控制器和服务中:
public HomeController(IOptions<MvcJsonOptions> optionsAccessor)
{
JsonSerializerSettings jsonSettings = optionsAccessor.Value.SerializerSettings;
}
要按请求覆盖这些序列化设置,您可以使用 Json
方法或创建 JsonResult
实例:
public IActionResult Get()
{
return Json(data, new JsonSerializerSettings());
return new JsonResult(data, new JsonSerializerSettings());
}
2。结果过滤器替换 JSON output
public class ModifyResultFilter : IAsyncResultFilter
{
public ModifyResultFilter(IOptions<MvcJsonOptions> optionsAccessor)
{
_globalSettings = optionsAccessor.Value.SerializerSettings;
}
public async Task OnResultExecutionAsync(
ResultExecutingContext context,
ResultExecutionDelegate next)
{
var originResult = context.Result as JsonResult;
context.Result = new JsonResult(originResult.Value, customSettings);
await next();
}
}
在 action/controller 上使用:
[ServiceFilter(typeof(ModifyResultFilter ))]
public IActionResult Index() {}
或按照 documentation 中所述创建自定义属性:
[ModifyResultAttribute]
public IActionResult Index() {}
不要忘记在 DI 中注册过滤器。
感谢您的评论和回答。我找到了输入和输出格式化程序的解决方案。感谢 http://rovani.net/Explicit-Model-Constructor/ 为我指明了正确的方向。
我创建了自己的输入和输出格式化程序,它们继承自 JsonInputFormatter
以保持尽可能多的功能相同。
在构造函数中,我设置了支持的媒体类型(使用了一些看起来像 JSON 的现有媒体类型)。
还必须覆盖 CreateJsonSerializer
以将 ContractResolver
设置为所需的(可以实现单例)。
必须这样做,因为在构造函数中更改 serializerSettings
会更改所有 input/outputformatters 的序列化程序设置,这意味着默认的 JSON 格式化程序也将使用新的合同解析器。
同样这样做意味着您可以通过 AddMvc().AddJsonOption()
示例inputformatter,outputformatter使用相同的原理:
static MediaTypeHeaderValue protoMediaType = MediaTypeHeaderValue.Parse("application/jsonfull");
public JsonFullInputFormatter(ILogger logger, JsonSerializerSettings serializerSettings, ArrayPool<char> charPool, ObjectPoolProvider objectPoolProvider)
: base(logger, serializerSettings, charPool, objectPoolProvider)
{
this.SupportedMediaTypes.Clear();
this.SupportedMediaTypes.Add(protoMediaType);
}
protected override JsonSerializer CreateJsonSerializer()
{
var serializer = base.CreateJsonSerializer();
serializer.ContractResolver = new NoJsonPropertyNameContractResolver();
return serializer;
}
根据上面提到的 URL 设置 class:
public class YourMvcOptionsSetup : IConfigureOptions<MvcOptions>
{
private readonly ILoggerFactory _loggerFactory;
private readonly JsonSerializerSettings _jsonSerializerSettings;
private readonly ArrayPool<char> _charPool;
private readonly ObjectPoolProvider _objectPoolProvider;
public YourMvcOptionsSetup(ILoggerFactory loggerFactory, IOptions<MvcJsonOptions> jsonOptions, ArrayPool<char> charPool, ObjectPoolProvider objectPoolProvider)
{
//Validate parameters and set fields
}
public void Configure(MvcOptions options)
{
var jsonFullInputFormatter = new JsonFullInputFormatter(
_loggerFactory.CreateLogger<JsonFullInputFormatter>(),
_jsonSerializerSettings,
_charPool,
_objectPoolProvider
);
options.InputFormatters.Add(jsonFullInputFormatter);
options.OutputFormatters.Add(new JsonFullOutputFormatter(
_jsonSerializerSettings,
_charPool
));
}
然后是注册它的扩展方法:
public static class MvcBuilderExtensions
{
public static IMvcBuilder AddJsonFullFormatters(this IMvcBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
ServiceDescriptor descriptor = ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, YourMvcOptionsSetup>();
builder.Services.TryAddEnumerable(descriptor);
return builder;
}
}
在ConfigureServices
中调用它:
services.AddMvc(config =>
{
config.RespectBrowserAcceptHeader = true; // To use the JsonFullFormatters if clients asks about it via Accept Header
})
.AddJsonFullFormatters() //Add our own JSON Formatters
.AddJsonOptions(opt =>
{
//Set up some default options all JSON formatters must use (if any)
});
现在我们的 Xamarin 应用程序可以访问 webapi 并接收 JSON 以及通过 JsonProperty
属性设置的(短)属性 名称。
在网站中,我们可以通过添加 Accept(获取调用)和 ContentType(post/put 调用)header 来获取完整的 JSON 属性 名称。我们通过 jQuery 的 $.ajaxSetup(
做了一次。
$.ajaxSetup({
contentType: "application/jsonfull; charset=utf-8",
headers: { 'Accept': 'application/jsonfull' }
});