ASP.NET Core API JSON serializersettings per request

后端 未结 2 478
悲&欢浪女
悲&欢浪女 2020-12-06 06:56

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\")]

相关标签:
2条回答
  • 2020-12-06 07:09

    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.

    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()

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

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

    Call it in 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.
    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' }
    });
    
    0 讨论(0)
  • 2020-12-06 07:14

    Here are two options:

    1. Manual formatting

    Options you set by services.AddMvc().AddJsonOptions() are registered in DI and you can inject it into your controllers and services:

    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:

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

    2. Result filter to replace JSON output

    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.

    0 讨论(0)
提交回复
热议问题