每个请求的 ASP.NET Core API JSON 序列化程序设置
Posted
技术标签:
【中文标题】每个请求的 ASP.NET Core API JSON 序列化程序设置【英文标题】:ASP.NET Core API JSON serializersettings per request 【发布时间】:2017-12-03 08:12:28 【问题描述】:基于请求中的某些值(标头或 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。 如果有帮助,我可以更改诸如 routesconfig 之类的内容。
更新 不仅 JSON 响应必须以两种不同的方式进行序列化,反序列化也必须以两种不同的方式进行。
【问题讨论】:
您是否有理由要在网站中使用不同的属性名称? 是的。对于应用程序,我们使用属性名称的简短版本来使 JSON 尽可能短。在应用程序中,JSON 被序列化回 DTO,因此在代码中我们有完整的属性名称。在网站中,我们使用 jQuery 来获取/发布/放置数据,因此我们需要完整的属性名称以保持 javascript 代码的可读性。我认为 Input 和 OutputFormatters 是要走的路。现在搜索如何初始化 JsonInputFormatter。 ***.com/questions/20622492/… 和自定义模型绑定器怎么样? 【参考方案1】:这里有两个选项:
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 输出的结果过滤器
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();
在动作/控制器上使用它:
[ServiceFilter(typeof(ModifyResultFilter ))]
public IActionResult Index()
或者按照documentation中的描述创建一个自定义属性:
[ModifyResultAttribute]
public IActionResult Index()
不要忘记在 DI 中注册过滤器。
【讨论】:
谢谢,但这意味着要更改所有控制器(虽然还没有那么多) @ArieKanarie,我想了两次并添加了结果过滤器,所以唯一的改变可能是用属性标记控制器。 嗯,这也可能是一个解决方案。如果我没记错的话,我们可以通过context
属性访问标题,并且根据标题值我们可以使用不同的设置。也许这比我想出的解决方案要少。会想到我接受哪一个作为答案
现在我也想了两次,这只会处理结果(=输出),这意味着我们还必须为输入创建一个过滤器,因为客户端发送的 JSON 也因客户端而异。我认为我的问题并不清楚。
如果您只需要更改输出,那么我会推荐这个而不是我的答案。工作量会减少。【参考方案2】:
感谢 cmets 和答案。我找到了输入和输出格式化程序的解决方案。感谢http://rovani.net/Explicit-Model-Constructor/ 为我指明了正确的方向。
我创建了自己的输入和输出格式化程序,它们继承自 JsonInputFormatter
,以保持尽可能多的功能相同。
在构造函数中,我设置了支持的媒体类型(使用了一些看起来像现有 JSON 的媒体类型)。
还必须覆盖 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。
在网站中,我们可以通过添加 Accept(get 调用)和 ContentType(post/put 调用)标头来获取完整的 JSON 属性名称。我们通过 jQuery 的$.ajaxSetup(
执行一次。
$.ajaxSetup(
contentType: "application/jsonfull; charset=utf-8",
headers: 'Accept': 'application/jsonfull'
);
【讨论】:
以上是关于每个请求的 ASP.NET Core API JSON 序列化程序设置的主要内容,如果未能解决你的问题,请参考以下文章
根据 ASP.NET Core 请求标头中提供的 API 密钥授权用户
如何在 Web API Asp.NET Core 5 中持久化常用数据?
ASP.Net Core 2.1 API JWT 无 cookie 会话?
如何继续等待 API 请求响应值完成 ASP .Net Core MVC