onebeyond/onebeyond-studio-core

View on GitHub
src/OneBeyond.Studio.Hosting.AspNet/ModelBinders/MixedSource/MixedSourceBinder.cs

Summary

Maintainability
A
0 mins
Test Coverage
using System.Collections.Generic;
using System.Threading.Tasks;
using EnsureThat;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OneBeyond.Studio.Hosting.AspNet.ModelBinders.MixedSource.Converters;

namespace OneBeyond.Studio.Hosting.AspNet.ModelBinders.MixedSource;

internal sealed class MixedSourceBinder : IModelBinder
{
    private static readonly IReadOnlyDictionary<string, IRequestToJTokenConverter> Converters =
        new Dictionary<string, IRequestToJTokenConverter>
        {
                { Source.ROUTE, new FromRouteConverter() },
                { Source.BODY, new FromBodyConverter() }
        };

    private static readonly JsonMergeSettings JsonMergeSettings =
        new()
        {
            MergeArrayHandling = MergeArrayHandling.Union
        };

    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
        EnsureArg.IsNotNull(bindingContext, nameof(bindingContext));

        var values = await RetrieveValuesAsync(bindingContext);

        var currentJsonOptions = bindingContext.HttpContext.RequestServices
            .GetService<IOptions<MvcNewtonsoftJsonOptions>>()?.Value
            ?? new MvcNewtonsoftJsonOptions();

        var model = values.ToObject(
            bindingContext.ModelMetadata.UnderlyingOrModelType,
            JsonSerializer.Create(currentJsonOptions.SerializerSettings));

        bindingContext.Result = ModelBindingResult.Success(model);
    }

    internal static async Task<JObject> RetrieveValuesAsync(ModelBindingContext bindingContext)
    {
        EnsureArg.IsNotNull(bindingContext, nameof(bindingContext));
        EnsureArg.IsNotNull(bindingContext.BindingSource, nameof(bindingContext.BindingSource));

        var bindingOrder = ((MixedSourceBinderSource)bindingContext.BindingSource).BindingOrder;

        var values = new JObject();

        foreach (var binderSource in bindingOrder)
        {
            if (Converters.TryGetValue(binderSource, out var converter))
            {
                values.Merge(
                    await converter.ConvertAsync(bindingContext),
                    JsonMergeSettings);
            }
        }

        return values;
    }
}