Сделать веб-API IModelBinder применимым ко всем экземплярам типа

Я использую пользовательский IModelBinder, чтобы попытаться преобразовать строки в NodaTime LocalDates. Мой LocalDateBinder выглядит так:

public class LocalDateBinder : IModelBinder
{
    private readonly LocalDatePattern _localDatePattern = LocalDatePattern.IsoPattern;

    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType != typeof(LocalDate))
            return false;

        var val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (val == null)
            return false;

        var rawValue = val.RawValue as string;

        var result = _localDatePattern.Parse(rawValue);
        if (result.Success)
            bindingContext.Model = result.Value;

        return result.Success;
    }
}

В моем WebApiConfig я регистрирую этот связыватель моделей, используя SimpleModelBinderProvider, а-ля

var provider = new SimpleModelBinderProvider(typeof(LocalDate), new LocalDateBinder());
config.Services.Insert(typeof(ModelBinderProvider), 0, provider);

Это прекрасно работает, когда у меня есть действие, которое принимает параметр типа LocalDate, но если у меня есть более сложное действие, которое использует LocalDate внутри другой модели, оно никогда не запускается. Например:

[HttpGet]
[Route("validateDates")]
public async Task<IHttpActionResult> ValidateDates(string userName, [FromUri] LocalDate beginDate, [FromUri] LocalDate endDate)
{
     //works fine
}

[HttpPost]
[Route("")]
public async Task<IHttpActionResult> Create(CreateRequest createRequest)
{
     //doesn't bind LocalDate properties inside createRequest (other properties are bound correctly)
     //i.e., createRequest.StartDate isn't bound
}

Я полагаю, что это как-то связано с тем, как я регистрирую связыватель модели с помощью веб-API, но я не понимаю, что мне нужно исправить - нужен ли мне пользовательский поставщик связывателя?


person Joshua Barron    schedule 01.10.2014    source источник
comment
Для тех, кто смотрит на это, я никогда не решал эту проблему. Однако реальная проблема заключалась в том, что мои настройки сериализации JSON позволяли десериализовать объекты NodaTime — мне нужно было переопределить обработчики DateTime по умолчанию.   -  person Joshua Barron    schedule 07.10.2014


Ответы (1)


Ваш CreateRequest является сложным типом, для которого не определен преобразователь типов связывателя модели. В этом случае Web Api попытается использовать средство форматирования медиа-типа. Поскольку вы используете JSON, он попытается использовать средство форматирования JSON по умолчанию, которым является Newtonsoft Json.Net в стандартной конфигурации. Json.Net не знает, как обрабатывать типы Noda Time из коробки.

Чтобы Json.Net мог обрабатывать типы Noda Time, необходимо установить NodaTime.Serialization.JsonNet. и добавьте что-то подобное в код запуска...

public void Config(IAppBuilder app)
{
    var config = new HttpConfiguration();
    config.Formatters.JsonFormatter.SerializerSettings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
    app.UseWebApi(config);
}

После этого ваш второй пример будет работать, как и ожидалось, и ModelState.IsValid будет false, если ввод был не в правильном формате Noda Time.

См. http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api для получения дополнительной информации о привязке параметров в веб-API.

person Zac Charles    schedule 11.06.2015