Связывание поля NodaTime Instant в теле InputModel с использованием C# ASP.NET WebAPI 2.0

У меня есть проект WebAPI, который принимает строки даты ISO внутри входной модели. Я анализировал их, используя DateTimeOffset?. Я хочу изгнать дату и время BCL из своего проекта, поэтому я хочу найти способ напрямую связать эти строки с Instant.

public class MyInputModel
{
    public DateTimeOffset? OldTime { get; set; }

    public Instant NewTime { get; set; }
}

Пример модели ввода JSON выглядит следующим образом:

{
    "oldtime":"2016-01-01T12:00:00Z",
    "newtime":"2016-01-01T12:00:00Z"
}

И мой код контроллера:

[HttpPost]
public async Task<IActionResult> PostTimesAsync([FromBody]MyInputModel input)
{
    Instant myOldTime = Instant.FromDateTimeUtc(input.oldTime.Value.UtcDateTime);
    Instant myNewTime = input.newTime; // This cannot be used with ISO date strings.
}

Я попытался создать пользовательскую привязку модели следующим образом. Это работает для моделей в строке запроса, но не для моделей в теле запроса POST. Как привязать ввод даты в строковом формате ISO 8601 к NodaTime Instant?

public Task BindModelAsync(ModelBindingContext bindingContext)
{
    if (!String.IsNullOrWhiteSpace(bindingContext.ModelName) && 
            bindingContext.ModelType == typeof(Instant?) && 
            bindingContext.ValueProvider.GetValue(bindingContext.ModelName) != null)
    {
        Instant? value;
        var val = bindingContext.ValueProvider
.GetValue(bindingContext.ModelName).FirstValue as string;

        if (String.IsNullOrWhiteSpace(val))
        {
            bindingContext.Result = ModelBindingResult.Failed();
            return Task.FromResult(0);
        }
        else if (InstantExtensions.TryParse(val, out value))
        {
            bindingContext.Result = ModelBindingResult.Success(value);
            return Task.FromResult(0);
        }
        else
        {
            bindingContext.ModelState.AddModelError(bindingContext.ModelName, 
"The date is invalid.");
        }
    }

    bindingContext.Result = ModelBindingResult.Failed();
    return Task.FromResult(0);
}

public static bool TryParse(string value, out Instant? result)
{
    result = null;

    // If this is date-only, add Utc Midnight to the value.
    if (value.Length.Equals(10))
    {
        value += "T00:00:00Z";
    }

    // Trim milliseconds if present
    var decimalPointIndex = value.IndexOf('.');
    if (decimalPointIndex > 0)
    {
        value = value.Substring(0, decimalPointIndex) + "Z";
    }

    // Attempt to parse
    var parseResult = InstantPattern.GeneralPattern.Parse(value);
    if (parseResult.Success)
    {
        result = parseResult.Value;
        return true;
    }

    return false;
}

person 08Dc91wk    schedule 01.08.2016    source источник
comment
Для чего Type вы зарегистрировали свою модель подшивки?   -  person bot_insane    schedule 01.08.2016
comment
Привет, Арам, я его не регистрировал - в строках запроса я использую его следующим образом: [ModelBinder(BinderType = typeof(InstantModelBinder))]Instant? time = null)   -  person 08Dc91wk    schedule 01.08.2016
comment
Попробуйте добавить этот ModelBinders.Binders.Add(typeof(Instant?), new InstantModelBinder()); в Global.asax.cs Или, может быть, без вопросительного знака, как я вижу в MyInputModel, он не может быть обнулен.   -  person bot_insane    schedule 01.08.2016
comment
Я должен был упомянуть - это проект dotnet ASP.NET WebApi 2.0. Мы не используем файл Global.asax.cs.   -  person 08Dc91wk    schedule 01.08.2016
comment
Добавьте его в Startup.cs файл. В функции Configuration вот так: public void Configuration(IAppBuilder app) { ModelBinders.Binders.Add(typeof(Instant?), new InstantModelBinder()); ... }   -  person bot_insane    schedule 01.08.2016
comment
Я добавил это. В нем говорится, что имя ModelBinders не существует в текущем контексте. Откуда я могу получить объект ModelBinders?   -  person 08Dc91wk    schedule 01.08.2016
comment
Извините, я забыл, что это было для MVC, пожалуйста, посмотрите мой ответ.   -  person bot_insane    schedule 01.08.2016


Ответы (1)


Вы должны добавить связыватель модели следующим образом: (в методе WebApiConfig Register )

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.BindParameter(typeof(Instant), new InstantModelBinder())
        ...
    }
}

WebApiConfig.Register вызывается в Configuration функции в Startup.cs файле. В большинстве случаев так:

var config = new HttpConfiguration();
WebApiConfig.Register(config);

Если он не вызывается, вы можете просто добавить эту строку:

config.BindParameter(typeof(Instant), new InstantModelBinder())

где объект HttpConfiguration создается в Startup.cs.

person bot_insane    schedule 01.08.2016
comment
Извините - я до сих пор не знаю, куда это должно идти. Этот метод вызывается классом запуска? - person 08Dc91wk; 01.08.2016
comment
Да, вы должны иметь это в своем классе запуска или, может быть, что-то подобное, вам просто нужно HttpConfiguration object для вызова BindParameter. - person bot_insane; 01.08.2016
comment
У меня есть метод Configure, но нет ссылок на HttpConfiguration, и если я попробую var config = new HttpConfiguration();, я не смогу найти для него пространство имен — это большой проект, но у нас не установлены соответствующие пакеты; Я чувствую, что это неправильный подход? - person 08Dc91wk; 01.08.2016
comment
Если HttpConfiguration не создано, как зарегистрирован ваш веб-API? - person bot_insane; 03.08.2016
comment
Могу я увидеть ваш Startup.cs файл. - person bot_insane; 03.08.2016