web-dev-qa-db-ja.com

リクエストごとのASP.NETCore APIJSONシリアライザー設定

リクエスト(ヘッダーまたはURL)の値に基づいて、DTOオブジェクトのシリアル化を変更したいと思います。どうして? [JsonProperty("A")]をDTOに適用しましたが、クライアント(Webサイトまたはモバイルアプリ)によっては、そのプロパティを使用するかどうかが決まります。私はから始めました

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

したがって、デバッグ中に、完全なプロパティ名を持つJSONを取得します。 JsonProperty属性を使用して、応答JSONを短縮します。これは、同じDTOに逆シリアル化するモバイルアプリ(Xamarin)で正常に機能します。しかし、同じAPIを使用してjQueryを介してデータを取得するWebサイトができましたが、そこでは、JsonProperty属性で指定された名前ではなく、DTOの完全なプロパティ名を処理したいと思います。 WebサイトとWebApiは同じサーバー上にあるため、応答が少し大きくても問題ありません。

顧客のヘッダー値に反応するミドルウェアクラスから始めましたが、これは機能しますが、JSONSerializerSettingsにアクセスする方法がわかりません。 Webを検索しましたが、見つかりません。

検索中に、InputFormattersとOutputFormatters、およびコンテンツネゴシエーションについて読みましたが、どちらの方向に進む必要があるのか​​わかりません。

同じAPIを異なる設定で2回デプロイしたくありません。
それが助けになるなら、routesconfigのようなものを変更することができます。

更新
JSON応答を2つの異なる方法でシリアル化する必要があるだけでなく、逆シリアル化も2つの異なる方法で実行する必要がありました。

10
ArieKanarie

コメントと回答をありがとう。 Inputformatterとoutputformatterを使用した解決策を見つけました。 http://rovani.net/Explicit-Model-Constructor/ に感謝し、正しい方向に向けてください。

JsonInputFormatterを継承して、同じ機能を維持するために、独自のinputformatterとoutputformatterを作成しました。
コンストラクターで、サポートされているメディアタイプを設定しました(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を受信できるようになりました。
[。これは、jQueryの$.ajaxSetup(を介して一度行います。

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

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

または、 documentation :の説明に従ってカスタム属性を作成します。

[ModifyResultAttribute]
public IActionResult Index() {}

フィルタをDIに登録することを忘れないでください。

17
Ilya Chumakov