我希望用自定义输入格式化程序替换默认的SystemTextJsonInputFormatter,或者作为TextInputFormatter的子类,或者(最好是) SystemTextJsonInputFormatter;最好是后者,尽可能接近内置的ASp.NET核心行为,而不必将代码复制到类中,在新的ASP.NET Core版本对SystemTextJsonInputFormatter进行更改时,需要定期更新。
这里有一些相关信息:
提供如何注册自定义输入格式化程序的示例:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(options =>
{
options.InputFormatters.Insert(0, new VcardInputFormatter());
});但是,我目前也对JSON选项进行了自定义,如下所示:
internal static IServiceCollection ConfigureJsonSerialization(this IServiceCollection services)
{
services.AddControllers().AddJsonOptions(opts => {
// Note. some of these settings are defaults, but we ensure they are set as required.
opts.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
opts.JsonSerializerOptions.PropertyNameCaseInsensitive = false;
opts.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
// Add our custom converters.
opts.JsonSerializerOptions.Converters.Add(new DateOnlyJsonConverter());
});
return services;
}opts有一个Microsoft.AspNetCore.Mvc.JsonOptions对象。
当使用内置SystemTextJsonInputFormatter时,这些选项通过其构造函数将其放入格式化程序实例(相关代码似乎在MvcCoreMvcOptionsSetup中)。
我希望在我的自定义输入格式化程序中使用相同的JSON选项,但是无法看到如何在构建时获得JsonOptions (从上面的options.InputFormatters.Insert(0, new VcardInputFormatter());行)。
我将继续查看ASP.NET核心内部,试图找出一种方法,但我想我会在这里要求,以防任何人可以提供一些指导。
谢谢。
发布于 2022-04-21 22:02:46
一种有点麻烦的方法是简单地从JsonSerializerOptions核心提供的现有SystemTextJsonInputFormatter实例中复制ASP.NET属性,如下所示:
public static class ServiceCollectionExtensions
{
internal static IServiceCollection ConfigureJsonSerialization(this IServiceCollection services)
{
services.AddControllers(
opts => {
// Search for an existing instance of SystemTextJsonInputFormatter in the InputFormatters list;
// this is the instance provided as standard by ASP.NET Core.
int idx = FindSystemTextJsonInputFormatter(
opts.InputFormatters,
out SystemTextJsonInputFormatter? sysTextJsonInputFormatter);
// We expect to find the SystemTextJsonInputFormatter.
if (sysTextJsonInputFormatter is null)
throw new InvalidOperationException("No SystemTextJsonInputFormatter is defined.");
// We will now create our own input formatter, and swap it in to substitute the
// SystemTextJsonInputFormatter instance. First we have some objects to setup.
// Copy the json serializer settings from the existing SystemTextJsonInputFormatter.
var jsonOptions = new JsonOptions();
CopyJsonSerializerOptions(
jsonOptions.JsonSerializerOptions,
sysTextJsonInputFormatter.SerializerOptions);
// TODO/FIXME: Research how best to get a logger for our input formatter!
var loggerFactory = LoggerFactory.Create(builder => {
builder.AddConsole();
});
var logger = loggerFactory.CreateLogger<SystemTextJsonInputFormatter>();
// Perform the substitution.
opts.InputFormatters[idx] = new JsonMergePatchInputFormatter(jsonOptions, logger);
})
.AddJsonOptions(opts => {
// Note. some of these settings are defaults, but we ensure they are set as required.
opts.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
opts.JsonSerializerOptions.PropertyNameCaseInsensitive = false;
opts.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
// Add our custom converters.
opts.JsonSerializerOptions.Converters.Add(new DateOnlyJsonConverter());
});
return services;
}
private static int FindSystemTextJsonInputFormatter(
FormatterCollection<IInputFormatter> inputFormatters,
out SystemTextJsonInputFormatter? sysTextJsonInputFormatter)
{
// Search for an instance of SystemTextJsonInputFormatter, and store it's index in the InputFormatters list.
// Note. We expect only one instance at most, but if there is more then one then we simply take the first instance.
for (int i = 0; i < inputFormatters.Count; i++)
{
if (inputFormatters[i] is SystemTextJsonInputFormatter formatter)
{
sysTextJsonInputFormatter = formatter;
return i;
}
}
// Not found.
sysTextJsonInputFormatter = null;
return -1;
}
private static void CopyJsonSerializerOptions(JsonSerializerOptions targetOptions, JsonSerializerOptions sourceOptions)
{
// Notes. A hacky but necessary approach, because we are given a pre-built JsonSerializerOptions object
// that can't be replaced, therefore we must copy the various setting into it.
targetOptions.AllowTrailingCommas = sourceOptions.AllowTrailingCommas;
targetOptions.DefaultBufferSize = sourceOptions.DefaultBufferSize;
targetOptions.DefaultIgnoreCondition = sourceOptions.DefaultIgnoreCondition;
targetOptions.DictionaryKeyPolicy = sourceOptions.DictionaryKeyPolicy;
targetOptions.Encoder = sourceOptions.Encoder;
targetOptions.IgnoreNullValues = sourceOptions.IgnoreNullValues;
targetOptions.IgnoreReadOnlyFields = sourceOptions.IgnoreReadOnlyFields;
targetOptions.IncludeFields = sourceOptions.IncludeFields;
targetOptions.MaxDepth = sourceOptions.MaxDepth;
targetOptions.NumberHandling = sourceOptions.NumberHandling;
targetOptions.PropertyNameCaseInsensitive = sourceOptions.PropertyNameCaseInsensitive;
targetOptions.PropertyNamingPolicy = sourceOptions.PropertyNamingPolicy;
targetOptions.ReadCommentHandling = sourceOptions.ReadCommentHandling;
targetOptions.ReferenceHandler = sourceOptions.ReferenceHandler;
targetOptions.UnknownTypeHandling = sourceOptions.UnknownTypeHandling;
targetOptions.WriteIndented = sourceOptions.WriteIndented;
targetOptions.DefaultIgnoreCondition = sourceOptions.DefaultIgnoreCondition;
targetOptions.DefaultIgnoreCondition = sourceOptions.DefaultIgnoreCondition;
// Re-use the same converter instances; this is OK because their current parent
// SystemTextJsonInputFormatter is about to be discarded.
targetOptions.Converters.Clear();
foreach (var jsonConverter in sourceOptions.Converters)
{
targetOptions.Converters.Add(jsonConverter);
}
}
}我花了一些时间仔细研究ASP.NET核心源代码和调用堆栈,试图找到一种更好/更干净/更正式的方式,但最终不得不放弃并使用上面的方法。如果有更好的解决办法,请告诉我。
https://stackoverflow.com/questions/71958817
复制相似问题