Gå til indholdet

Validering

Validering handler om at kontrollere, at data og input opfylder de forventede krav, før de bruges, gemmes eller sendes videre i et system. Formålet er at sikre dataintegritet, forhindre fejl og misbrug, forbedre sikkerheden og give en bedre brugeroplevelse ved at give klare fejlmeddelelser. Validering anvendes typisk på brugerinput i formularer, DTO’er og API-kald, ved databaseoperationer og i implementeringen af forretningsregler. Typiske valideringschecks inkluderer obligatoriske felter, formater (fx e-mail, telefon), numeriske intervaller, afhængigheder mellem felter og unikke værdier. Ved at centralisere valideringslogik bliver koden mere vedligeholdbar og konsistent.

Validering kan bruges på flere niveauer i en applikation:

  • Klient-side: Hurtig feedback til brugeren (fx JavaScript i en webformular). Dette forbedrer brugeroplevelsen, men kan ikke stå alene, da klientkode kan manipuleres.
  • Server-side: Den primære beskyttelse mod ugyldigt eller ondsindet input. Server-side validering sikrer konsistens og sikkerhed før data gemmes eller behandles.
  • Forretningslag: Implementer regler, der sikrer at forretningslogikens antagelser holdes (fx “en ordre må ikke bekræftes hvis lager er tomt”).
  • Database: Unikke constraints, referentiel integritet og kolonnebegrænsninger er en sidste sikkerhedsbarriere.

God validering følger princippet om “fail fast”: fejl opdages og rapporteres tidligt med klare, handlingsorienterede fejlbeskeder. Det er også en god praksis at holde valideringsregler tæt på de datatyper de gælder for (fx DTO’er eller domain-modeller) for at undgå duplikation og sikre konsistens.

Intro til FluentValidation

NuGet-pakken FluentValidation er et fleksibelt og kraftfuldt bibliotek til validering i .NET-applikationer, som lader udviklere definere valideringsregler på en klar og koncis måde. Med FluentValidation kan du let tilføje komplekse valideringslogikker til dine objekter uden at gøre din kode rodet eller svært at læse. Dette gør det ideelt for både små og store projekter, hvor vedligeholdelse og læsbarhed af kode er essentielt. I denne artikel vil vi gå igennem, hvordan du kan anvende FluentValidation i en konsolapplikation for at sikre, at dine data overholder de specificerede regler før de behandles eller gemmes.

Installation af FluentValidation

For at tage FluentValidation i brug, skal det først tilføjes til dit .NET-projekt. Det kan gøres gennem NuGet Package Manager i Visual Studio eller ved at køre nedenstående kommando i din terminal eller kommandoprompt:

dotnet add package FluentValidation

Efter installationen er du klar til at begynde at definere dine valideringsregler.

Oprettelse af Valideringsregler

Med FluentValidation defineres valideringsregler ved at oprette en klasse, der nedarver fra AbstractValidator<T>, hvor T er typen af det objekt, du ønsker at validere. Her er et simpelt eksempel på, hvordan en validator for en brugerklasse (User) kunne se ud:

using FluentValidation;

public class UserValidator : AbstractValidator<User>
{
    public UserValidator()
    {
        RuleFor(user => user.Name).NotEmpty().WithMessage("Navn må ikke være tomt");
        RuleFor(user => user.Email).NotEmpty().EmailAddress().WithMessage("Ugyldig e-mail adresse");
        RuleFor(user => user.Age).InclusiveBetween(18, 99).WithMessage("Alder skal være mellem 18 og 99");
    }
}

Mest brugte regler (kort oversigt)

Her er en oversigt over de mest almindelige regler du vil bruge i FluentValidation, sammen med en kort forklaring og et enkelt eksempel:

  • NotEmpty / NotNull: Sikrer at et felt ikke er tomt eller ikke-null. Brug NotEmpty for både streng og samlinger, NotNull hvis tom streng er acceptabel.
    • Eksempel: RuleFor(x => x.Name).NotEmpty().WithMessage("Navn må ikke være tomt");
  • Length / MinimumLength / MaximumLength: Tjekker strenglængde.
    • Eksempel: RuleFor(x => x.Password).Length(8, 100);
  • EmailAddress: Validerer e-mail format.
    • Eksempel: RuleFor(x => x.Email).EmailAddress();
  • Matches: Tjekker om en streng matcher et regex-mønster.
    • Eksempel: RuleFor(x => x.Phone).Matches(@"^\\+?[0-9]{8,15}$");
  • InclusiveBetween / GreaterThan / LessThan: Numeriske intervaller og sammenligninger.
    • Eksempel: RuleFor(x => x.Age).InclusiveBetween(18, 99);
  • Must: Bruges til brugerdefinerede valideringsfunktioner.
    • Eksempel: RuleFor(x => x.Username).Must(BeUnique).WithMessage("Brugernavn findes allerede");
  • When / Unless: Betingede regler der kun kører i bestemte sammenhænge.
    • Eksempel: RuleFor(x => x.Company).NotEmpty().When(x => x.IsCompany);
  • RuleForEach: Validering af elementer i en samling.
    • Eksempel: RuleForEach(x => x.Tags).NotEmpty();
  • SetValidator: Nestede objekter kan valideres ved at sætte en anden validator.
    • Eksempel: RuleFor(x => x.Address).SetValidator(new AddressValidator());

Disse regler kan kædes og kombineres for at bygge komplekse valideringsscenarier. Husk også at sætte meningsfulde fejlbeskeder via WithMessage og at gruppere regler hvis du har brug for at håndtere forskellige valideringskontekster.

Egne regler

Udover de indbyggede regler kan du definere egne, brugerdefinerede valideringer når forretningslogikken kræver det. Der er to almindelige mønstre:

  • Synkrone checks med Must: Bruges når valideringen kan udføres hurtigt og uden I/O (fx tjek mod andre felter eller statiske beregninger).
  • Asynkrone checks med MustAsync: Bruges når valideringen kræver I/O, fx opslag i en database eller et eksternt API.

Kort eksempel (synkront) - tjek at brugernavn ikke indeholder mellemrum:

RuleFor(x => x.Username)
    .NotEmpty()
    .Must(username => !username.Contains(' '))
    .WithMessage("Brugernavn må ikke indeholde mellemrum");

Kort eksempel (asynkront) - tjek at brugernavn er unikt i databasen:

public class UserValidator : AbstractValidator<User>
{
    public UserValidator(IUserRepository repo)
    {
        RuleFor(x => x.Username)
            .NotEmpty()
            .MustAsync(async (username, ct) => !await repo.ExistsAsync(username, ct))
            .WithMessage("Brugernavn findes allerede");
    }
}

Bemærk: Når du bruger asynkrone valideringer i fx ASP.NET Core, skal du sørge for at kalde de asynkrone valideringsmetoder korrekt (FluentValidation understøtter dette) og at din pipeline kan håndtere async validering.

Validering af Objekter

Når dine valideringsregler er defineret, kan du validere objekter ved at oprette en instans af din validator og kalde Validate metoden med objektet som argument. Hvis objektet ikke overholder de definerede regler, vil resultatet indeholde en liste over fejl.

Her er et eksempel på, hvordan et User objekt valideres:

var user = new User 
{
    Name = "John Doe",
    Email = "john.doe@example.com",
    Age = 25
};

var validator = new UserValidator();
var result = validator.Validate(user);

if (!result.IsValid)
{
    foreach (var failure in result.Errors)
    {
        Console.WriteLine($"Property {failure.PropertyName} failed validation. Error was: {failure.ErrorMessage}");
    }
}
else
{
    Console.WriteLine("User is valid");
}

Avancerede Scenarier

FluentValidation understøtter en lang række funktioner for mere avancerede valideringsscenarier, såsom tilstandsbetingede regler, brugerdefinerede valideringsmetoder, gruppevalidering, og meget mere. Dette giver dig stor fleksibilitet til at dække næsten enhver valideringsbehov du måtte have i dine applikationer.

FluentValidation er et kraftfuldt værktøj for .NET-udviklere, der kræver robust valideringslogik i deres applikationer. Med dens fleksible og udtryksfulde syntax kan komplekse valideringsregler defineres og vedligeholdes med minimal indsats. Ved at integrere FluentValidation i dine udviklingsprocesser, kan du sikre, at dine data er korrekte og valide, hvilket fører til mere stabile og pålidelige applikationer.

Opgaver

Spørgsmål til AI

For at få mest muligt ud af AI-værktøjer som ChatGPT, er det vigtigt at stille klare og præcise spørgsmål (og skabe det rigtige kontekst - se her). Her er nogle spørgsmål til denne side:

Grundlæggende spørgsmål til AI

  • Hvad er FluentValidation og hvorfor bruge det?
  • Hvordan installerer og konfigurerer jeg FluentValidation?
  • Hvordan skaber jeg validation rules?
  • Hvordan håndterer jeg kompleks validation?
  • Hvordan integrerer jeg FluentValidation med web APIs?
  • Hvad er fordele ved FluentValidation frem for DataAnnotations?