Token导航 LogoToken导航

Agent Skill · AI 工具

fluentvalidation-patterns(流畅的验证模式)

fluentvalidation-patterns 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

fluentvalidation-patterns 是AI 工具类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证为 MIT,第三方扫描已通过,按安装量在AI 工具类的 2,536 个 Skill 里排第 1,591。

总安装 588周安装 24GitHub Stars 21下载 188核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
MIT

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:fluentvalidation-patterns(流畅的验证模式)
来源仓库:https://github.com/thapaliyabikendra/ai-artifacts
仓库路径:skills/fluentvalidation-patterns
安装命令:
npx skills add https://github.com/thapaliyabikendra/ai-artifacts --skill fluentvalidation-patterns
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/thapaliyabikendra/ai-artifacts --skill fluentvalidation-patterns

这个 Skill 是做什么的

AI 工具类 Skill 用来扩展助手本身的能力,例如接模型、接检索、接外部服务。

fluentvalidation-patterns 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息。适合在 Codex、Claude、Cursor、Gemini CLI 中围绕仓库状态、代码变更或协作事项进行整理。

通过 npx skills add 命令从指定 GitHub 仓库安装并使用。安装前需确认权限范围、维护状态,注意是否触发联网、命令执行或文件读写。建议结合原始 README 和仓库内容核验具体用法和功能边界。

来源仓库内路径:skills/fluentvalidation-patterns

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/thapaliyabikendra/ai-artifacts --skill fluentv…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

fluentvalidation-patterns 怎么安装?

在终端执行 npx skills add https://github.com/thapaliyabikendra/ai-artifacts --skill fluentvalidation-…。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

fluentvalidation-patterns 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

fluentvalidation-patterns 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

fluentvalidation-patterns 是免费的吗?

来源标注的许可证是 MIT。本站不代收任何费用,也不托管安装包;能否商用、要不要署名,以许可证原文为准。

fluentvalidation-patterns 用的人多吗?

公开数据是累计安装 588 次,近一周安装 24 次,下载 188 次,GitHub 21 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

fluentvalidation-patterns 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 3 个目录同时收录,说明这个 Skill 流传比较广。
  • 许可证明确为 MIT,商用和分发边界好判断。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理AI 工具类任务、又不想每次都手写提示词的人。
  • 需要许可证清楚、并且有第三方扫描结果的团队项目。

不太适合

  • 需要厂商级技术支持和 SLA 的生产系统。

AI 工具类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
executing-plans

执行计划 · Codex、Claude

1,515,336173,110只读MIT通过
verification-before-completion

完成前的验证 · Codex、Claude

1,319,472173,121权限未标注MIT通过
subagent-driven-development

子 Agent 驱动开发 · Codex、Claude

1,312,416173,138只读MIT通过
copy-editing

复制编辑 · Codex、Claude

1,209,57625,632只读MIT通过
paywall-upgrade-cro

付费专区升级 cro · Codex、Claude

1,030,17625,617只读MIT通过
fluentvalidation-patterns

流畅的验证模式 · 本页

58821需要联网MIT通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

FluentValidation Patterns

FluentValidation patterns for ABP Framework input DTO validation.

When to Use

  • Creating validators for Create/Update DTOs
  • Implementing async validation with repository checks
  • Building conditional validation rules
  • Creating reusable custom validators
  • Localizing validation error messages

ABP Integration

Basic Validator Structure

// Application/{Feature}/CreateUpdate{Entity}DtoValidator.cs
public class CreateUpdate{Entity}DtoValidator : AbstractValidator<CreateUpdate{Entity}Dto>
{
    public CreateUpdatePatientDtoValidator()
    {
        RuleFor(x => x.FirstName)
            .NotEmpty()
            .MaximumLength(100);

        RuleFor(x => x.LastName)
            .NotEmpty()
            .MaximumLength(100);

        RuleFor(x => x.Email)
            .NotEmpty()
            .EmailAddress()
            .MaximumLength(255);

        RuleFor(x => x.DateOfBirth)
            .NotEmpty()
            .LessThan(DateTime.Today)
            .WithMessage("Date of birth must be in the past");
    }
}

ABP Module Registration

// ApplicationModule.cs
public override void PreConfigureServices(ServiceConfigurationContext context)
{
    PreConfigure<AbpFluentValidationAutoValidationOptions>(options =>
    {
        options.AutoValidateMethodInvocations = true;
    });
}

public override void ConfigureServices(ServiceConfigurationContext context)
{
    // Auto-register all validators in assembly
    context.Services.AddValidatorsFromAssembly(typeof({ProjectName}ApplicationModule).Assembly);
}

Common Validation Rules

String Validation

RuleFor(x => x.Name)
    .NotEmpty()
    .WithMessage("Name is required")
    .MinimumLength(2)
    .MaximumLength(100)
    .Matches(@"^[a-zA-Z\s]+$")
    .WithMessage("Name can only contain letters and spaces");

RuleFor(x => x.Email)
    .NotEmpty()
    .EmailAddress()
    .Must(email => !email.EndsWith("@test.com"))
    .WithMessage("Test emails are not allowed");

RuleFor(x => x.Phone)
    .NotEmpty()
    .Matches(@"^\+?[1-9]\d{1,14}$")
    .WithMessage("Invalid phone number format");

Numeric Validation

RuleFor(x => x.Age)
    .InclusiveBetween(0, 150)
    .WithMessage("Age must be between 0 and 150");

RuleFor(x => x.Price)
    .GreaterThan(0)
    .LessThanOrEqualTo(1000000)
    .PrecisionScale(10, 2, false);

RuleFor(x => x.Quantity)
    .GreaterThanOrEqualTo(1)
    .When(x => x.IsRequired);

Date Validation

RuleFor(x => x.DateOfBirth)
    .NotEmpty()
    .LessThan(DateTime.Today)
    .GreaterThan(DateTime.Today.AddYears(-150));

RuleFor(x => x.AppointmentDate)
    .NotEmpty()
    .GreaterThan(DateTime.Now)
    .WithMessage("Appointment must be in the future");

RuleFor(x => x.EndDate)
    .GreaterThan(x => x.StartDate)
    .When(x => x.EndDate.HasValue)
    .WithMessage("End date must be after start date");

Collection Validation

RuleFor(x => x.Tags)
    .NotEmpty()
    .Must(tags => tags.Count <= 10)
    .WithMessage("Maximum 10 tags allowed");

RuleForEach(x => x.Items)
    .ChildRules(item =>
    {
        item.RuleFor(i => i.ProductId).NotEmpty();
        item.RuleFor(i => i.Quantity).GreaterThan(0);
    });

RuleFor(x => x.Emails)
    .Must(emails => emails.Distinct().Count() == emails.Count)
    .WithMessage("Duplicate emails are not allowed");

Async Validation with Repository

Unique Email Check

public class CreateUpdate{Entity}DtoValidator : AbstractValidator<CreateUpdate{Entity}Dto>
{
    private readonly IRepository<{Entity}, Guid> _repository;

    public CreateUpdate{Entity}DtoValidator(IRepository<{Entity}, Guid> repository)
    {
        _repository = repository;

        RuleFor(x => x.Email)
            .NotEmpty()
            .EmailAddress()
            .MustAsync(BeUniqueEmail)
            .WithMessage("Email already exists");
    }

    private async Task<bool> BeUniqueEmail(string email, CancellationToken cancellationToken)
    {
        return !await _repository.AnyAsync(e => e.Email == email);
    }
}

Unique Check with Edit Context

public class CreateUpdate{Entity}DtoValidator : AbstractValidator<CreateUpdate{Entity}Dto>
{
    private readonly IRepository<{Entity}, Guid> _repository;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public CreateUpdate{Entity}DtoValidator(
        IRepository<{Entity}, Guid> repository,
        IHttpContextAccessor httpContextAccessor)
    {
        _repository = repository;
        _httpContextAccessor = httpContextAccessor;

        RuleFor(x => x.Email)
            .NotEmpty()
            .EmailAddress()
            .MustAsync(BeUniqueEmailAsync)
            .WithMessage("Email already exists");
    }

    private async Task<bool> BeUniqueEmailAsync(string email, CancellationToken cancellationToken)
    {
        // Get entity ID from route (for updates)
        var routeData = _httpContextAccessor.HttpContext?.GetRouteData();
        var idString = routeData?.Values["id"]?.ToString();

        if (Guid.TryParse(idString, out var entityId))
        {
            // Exclude current entity from check (update scenario)
            return !await _repository.AnyAsync(
                e => e.Email == email && e.Id != entityId);
        }

        // Create scenario
        return !await _repository.AnyAsync(e => e.Email == email);
    }
}

Foreign Key Exists Check

public class Create{Entity}DtoValidator : AbstractValidator<Create{Entity}Dto>
{
    private readonly IRepository<{ParentEntity}, Guid> _parentRepository;
    private readonly IRepository<{RelatedEntity}, Guid> _relatedRepository;

    public Create{Entity}DtoValidator(
        IRepository<{ParentEntity}, Guid> parentRepository,
        IRepository<{RelatedEntity}, Guid> relatedRepository)
    {
        _parentRepository = parentRepository;
        _relatedRepository = relatedRepository;

        RuleFor(x => x.{ParentEntity}Id)
            .NotEmpty()
            .MustAsync(ParentExistsAsync)
            .WithMessage("{ParentEntity} not found");

        RuleFor(x => x.{RelatedEntity}Id)
            .NotEmpty()
            .MustAsync(RelatedExistsAsync)
            .WithMessage("{RelatedEntity} not found");
    }

    private async Task<bool> ParentExistsAsync(Guid id, CancellationToken ct)
    {
        return await _parentRepository.AnyAsync(e => e.Id == id);
    }

    private async Task<bool> RelatedExistsAsync(Guid id, CancellationToken ct)
    {
        return await _relatedRepository.AnyAsync(e => e.Id == id);
    }
}

Conditional Validation

When/Unless

RuleFor(x => x.CompanyName)
    .NotEmpty()
    .When(x => x.CustomerType == CustomerType.Business)
    .WithMessage("Company name is required for business customers");

RuleFor(x => x.TaxId)
    .NotEmpty()
    .Matches(@"^\d{9}$")
    .When(x => x.CustomerType == CustomerType.Business);

RuleFor(x => x.BirthDate)
    .NotEmpty()
    .Unless(x => x.CustomerType == CustomerType.Business);

Complex Conditional Logic

RuleFor(x => x.EndDate)
    .NotEmpty()
    .GreaterThan(x => x.StartDate)
    .When(x => x.IsRecurring && x.RecurrenceType != RecurrenceType.Indefinite);

When(x => x.PaymentMethod == PaymentMethod.CreditCard, () =>
{
    RuleFor(x => x.CardNumber).NotEmpty().CreditCard();
    RuleFor(x => x.ExpirationDate).NotEmpty().GreaterThan(DateTime.Today);
    RuleFor(x => x.CVV).NotEmpty().Length(3, 4);
});

When(x => x.PaymentMethod == PaymentMethod.BankTransfer, () =>
{
    RuleFor(x => x.BankAccountNumber).NotEmpty();
    RuleFor(x => x.RoutingNumber).NotEmpty();
});

Custom Validators

Reusable Property Validator

// Validators/PhoneNumberValidator.cs
public class PhoneNumberValidator<T> : PropertyValidator<T, string>
{
    public override string Name => "PhoneNumberValidator";

    public override bool IsValid(ValidationContext<T> context, string value)
    {
        if (string.IsNullOrEmpty(value))
            return true; // Let NotEmpty handle required check

        // E.164 format
        return Regex.IsMatch(value, @"^\+[1-9]\d{1,14}$");
    }

    protected override string GetDefaultMessageTemplate(string errorCode)
        => "'{PropertyName}' must be a valid phone number in E.164 format";
}

// Extension method for fluent usage
public static class ValidatorExtensions
{
    public static IRuleBuilderOptions<T, string> PhoneNumber<T>(
        this IRuleBuilder<T, string> ruleBuilder)
    {
        return ruleBuilder.SetValidator(new PhoneNumberValidator<T>());
    }
}

// Usage
RuleFor(x => x.Phone).PhoneNumber();

Async Custom Validator

public class UniqueEmailValidator<T> : AsyncPropertyValidator<T, string>
{
    private readonly IRepository<{Entity}, Guid> _repository;

    public UniqueEmailValidator(IRepository<{Entity}, Guid> repository)
    {
        _repository = repository;
    }

    public override string Name => "UniqueEmailValidator";

    public override async Task<bool> IsValidAsync(
        ValidationContext<T> context,
        string value,
        CancellationToken cancellation)
    {
        if (string.IsNullOrEmpty(value))
            return true;

        return !await _repository.AnyAsync(e => e.Email == value);
    }

    protected override string GetDefaultMessageTemplate(string errorCode)
        => "'{PropertyName}' must be unique";
}

Localization

Using ABP Localization

public class CreateUpdate{Entity}DtoValidator : AbstractValidator<CreateUpdate{Entity}Dto>
{
    private readonly IStringLocalizer<{ProjectName}Resource> _localizer;

    public CreateUpdate{Entity}DtoValidator(
        IStringLocalizer<{ProjectName}Resource> localizer)
    {
        _localizer = localizer;

        RuleFor(x => x.FirstName)
            .NotEmpty()
            .WithMessage(_localizer["Validation:FirstNameRequired"])
            .MaximumLength(100)
            .WithMessage(_localizer["Validation:FirstNameMaxLength", 100]);

        RuleFor(x => x.Email)
            .NotEmpty()
            .WithMessage(_localizer["Validation:EmailRequired"])
            .EmailAddress()
            .WithMessage(_localizer["Validation:EmailInvalid"]);
    }
}

// Localization JSON: Localization/{ProjectName}/en.json
{
  "Validation:FirstNameRequired": "First name is required",
  "Validation:FirstNameMaxLength": "First name cannot exceed {0} characters",
  "Validation:EmailRequired": "Email is required",
  "Validation:EmailInvalid": "Please enter a valid email address"
}

Validation in AppService

Manual Validation

public class {Entity}AppService : ApplicationService, I{Entity}AppService
{
    private readonly IValidator<CreateUpdate{Entity}Dto> _validator;

    public async Task<{Entity}Dto> CreateAsync(CreateUpdate{Entity}Dto input)
    {
        // Manual validation (if auto-validation disabled)
        var validationResult = await _validator.ValidateAsync(input);
        if (!validationResult.IsValid)
        {
            throw new AbpValidationException(
                validationResult.Errors
                    .Select(e => new ValidationResult(e.ErrorMessage, new[] { e.PropertyName }))
                    .ToList()
            );
        }

        // Proceed with creation
    }
}

Validation with Custom Context

public async Task<{Entity}Dto> UpdateAsync(Guid id, CreateUpdate{Entity}Dto input)
{
    var context = new ValidationContext<CreateUpdate{Entity}Dto>(input)
    {
        RootContextData =
        {
            ["EntityId"] = id
        }
    };

    var validationResult = await _validator.ValidateAsync(context);
    // ...
}

// In validator
private async Task<bool> BeUniqueEmailAsync(
    CreateUpdate{Entity}Dto dto,
    string email,
    ValidationContext<CreateUpdate{Entity}Dto> context,
    CancellationToken ct)
{
    var entityId = context.RootContextData.TryGetValue("EntityId", out var id)
        ? (Guid?)id
        : null;

    if (entityId.HasValue)
    {
        return !await _repository.AnyAsync(p => p.Email == email && p.Id != entityId);
    }

    return !await _repository.AnyAsync(p => p.Email == email);
}

Best Practices

  1. One validator per DTO - Keep validators focused
  2. Inject dependencies - Use constructor injection for repositories
  3. Use async for DB checks - Always use MustAsync for repository queries
  4. Localize messages - Use ABP localization for user-facing messages
  5. Reuse validators - Create custom validators for common patterns
  6. Conditional rules - Use When/Unless for context-dependent validation
  7. Order matters - Put cheap validations first, async last
  8. Handle null gracefully - Let NotEmpty handle required checks

Quality Checklist

  • Validator created for each input DTO
  • All required fields have NotEmpty() rule
  • String fields have MaximumLength() matching entity
  • Email fields use EmailAddress() validator
  • Foreign keys validated with MustAsync existence check
  • Unique constraints validated with repository check
  • Error messages are localized
  • Validators registered in module

Shared Knowledge

For foundational patterns, see the shared knowledge base:

TopicFileDescription
Naming conventionsknowledge/conventions/naming.mdValidator naming patterns
Validation exampleknowledge/examples/validation-chain.mdComplete validation examples
Folder structureknowledge/conventions/folder-structure.mdValidator file locations

Integration Points

This skill is used by:

  • abp-developer: DTO validator implementation
  • abp-code-reviewer: Validation pattern review
  • /generate:entity: Validator scaffolding

平台分布

Cursor

27.95%
按下载量换算53

Claude Code

21.08%
按下载量换算40

github-copilot

17.6%
按下载量换算33

mcpjam

13.51%
按下载量换算25

crush

8.39%
按下载量换算16

cline

3.78%
按下载量换算7

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

继续浏览同类 Skills