Nat*_*ley 12 c# model-binding asp.net-web-api
在发布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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是我正在使用的基类:
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);
}
Run Code Online (Sandbox Code Playgroud)
我使用这样的基类来简化创建其他自定义模型绑定器.
这是我的模型绑定提供程序.请注意,这是从我的IoC容器中正确调用的,所以我不打算显示我的代码方面.
public class SystemizerModelBinderProvider : ModelBinderProvider
{
public override IModelBinder GetBinder(HttpConfiguration configuration, Type modelType)
{
if(modelType == typeof(TimeZoneInfo))
return new TimeZoneModelBinder();
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
最后,这是动作方法和模型类:
[DataContract)]
public class TestModel
{
[DataMember]
public TimeZoneInfo TimeZone { get; set; }
}
[HttpPost]
public HttpResponseMessage Test(TestModel model)
{
return Request.CreateResponse(HttpStatusCode.OK, model);
}
Run Code Online (Sandbox Code Playgroud)
对于动作方法,我尝试过:
public HttpResponseMessage Test([FromBody] TestModel model)
Run Code Online (Sandbox Code Playgroud)
这会调用FormUrlEncodedMediaFormatter,这似乎完全忽略了我的自定义模型绑定器.
public HttpResponseMessage Test([ModelBinder] TestModel model)
Run Code Online (Sandbox Code Playgroud)
这会调用我的自定义模型绑定器,如预期的那样,但它只提供ValueProviders RouteData,QueryString并且由于某种原因不提供任何正文内容.见下文:

我也尝试过装饰课程本身 ModelBinder(typeof(SystemizerModelBinderProvider))
为什么只在我使用[ModelBinder]属性时才会出现模型绑定,为什么它只会尝试读取路由和查询字符串值并忽略正文内容?为什么FromBody忽略我的定制模型绑定提供程序?
如何创建一个场景,我可以x-www-form-urlencoded使用自定义逻辑接收POSTED 数据并成功绑定模型属性?
Dar*_*rov 29
我建议您阅读following blog postMike Stall详细解释模型绑定在Web API中的工作原理:
绑定参数有两种技术:模型绑定和格式化.实际上,WebAPI使用模型绑定从查询字符串中读取,使用Formatters从正文中读取.
以下是确定是使用模型绑定还是格式化程序读取参数的基本规则:
- 如果参数没有属性,那么决定完全取决于参数的.NET类型."简单类型"使用模型绑定.复杂类型使用格式化程序."简单类型"包括:基元,TimeSpan,DateTime,Guid,Decimal,String或具有从字符串转换的TypeConverter的东西.
- 您可以使用
[FromBody]属性指定应从正文中读取参数.- 您可以
[ModelBinder]在参数或参数的类型上使用属性来指定参数应该是模型绑定的.此属性还允许您配置模型绑定器.[FromUri]是一个派生实例,[ModelBinder]它专门配置模型绑定器只查看URI.- 身体只能读一次.因此,如果签名中有2个复杂类型,则其中至少有一个必须具有[ModelBinder]属性.
因此,如果数据源是请求主体,那么您可以创建自定义MediaTypeFormatter而不是模型绑定器.
使用ModelBinder比使用MediaTypeFormatter要好一些.您无需在全球注册.
我找到了另一种使用模型绑定器来绑定Web API中的复杂对象类型的替代方法.在模型绑定器中,我正在读取请求体作为字符串,然后使用JSON.NET将其反序列化为所需的对象类型.它也可用于映射复杂对象类型的数组.
我添加了一个模型绑定器如下:
public class PollRequestModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
var body = actionContext.Request.Content.ReadAsStringAsync().Result;
var pollRequest = JsonConvert.DeserializeObject<PollRequest>(body);
bindingContext.Model = pollRequest;
return true;
}
}
Run Code Online (Sandbox Code Playgroud)
然后我在Web API控制器中使用它如下:
public async Task<PollResponse> Post(Guid instanceId, [ModelBinder(typeof(PollRequestModelBinder))]PollRequest request)
{
// api implementation
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
12480 次 |
| 最近记录: |