I am in the process of rewriting an old ASP.NET WebAPI 2.1 project to 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. 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.
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:
[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:
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. 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.
I hopes it helps other because I was struggling on this...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With