简体   繁体   English

使用 ASP.NET Core 2.1 在控制器上配置输入/输出格式化程序

[英]Configure input/output formatters on controllers with ASP.NET Core 2.1

I am in the process of rewriting an old ASP.NET WebAPI 2.1 project to ASP.NET Core MVC 2.1.我正在将旧的 ASP.NET WebAPI 2.1 项目重写为 ASP.NET Core MVC 2.1。 One of the problem I am facing is about porting the old behavior of the service which configure the input and output formatters through custom attributes whom implement IControllerConfiguration interface.我面临的问题之一是移植服务的旧行为,这些行为通过实现 IControllerConfiguration 接口的自定义属性来配置输入和输出格式化程序。 I have not been able to find a replacement for this interface nor any alternative to configure formatters on controller-basis, other than injecting them at global level with the AddMvc(options) method.除了使用 AddMvc(options) 方法在全局级别注入它们之外,我无法找到此接口的替代品,也找不到任何替代方案来配置基于控制器的格式化程序。

I haven't found anything that can be configured at the controller level, but I did find a solution that involves changes to each action where you need this functionality.我还没有找到任何可以在控制器级别进行配置的内容,但我确实找到了一个解决方案,该解决方案涉及对需要此功能的每个操作进行更改。 In my case I needed to customize the JSON serializer settings, which can be done like this for the output:在我的情况下,我需要自定义 JSON 序列化器设置,可以像这样输出:

[HttpGet]
public IActionResult Get()
{
    ...
    return Json(result, _serializerSettings);
}

and like this for input:像这样输入:

[HttpPost]
public IActionResult Post([FromBodyCustomSerializationSettings]MyPostDto postDto)
{
    ...
}

class FromBodyCustomSerializationSettingsAttribute : ModelBinderAttribute
{
    public FromBodyCustomSerializationSettingsAttribute() : base(typeof(MyModelBinder))
    {
        BindingSource = BindingSource.Body;
    }
}

class MyModelBinder : IModelBinder
{
    private readonly BodyModelBinder _bodyModelBinder;

    public MyModelBinder(IHttpRequestStreamReaderFactory readerFactory, ILoggerFactory loggerFactory, IOptions<MvcOptions> options, IOptions<MvcJsonOptions> jsonOptions, ArrayPool<char> charPool, ObjectPoolProvider objectPoolProvider)
    {
        var formatters = options.Value.InputFormatters.ToList();
        int jsonFormatterIndex = formatters.FirstIndexOf(formatter => formatter is JsonInputFormatter);
        JsonSerializerSettings myCustomSettings = ...
        formatters[jsonFormatterIndex] = new JsonInputFormatter(loggerFactory.CreateLogger("MyCustomJsonFormatter"), myCustomSettings, charPool, objectPoolProvider, options.Value, jsonOptions.Value);
        _bodyModelBinder = new BodyModelBinder(formatters, readerFactory, loggerFactory, options.Value);
    }

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        return _bodyModelBinder.BindModelAsync(bindingContext);
    }
}

Actually I found a way.其实我找到了办法。 I created an attribute which also implements IResultFilter and here is the OnResultExecuting method, where the magic happens:我创建了一个也实现了 IResultFilter 的属性,这里是 OnResultExecuting 方法,魔法发生的地方:

public void OnResultExecuting(ResultExecutingContext context)
{
  var objectResult = context.Result as ObjectResult;
  if (objectResult != null)
  {
    var serializerSettings = new JsonSerializerSettings
    {
        ContractResolver = new DefaultContractResolver()
    };

    var jsonFormatter = new JsonOutputFormatter(
        serializerSettings,
        ArrayPool<char>.Shared);

    objectResult.Formatters.Add(jsonFormatter);
  }
}

Basically here I am injecting a custom JSON formatter in every object result, before it is sent to the client.基本上在这里,我在每个对象结果中注入一个自定义 JSON 格式化程序,然后再将其发送到客户端。 It appears (but I did not find any documentation about this) that in this way ASP.NET Core MVC prefers the injected formatter over the globally defined one.看起来(但我没有找到任何关于此的文档),ASP.NET Core MVC 以这种方式更喜欢注入的格式化程序而不是全局定义的格式化程序。

I hopes it helps other because I was struggling on this...我希望它可以帮助其他人,因为我在这方面苦苦挣扎...

In order to be able to use @dcstraw's code in ASP .Net Core 3.1, I needed to modify MyModelBinder implementation slightly:为了能够在 ASP .Net Core 3.1 中使用 @dcstraw 的代码,我需要稍微修改MyModelBinder实现:

class MyModelBinder : IModelBinder
{
    private readonly BodyModelBinder _bodyModelBinder;

    public MyModelBinder(IHttpRequestStreamReaderFactory readerFactory, ILoggerFactory loggerFactory, IOptions<MvcOptions> options, IOptions<MvcNewtonsoftJsonOptions> jsonOptions, ArrayPool<char> charPool, ObjectPoolProvider objectPoolProvider)
    {
        var formatters = options.Value.InputFormatters.ToList();
        int jsonFormatterIndex = formatters.FindIndex(formatter => formatter is NewtonsoftJsonInputFormatter);
        JsonSerializerSettings myCustomSettings = ...
        formatters[jsonFormatterIndex] = new NewtonsoftJsonInputFormatter(loggerFactory.CreateLogger("MyCustomJsonFormatter"), myCustomSettings, charPool, objectPoolProvider, options.Value, jsonOptions.Value);
        _bodyModelBinder = new BodyModelBinder(formatters, readerFactory, loggerFactory, options.Value);
    }

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        return _bodyModelBinder.BindModelAsync(bindingContext);
    }
}

Basically :基本上

  • JsonInputFormatter changes into NewtonsoftJsonInputFormatter JsonInputFormatter变成NewtonsoftJsonInputFormatter
  • MvcJsonOptions changes into MvcNewtonsoftJsonOptions MvcJsonOptions更改为MvcNewtonsoftJsonOptions

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

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