Итак, покопавшись некоторое время, я придумал следующее:
public class JsonConverterBindingAttribute : ParameterBindingAttribute
{
public IEnumerable<JsonConverter> Converters { get; private set; }
public JsonConverterBindingAttribute(params Type[] converters)
{
if (converters.Any(converter => !typeof(JsonConverter).IsAssignableFrom(converter)))
{
throw new ArgumentException($"a converter does not derive from JsonConverter");
}
Converters = converters.Select(converter => (JsonConverter)Activator.CreateInstance(converter));
}
public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter)
{
if (parameter == null)
throw new ArgumentException("Invalid parameter");
return new JsonConverterParameterBinding(parameter, Converters.ToArray());
}
}
public class JsonConverterParameterBinding : HttpParameterBinding
{
public JsonConverter[] Converters { get; private set; }
public JsonConverterParameterBinding(HttpParameterDescriptor parameter, JsonConverter[] converter) : base(parameter)
{
Converters = converter;
}
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
HttpActionContext actionContext,
CancellationToken cancellationToken)
{
var binding = actionContext
.ActionDescriptor
.ActionBinding;
var type = binding
.ParameterBindings[0]
.Descriptor.ParameterType;
var existingConverters = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Converters;
return actionContext.Request.Content
.ReadAsStringAsync()
.ContinueWith(t =>
{
var str = t.Result;
var obj = JsonConvert.DeserializeObject(str, type, existingConverters.Concat(Converters).ToArray());
SetValue(actionContext, obj);
});
}
public override bool WillReadBody => true;
}
Что можно использовать так:
[HttpPost]
public IFoo RunBar([JsonConverterBinding(typeof(ConcreteTypeConverter<Bar,IBar>)]IBar parameters)
{
//...
}
Что работает, но я не уверен Я на 100% влюблен в это решение. Несколько примечаний:
Я настроил его на прием нескольких конвертеров, потому что если IBar
содержит свойство, которое само по себе также является интерфейсом (назовем его IFar
), тогда вам нужно иметь возможность предоставить конвертер и для этого! Таким образом, вы можете сделать что-то вроде:
public IFoo RunBar([JsonConverterBinding(typeof(ConcreteTypeConverter<Bar,IBar>),
typeof(ConcreteTypeConverter<Far, IFar>)]IBar parameters)
Но это также немного громоздко, поэтому я подумал, что было бы полезно иметь возможность использовать любые существующие конвертеры, которые вы уже зарегистрировали глобально (потому что они используются во многих местах), поэтому я объединяю преобразователи из GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Converters
, вероятно, лучше было бы клонировать все настройки оттуда и просто добавить дополнительные преобразователи, которые мне нужны / нужны. Или, может быть, это просто побеждает весь смысл.
Кроме того, вероятно, есть некоторая проверка ошибок, которую я должен сделать, но в качестве первого наброска, чтобы посмотреть, можно ли сделать, работает в принципе.