我有很多这种形式的控制器:
public IActionResult GetSomething(int id, DateTime from) {
...
}
id
和from
参数在查询中作为查询参数给出。如果未提供id
,则ModelValid
状态将设置为false。但是如果未提供from
,则ModelValid为true,from设置为1900-01-01 00:00:00(DateTime.Min)。
如果未提供所需的DateTime参数,如何将ModelState设为false?
答案 0 :(得分:1)
我决定去实现一个DateTime模型绑定器。如果缺少DateTime参数,则以下代码不会在ModelState上设置IsValid = true。约会时间? (可以为空的DateTime)处理得很好,但同样,如果缺少查询参数,则IsValid设置为false,而不是将参数设置为默认值。
首先是DateTimeModelBinderProvider:
public class DateTimeModelBinderProvider : IModelBinderProvider
{
/// <inheritdoc />
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (!context.Metadata.IsComplexType)
{
// We can handle DateTime and nullable DateTime
if ((context.Metadata.ModelType == typeof(DateTime)) ||
(context.Metadata.IsNullableValueType && context.Metadata.UnderlyingOrModelType == typeof(DateTime)))
return new DateTimeModelBinder(context.Metadata.ModelType);
}
return null;
}
}
接下来是DateTimeModelBinder。大多数代码都是从github逐字复制的。其中一些可能被遗漏,但它的工作原理如下:
public class DateTimeModelBinder : IModelBinder
{
private readonly TypeConverter _typeConverter;
public DateTimeModelBinder(Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
_typeConverter = TypeDescriptor.GetConverter(type);
}
/// <inheritdoc />
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (valueProviderResult == ValueProviderResult.None)
{
// Do not accept an empty value provider result as being ok for DateTime (is ok for DateTime?)
bindingContext.ModelState.TryAddModelError(
bindingContext.ModelName,
bindingContext.ModelMetadata.ModelBindingMessageProvider.ValueMustNotBeNullAccessor(
valueProviderResult.ToString()));
// no entry
return Task.CompletedTask;
}
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
try
{
var value = valueProviderResult.FirstValue;
object model = null;
if (!string.IsNullOrWhiteSpace(value))
{
model = new DateTimeConverter().ConvertFrom(
context: null,
culture: valueProviderResult.Culture,
value: value);
}
if (bindingContext.ModelType == typeof(string))
{
var modelAsString = model as string;
if (bindingContext.ModelMetadata.ConvertEmptyStringToNull &&
string.IsNullOrEmpty(modelAsString))
{
model = null;
}
}
// When converting newModel a null value may indicate a failed conversion for an otherwise required
// model (can't set a ValueType to null). This detects if a null model value is acceptable given the
// current bindingContext. If not, an error is logged.
if (model == null && !bindingContext.ModelMetadata.IsReferenceOrNullableType)
{
bindingContext.ModelState.TryAddModelError(
bindingContext.ModelName,
bindingContext.ModelMetadata.ModelBindingMessageProvider.ValueMustNotBeNullAccessor(
valueProviderResult.ToString()));
return Task.CompletedTask;
}
else
{
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
catch (Exception exception)
{
var isFormatException = exception is FormatException;
if (!isFormatException && exception.InnerException != null)
{
// TypeConverter throws System.Exception wrapping the FormatException,
// so we capture the inner exception.
exception = ExceptionDispatchInfo.Capture(exception.InnerException).SourceException;
}
bindingContext.ModelState.TryAddModelError(
bindingContext.ModelName,
exception,
bindingContext.ModelMetadata);
// Were able to find a converter for the type but conversion failed.
return Task.CompletedTask;
}
}
}
还要记得激活它。我将它插入提供程序列表的开头,以确保我的DateTime提供程序优先使用默认处理程序:
var mvc = services.AddMvc(config => {
config.ModelBinderProviders.Insert(0, new DateTimeModelBinderProvider());
});
答案 1 :(得分:0)
您可以通过创建具有“From”属性的验证属性的模型来解决此问题。 我还没有测试出代码。但代码应该是:
public class Model
{
public int Id { get; set; }
[DateTimeShouldHaveValue]
public DateTime From { get; set; }
}
public class DateTimeShouldHaveValueAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
if (value == null)
return false;
var dateTimeTmp = value.ToString();
DateTime dateTime;
DateTime.TryParse(dateTimeTmp, out dateTime);
if (dateTime == DateTime.MinValue)
return false;
return true;
}
}
public IActionResult GetSomething(Model model)
{
}