基于请求中的某些值(标头或 url),我想更改 DTO 对象的序列化。 为什么?好吧,我已将
[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在同一台服务器上,所以响应大一点也没有问题。
我从一个中间件类开始对客户标头值做出反应,这很有效,但现在我不知道如何访问 JSON SerializerSettings。网上搜了没找到。
在搜索时,我读过有关InputFormatters和OutputFormatters的内容,以及内容协商,但我不知道我必须朝哪个方向走。
我不想使用不同的设置部署相同的 API 两次。
如果有帮助的话,我可以更改路由配置之类的内容。
更新
不仅 JSON 响应必须以两种不同的方式序列化,反序列化也必须以两种不同的方式完成。
至少有两种独立于序列化器的方法可以做到这一点:将自定义选项传递给
JsonResult
,并创建自定义响应过滤器。下面针对 Newtonsoft 和 System.Text.Json 库介绍了这些选项。
创建/修改设置实例(仅其类型依赖于库)并将其传递到
JsonResult
构造函数。要修改的全局 JSON 设置可以通过注入 IOptions<JsonOptions>
通过 DI 获取。
牛顿软件:
public IActionResult Foo()
{
var settings = new JsonSerializerSettings
{
ContractResolver = new NoJsonPropertyNameContractResolver()
};
return new JsonResult(new FooApiModel(), settings);
}
系统.Text.Json:
public IActionResult Foo()
{
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web);
return new JsonResult(new FooApiModel(), options);
}
同样的想法:只需使用依赖于库的设置实例创建一个新的 JsonResult,因此我仅显示 Newtonsoft 版本:
public class ModifyResultFilter : IAsyncResultFilter
{
public async Task OnResultExecutionAsync(
ResultExecutingContext context,
ResultExecutionDelegate next)
{
var settings = new JsonSerializerSettings
{
ContractResolver = new NoJsonPropertyNameContractResolver()
};
var originResult = context.Result as JsonResult;
context.Result = new JsonResult(originResult.Value, settings);
await next();
}
}
过滤器必须在 DI 中注册:
builder.Services.AddScoped<ModifyResultFilter>();
最后,让我们在动作/控制器上使用它:
[ServiceFilter(typeof(ModifyResultFilter))]
public IActionResult Index() {}
有关不同过滤器的更多信息,请参阅文档。
感谢您的评论和回答。我找到了一个带有输入和输出格式化程序的解决方案。感谢 http://rovani.net/Explicit-Model-Constructor/ 为我指明了正确的方向。
我创建了自己的输入和输出格式化程序,它们继承自
JsonInputFormatter
以保持尽可能多的功能相同。CreateJsonSerializer
将 ContractResolver
设置为所需的(可以实现单例)。serializerSettings
会更改所有输入/输出格式化程序的序列化器设置,这意味着默认 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,设置类:
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 并接收通过
JsonProperty
属性设置的(短)属性名称的 JSON。$.ajaxSetup(
完成一次。
$.ajaxSetup({
contentType: "application/jsonfull; charset=utf-8",
headers: { 'Accept': 'application/jsonfull' }
});