简体   繁体   English

每个请求的ASP.NET Core API JSON serializersettings

[英]ASP.NET Core API JSON serializersettings per request

Based on some value in the request (header or in the url) I want to change the serialization of my DTO objects. 根据请求中的某些值(标题或URL),我想更改DTO对象的序列化。 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. 好吧,我已将[JsonProperty("A")]应用于我的DTO,但根据客户端(网站或移动应用程序),它是否要使用该属性。 I started with 我开始了

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

So while debugging I get JSON with full propertynames. 因此,在调试时,我获得了具有完整属性名称的JSON。 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. 我使用JsonProperty属性来缩短响应JSON,这适用于移动应用程序(Xamarin),后者反序列化回到相同的DTO。 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. 但现在我有一个网站使用相同的API通过jQuery获取数据,但在那里我想要处理DTO的完整属性名称,而不是JsonProperty属性中给出的名称。 Website and WebApi are on the same server so it's no problem if the response is a little bigger. 网站和WebApi位于同一台服务器上,因此如果响应更大,则没有问题。

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. 我开始使用中间件类来对客户标头值做出反应,这有效,但现在我不知道如何进入JSON SerializerSettings。 Searched the web but cannot find it. 搜索网络但找不到它。

While searching I've read about InputFormatters and OutputFormatters, and also content negotiation, but I don't know which direction I must go. 在搜索时我读过有关InputFormatters和OutputFormatters,以及内容协商,但我不知道我必须去哪个方向。

I don't want to deploy the same API twice with different settings. 我不想使用不同的设置两次部署相同的API。
I'am able to change things like the routesconfig if that would help. 如果能有所帮助,我能够改变routesconfig之类的东西。

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. 不仅JSON响应必须以两种不同的方式序列化,而且反序列化必须以两种不同的方式完成。

Here are two options: 这有两个选择:

1. Manual formatting 1.手动格式化

Options you set by services.AddMvc().AddJsonOptions() are registered in DI and you can inject it into your controllers and services: 您通过services.AddMvc().AddJsonOptions()设置的选项services.AddMvc().AddJsonOptions()在DI中注册,您可以将其注入您的控制器和服务:

public HomeController(IOptions<MvcJsonOptions> optionsAccessor)
{
    JsonSerializerSettings jsonSettings = optionsAccessor.Value.SerializerSettings;
}

To per-request override these serialization settings, you could use Json method or create JsonResult instance: 要按请求覆盖这些序列化设置,您可以使用Json方法或创建JsonResult实例:

public IActionResult Get()
{
    return Json(data, new JsonSerializerSettings());

    return new JsonResult(data, new JsonSerializerSettings());
}

2. Result filter to replace JSON output 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();
    }
}

Use it on action/controller: 在动作/控制器上使用它:

[ServiceFilter(typeof(ModifyResultFilter ))]
public IActionResult Index() {}

Or create a custom attribute as described in documentation : 或者按照文档中的描述创建自定义属性:

[ModifyResultAttribute]
public IActionResult Index() {}

Don't forget to register the filter in DI. 不要忘记在DI中注册过滤器。

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. 感谢http://rovani.net/Explicit-Model-Constructor/指出我正确的方向。

I've created my own input and outputformatters, which inherit from JsonInputFormatter to keep as much functionality the same. 我创建了自己的输入和输出格式,它继承自JsonInputFormatter以保持相同的功能。
In the constructor I set the supported mediatype (used some that looks like the existing one for JSON). 在构造函数中,我设置了支持的mediatype(使用了一些类似于JSON的现有类型)。
Also must override CreateJsonSerializer to set the ContractResolver to the desired one (could implement singleton). 还必须覆盖CreateJsonSerializer以将ContractResolver设置为所需的(可以实现单例)。
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. 一定要做到这样,因为改变serializerSettings在构造函数将改变所有输入/ outputformatters的serializersettings,意味着默认的JSON格式化也将使用新的合同解析器。
Also doing it this way means you can setup some default JSON options via AddMvc().AddJsonOption() 这样做也意味着你可以通过AddMvc().AddJsonOption()设置一些默认的JSON选项AddMvc().AddJsonOption()

Example inputformatter, outputformatter uses the same principle: 示例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;
}

As per the mentioned URL above the setup class: 根据安装类上面提到的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
        ));
    }

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

Call it in ConfigureServices : 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)
});

Now our Xamarin App can access the webapi and receive JSON with (short) property names set via JsonProperty attribute. 现在,我们的Xamarin应用程序可以访问webapi并通过JsonProperty属性设置(短)属性名称来接收JSON。
And in the website we can get the full JSON property names by adding an Accept (get calls) and ContentType (post/put calls) header. 在网站中,我们可以通过添加Accept(获取调用)和ContentType(post / put调用)标头来获取完整的JSON属性名称。 Which we do once via jQuery's $.ajaxSetup( . 我们通过jQuery的$.ajaxSetup(

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM