如何在 .NET 6.0 中使用 MinimalApi 配置 NewtonsoftJson
How to configure NewtonsoftJson with MinimalApi in .NET 6.0
我有 net6.0
项目,其中 api 最少,我想使用 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 了吗?
根据我的理解,Minimal API 依赖于一些关于类型绑定的约定。据我所知,他们在类型上搜索具有下一个签名的方法 - ValueTask<TModel?> BindAsync(HttpContext context, ParameterInfo parameter)
否则将尝试使用 httpContext.Request.ReadFromJsonAsync
,它在内部使用 System.Text.Json
并且无法更改,所以 services.Add...().AddNewtonsoftJson((options) => //options);
方法行不通。
要使用 Newtonsoft.Json
你可以尝试下一步(除了直接通过 app.MapPost("/pst", (HttpContext c) => c.Request...)
处理请求):
如果您可以控制所有需要使用它反序列化的 classes,您可以从一些通用基础 class 继承它们,这些基础将具有具有所需签名的方法(您也可以可以使用接口 implemented static method):
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 model binders - 大卫福勒。虽然我无法让它为
services.AddControllers().AddNewtonsoftJson((options) => //options);
工作
- ParameterBinder - Damian Edwards 的类似方法
我有 net6.0
项目,其中 api 最少,我想使用 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 了吗?
根据我的理解,Minimal API 依赖于一些关于类型绑定的约定。据我所知,他们在类型上搜索具有下一个签名的方法 - ValueTask<TModel?> BindAsync(HttpContext context, ParameterInfo parameter)
否则将尝试使用 httpContext.Request.ReadFromJsonAsync
,它在内部使用 System.Text.Json
并且无法更改,所以 services.Add...().AddNewtonsoftJson((options) => //options);
方法行不通。
要使用 Newtonsoft.Json
你可以尝试下一步(除了直接通过 app.MapPost("/pst", (HttpContext c) => c.Request...)
处理请求):
如果您可以控制所有需要使用它反序列化的 classes,您可以从一些通用基础 class 继承它们,这些基础将具有具有所需签名的方法(您也可以可以使用接口 implemented static method):
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 model binders - 大卫福勒。虽然我无法让它为
services.AddControllers().AddNewtonsoftJson((options) => //options);
工作
- ParameterBinder - Damian Edwards 的类似方法