我有Web API的奇怪行为,.Net 4.5.2。如果可选字符串参数为null,则ModelState没有错误。如果它不为空且不为空,则不会再出现错误。但如果它只是一个空字符串,我就会出现模型状态错误。
为什么我会得到它以及如何禁用它?
假设在localhost:82
上投放了应用,我会得到以下结果:
Url: http://localhost:82/
Response: "null"
Url: http://localhost:82/?q=1
Response: "1"
Url: http://localhost:82/?q=
Response: {
"Message": "The request is invalid.",
"ModelState": {
"q.String": [
"A value is required but was not present in the request."
]
}
}
测试控制器和配置如下。在VS2013中使用“WebApi”将其减少到最低默认值“Asp.net Web应用程序”。
namespace Web.Api.Test.Controllers
{
using System.Web.Http;
[Route]
public class HomeController : ApiController
{
[Route]
[HttpGet]
public IHttpActionResult Search(string q = default(string))
{
return this.ModelState.IsValid
? this.Ok(q ?? "null")
: (IHttpActionResult)this.BadRequest(this.ModelState);
}
}
}
Startup.cs是:
using Microsoft.Owin;
using WebApplication1;
[assembly: OwinStartup(typeof(Startup))]
namespace WebApplication1
{
using System.Web.Http;
using Newtonsoft.Json;
using Owin;
public class Startup
{
public void Configuration(IAppBuilder app)
{
GlobalConfiguration.Configure(config =>
{
config.MapHttpAttributeRoutes();
config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented;
config.Formatters.Remove(config.Formatters.XmlFormatter);
});
}
}
}
PS:This问题有一个解决方法,但它没有回答主要问题:为什么会出现这种情况以及这个设计决策背后的原因。
答案 0 :(得分:4)
我遇到了同样的问题,最终提出了以下问题:
public class SimpleTypeParameterBindingFactory
{
private readonly TypeConverterModelBinder converterModelBinder = new TypeConverterModelBinder();
private readonly IEnumerable<ValueProviderFactory> factories;
public SimpleTypeParameterBindingFactory(HttpConfiguration configuration)
{
factories = configuration.Services.GetValueProviderFactories();
}
public HttpParameterBinding BindOrNull(HttpParameterDescriptor descriptor)
{
return IsSimpleType(descriptor.ParameterType)
? new ModelBinderParameterBinding(descriptor, converterModelBinder, factories)
: null;
}
private static bool IsSimpleType(Type type)
{
return TypeDescriptor.GetConverter(type).CanConvertFrom(typeof (string));
}
}
public class Startup
{
public void Configure(IAppBuilder appBuilder)
{
var configuration = new HttpConfiguration();
configuration.ParameterBindingRules.Insert(0, new SimpleTypeParameterBindingFactory(configuration).BindOrNull);
configuration.EnsureInitialized();
}
}
问题源于ModelValidationNode中的一些魔术代码,即使相应参数具有默认值,也会为null模型创建模型错误。上面的代码只是将TypeModelBinder(它调用ModelValidationNode)替换为TypeConverterModelBinder,用于简单的类型参数。
答案 1 :(得分:2)
为什么我会得到它以及如何禁用它?
不知道你为什么得到它。 This也许你如何禁用它,但在阅读后我认为你不想真正有更简单的解决方案,例如:
使用模型类可以更清晰地解决这个问题。
public class SearchModel
{
public string Q { get; set; }
}
public IHttpActionResult Search([FromUri] SearchModel model)
{
return ModelState.IsValid
? Ok(model.Q ?? "null")
: (IHttpActionResult) BadRequest(ModelState);
}
答案 2 :(得分:1)
这就是原因:
我们在应用程序中发现了相同的行为,并使用源代码进行深度调试
git clone https://github.com/ASP-NET-MVC/aspnetwebstack
有意义地搜索正确的方向。将空格字符串设置为null的Here the method,并将here错误添加到模型状态:
if (parentNode == null && ModelMetadata.Model == null)
{
string trueModelStateKey = ModelBindingHelper.CreatePropertyModelName(ModelStateKey, ModelMetadata.GetDisplayName());
modelState.AddModelError(trueModelStateKey, SRResources.Validation_ValueNotFound);
return;
}
恕我直言,这是一个错误。但谁在乎。我们使用了this workaround
答案 3 :(得分:0)
您是否尝试过[DisplayFormat(ConvertEmptyStringToNull = false)]
?
答案 4 :(得分:0)
我们找到了另一种解决方案
public class EmptyStringToNullModelBinder : Attribute, IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
ValueProviderResult valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
bindingContext.Model = string.IsNullOrWhiteSpace(valueResult?.RawValue?.ToString()) ? null : valueResult.RawValue;
return true;
}
}
对于你的情况,它会是这样的:
[Route]
[HttpGet]
public IHttpActionResult Search([FromUri(BinderType = typeof(EmptyStringToNullModelBinder))]string q = null)
{
return this.ModelState.IsValid
? this.Ok(q ?? "null")
: (IHttpActionResult)this.BadRequest(this.ModelState);
}