У меня возникли проблемы с привязкой пользовательской модели к работе при отправке данных x-www-form-urlencoded
. Я пробовал все, о чем я могу думать, и ничто, кажется, не дает желаемого результата. Обратите внимание, что при отправке данных JSON, моих JsonConverters и т.д. Все работает нормально. Это когда я отправляю как x-www-form-urlencoded
, что система не может понять, как связать мою модель.
Мой тестовый пример: я хотел бы привязать объект TimeZoneInfo как часть моей модели.
Здесь мое модельное связующее:
public class TimeZoneModelBinder : SystemizerModelBinder
{
protected override object BindModel(string attemptedValue, Action<string> addModelError)
{
try
{
return TimeZoneInfo.FindSystemTimeZoneById(attemptedValue);
}
catch(TimeZoneNotFoundException)
{
addModelError("The value was not a valid time zone ID. See the GetSupportedTimeZones Api call for a list of valid time zone IDs.");
return null;
}
}
}
Вот базовый класс, который я использую:
public abstract class SystemizerModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
var name = GetModelName(bindingContext.ModelName);
var valueProviderResult = bindingContext.ValueProvider.GetValue(name);
if(valueProviderResult == null || string.IsNullOrWhiteSpace(valueProviderResult.AttemptedValue))
return false;
var success = true;
var value = BindModel(valueProviderResult.AttemptedValue, s =>
{
success = false;
bindingContext.ModelState.AddModelError(name, s);
});
bindingContext.Model = value;
bindingContext.ModelState.SetModelValue(name, new System.Web.Http.ValueProviders.ValueProviderResult(value, valueProviderResult.AttemptedValue, valueProviderResult.Culture));
return success;
}
private string GetModelName(string name)
{
var n = name.LastIndexOf(".", StringComparison.Ordinal);
return n < 0 || n >= name.Length - 1 ? name : name.Substring(n + 1);
}
protected abstract object BindModel(string attemptedValue, Action<string> addModelError);
}
Я использовал базовый класс, подобный этому, чтобы упростить создание дополнительных пользовательских привязок к образцам.
Здесь моя модель поставщика связующего. Обратите внимание, что это правильно вызвано из моего контейнера IoC, поэтому я не буду пытаться показать этот аспект моего кода.
public class SystemizerModelBinderProvider : ModelBinderProvider
{
public override IModelBinder GetBinder(HttpConfiguration configuration, Type modelType)
{
if(modelType == typeof(TimeZoneInfo))
return new TimeZoneModelBinder();
return null;
}
}
Наконец, здесь метод действия и класс модели:
[DataContract)]
public class TestModel
{
[DataMember]
public TimeZoneInfo TimeZone { get; set; }
}
[HttpPost]
public HttpResponseMessage Test(TestModel model)
{
return Request.CreateResponse(HttpStatusCode.OK, model);
}
Для метода действия я попытался:
public HttpResponseMessage Test([FromBody] TestModel model)
Это вызывает FormUrlEncodedMediaFormatter
, который, кажется, вообще игнорирует мое собственное связующее устройство.
public HttpResponseMessage Test([ModelBinder] TestModel model)
Это вызывает мое настраиваемое связующее устройство, как и ожидалось, но тогда оно предоставляет только ValueProviders для RouteData
и QueryString
и по какой-то причине не предоставляет ничего для содержимого тела. См. Ниже:
Я также попытался украсить сам класс ModelBinder(typeof(SystemizerModelBinderProvider))
Почему привязка модели ТОЛЬКО возникает, когда я использую атрибут [ModelBinder] и почему она ТОЛЬКО пытается читать значения маршрута и запроса и игнорировать содержимое тела? Почему FromBody
игнорирует моего поставщика привязки для конкретной модели?
Как создать сценарий, в котором я могу получать данные POSTED x-www-form-urlencoded
и успешно связывать свойства модели с помощью пользовательской логики?