Guid的验证

Hes*_*sky 13 c# asp.net-mvc guid data-annotations

我有一个强类型视图,它有一个DropDownListFor属性.

下拉列表中的每个项目都由GUID表示.

我所追求的是一种验证用户是否从下拉列表中选择项目的方法.目前我还没有看到使用数据注释做这件事.

无论如何使用数据注释实现这一点,因此客户端和服务器端验证将起作用.

我猜我需要制作一个自定义方法来做到这一点但是想知道是否已经存在任何东西.

Nic*_*cht 22

实际上,你不能使用Required带有GUID的属性(没有我在下面提到的方法)因为它们继承struct,因此它们的默认值实际上是Guid.Empty的一个实例,它将满足Required属性的要求.现在说,有可能得到你想要的东西,只需要让你的财产可以为空,举个例子......

public class Person
{
    [Required] //Only works because the Guid is nullable
    public Guid? PersonId { get; set;}
    public string FirstName { get; set;}
    public string LastName { get; set;}
}
Run Code Online (Sandbox Code Playgroud)

通过将GUID标记为可空(使用?或Nullable,如果您更喜欢漫长的方式),在绑定浏览器发送的内容时,让它保持为null.在您的情况下,只需确保下拉列表的默认选项的值使用空字符串作为其值.

编辑:这种方法的唯一警告是你最终必须使用像Person.GetValueOfDefault()所有地方和可能测试的东西Guid.Empty.我厌倦了这样做,最终创建了自己的验证属性,以帮助简化验证Guids(以及任何其他具有我想要视为无效的默认值的类型,如int,DateTime等).但是我还没有客户端验证,所以验证只发生在服务器上.如果您可以使用可空类型,则可以将其与[Required](设计为不重复功能[Required])结合使用.这意味着您仍然需要使用GetValueOrDefault(),但至少那时您不必再进行测试Guid.Empty了.Gist链接有一些带有示例的XMLDocs,为简洁起见,我将它们留在这里.我目前正在使用ASP.NET Core.

编辑:更新以修复Nullable <>的错误,以及将null视为无效的错误.添加了支持类来处理客户端验证.有关完整代码,请参阅Gist.

要点:RequireNonDefaultAttribute

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class RequireNonDefaultAttribute : ValidationAttribute
{
    public RequireNonDefaultAttribute()
        : base("The {0} field requires a non-default value.")
    {
    }

    public override bool IsValid(object value)
    {
        if (value is null)
            return true; //You can flip this if you want. I wanted leave the responsability of null to RequiredAttribute
        var type = value.GetType();
        return !Equals(value, Activator.CreateInstance(Nullable.GetUnderlyingType(type) ?? type));
    }
}
Run Code Online (Sandbox Code Playgroud)


Rus*_*Cam 11

编辑答案

重新阅读您的问题后,听起来您只想知道是否选择了某个值.如果是这样的话,那么只适用RequiredAttributeGuid财产,并使其可为空的模型

public class GuidModel
{
    [Required]
    public Guid? Guid { get; set; }

    public IEnumerable<Guid> Guids { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后在强类型视图中(带@model GuidModel)

@Html.ValidationMessageFor(m => m.Guid)
@Html.DropDownListFor(
    m => m.Guid,
    Model.Guids.Select(g => new SelectListItem {Text = g.ToString(), Value = g.ToString()}),
    "-- Select Guid --")
Run Code Online (Sandbox Code Playgroud)

添加客户端验证JavaScript脚本引用以进行客户端验证.

控制器看起来像

public class GuidsController : Controller
{
    public GuidRepository GuidRepo { get; private set; }

    public GuidsController(GuidRepository guidRepo)
    {
        GuidRepo = guidRepo;
    }

    [HttpGet]
    public ActionResult Edit(int id)
    {
        var guid = GuidRepo.GetForId(id);
        var guids - GuidRepo.All();

        return View(new GuidModel { Guid = guid, Guids = guids });
    }

    [HttpPost]
    public ActionResult Edit(GuidModel model)
    {
        if (!ModelState.IsValid)
        {
            model.Guids = GuidRepo.All();
            return View(model);
        }

        /* update db */

        return RedirectToAction("Edit");
    }
}
Run Code Online (Sandbox Code Playgroud)

这将确保Guid模型绑定需要该属性GuidModel.

原始答案

我不相信有一个现成的Data Annotation Validation属性能够做到这一点.我写了一篇关于实现这一目标的方法的博客文章 ; 该帖子正在使用IoC容器,但如果您想要获得某些工作,则可以使用硬编码依赖项.

就像是

public class ValidGuidAttribute : ValidationAttribute
{
    private const string DefaultErrorMessage = "'{0}' does not contain a valid guid";

    public ValidGuidAttribute() : base(DefaultErrorMessage)
    {
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var input = Convert.ToString(value, CultureInfo.CurrentCulture);

        // let the Required attribute take care of this validation
        if (string.IsNullOrWhiteSpace(input))
        {
            return null;
        }

        // get all of your guids (assume a repo is being used)
        var guids = new GuidRepository().AllGuids();

        Guid guid;
        if (!Guid.TryParse(input, out guid))
        {
            // not a validstring representation of a guid
            return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
        }

        // is the passed guid one we know about?
        return guids.Any(g => g == guid) ?
            new ValidationResult(FormatErrorMessage(validationContext.DisplayName)) : null;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在您发送到控制器操作的模型上

public class GuidModel
{
    [ValidGuid]
    public Guid guid { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这为您提供服务器端验证.您可以编写客户端验证来执行此操作,也许使用RemoteAttribute但在这种情况下我没有看到很多价值,因为唯一会看到此客户端验证的人是那些在DOM中弄乱值的人; 这对普通用户没有任何好处.


Eri*_*eet 8

正则表达式确实有效(如果你使用正确的话!)

[Required]
[RegularExpression("^((?!00000000-0000-0000-0000-000000000000).)*$", ErrorMessage = "Cannot use default Guid")]
public Guid Id { get; set; }
Run Code Online (Sandbox Code Playgroud)


小智 5

我现在知道这是一个老问题,但是如果其他人感兴趣,我设法通过创建 [IsNotEmpty] 注释来解决这个问题(在我的情况下,使 Guid 可以为空不是一个选项)。

这使用反射来确定属性上是否有 Empty 的实现,如果有,则比较它。

public class IsNotEmptyAttribute : ValidationAttribute
{

    public override bool IsValid(object value)
    {

        if (value == null) return false;

        var valueType = value.GetType();
        var emptyField = valueType.GetField("Empty");

        if (emptyField == null) return true;

        var emptyValue = emptyField.GetValue(null);

        return !value.Equals(emptyValue);

    }
}
Run Code Online (Sandbox Code Playgroud)


Ro.*_*Ro. 5

非空引导验证器

防止 00000000-0000-0000-0000-000000000000

属性:

using System.ComponentModel.DataAnnotations;
[AttributeUsage(AttributeTargets.Property)]
internal class NonEmptyGuidAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if ((value is Guid) && Guid.Empty == (Guid)value)
        {
            return new ValidationResult("Guid cannot be empty.");
        }
        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

模型:

using System.ComponentModel.DataAnnotations;
public class Material
{
    [Required]
    [NonEmptyGuid]
    public Guid Guid { get; set; }
}
Run Code Online (Sandbox Code Playgroud)