4

.Net Framework 4.7 を使用する ASP.NET Core Web API プロジェクトがあり、.Net Core 3.1 にアップグレードしています。アップグレードする理由の 1 つは、新しい System.Text.Json シリアライザーを使用することです。

現在、次のようなルートに基づいたいくつかのバージョンの API があります。

/api/v1/controller
/api/v2/controller

そして、新しいシリアライザーを使用するために新しいもの (v3) を作成します。しかし、ここに問題があります。統合されたクライアントで発生する可能性のあるフォーマットの問題を回避するために、古いルートで JSON.Net を使用し続けたいと考えています。

ルートに基づいて正しい JSON シリアライザーを自動的に選択するように Asp.Net Core を構成する簡単な方法はありますか?

4

1 に答える 1

5

独自の super InputFormatter/を作成してOutputFormatter、実行時に条件をチェックし、使用するか動的に使用するSystem.Text.Jsonかを決定できます。Newtonsoft.Json

たとえば、現在のアクション メソッド (またはコントローラー クラス) を確認できます。

  • のカスタム属性がある場合は[UseSystemTextJsonAttribute]、使用しますSystem.Text.Json
  • のカスタム属性がある場合は[UseNewtonsoftJsonAttribute]、 を使用しますNewtonsoft.Json

参照用にカスタム InputFormatter を作成します。

// the custom attribute
internal abstract class UseJsonAttribute : Attribute, IAsyncActionFilter
{
    public Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) => next();
}
internal class UseSystemTextJsonAttribute : UseJsonAttribute { }
internal class UseNewtonsoftJsonAttribute : UseJsonAttribute { }

// Our Super Input Formatter
internal class MySuperJsonInputFormatter : TextInputFormatter
{
    public MySuperJsonInputFormatter()
    {
        SupportedEncodings.Add(UTF8EncodingWithoutBOM);
        SupportedEncodings.Add(UTF16EncodingLittleEndian);
        SupportedMediaTypes.Add("application/json");
    }

    public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
    {
        var mvcOpt= context.HttpContext.RequestServices.GetRequiredService<IOptions<MvcOptions>>().Value;
        var formatters = mvcOpt.InputFormatters;
        TextInputFormatter formatter =null; // the real formatter : SystemTextJsonInput or Newtonsoft

        Endpoint endpoint = context.HttpContext.GetEndpoint();
        if(endpoint.Metadata.GetMetadata<UseSystemTextJsonAttribute>()!= null)
        {
            formatter= formatters.OfType<SystemTextJsonInputFormatter>().FirstOrDefault();
            //formatter = formatter ?? SystemTextJsonInputFormatter
        }
        else if( endpoint.Metadata.GetMetadata<UseNewtonsoftJsonAttribute>() != null){
            // don't use `Of<NewtonsoftJsonInputFormatter>` here because there's a NewtonsoftJsonPatchInputFormatter
            formatter= (NewtonsoftJsonInputFormatter)(formatters
                .Where(f =>typeof(NewtonsoftJsonInputFormatter) == f.GetType())
                .FirstOrDefault());
        }
        else{
            throw new Exception("This formatter is only used for System.Text.Json InputFormatter or NewtonsoftJson InputFormatter");
        }
        var result = await formatter.ReadRequestBodyAsync(context,encoding);
        return result;
    }
}

internal class MySuperJsonOutputFormatter : TextOutputFormatter
{
    ... // similar to MySuperJsonInputFormatter, omitted for brevity 
}

次に、起動時に Json 設定/オプションを構成します。

services.AddControllers(opts =>{ })
    .AddNewtonsoftJson(opts =>{ /**/ })
    .AddJsonOptions(opts =>{ /**/ });

AddNewtonsoftJson()は、ビルトインを削除しSystemTextJsonInputFormattersます。したがって、MvcOptions手動で構成する必要があります。

services.AddOptions<MvcOptions>()
    .PostConfigure<IOptions<JsonOptions>, IOptions<MvcNewtonsoftJsonOptions>,ArrayPool<char>, ObjectPoolProvider,ILoggerFactory>((opts, jsonOpts, newtonJsonOpts, charPool, objectPoolProvider, loggerFactory )=>{
        // configure System.Text.Json formatters
        if(opts.InputFormatters.OfType<SystemTextJsonInputFormatter>().Count() ==0){
            var systemInputlogger = loggerFactory.CreateLogger<SystemTextJsonInputFormatter>();
            opts.InputFormatters.Add(new SystemTextJsonInputFormatter(jsonOpts.Value, systemInputlogger));
        }
        if(opts.OutputFormatters.OfType<SystemTextJsonOutputFormatter>().Count() ==0){
            opts.OutputFormatters.Add(new SystemTextJsonOutputFormatter(jsonOpts.Value.JsonSerializerOptions));
        }
        // configure Newtonjson formatters
        if(opts.InputFormatters.OfType<NewtonsoftJsonInputFormatter>().Count() ==0){
            var inputLogger= loggerFactory.CreateLogger<NewtonsoftJsonInputFormatter>();
            opts.InputFormatters.Add(new NewtonsoftJsonInputFormatter(
                inputLogger, newtonJsonOpts.Value.SerializerSettings, charPool, objectPoolProvider, opts, newtonJsonOpts.Value
            )); 
        }
        if(opts.OutputFormatters.OfType<NewtonsoftJsonOutputFormatter>().Count()==0){
            opts.OutputFormatters.Add(new NewtonsoftJsonOutputFormatter(newtonJsonOpts.Value.SerializerSettings, charPool, opts));
        }
        opts.InputFormatters.Insert(0, new MySuperJsonInputFormatter());
        opts.OutputFormatters.Insert(0, new MySuperJsonOutputFormatter());
    });

これで問題なく動作するはずです。

于 2020-01-09T10:59:51.120 に答える