如何在.NET 6.0中使用MinimalApi配置NewtonsoftJson



我有一个带有最小api的net6.0项目,我想使用NetwtonsoftJson而不是内置的System.Text.Json库进行序列化和反序列化。

目前,我有JsonOptions的这种配置,它可以像预期的一样工作

builder.Services.Configure<JsonOptions>(options =>
{
options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.SerializerOptions.WriteIndented = true;    
options.SerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
});

若我尝试更改为类似于下面使用Newtonsoft.Json.JsonSerializerSettings的东西,我将不会得到相同的行为。相反,它看起来使用默认的System.Text.Json配置。

builder.Services.Configure<JsonSerializerSettings>(options =>
{
options.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
options.Converters.Add(
new StringEnumConverter
{
NamingStrategy = new Newtonsoft.Json.Serialization.CamelCaseNamingStrategy()
});
});

net5.0中,我知道我可以使用这个

services.AddControllers().AddNewtonsoftJson((options) => //options); // OR
services.AddMvc().AddNewtonsoftJson((options) => //options);

然而,如果我在net6.0项目中像上面那样使用它,那么我就不再使用MinimalApi了?

如文档中所述:

主体绑定源使用System.Text.Json进行反序列化。更改此默认是不可能的

但也有变通办法。

根据我的理解,Minimal API依赖于一些关于类型绑定的约定。据我所见,他们在类型上搜索具有下一个签名的方法——ValueTask<TModel?> BindAsync(HttpContext context, ParameterInfo parameter),否则将尝试使用内部使用System.Text.JsonhttpContext.Request.ReadFromJsonAsync,这是不可更改的,因此services.Add...().AddNewtonsoftJson((options) => //options);方法将不起作用。

要使用Newtonsoft.Json,您可以尝试下一步(而不是通过app.MapPost("/pst", (HttpContext c) => c.Request...)直接处理请求(:

如果你可以控制所有需要使用它进行反序列化的类,你可以从一些通用基类继承它们,这些基类将具有具有所需签名的方法(也可以使用具有已实现静态方法的接口(:

public class BaseModel<TModel>
{
public static async ValueTask<TModel?> BindAsync(HttpContext context, ParameterInfo parameter)
{
if (!context.Request.HasJsonContentType())
{
throw new BadHttpRequestException(
"Request content type was not a recognized JSON content type.",
StatusCodes.Status415UnsupportedMediaType);
}
using var sr = new StreamReader(context.Request.Body);
var str = await sr.ReadToEndAsync();

return JsonConvert.DeserializeObject<TModel>(str);
}
}

用途:

class PostParams : BaseModel<PostParams>
{
[JsonProperty("prop")]
public int MyProperty { get; set; }
}
// accepts json body {"prop": 2}
app.MapPost("/pst", (PostParams po) => po.MyProperty);

请注意,本例中的BaseModel<TModel>实现非常简单,并且可能会有所改进(至少查看HttpRequestJsonExtensions.ReadFromJsonAsync(。

如果你不能控制模型,或者不想从一些基础继承它们,你可以考虑创建包装器:

public class Wrapper<TModel>
{
public Wrapper(TModel? value)
{
Value = value;
}
public TModel? Value { get; }
public static async ValueTask<Wrapper<TModel>?> BindAsync(HttpContext context, ParameterInfo parameter)
{
if (!context.Request.HasJsonContentType())
{
throw new BadHttpRequestException(
"Request content type was not a recognized JSON content type.",
StatusCodes.Status415UnsupportedMediaType);
}
using var sr = new StreamReader(context.Request.Body);
var str = await sr.ReadToEndAsync();
return new Wrapper<TModel>(JsonConvert.DeserializeObject<TModel>(str));
}
}

用法更改为:

class PostParams
{
[JsonProperty("prop")]
public int MyProperty { get; set; }
}
// accepts json body {"prop": 2}
app.MapPost("/pst", (Wrapper<PostParams> po) => po.Value.MyProperty);

一些额外有用的链接:

  • MVC模型绑定器-David Fowler著。尽管我没能为services.AddControllers().AddNewtonsoftJson((options) => //options);工作
  • ParameterBinder-Damian Edwards的类似方法

最新更新