C# MVC ICollection<;格式文件>;ValidationState始终设置为Skipped
作为ASP.NET核心MVC 1.0项目的一部分,我有一个带有ICollection属性的ViewModel。我需要验证此集合是否包含一个或多个项。未执行我的自定义验证属性 在我的实例中,它保存来自C# MVC ICollection<;格式文件>;ValidationState始终设置为Skipped,c#,.net,validation,asp.net-core,asp.net-core-mvc,C#,.net,Validation,Asp.net Core,Asp.net Core Mvc,作为ASP.NET核心MVC 1.0项目的一部分,我有一个带有ICollection属性的ViewModel。我需要验证此集合是否包含一个或多个项。未执行我的自定义验证属性 在我的实例中,它保存来自多部分/表单数据的多个文件附件 我已使用自定义验证属性装饰ViewModel中的属性: [RequiredCollection] 公共ICollection附件{get;set;} 下面是自定义属性类。它只是检查集合是否不为null,并且包含大于零的元素: 公共类RequiredCollection
多部分/表单数据的多个文件附件
我已使用自定义验证属性装饰ViewModel中的属性:
[RequiredCollection]
公共ICollection附件{get;set;}
下面是自定义属性类。它只是检查集合是否不为null,并且包含大于零的元素:
公共类RequiredCollectionAttribute:ValidationAttribute
{
受保护的常量字符串DefaultErrorMessageFormatString=“您必须至少提供一个。”;
public RequiredCollectionAttribute():base(DefaultErrorMessageFormatString){}
受保护的重写ValidationResult有效(对象值,ValidationContext ValidationContext)
{
var collection=(ICollection)值;
return collection==null | | collection.Count>0
?验证结果。成功
:新建验证结果(ErrorMessageString);
}
}
最后,在控制器中,我要确保POST
请求中的ViewModel有效,这将触发验证:
[HttpPost]
公共异步任务方法(MethodViewModel viewModel)
{
如果(!ModelState.IsValid)
返回视图(viewModel);
...
}
如果在调用ModelState.IsValid
时中断,Attachments
属性的ModelState.Values
的内容是:
问题:
- 为什么
RequiredCollectionAttribute.IsValid()方法中的断点没有被命中?
方法
- 为什么
Attachments
属性的ValidationState
设置为Skipped
--
编辑1:
MethodViewModel定义,根据要求:
public class MethodViewModel
{
...
[Display(Name = "Attachments")]
[RequiredCollection(ErrorMessage = "You must attached at least one file.")]
public ICollection<IFormFile> Attachments { get; set; }
...
}
--
编辑3:
视图的razor语法用于呈现附件
输入字段
<form role="form" asp-controller="Controller" asp-action="Method" method="post" enctype="multipart/form-data">
...
<div class="form-group">
<label asp-for="Attachments" class="control-label col-xs-3 col-sm-2"></label>
<div class="col-xs-9 col-sm-10">
<input asp-for="Attachments" class="form-control" multiple required>
<span asp-validation-for="Attachments" class="text-danger"></span>
</div>
</div>
...
</form>
...
...
部分答案(仅用于代码共享目的)
试试这个:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public sealed class RequiredCollectionAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
ErrorMessage = "You must provide at least one.";
var collection = value as ICollection;
return collection != null || collection.Count > 0;
}
}
另外,尝试添加一个过滤器
GlobalConfiguration.Configuration.Filters.Add(new RequestValidationFilter());
并将过滤器本身写入:
public class RequestValidationFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ModelState.IsValid == false)
{
var errors = actionContext.ModelState
.Values
.SelectMany(m => m.Errors
.Select(e => e.ErrorMessage));
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest, actionContext.ModelState);
actionContext.Response.ReasonPhrase = string.Join("\n", errors);
}
}
}
只供我们检查是否在过滤器内部触发了断点。如果发现ifformfile
或ifformfile
的集合不为空,MVC似乎正在抑制进一步的验证。
如果您查看FormFileModelBinder.cs
代码。如果绑定器能够从上面的if/elseif/else子句中获得非空结果,则将禁止验证
在测试中,我使用如下代码创建了一个视图模型:
[ThisAttriuteAlwaysReturnsAValidationError]
public IFormFile Attachment { get;set; }
当我实际上传一个文件到这个例子中时,上面的always erroring属性永远不会被调用
由于这是来自MVC本身,我认为最好的选择是实现ivalidTeableObject
接口
public class YourViewModel : IValidatableObject
{
public ICollection<IFormFile> Attachments { get;set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var numAttachments = Attachments?.Count() ?? 0;
if (numAttachments == 0)
{
yield return new ValidationResult(
"You must attached at least one file.",
new string[] { nameof(Attachments) });
}
}
}
公共类YourViewModel:IValidatableObject
{
公共ICollection附件{get;set;}
公共IEnumerable验证(ValidationContext ValidationContext)
{
var numAttachments=附件?计数()?0;
如果(numAttachments==0)
{
返回新的ValidationResult(
“您必须至少附加一个文件。”,
新字符串[]{nameof(附件)});
}
}
}
此方法仍将被调用,因为它不与任何单个属性关联,因此不会像属性一样被MVC抑制
如果您必须在多个地方执行此操作,您可以创建一个扩展方法来提供帮助
public static bool IsNullOrEmpty<T>(this IEnumerable<T> collection) =>
collection == null || !collection.GetEnumerator().MoveNext();
publicstaticbool为空(此IEnumerable集合)=>
集合==null | |!collection.GetEnumerator().MoveNext();
更新
这在1.0.0 RTM中已经并且应该得到修复。尝试覆盖上面建议的方法签名,但这无法解决问题。viewModel.Attachments
的ValidationState
仍然设置为Skipped
。请提供MethodViewModel的声明好吗?\n为问题添加了定义我编辑了一个错误,我在这里检查集合是否为null而不是与null不同。你注意到了吗?在我的服务器上,它正在工作,所以我们可能缺少smth。在我给你的代码中,是否触发了错误消息行上的断点?你是对的,这是一个输入错误,应该是!=空
。我在IsValid
方法的条目上有一个断点,它从不中断。我认为不会为此ICollection属性触发此验证。相反,在类级别应用属性并检查集合。@E-Bat请您解释一下“在类级别应用属性”是什么意思?我的意思是,您是如何具体化集合的,您在运行时使用的ICollection实现是什么?@E-Bat通过对视图中的POST
数据进行反序列化,“幕后”初始化该集合。查看“我的本地人”窗口,它的形式是ASP.NET标记帮助程序自动添加的System.Collections.Generic.List
@E-Battype=“file”
。感谢您的精彩回答和提出错误报告。很好,ASPNET团队承认这是一个有效的问题。赏金。
public static bool IsNullOrEmpty<T>(this IEnumerable<T> collection) =>
collection == null || !collection.GetEnumerator().MoveNext();