每个请求的 ASP.NET Core API JSON 序列化器设置 [英] ASP.NET Core API JSON serializersettings per request

查看:29
本文介绍了每个请求的 ASP.NET Core API JSON 序列化器设置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

基于请求中的某些值(标头或 url),我想更改 DTO 对象的序列化.为什么?好吧,我已经将 [JsonProperty("A")] 应用于我的 DTO,但取决于它是否想要使用该属性的客户端(网站或移动应用程序).我开始于

Based on some value in the request (header or in the url) I want to change the serialization of my DTO objects. Why? Well I've applied the [JsonProperty("A")] to my DTO's but depending on the client (website or mobile app) it want to use that property or not. I started with

services
.AddMvc()
.AddJsonOptions(opt =>
{
#if DEBUG
    opt.SerializerSettings.ContractResolver = new NoJsonPropertyNameContractResolver();
#endif
}

所以在调试时我得到了带有完整属性名的 JSON.我使用 JsonProperty 属性来缩短响应 JSON,它适用于反序列化回相同 DTO 的移动应用程序 (Xamarin).但现在我有一个网站,它使用相同的 API 通过 jQuery 获取数据,但在那里我想处理 DTO 的完整属性名称,而不是 JsonProperty 属性中给出的名称.网站和WebApi在同一台服务器上,响应大一点也没有问题.

So while debugging I get JSON with full propertynames. I use the JsonProperty attribute to shorten the response JSON, which works fine with the mobile app (Xamarin) which deserialize back to the same DTO's. But now I have a website which uses the the same API to get data via jQuery, but in there I want to deal with the full property names of the DTO's, not the name given in the JsonProperty attribute. Website and WebApi are on the same server so it's no problem if the response is a little bigger.

我开始使用一个中间件类来对客户标头值做出反应,这很有效,但现在我不知道如何访问 JSON SerializerSettings.网上搜了一下,没找到.

I started with a middleware class to react on a customer header value, which works, but now I don't know how to get to the JSON SerializerSettings. Searched the web but cannot find it.

在搜索时,我已经阅读了 InputFormatters 和 OutputFormatters,以及内容协商,但我不知道我该往哪个方向发展.

While searching I've read about InputFormatters and OutputFormatters, and also content negotiation, but I don't know which direction I must go.

我不想使用不同的设置两次部署相同的 API.
如果有帮助,我可以更改 routesconfig 之类的内容.

I don't want to deploy the same API twice with different settings.
I'am able to change things like the routesconfig if that would help.

更新
不仅 JSON 响应必须以两种不同的方式序列化,反序列化也必须以两种不同的方式完成.

Update
Not only the JSON response had to be serialized in 2 different ways, also the deserializing had to be done in 2 different ways.

推荐答案

感谢您的评论和回答.我找到了 Input 和 outputformatters 的解决方案.感谢 http://rovani.net/Explicit-Model-Constructor/ 指出我朝着正确的方向前进.

Thanks for the comments and answers. I found a solution with Input and outputformatters. With thanks to http://rovani.net/Explicit-Model-Constructor/ to point me in the right direction.

我创建了自己的输入和输出格式化程序,它们继承自 JsonInputFormatter 以保持尽可能多的功能相同.
在构造函数中,我设置了支持的媒体类型(使用了一些看起来像 JSON 现有的媒体类型).
还必须覆盖 CreateJsonSerializer 以将 ContractResolver 设置为所需的(可以实现单例).
必须这样做,因为在构造函数中更改 serializerSettings 将更改所有输入/输出格式化程序的序列化程序设置,这意味着默认 JSON 格式化程序也将使用新的合同解析程序.
同样这样做意味着您可以通过 AddMvc().AddJsonOption()

I've created my own input and outputformatters, which inherit from JsonInputFormatter to keep as much functionality the same.
In the constructor I set the supported mediatype (used some that looks like the existing one for JSON).
Also must override CreateJsonSerializer to set the ContractResolver to the desired one (could implement singleton).
Must do it this way, because changing the serializerSettings in the constructor would change the serializersettings for all input/outputformatters, meaning the default JSON formatters will also use the new contract resolver.
Also doing it this way means you can setup some default JSON options via AddMvc().AddJsonOption()

例如inputformatter、outputformatter使用相同的原理:

Example inputformatter, outputformatter uses the same principle:

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:

As per the mentioned URL above the setup class:

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
        ));
    }

然后是一个扩展方法来注册它:

And then an extension method to register it:

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(获取调用)和 ContentType(发布/放置调用)标头来获取完整的 JSON 属性名称.我们通过 jQuery 的 $.ajaxSetup(.

Now our Xamarin App can access the webapi and receive JSON with (short) property names set via JsonProperty attribute.
And in the website we can get the full JSON property names by adding an Accept (get calls) and ContentType (post/put calls) header. Which we do once via jQuery's $.ajaxSetup(.

$.ajaxSetup({
    contentType: "application/jsonfull; charset=utf-8",
    headers: { 'Accept': 'application/jsonfull' }
});

这篇关于每个请求的 ASP.NET Core API JSON 序列化器设置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆