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)
正如预期的那样,这调用了我的自定义模型绑定器,但是它只提供了 ValueProvidersRouteData
并且QueryString
由于某种原因不为正文内容提供任何东西。见下文:
我也尝试过用ModelBinder(typeof(SystemizerModelBinderProvider))
为什么模型绑定只在我使用 [ModelBinder] 属性时发生,为什么它只尝试读取路由和查询字符串值并忽略正文内容?为什么FromBody
忽略我的自定义模型绑定器提供程序?
如何创建可以接收 POSTEDx-www-form-urlencoded
数据并使用自定义逻辑成功绑定模型属性的场景?