ASP.NET Core API JSON serializersettings для каждого запроса

на основе некоторого значения в запросе (заголовок или url) я хочу изменить сериализацию моих объектов DTO. Почему? Ну, я применил [JsonProperty("A")] к моему DTO, но в зависимости от клиента (веб-сайта или мобильного приложения) он хочет использовать это свойство или нет. Я начал с

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

поэтому при отладке я получаю JSON с полными именами свойств. Я использую JsonProperty атрибут для сокращения ответа JSON, который отлично работает с мобильным приложением (Xamarin), которое десериализуется обратно к тому же ДТО это. Но теперь у меня есть сайт, который использует тот же API для получения данных через jQuery, но там я хочу иметь дело с полными именами свойств DTO, а не с именем, указанным в . Веб-сайт и WebApi находятся на одном сервере, поэтому нет проблем, если ответ немного больше.

Я начал с промежуточного класса, чтобы реагировать на значение заголовка клиента, которое работает, но теперь я не знаю, как добраться до JSON SerializerSettings. Поиск в интернете, но не может найти он.

во время поиска я читал о InputFormatters и OutputFormatters, а также о согласовании контента, но я не знаю, в каком направлении я должен идти.

Я не хочу развертывать один и тот же API дважды с разными настройками.
Я могу изменить такие вещи, как routesconfig, если это поможет.

обновление
Не только ответ JSON должен был быть сериализован 2 различными способами, также десериализация должна была быть выполнена в 2 разных способов.

2 ответов


здесь два варианта:

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

или создать пользовательский атрибут, как описано в документация:

[ModifyResultAttribute]
public IActionResult Index() {}

Не забудьте зарегистрировать фильтр в DI.


Спасибо за комментарии и ответы. Я нашел решение с помощью Input и outputformatters. С благодарностью http://rovani.net/Explicit-Model-Constructor/ чтобы указать мне в правильном направлении.

Я создал свои собственные входные и выходные формататоры, которые наследуются от JsonInputFormatter чтобы сохранить столько же функциональности.
В конструкторе я установил поддерживаемый mediatype (использовал некоторые, которые выглядят как существующие для JSON).
Также необходимо переопределить CreateJsonSerializer установить ContractResolver для нужный (может реализовать синглтон).
Необходимо сделать это таким образом, потому что изменение serializerSettings в конструкторе изменит serializersettings для всех input / outputformatters, что означает, что форматтеры JSON по умолчанию также будут использовать новый сопоставитель контрактов.
Также это означает, что вы можете настроить некоторые параметры JSON по умолчанию через AddMvc().AddJsonOption()

пример 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 и получить JSON с (короткими) именами свойств, установленными через .
И на веб-сайте мы можем получить полные имена свойств JSON, добавив заголовок Accept (get calls) и ContentType (post/put calls). Что мы делаем один раз через jQuery $.ajaxSetup(.

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