Dela via


Modellverifiering i ASP.NET Core MVC och Razor sidor

Den här artikeln beskriver hur du verifierar användarindata i en ASP.NET Core MVC- eller Razor Pages-app.

Visa eller ladda ned exempelkod (hur du laddar ned).

Validering i .NET 10

I .NET 10 har api:erna för enhetlig validering flyttats till Microsoft.Extensions.Validation NuGet-paketet. Den här ändringen gör validerings-API:erna tillgängliga utanför ASP.NET Core HTTP-scenarier.

Så här använder du API:erna Microsoft.Extensions.Validation :

  • Lägg till följande paketreferens:

    <PackageReference Include="Microsoft.Extensions.Validation" Version="10.0.0" />
    

    Funktionen är densamma men kräver nu en explicit paketreferens.

  • Registrera valideringstjänster med beroendeinmatning:

    builder.Services.AddValidation();
    

Modelltillstånd

Modelltillståndet representerar fel som kommer från två undersystem: modellbindning och modellverifiering. Fel som kommer från modellbindning är vanligtvis datakonverteringsfel. Till exempel anges ett "x" i ett heltalsfält. Modellverifiering sker efter modellbindning och rapporterar fel där data inte överensstämmer med affärsregler. Till exempel anges en 0 i ett fält som förväntar sig ett omdöme mellan 1 och 5.

Både modellbindning och modellverifiering sker innan en kontrollantåtgärd eller en Razor pages-hanteringsmetod körs. För webbappar är det appens ansvar att inspektera ModelState.IsValid och reagera på rätt sätt. Webbappar visar vanligtvis om sidan med ett felmeddelande, vilket visas i följande Razor exempel på en sida:

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movies.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

För ASP.NET Core MVC med kontrollanter och vyer visar följande exempel hur du kontrollerar ModelState.IsValid inuti en kontrollantåtgärd:

public async Task<IActionResult> Create(Movie movie)
{
    if (!ModelState.IsValid)
    {
        return View(movie);
    }

    _context.Movies.Add(movie);
    await _context.SaveChangesAsync();

    return RedirectToAction(nameof(Index));
}

Webb-API-kontrollanter behöver inte kontrollera ModelState.IsValid om de har attributet [ApiController]. I så fall returneras ett automatiskt HTTP 400-svar som innehåller felinformation när modelltillståndet är ogiltigt. Mer information finns i automatiska HTTP 400-svar.

Kör valideringen igen

Verifieringen är automatisk, men du kanske vill upprepa den manuellt. Du kan till exempel beräkna ett värde för en egenskap och vill köra valideringen igen när du har angett egenskapen till det beräknade värdet. Om du vill köra valideringen igen anropar ModelStateDictionary.ClearValidationState du för att rensa valideringen som är specifik för den modell som verifieras följt av TryValidateModel:

public async Task<IActionResult> OnPostTryValidateAsync()
{
    var modifiedReleaseDate = DateTime.Now.Date;
    Movie.ReleaseDate = modifiedReleaseDate;

    ModelState.ClearValidationState(nameof(Movie));
    if (!TryValidateModel(Movie, nameof(Movie)))
    {
        return Page();
    }

    _context.Movies.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Valideringsattribut

Med valideringsattribut kan du ange verifieringsregler för modellegenskaper. I följande exempel från exempelappen visas en modellklass som kommenteras med valideringsattribut. Attributet [ClassicMovie] är ett anpassat valideringsattribut och de andra är inbyggda. Det visas inte är [ClassicMovieWithClientValidator], som visar ett alternativt sätt att implementera ett anpassat attribut.

public class Movie
{
    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; } = null!;

    [ClassicMovie(1960)]
    [DataType(DataType.Date)]
    [Display(Name = "Release Date")]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; } = null!;

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    public Genre Genre { get; set; }

    public bool Preorder { get; set; }
}

Inbyggda attribut

En fullständig lista över valideringsattribut finns i System.ComponentModel.DataAnnotations namnområdet.

Felmeddelanden

Med valideringsattribut kan du ange felmeddelandet som ska visas för ogiltiga indata. Till exempel:

[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]

Internt anropar attributen String.Format med en platshållare för fältnamnet och ibland ytterligare platshållare. Till exempel:

[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]

När det tillämpas på en Name egenskap är felmeddelandet som skapades av föregående kod "Namnlängden måste vara mellan 6 och 8".

Information om vilka parametrar som skickas till String.Format för ett visst attributs felmeddelande finns i DataAnnotations-källkoden.

Använda JSON-egenskapsnamn i valideringsfel

När ett valideringsfel inträffar skapar modellverifiering som standard en ModelStateDictionary med egenskapsnamnet som felnyckel. Vissa appar, till exempel ensidiga applikationer, drar fördel av att använda JSON-egenskapsnamn för valideringsfel som genereras från webb-API:er. Följande kod konfigurerar validering för att använda SystemTextJsonValidationMetadataProvider för att använda JSON-egenskapsnamn:

using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers(options =>
{
    options.ModelMetadataDetailsProviders.Add(new SystemTextJsonValidationMetadataProvider());
});

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Följande kod konfigurerar validering för att använda NewtonsoftJsonValidationMetadataProvider för att använda JSON-egenskapsnamn när du använder Json.NET:

using Microsoft.AspNetCore.Mvc.NewtonsoftJson;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers(options =>
{
    options.ModelMetadataDetailsProviders.Add(new NewtonsoftJsonValidationMetadataProvider());
}).AddNewtonsoftJson();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Ett exempel på principen för att använda kamelhölje finns Program.cs på GitHub.

Referenstyper som inte kan ogiltigförklaras och attributet [Krävs]

Valideringssystemet behandlar icke-nullbara parametrar eller bundna egenskaper som om de hade ett [Required(AllowEmptyStrings = true)] attribut. Genom att aktivera Nullable kontexter börjar MVC implicit validera icke-nullbara egenskaper eller parametrar som om de hade tillskrivits [Required(AllowEmptyStrings = true)] attributet. Överväg följande kod:

public class Person
{
    public string Name { get; set; }
}

Om appen har skapats med <Nullable>enable</Nullable>resulterar ett värde som saknas för Name i ett JSON- eller formulärinlägg i ett valideringsfel. Detta kan verka motsägelsefullt eftersom [Required(AllowEmptyStrings = true)] attributet är underförstått, men detta är förväntat eftersom tomma strängar konverteras till null som standard. Använd en nullbar referenstyp för att tillåta att null- eller saknade värden anges för Name egenskapen:

public class Person
{
    public string? Name { get; set; }
}

Det här beteendet kan inaktiveras genom att SuppressImplicitRequiredAttributeForNonNullableReferenceTypes konfigurera i Program.cs:

builder.Services.AddControllers(
    options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

[Obligatorisk] validering på servern

På servern anses ett obligatoriskt värde saknas om egenskapen är null. Ett fält som inte är nullbart är alltid giltigt och [Required] attributets felmeddelande visas aldrig.

Modellbindningen för en icke-nullbar egenskap kan dock misslyckas, vilket resulterar i ett felmeddelande som The value '' is invalid. Om du vill ange ett anpassat felmeddelande för validering på serversidan av icke-nullbara typer har du följande alternativ:

  • Gör fältet null (till exempel decimal? i stället för decimal). Nullbar<T-värdetyper> behandlas som standardtyper som kan ogiltigas.

  • Ange standardfelmeddelandet som ska användas av modellbindningen, enligt följande exempel:

    builder.Services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                _ => "The field is required.");
        });
    
    builder.Services.AddSingleton
        <IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
    

    Mer information om modellbindningsfel som du kan ange standardmeddelanden för finns i DefaultModelBindingMessageProvider.

[Krävs] validering på klienten

Icke-nullbara typer och strängar hanteras på olika sätt på klienten jämfört med servern. På klienten:

  • Ett värde anses vara närvarande endast om indata anges för det. Därför hanterar validering på klientsidan icke-nullbara typer på samma sätt som nullbara typer.
  • Blanksteg i ett strängfält anses vara giltiga indata med den metod som krävs för jQuery-validering. Verifiering på serversidan anser att ett obligatoriskt strängfält är ogiltigt om endast blanksteg anges.

Som tidigare nämnts behandlas icke-nullbara typer som om de hade ett [Required(AllowEmptyStrings = true)] attribut. Det innebär att du får validering på klientsidan även om du inte tillämpar attributet [Required(AllowEmptyStrings = true)] . Men om du inte använder attributet får du ett standardfelmeddelande. Om du vill ange ett anpassat felmeddelande använder du attributet.

[Fjärr]-attribut

Attributet [Remote] implementerar validering på klientsidan som kräver att en metod anropas på servern för att avgöra om fältindata är giltiga. Appen kan till exempel behöva kontrollera om ett användarnamn redan används.

Så här implementerar du fjärrverifiering:

  1. Skapa en åtgärdsmetod för JavaScript att anropa. Fjärrmetoden jQuery-validering förväntar sig ett JSON-svar:

    • true innebär att indata är giltiga.
    • false, undefined, eller null betyder att indata är ogiltiga. Visa standardfelmeddelandet.
    • Alla andra strängar innebär att indata är ogiltiga. Visa strängen som ett anpassat felmeddelande.

    Här är ett exempel på en åtgärdsmetod som returnerar ett anpassat felmeddelande:

    [AcceptVerbs("GET", "POST")]
    public IActionResult VerifyEmail(string email)
    {
        if (!_userService.VerifyEmail(email))
        {
            return Json($"Email {email} is already in use.");
        }
    
        return Json(true);
    }
    
  2. I modellklassen kommenterar du egenskapen med ett [Remote] attribut som pekar på verifieringsåtgärdsmetoden, som du ser i följande exempel:

    [Remote(action: "VerifyEmail", controller: "Users")]
    public string Email { get; set; } = null!;
    

Verifiering på serversidan måste också implementeras för klienter som har inaktiverat JavaScript.

Ytterligare fält

Med AdditionalFields egenskapen för [Remote] attributet kan du verifiera kombinationer av fält mot data på servern. Om User modellen till exempel hade FirstName och LastName egenskaper kanske du vill kontrollera att inga befintliga användare redan har det namnparet. I följande exempel visas hur du använder AdditionalFields:

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(LastName))]
[Display(Name = "First Name")]
public string FirstName { get; set; } = null!;

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName))]
[Display(Name = "Last Name")]
public string LastName { get; set; } = null!;

AdditionalFields kan explicit sättas till strängarna "FirstName" och "LastName", men med hjälp av nameof-operatorn förenklas senare refaktorisering. Åtgärdsmetoden för den här valideringen måste acceptera både firstName argument och lastName argument:

[AcceptVerbs("GET", "POST")]
public IActionResult VerifyName(string firstName, string lastName)
{
    if (!_userService.VerifyName(firstName, lastName))
    {
        return Json($"A user named {firstName} {lastName} already exists.");
    }

    return Json(true);
}

När användaren anger ett för- eller efternamn gör JavaScript ett fjärranrop för att se om det par med namn har tagits.

Om du vill verifiera två eller flera ytterligare fält anger du dem som en kommaavgränsad lista. Om du till exempel vill lägga till en MiddleName egenskap i modellen anger du [Remote] attributet enligt följande exempel:

[Remote(action: "VerifyName", controller: "Users",
    AdditionalFields = nameof(FirstName) + "," + nameof(LastName))]
public string MiddleName { get; set; }

AdditionalFields, liksom alla attributargument, måste vara ett konstant uttryck. Använd därför inte en interpolerad sträng eller ett anrop Join för att initiera AdditionalFields.

Alternativ till inbyggda attribut

Om du behöver verifiering som inte tillhandahålls av inbyggda attribut kan du:

Anpassade attribut

För scenarier som de inbyggda valideringsattributen inte hanterar kan du skapa anpassade valideringsattribut. Skapa en klass som ärver från ValidationAttributeoch åsidosätt IsValid metoden.

Metoden IsValid accepterar ett objekt med namnet value, vilket är indata som ska verifieras. En överlagring accepterar också ett ValidationContext objekt som innehåller ytterligare information, till exempel modellinstansen som skapas av modellbindningen.

I följande exempel verifieras att utgivningsdatumet för en film i den klassiska genren inte är senare än ett angivet år. Attributet [ClassicMovie] :

  • Körs bara på servern.
  • För klassiska filmer validerar du utgivningsdatumet:
public class ClassicMovieAttribute : ValidationAttribute
{
    public ClassicMovieAttribute(int year)
        => Year = year;

    public int Year { get; }

    public string GetErrorMessage() =>
        $"Classic movies must have a release year no later than {Year}.";

    protected override ValidationResult? IsValid(
        object? value, ValidationContext validationContext)
    {
        var movie = (Movie)validationContext.ObjectInstance;
        var releaseYear = ((DateTime)value!).Year;

        if (movie.Genre == Genre.Classic && releaseYear > Year)
        {
            return new ValidationResult(GetErrorMessage());
        }

        return ValidationResult.Success;
    }
}

Variabeln movie i föregående exempel representerar ett Movie objekt som innehåller data från formuläröverföringen. När verifieringen misslyckas returneras ett ValidationResult med ett felmeddelande.

IValidatableObject

Föregående exempel fungerar endast med Movie typer. Ett annat alternativ för validering på klassnivå är att implementera IValidatableObject i modellklassen, som du ser i följande exempel:

public class ValidatableMovie : IValidatableObject
{
    private const int _classicYear = 1960;

    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; } = null!;

    [DataType(DataType.Date)]
    [Display(Name = "Release Date")]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; } = null!;

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    public Genre Genre { get; set; }

    public bool Preorder { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Genre == Genre.Classic && ReleaseDate.Year > _classicYear)
        {
            yield return new ValidationResult(
                $"Classic movies must have a release year no later than {_classicYear}.",
                new[] { nameof(ReleaseDate) });
        }
    }
}

Anpassad validering

Följande kod visar hur du lägger till ett modellfel när du har undersökt modellen:

if (Contact.Name == Contact.ShortName)
{
    ModelState.AddModelError("Contact.ShortName", 
                             "Short name can't be the same as Name.");
}

Följande kod implementerar valideringstestet i en kontrollant:

if (contact.Name == contact.ShortName)
{
    ModelState.AddModelError(nameof(contact.ShortName),
                             "Short name can't be the same as Name.");
}

Följande kod verifierar att telefonnummer och e-post är unika:

public async Task<IActionResult> OnPostAsync()
{
    // Attach Validation Error Message to the Model on validation failure.          

    if (Contact.Name == Contact.ShortName)
    {
        ModelState.AddModelError("Contact.ShortName", 
                                 "Short name can't be the same as Name.");
    }

    if (_context.Contact.Any(i => i.PhoneNumber == Contact.PhoneNumber))
    {
        ModelState.AddModelError("Contact.PhoneNumber",
                                  "The Phone number is already in use.");
    }
    if (_context.Contact.Any(i => i.Email == Contact.Email))
    {
        ModelState.AddModelError("Contact.Email", "The Email is already in use.");
    }

    if (!ModelState.IsValid || _context.Contact == null || Contact == null)
    {
        // if model is invalid, return the page with the model state errors.
        return Page();
    }
    _context.Contact.Add(Contact);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Följande kod implementerar valideringstestet i en kontrollant:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,ShortName,Email,PhoneNumber")] Contact contact)
{
    // Attach Validation Error Message to the Model on validation failure.
    if (contact.Name == contact.ShortName)
    {
        ModelState.AddModelError(nameof(contact.ShortName),
                                 "Short name can't be the same as Name.");
    }

    if (_context.Contact.Any(i => i.PhoneNumber == contact.PhoneNumber))
    {
        ModelState.AddModelError(nameof(contact.PhoneNumber),
                                  "The Phone number is already in use.");
    }
    if (_context.Contact.Any(i => i.Email == contact.Email))
    {
        ModelState.AddModelError(nameof(contact.Email), "The Email is already in use.");
    }

    if (ModelState.IsValid)
    {
        _context.Add(contact);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }
    return View(contact);
}

Kontrollen av ett unikt telefonnummer eller e-postmeddelande görs vanligtvis också med fjärrvalidering.

ValidationResult

Överväg följande anpassade ValidateNameAttribute:

public class ValidateNameAttribute : ValidationAttribute
{
    public ValidateNameAttribute()
    {
        const string defaultErrorMessage = "Error with Name";
        ErrorMessage ??= defaultErrorMessage;
    }

    protected override ValidationResult? IsValid(object? value,
                                         ValidationContext validationContext)
    {
        if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
        {
            return new ValidationResult("Name is required.");
        }

        if (value.ToString()!.ToLower().Contains("zz"))
        {

            return new ValidationResult(
                        FormatErrorMessage(validationContext.DisplayName));
        }

        return ValidationResult.Success;
    }
}

I följande kod tillämpas det anpassade attributet [ValidateName] :

public class Contact
{
    public Guid Id { get; set; }

    [ValidateName(ErrorMessage = "Name must not contain `zz`")] 
    public string? Name { get; set; }
    public string? Email { get; set; }
    public string? PhoneNumber { get; set; }
}

När modellen innehåller zz returneras en ny ValidationResult.

Nodvalidering på toppnivå

Noder på den översta nivån är:

  • Åtgärdsparameter
  • Egenskaper för styrenhet
  • Sidhanterarparametrar
  • Egenskaper för sidmodell

Modellbundna noder på den översta nivån verifieras utöver validering av modellegenskaper. I följande exempel från exempelappenVerifyPhone använder RegularExpressionAttribute metoden för att verifiera åtgärdsparameternphone:

[AcceptVerbs("GET", "POST")]
public IActionResult VerifyPhone(
    [RegularExpression(@"^\d{3}-\d{3}-\d{4}$")] string phone)
{
    if (!ModelState.IsValid)
    {
        return Json($"Phone {phone} has an invalid format. Format: ###-###-####");
    }

    return Json(true);
}

Noder på högsta nivån kan använda BindRequiredAttribute med valideringsattribut. I följande exempel från exempelappenCheckAge anger metoden att parametern age måste vara bunden från frågesträngen när formuläret skickas:

[HttpPost]
public IActionResult CheckAge([BindRequired, FromQuery] int age)
{

På sidan Kontrollera ålder (CheckAge.cshtml) finns det två formulär. Det första formuläret skickar ett Age värde för 99 som en frågesträngsparameter: https://localhost:5001/Users/CheckAge?Age=99.

När en korrekt formaterad age parameter från frågesträngen skickas verifieras formuläret.

Det andra formuläret på sidan Kontrollera ålder skickar Age värdet i begärans brödtext, och verifieringen misslyckas. Bindningen misslyckas eftersom parametern age måste komma från en frågesträng.

Maximalt antal fel

Verifieringen stoppas när det maximala antalet fel har nåtts (200 som standard). Du kan konfigurera det här numret med följande kod i Program.cs:

builder.Services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.MaxModelValidationErrors = 50;
        options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
            _ => "The field is required.");
    });

builder.Services.AddSingleton
    <IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();

Maximum rekursion

ValidationVisitor bläddrar igenom objektdiagrammet för modellen som verifieras. För modeller som är djupa eller oändligt rekursiva kan validering resultera i stacköverflöde. MvcOptions.MaxValidationDepth ger ett sätt att stoppa valideringen tidigt om besökarens rekursion överskrider ett konfigurerat djup. Standardvärdet MvcOptions.MaxValidationDepth för är 32.

Automatisk kortslutning

Valideringen kortsluts automatiskt (hoppas över) om modelldiagrammet inte kräver validering. Objekt som körningen hoppar över validering för inkluderar samlingar av primitiver (till exempel byte[], string[], Dictionary<string, string>) och komplexa objektdiagram som inte har några validatorer.

Validering på klientsidan

Validering på klientsidan förhindrar överföring tills formuläret är giltigt. Knappen Skicka kör JavaScript som antingen skickar formuläret eller visar felmeddelanden.

Validering på klientsidan undviker en onödig förfrågan till servern när det finns felaktiga uppgifter i ett formulär. Följande skriptreferenser i _Layout.cshtml och _ValidationScriptsPartial.cshtml stöder validering på klientsidan:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.3/jquery.validate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.12/jquery.validate.unobtrusive.js"></script>

JQuery Unobtrusive Validation-skriptet är ett anpassat Microsoft-klientdelsbibliotek som bygger på det populära plugin-programmet jQuery Validation. Utan jQuery Unobtrusive Validation skulle du behöva koda samma valideringslogik på två platser: en gång i valideringsattributen på serversidan för modellegenskaper och sedan igen i skript på klientsidan. I stället använder Tag Helpers och HTML-hjälparna valideringsattributen och skriver metadata från modellegenskaper för att återge HTML 5-attribut data- för de formulärelement som behöver validering. jQuery Diskret validering tolkar attributen data- och vidarebefordrar logiken till jQuery Validation, vilket effektivt "kopierar" valideringens logik på serversidan till klienten. Du kan visa valideringsfel på klienten med hjälp av tagghjälpare som visas här:

<div class="form-group">
    <label asp-for="Movie.ReleaseDate" class="control-label"></label>
    <input asp-for="Movie.ReleaseDate" class="form-control" />
    <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
</div>

De föregående tagghjälparna återger följande HTML:

<div class="form-group">
    <label class="control-label" for="Movie_ReleaseDate">Release Date</label>
    <input class="form-control" type="date" data-val="true"
        data-val-required="The Release Date field is required."
        id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
    <span class="text-danger field-validation-valid"
        data-valmsg-for="Movie.ReleaseDate" data-valmsg-replace="true"></span>
</div>

Observera att attributen data- i HTML-utdata motsvarar verifieringsattributen Movie.ReleaseDate för egenskapen. Attributet data-val-required innehåller ett felmeddelande som visas om användaren inte fyller i publiceringsdatumfältet. jQuery Unobtrusive Validation skickar det här värdet till metoden jQuery Validation required(), som sedan visar meddelandet i det medföljande <span-elementet> .

Verifiering av datatyp baseras på .NET-typen för en egenskap, såvida den inte åsidosätts av ett [DataType]- attribut. Webbläsare har sina egna standardfelmeddelanden, men jQuery Validation Unobtrusive Validation-paketet kan åsidosätta dessa meddelanden. [DataType] med attribut och underklasser som [EmailAddress] kan du ange felmeddelandet.

Oansträngd validering

Information om diskret validering finns i det här GitHub-problemet.

Lägga till validering i dynamiska formulär

jQuery Unobtrusive Validation förmedlar valideringslogik och parametrar till jQuery Validation när sidan först läses in. Valideringen fungerar därför inte automatiskt på dynamiskt genererade formulär. Om du vill aktivera validering ber du jQuery Unobtrusive Validation att parsa det dynamiska formuläret direkt efter att du har skapat det. Följande kod konfigurerar till exempel validering på klientsidan i ett formulär som lagts till via AJAX.

$.get({
    url: "https://url/that/returns/a/form",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add form. " + errorThrown);
    },
    success: function(newFormHTML) {
        var container = document.getElementById("form-container");
        container.insertAdjacentHTML("beforeend", newFormHTML);
        var forms = container.getElementsByTagName("form");
        var newForm = forms[forms.length - 1];
        $.validator.unobtrusive.parse(newForm);
    }
})

Metoden $.validator.unobtrusive.parse() accepterar en jQuery-väljare för sitt enda argument. Den här metoden instruerar jQuery Unobtrusive Validation att parsa attributen för formulär inom den valda selektorn. Värdena för dessa attribut skickas sedan till plugin-programmet jQuery Validation.

Lägga till validering i dynamiska kontroller

Metoden $.validator.unobtrusive.parse() fungerar i ett helt formulär, inte på enskilda dynamiskt genererade kontroller, till exempel <input> och <select/>. Om du vill skriva om formuläret tar du bort valideringsdata som lades till när formuläret parsades tidigare, som du ser i följande exempel:

$.get({
    url: "https://url/that/returns/a/control",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add control. " + errorThrown);
    },
    success: function(newInputHTML) {
        var form = document.getElementById("my-form");
        form.insertAdjacentHTML("beforeend", newInputHTML);
        $(form).removeData("validator")    // Added by jQuery Validation
               .removeData("unobtrusiveValidation");   // Added by jQuery Unobtrusive Validation
        $.validator.unobtrusive.parse(form);
    }
})

Anpassad validering på klientsidan

Anpassad validering på klientsidan görs genom att generera data- HTML-attribut som fungerar med ett anpassat jQuery-valideringskort. Följande exempeladapterkod skrevs för [ClassicMovie] och [ClassicMovieWithClientValidator]-attributen som presenterades tidigare i den här artikeln:

$.validator.addMethod('classicmovie', function (value, element, params) {
    var genre = $(params[0]).val(), year = params[1], date = new Date(value);

    // The Classic genre has a value of '0'.
    if (genre && genre.length > 0 && genre[0] === '0') {
        // The release date for a Classic is valid if it's no greater than the given year.
        return date.getUTCFullYear() <= year;
    }

    return true;
});

$.validator.unobtrusive.adapters.add('classicmovie', ['year'], function (options) {
    var element = $(options.form).find('select#Movie_Genre')[0];

    options.rules['classicmovie'] = [element, parseInt(options.params['year'])];
    options.messages['classicmovie'] = options.message;
});

Information om hur du skriver adaptrar finns i dokumentationen för jQuery-validering.

Användningen av en adapter för ett givet fält utlöses av data- attribut som:

  • Flagga fältet som föremål för validering (data-val="true").
  • Identifiera ett namn på verifieringsregeln och felmeddelandetexten (till exempel data-val-rulename="Error message.").
  • Ange eventuella ytterligare parametrar som validatorn behöver (till exempel data-val-rulename-param1="value").

I följande exempel visas attributen data- för exempelappensClassicMovie attribut:

<input class="form-control" type="date"
    data-val="true"
    data-val-classicmovie="Classic movies must have a release year no later than 1960."
    data-val-classicmovie-year="1960"
    data-val-required="The Release Date field is required."
    id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">

Som tidigare nämnts använder Tag Helpers och HTML-hjälpare information från valideringsattribut för att återge data- attribut. Det finns två alternativ för att skriva kod som resulterar i skapandet av anpassade data- HTML-attribut:

AttributeAdapter för validering på klientsidan

Den här metoden för renderingsattribut data- i HTML används av ClassicMovie attributet i exempelappen. Så här lägger du till klientverifiering med hjälp av den här metoden:

  1. Skapa en adapterklass för det anpassade valideringsattributet. Härled klassen från AttributeAdapterBase<TAttribute>. Skapa en AddValidation-metod som lägger till data--attribut till de renderingar som visas i detta exempel.

    public class ClassicMovieAttributeAdapter : AttributeAdapterBase<ClassicMovieAttribute>
    {
        public ClassicMovieAttributeAdapter(
            ClassicMovieAttribute attribute, IStringLocalizer? stringLocalizer)
            : base(attribute, stringLocalizer)
        {
    
        }
    
        public override void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage(context));
    
            var year = Attribute.Year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
    
        public override string GetErrorMessage(ModelValidationContextBase validationContext)
            => Attribute.GetErrorMessage();
    }
    
  2. Skapa en adapterproviderklass som implementerar IValidationAttributeAdapterProvider. GetAttributeAdapter I metoden skickar du det anpassade attributet till adapterns konstruktor, som du ser i det här exemplet:

    public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider
    {
        private readonly IValidationAttributeAdapterProvider baseProvider =
            new ValidationAttributeAdapterProvider();
    
        public IAttributeAdapter? GetAttributeAdapter(
            ValidationAttribute attribute, IStringLocalizer? stringLocalizer)
        {
            if (attribute is ClassicMovieAttribute classicMovieAttribute)
            {
                return new ClassicMovieAttributeAdapter(classicMovieAttribute, stringLocalizer);
            }
    
            return baseProvider.GetAttributeAdapter(attribute, stringLocalizer);
        }
    }
    
  3. Registrera adapterprovidern för DI i Program.cs:

    builder.Services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                _ => "The field is required.");
        });
    
    builder.Services.AddSingleton
        <IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
    

IClientModelValidator för validering på klientsidan

Den här metoden för renderingsattribut data- i HTML används av ClassicMovieWithClientValidator attributet i exempelappen. Så här lägger du till klientverifiering med hjälp av den här metoden:

  • I det anpassade valideringsattributet implementerar du IClientModelValidator gränssnittet och skapar en AddValidation metod. AddValidation I metoden lägger du till data- attribut för validering, som du ser i följande exempel:

    public class ClassicMovieWithClientValidatorAttribute :
        ValidationAttribute, IClientModelValidator
    {
        public ClassicMovieWithClientValidatorAttribute(int year)
            => Year = year;
    
        public int Year { get; }
    
        public void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage());
    
            var year = Year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
    
        public string GetErrorMessage() =>
            $"Classic movies must have a release year no later than {Year}.";
    
        protected override ValidationResult? IsValid(
            object? value, ValidationContext validationContext)
        {
            var movie = (Movie)validationContext.ObjectInstance;
            var releaseYear = ((DateTime)value!).Year;
    
            if (movie.Genre == Genre.Classic && releaseYear > Year)
            {
                return new ValidationResult(GetErrorMessage());
            }
    
            return ValidationResult.Success;
        }
    
        private static bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
        {
            if (attributes.ContainsKey(key))
            {
                return false;
            }
    
            attributes.Add(key, value);
            return true;
        }
    }
    

Inaktivera validering på klientsidan

Följande kod inaktiverar klientverifiering på Razor sidor:

builder.Services.AddRazorPages()
    .AddViewOptions(options =>
    {
        options.HtmlHelperOptions.ClientValidationEnabled = false;
    });

Andra alternativ för att inaktivera validering på klientsidan:

  • Kommentera ut referensen till _ValidationScriptsPartial i alla .cshtml filer.
  • Ta bort innehållet i filen Pages\Shared_ValidationScriptsPartial.cshtml .

Föregående metod förhindrar inte validering på klientsidan av ASP.NET Core-klassbibliotek IdentityRazor . Mer information finns i Scaffold Identity i ASP.NET Core-projekt.

Probleminformation

Probleminformation är inte det enda svarsformatet som beskriver ett HTTP API-fel, men de används ofta för att rapportera fel för HTTP-API:er.

Tjänsten probleminformation implementerar IProblemDetailsService-gränssnittet, som har stöd för att skapa probleminformation i ASP.NET Core. AddProblemDetails(IServiceCollection)-tilläggsmetoden på IServiceCollection registrerar standardimplementeringen IProblemDetailsService.

I ASP.NET Core-appar genererar följande mellanprogramvara HTTP-svar med probleminformation när AddProblemDetails anropas, förutom när HTTP-huvudet för Accept-begäran inte innehåller någon av de innehållstyper som stöds av den registrerade IProblemDetailsWriter (standard: application/json):

Ytterligare resurser

Den här artikeln beskriver hur du verifierar användarindata i en ASP.NET Core MVC- eller Razor Pages-app.

Visa eller ladda ned exempelkod (hur du laddar ned).

Modelltillstånd

Modelltillståndet representerar fel som kommer från två undersystem: modellbindning och modellverifiering. Fel som kommer från modellbindning är vanligtvis datakonverteringsfel. Till exempel anges ett "x" i ett heltalsfält. Modellverifiering sker efter modellbindning och rapporterar fel där data inte överensstämmer med affärsregler. Till exempel anges en 0 i ett fält som förväntar sig ett omdöme mellan 1 och 5.

Både modellbindning och modellverifiering sker innan en kontrollantåtgärd eller en Razor pages-hanteringsmetod körs. För webbappar är det appens ansvar att inspektera ModelState.IsValid och reagera på rätt sätt. Webbappar visar vanligtvis om sidan med ett felmeddelande, vilket visas i följande Razor exempel på en sida:

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movies.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

För ASP.NET Core MVC med kontrollanter och vyer visar följande exempel hur du kontrollerar ModelState.IsValid inuti en kontrollantåtgärd:

public async Task<IActionResult> Create(Movie movie)
{
    if (!ModelState.IsValid)
    {
        return View(movie);
    }

    _context.Movies.Add(movie);
    await _context.SaveChangesAsync();

    return RedirectToAction(nameof(Index));
}

Webb-API-kontrollanter behöver inte kontrollera ModelState.IsValid om de har attributet [ApiController]. I så fall returneras ett automatiskt HTTP 400-svar som innehåller felinformation när modelltillståndet är ogiltigt. Mer information finns i automatiska HTTP 400-svar.

Kör valideringen igen

Verifieringen är automatisk, men du kanske vill upprepa den manuellt. Du kan till exempel beräkna ett värde för en egenskap och vill köra valideringen igen när du har angett egenskapen till det beräknade värdet. Om du vill köra valideringen igen anropar ModelStateDictionary.ClearValidationState du för att rensa valideringen som är specifik för den modell som verifieras följt av TryValidateModel:

public async Task<IActionResult> OnPostTryValidateAsync()
{
    var modifiedReleaseDate = DateTime.Now.Date;
    Movie.ReleaseDate = modifiedReleaseDate;

    ModelState.ClearValidationState(nameof(Movie));
    if (!TryValidateModel(Movie, nameof(Movie)))
    {
        return Page();
    }

    _context.Movies.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Valideringsattribut

Med valideringsattribut kan du ange verifieringsregler för modellegenskaper. I följande exempel från exempelappen visas en modellklass som kommenteras med valideringsattribut. Attributet [ClassicMovie] är ett anpassat valideringsattribut och de andra är inbyggda. Det visas inte är [ClassicMovieWithClientValidator], som visar ett alternativt sätt att implementera ett anpassat attribut.

public class Movie
{
    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; } = null!;

    [ClassicMovie(1960)]
    [DataType(DataType.Date)]
    [Display(Name = "Release Date")]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; } = null!;

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    public Genre Genre { get; set; }

    public bool Preorder { get; set; }
}

Inbyggda attribut

Här är några av de inbyggda valideringsattributen:

En fullständig lista över valideringsattribut finns i System.ComponentModel.DataAnnotations namnområdet.

Felmeddelanden

Med valideringsattribut kan du ange felmeddelandet som ska visas för ogiltiga indata. Till exempel:

[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]

Internt anropar attributen String.Format med en platshållare för fältnamnet och ibland ytterligare platshållare. Till exempel:

[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]

När det tillämpas på en Name egenskap är felmeddelandet som skapades av föregående kod "Namnlängden måste vara mellan 6 och 8".

Information om vilka parametrar som skickas till String.Format för ett visst attributs felmeddelande finns i DataAnnotations-källkoden.

Referenstyper som inte kan ogiltigförklaras och attributet [Krävs]

Valideringssystemet behandlar icke-nullbara parametrar eller bundna egenskaper som om de hade ett [Required(AllowEmptyStrings = true)] attribut. Genom att aktivera Nullable kontexter börjar MVC implicit validera icke-nullbara egenskaper på icke-generiska typer eller parametrar som om de hade tillskrivits [Required(AllowEmptyStrings = true)] attributet. Överväg följande kod:

public class Person
{
    public string Name { get; set; }
}

Om appen har skapats med <Nullable>enable</Nullable>resulterar ett värde som saknas för Name i ett JSON- eller formulärinlägg i ett valideringsfel. Använd en nullbar referenstyp för att tillåta att null- eller saknade värden anges för Name egenskapen:

public class Person
{
    public string? Name { get; set; }
}

Det här beteendet kan inaktiveras genom att SuppressImplicitRequiredAttributeForNonNullableReferenceTypes konfigurera i Program.cs:

builder.Services.AddControllers(
    options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

Icke-nullbara egenskaper för generiska typer och attributet [Krävs]

Egenskaper som inte är nullbara för generiska typer måste innehålla [Required] attributet när typen krävs. I följande kod TestRequired krävs inte:

public class WeatherForecast<T>
{
    public string TestRequired { get; set; } = null!;
    public T? Inner { get; set; }
}

I följande kod TestRequired markeras uttryckligen som obligatorisk:

using System.ComponentModel.DataAnnotations;

public class WeatherForecast<T>
{
    [Required]
    public string TestRequired { get; set; } = null!;
    public T? Inner { get; set; }
}

[Obligatorisk] validering på servern

På servern anses ett obligatoriskt värde saknas om egenskapen är null. Ett fält som inte är nullbart är alltid giltigt och [Required] attributets felmeddelande visas aldrig.

Modellbindningen för en icke-nullbar egenskap kan dock misslyckas, vilket resulterar i ett felmeddelande som The value '' is invalid. Om du vill ange ett anpassat felmeddelande för validering på serversidan av icke-nullbara typer har du följande alternativ:

  • Gör fältet null (till exempel decimal? i stället för decimal). Nullbar<T-värdetyper> behandlas som standardtyper som kan ogiltigas.

  • Ange standardfelmeddelandet som ska användas av modellbindningen, enligt följande exempel:

    builder.Services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                _ => "The field is required.");
        });
    
    builder.Services.AddSingleton
        <IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
    

    Mer information om modellbindningsfel som du kan ange standardmeddelanden för finns i DefaultModelBindingMessageProvider.

[Krävs] validering på klienten

Icke-nullbara typer och strängar hanteras på olika sätt på klienten jämfört med servern. På klienten:

  • Ett värde anses vara närvarande endast om indata anges för det. Därför hanterar validering på klientsidan icke-nullbara typer på samma sätt som nullbara typer.
  • Blanksteg i ett strängfält anses vara giltiga indata med den metod som krävs för jQuery-validering. Verifiering på serversidan anser att ett obligatoriskt strängfält är ogiltigt om endast blanksteg anges.

Som tidigare nämnts behandlas icke-nullbara typer som om de hade ett [Required(AllowEmptyStrings = true)] attribut. Det innebär att du får validering på klientsidan även om du inte tillämpar attributet [Required(AllowEmptyStrings = true)] . Men om du inte använder attributet får du ett standardfelmeddelande. Om du vill ange ett anpassat felmeddelande använder du attributet.

[Fjärr]-attribut

Attributet [Remote] implementerar validering på klientsidan som kräver att en metod anropas på servern för att avgöra om fältindata är giltiga. Appen kan till exempel behöva kontrollera om ett användarnamn redan används.

Så här implementerar du fjärrverifiering:

  1. Skapa en åtgärdsmetod för JavaScript att anropa. Fjärrmetoden jQuery-validering förväntar sig ett JSON-svar:

    • true innebär att indata är giltiga.
    • false, undefined, eller null betyder att indata är ogiltiga. Visa standardfelmeddelandet.
    • Alla andra strängar innebär att indata är ogiltiga. Visa strängen som ett anpassat felmeddelande.

    Här är ett exempel på en åtgärdsmetod som returnerar ett anpassat felmeddelande:

    [AcceptVerbs("GET", "POST")]
    public IActionResult VerifyEmail(string email)
    {
        if (!_userService.VerifyEmail(email))
        {
            return Json($"Email {email} is already in use.");
        }
    
        return Json(true);
    }
    
  2. I modellklassen kommenterar du egenskapen med ett [Remote] attribut som pekar på verifieringsåtgärdsmetoden, som du ser i följande exempel:

    [Remote(action: "VerifyEmail", controller: "Users")]
    public string Email { get; set; } = null!;
    

Ytterligare fält

Med AdditionalFields egenskapen för [Remote] attributet kan du verifiera kombinationer av fält mot data på servern. Om User modellen till exempel hade FirstName och LastName egenskaper kanske du vill kontrollera att inga befintliga användare redan har det namnparet. I följande exempel visas hur du använder AdditionalFields:

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(LastName))]
[Display(Name = "First Name")]
public string FirstName { get; set; } = null!;

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName))]
[Display(Name = "Last Name")]
public string LastName { get; set; } = null!;

AdditionalFields kan explicit sättas till strängarna "FirstName" och "LastName", men med hjälp av nameof-operatorn förenklas senare refaktorisering. Åtgärdsmetoden för den här valideringen måste acceptera både firstName argument och lastName argument:

[AcceptVerbs("GET", "POST")]
public IActionResult VerifyName(string firstName, string lastName)
{
    if (!_userService.VerifyName(firstName, lastName))
    {
        return Json($"A user named {firstName} {lastName} already exists.");
    }

    return Json(true);
}

När användaren anger ett för- eller efternamn gör JavaScript ett fjärranrop för att se om det par med namn har tagits.

Om du vill verifiera två eller flera ytterligare fält anger du dem som en kommaavgränsad lista. Om du till exempel vill lägga till en MiddleName egenskap i modellen anger du [Remote] attributet enligt följande exempel:

[Remote(action: "VerifyName", controller: "Users",
    AdditionalFields = nameof(FirstName) + "," + nameof(LastName))]
public string MiddleName { get; set; }

AdditionalFields, liksom alla attributargument, måste vara ett konstant uttryck. Använd därför inte en interpolerad sträng eller ett anrop Join för att initiera AdditionalFields.

Alternativ till inbyggda attribut

Om du behöver verifiering som inte tillhandahålls av inbyggda attribut kan du:

Anpassade attribut

För scenarier som de inbyggda valideringsattributen inte hanterar kan du skapa anpassade valideringsattribut. Skapa en klass som ärver från ValidationAttributeoch åsidosätt IsValid metoden.

Metoden IsValid accepterar ett objekt med namnet value, vilket är indata som ska verifieras. En överlagring accepterar också ett ValidationContext objekt som innehåller ytterligare information, till exempel modellinstansen som skapas av modellbindningen.

I följande exempel verifieras att utgivningsdatumet för en film i den klassiska genren inte är senare än ett angivet år. Attributet [ClassicMovie] :

  • Körs bara på servern.
  • För klassiska filmer validerar du utgivningsdatumet:
public class ClassicMovieAttribute : ValidationAttribute
{
    public ClassicMovieAttribute(int year)
        => Year = year;

    public int Year { get; }

    public string GetErrorMessage() =>
        $"Classic movies must have a release year no later than {Year}.";

    protected override ValidationResult? IsValid(
        object? value, ValidationContext validationContext)
    {
        var movie = (Movie)validationContext.ObjectInstance;
        var releaseYear = ((DateTime)value!).Year;

        if (movie.Genre == Genre.Classic && releaseYear > Year)
        {
            return new ValidationResult(GetErrorMessage());
        }

        return ValidationResult.Success;
    }
}

Variabeln movie i föregående exempel representerar ett Movie objekt som innehåller data från formuläröverföringen. När verifieringen misslyckas returneras ett ValidationResult med ett felmeddelande.

IValidatableObject

Föregående exempel fungerar endast med Movie typer. Ett annat alternativ för validering på klassnivå är att implementera IValidatableObject i modellklassen, som du ser i följande exempel:

public class ValidatableMovie : IValidatableObject
{
    private const int _classicYear = 1960;

    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; } = null!;

    [DataType(DataType.Date)]
    [Display(Name = "Release Date")]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; } = null!;

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    public Genre Genre { get; set; }

    public bool Preorder { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Genre == Genre.Classic && ReleaseDate.Year > _classicYear)
        {
            yield return new ValidationResult(
                $"Classic movies must have a release year no later than {_classicYear}.",
                new[] { nameof(ReleaseDate) });
        }
    }
}

Nodvalidering på toppnivå

Noder på den översta nivån är:

  • Åtgärdsparameter
  • Egenskaper för styrenhet
  • Sidhanterarparametrar
  • Egenskaper för sidmodell

Modellbundna noder på den översta nivån verifieras utöver validering av modellegenskaper. I följande exempel från exempelappenVerifyPhone använder RegularExpressionAttribute metoden för att verifiera åtgärdsparameternphone:

[AcceptVerbs("GET", "POST")]
public IActionResult VerifyPhone(
    [RegularExpression(@"^\d{3}-\d{3}-\d{4}$")] string phone)
{
    if (!ModelState.IsValid)
    {
        return Json($"Phone {phone} has an invalid format. Format: ###-###-####");
    }

    return Json(true);
}

Noder på högsta nivån kan använda BindRequiredAttribute med valideringsattribut. I följande exempel från exempelappenCheckAge anger metoden att parametern age måste vara bunden från frågesträngen när formuläret skickas:

[HttpPost]
public IActionResult CheckAge([BindRequired, FromQuery] int age)
{

På sidan Kontrollera ålder (CheckAge.cshtml) finns det två formulär. Det första formuläret skickar ett Age värde för 99 som en frågesträngsparameter: https://localhost:5001/Users/CheckAge?Age=99.

När en korrekt formaterad age parameter från frågesträngen skickas verifieras formuläret.

Det andra formuläret på sidan Kontrollera ålder skickar Age värdet i begärans brödtext, och verifieringen misslyckas. Bindningen misslyckas eftersom parametern age måste komma från en frågesträng.

Maximalt antal fel

Verifieringen stoppas när det maximala antalet fel har nåtts (200 som standard). Du kan konfigurera det här numret med följande kod i Program.cs:

builder.Services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.MaxModelValidationErrors = 50;
        options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
            _ => "The field is required.");
    });

builder.Services.AddSingleton
    <IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();

Maximum rekursion

ValidationVisitor bläddrar igenom objektdiagrammet för modellen som verifieras. För modeller som är djupa eller oändligt rekursiva kan validering resultera i stacköverflöde. MvcOptions.MaxValidationDepth ger ett sätt att stoppa valideringen tidigt om besökarens rekursion överskrider ett konfigurerat djup. Standardvärdet MvcOptions.MaxValidationDepth för är 32.

Automatisk kortslutning

Valideringen kortsluts automatiskt (hoppas över) om modelldiagrammet inte kräver validering. Objekt som körningen hoppar över validering för inkluderar samlingar av primitiver (till exempel byte[], string[], Dictionary<string, string>) och komplexa objektdiagram som inte har några validatorer.

Validering på klientsidan

Validering på klientsidan förhindrar överföring tills formuläret är giltigt. Knappen Skicka kör JavaScript som antingen skickar formuläret eller visar felmeddelanden.

Validering på klientsidan undviker en onödig förfrågan till servern när det finns felaktiga uppgifter i ett formulär. Följande skriptreferenser i _Layout.cshtml och _ValidationScriptsPartial.cshtml stöder validering på klientsidan:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.3/jquery.validate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.12/jquery.validate.unobtrusive.js"></script>

JQuery Unobtrusive Validation-skriptet är ett anpassat Microsoft-klientdelsbibliotek som bygger på det populära plugin-programmet jQuery Validation. Utan jQuery Unobtrusive Validation skulle du behöva koda samma valideringslogik på två platser: en gång i valideringsattributen på serversidan för modellegenskaper och sedan igen i skript på klientsidan. I stället använder Tag Helpers och HTML-hjälparna valideringsattributen och skriver metadata från modellegenskaper för att återge HTML 5-attribut data- för de formulärelement som behöver validering. jQuery Diskret validering tolkar attributen data- och vidarebefordrar logiken till jQuery Validation, vilket effektivt "kopierar" valideringens logik på serversidan till klienten. Du kan visa valideringsfel på klienten med hjälp av tagghjälpare som visas här:

<div class="form-group">
    <label asp-for="Movie.ReleaseDate" class="control-label"></label>
    <input asp-for="Movie.ReleaseDate" class="form-control" />
    <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
</div>

De föregående tagghjälparna återger följande HTML:

<div class="form-group">
    <label class="control-label" for="Movie_ReleaseDate">Release Date</label>
    <input class="form-control" type="date" data-val="true"
        data-val-required="The Release Date field is required."
        id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
    <span class="text-danger field-validation-valid"
        data-valmsg-for="Movie.ReleaseDate" data-valmsg-replace="true"></span>
</div>

Observera att attributen data- i HTML-utdata motsvarar verifieringsattributen Movie.ReleaseDate för egenskapen. Attributet data-val-required innehåller ett felmeddelande som visas om användaren inte fyller i publiceringsdatumfältet. jQuery Unobtrusive Validation skickar det här värdet till metoden jQuery Validation required(), som sedan visar meddelandet i det medföljande <span-elementet> .

Verifiering av datatyp baseras på .NET-typen för en egenskap, såvida den inte åsidosätts av ett [DataType]- attribut. Webbläsare har sina egna standardfelmeddelanden, men jQuery Validation Unobtrusive Validation-paketet kan åsidosätta dessa meddelanden. [DataType] med attribut och underklasser som [EmailAddress] kan du ange felmeddelandet.

Oansträngd validering

Information om diskret validering finns i det här GitHub-problemet.

Lägga till validering i dynamiska formulär

jQuery Unobtrusive Validation förmedlar valideringslogik och parametrar till jQuery Validation när sidan först läses in. Valideringen fungerar därför inte automatiskt på dynamiskt genererade formulär. Om du vill aktivera validering ber du jQuery Unobtrusive Validation att parsa det dynamiska formuläret direkt efter att du har skapat det. Följande kod konfigurerar till exempel validering på klientsidan i ett formulär som lagts till via AJAX.

$.get({
    url: "https://url/that/returns/a/form",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add form. " + errorThrown);
    },
    success: function(newFormHTML) {
        var container = document.getElementById("form-container");
        container.insertAdjacentHTML("beforeend", newFormHTML);
        var forms = container.getElementsByTagName("form");
        var newForm = forms[forms.length - 1];
        $.validator.unobtrusive.parse(newForm);
    }
})

Metoden $.validator.unobtrusive.parse() accepterar en jQuery-väljare för sitt enda argument. Den här metoden instruerar jQuery Unobtrusive Validation att parsa attributen för formulär inom den valda selektorn. Värdena för dessa attribut skickas sedan till plugin-programmet jQuery Validation.

Lägga till validering i dynamiska kontroller

Metoden $.validator.unobtrusive.parse() fungerar i ett helt formulär, inte på enskilda dynamiskt genererade kontroller, till exempel <input> och <select/>. Om du vill skriva om formuläret tar du bort valideringsdata som lades till när formuläret parsades tidigare, som du ser i följande exempel:

$.get({
    url: "https://url/that/returns/a/control",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add control. " + errorThrown);
    },
    success: function(newInputHTML) {
        var form = document.getElementById("my-form");
        form.insertAdjacentHTML("beforeend", newInputHTML);
        $(form).removeData("validator")    // Added by jQuery Validation
               .removeData("unobtrusiveValidation");   // Added by jQuery Unobtrusive Validation
        $.validator.unobtrusive.parse(form);
    }
})

Anpassad validering på klientsidan

Anpassad validering på klientsidan görs genom att generera data- HTML-attribut som fungerar med ett anpassat jQuery-valideringskort. Följande exempeladapterkod skrevs för [ClassicMovie] och [ClassicMovieWithClientValidator]-attributen som presenterades tidigare i den här artikeln:

$.validator.addMethod('classicmovie', function (value, element, params) {
    var genre = $(params[0]).val(), year = params[1], date = new Date(value);

    // The Classic genre has a value of '0'.
    if (genre && genre.length > 0 && genre[0] === '0') {
        // The release date for a Classic is valid if it's no greater than the given year.
        return date.getUTCFullYear() <= year;
    }

    return true;
});

$.validator.unobtrusive.adapters.add('classicmovie', ['year'], function (options) {
    var element = $(options.form).find('select#Movie_Genre')[0];

    options.rules['classicmovie'] = [element, parseInt(options.params['year'])];
    options.messages['classicmovie'] = options.message;
});

Information om hur du skriver adaptrar finns i dokumentationen för jQuery-validering.

Användningen av en adapter för ett givet fält utlöses av data- attribut som:

  • Flagga fältet som föremål för validering (data-val="true").
  • Identifiera ett namn på verifieringsregeln och felmeddelandetexten (till exempel data-val-rulename="Error message.").
  • Ange eventuella ytterligare parametrar som validatorn behöver (till exempel data-val-rulename-param1="value").

I följande exempel visas attributen data- för exempelappensClassicMovie attribut:

<input class="form-control" type="date"
    data-val="true"
    data-val-classicmovie="Classic movies must have a release year no later than 1960."
    data-val-classicmovie-year="1960"
    data-val-required="The Release Date field is required."
    id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">

Som tidigare nämnts använder Tag Helpers och HTML-hjälpare information från valideringsattribut för att återge data- attribut. Det finns två alternativ för att skriva kod som resulterar i skapandet av anpassade data- HTML-attribut:

AttributeAdapter för validering på klientsidan

Den här metoden för renderingsattribut data- i HTML används av ClassicMovie attributet i exempelappen. Så här lägger du till klientverifiering med hjälp av den här metoden:

  1. Skapa en adapterklass för det anpassade valideringsattributet. Härled klassen från AttributeAdapterBase<TAttribute>. Skapa en AddValidation-metod som lägger till data--attribut till de renderingar som visas i detta exempel.

    public class ClassicMovieAttributeAdapter : AttributeAdapterBase<ClassicMovieAttribute>
    {
        public ClassicMovieAttributeAdapter(
            ClassicMovieAttribute attribute, IStringLocalizer? stringLocalizer)
            : base(attribute, stringLocalizer)
        {
    
        }
    
        public override void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage(context));
    
            var year = Attribute.Year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
    
        public override string GetErrorMessage(ModelValidationContextBase validationContext)
            => Attribute.GetErrorMessage();
    }
    
  2. Skapa en adapterproviderklass som implementerar IValidationAttributeAdapterProvider. GetAttributeAdapter I metoden skickar du det anpassade attributet till adapterns konstruktor, som du ser i det här exemplet:

    public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider
    {
        private readonly IValidationAttributeAdapterProvider baseProvider =
            new ValidationAttributeAdapterProvider();
    
        public IAttributeAdapter? GetAttributeAdapter(
            ValidationAttribute attribute, IStringLocalizer? stringLocalizer)
        {
            if (attribute is ClassicMovieAttribute classicMovieAttribute)
            {
                return new ClassicMovieAttributeAdapter(classicMovieAttribute, stringLocalizer);
            }
    
            return baseProvider.GetAttributeAdapter(attribute, stringLocalizer);
        }
    }
    
  3. Registrera adapterprovidern för DI i Program.cs:

    builder.Services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                _ => "The field is required.");
        });
    
    builder.Services.AddSingleton
        <IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
    

IClientModelValidator för validering på klientsidan

Den här metoden för renderingsattribut data- i HTML används av ClassicMovieWithClientValidator attributet i exempelappen. Så här lägger du till klientverifiering med hjälp av den här metoden:

  • I det anpassade valideringsattributet implementerar du IClientModelValidator gränssnittet och skapar en AddValidation metod. AddValidation I metoden lägger du till data- attribut för validering, som du ser i följande exempel:

    public class ClassicMovieWithClientValidatorAttribute :
        ValidationAttribute, IClientModelValidator
    {
        public ClassicMovieWithClientValidatorAttribute(int year)
            => Year = year;
    
        public int Year { get; }
    
        public void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage());
    
            var year = Year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
    
        public string GetErrorMessage() =>
            $"Classic movies must have a release year no later than {Year}.";
    
        protected override ValidationResult? IsValid(
            object? value, ValidationContext validationContext)
        {
            var movie = (Movie)validationContext.ObjectInstance;
            var releaseYear = ((DateTime)value!).Year;
    
            if (movie.Genre == Genre.Classic && releaseYear > Year)
            {
                return new ValidationResult(GetErrorMessage());
            }
    
            return ValidationResult.Success;
        }
    
        private static bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
        {
            if (attributes.ContainsKey(key))
            {
                return false;
            }
    
            attributes.Add(key, value);
            return true;
        }
    }
    

Inaktivera validering på klientsidan

Följande kod inaktiverar klientverifiering på Razor sidor:

builder.Services.AddRazorPages()
    .AddViewOptions(options =>
    {
        options.HtmlHelperOptions.ClientValidationEnabled = false;
    });

Andra alternativ för att inaktivera validering på klientsidan:

  • Kommentera ut referensen till _ValidationScriptsPartial i alla .cshtml filer.
  • Ta bort innehållet i filen Pages\Shared_ValidationScriptsPartial.cshtml .

Föregående metod förhindrar inte validering på klientsidan av ASP.NET Core-klassbibliotek IdentityRazor . Mer information finns i Scaffold Identity i ASP.NET Core-projekt.

Ytterligare resurser

Den här artikeln beskriver hur du verifierar användarindata i en ASP.NET Core MVC- eller Razor Pages-app.

Visa eller ladda ned exempelkod (hur du laddar ned).

Modelltillstånd

Modelltillståndet representerar fel som kommer från två undersystem: modellbindning och modellverifiering. Fel som kommer från modellbindning är vanligtvis datakonverteringsfel. Till exempel anges ett "x" i ett heltalsfält. Modellverifiering sker efter modellbindning och rapporterar fel där data inte överensstämmer med affärsregler. Till exempel anges en 0 i ett fält som förväntar sig ett omdöme mellan 1 och 5.

Både modellbindning och modellverifiering sker innan en kontrollantåtgärd eller en Razor pages-hanteringsmetod körs. För webbappar är det appens ansvar att inspektera ModelState.IsValid och reagera på rätt sätt. Webbappar visar ofta om sidan med ett felmeddelande.

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movies.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Webb-API-kontrollanter behöver inte kontrollera ModelState.IsValid om de har attributet [ApiController]. I så fall returneras ett automatiskt HTTP 400-svar som innehåller felinformation när modelltillståndet är ogiltigt. Mer information finns i automatiska HTTP 400-svar.

Kör valideringen igen

Verifieringen är automatisk, men du kanske vill upprepa den manuellt. Du kan till exempel beräkna ett värde för en egenskap och vill köra valideringen igen när du har angett egenskapen till det beräknade värdet. Om du vill köra valideringen igen anropar ModelStateDictionary.ClearValidationState du för att rensa valideringen som är specifik för den modell som verifieras följt av TryValidateModel:

public async Task<IActionResult> OnPostTryValidateAsync()
{
    var modifiedReleaseDate = DateTime.Now.Date;
    Movie.ReleaseDate = modifiedReleaseDate;

    ModelState.ClearValidationState(nameof(Movie));
    if (!TryValidateModel(Movie, nameof(Movie)))
    {
        return Page();
    }

    _context.Movies.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Valideringsattribut

Med valideringsattribut kan du ange verifieringsregler för modellegenskaper. I följande exempel från exempelappen visas en modellklass som kommenteras med valideringsattribut. Attributet [ClassicMovie] är ett anpassat valideringsattribut och de andra är inbyggda. Det visas inte är [ClassicMovieWithClientValidator], som visar ett alternativt sätt att implementera ett anpassat attribut.

public class Movie
{
    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; }

    [ClassicMovie(1960)]
    [DataType(DataType.Date)]
    [Display(Name = "Release Date")]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; }

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    public Genre Genre { get; set; }

    public bool Preorder { get; set; }
}

Inbyggda attribut

Här är några av de inbyggda valideringsattributen:

En fullständig lista över valideringsattribut finns i System.ComponentModel.DataAnnotations namnområdet.

Felmeddelanden

Med valideringsattribut kan du ange felmeddelandet som ska visas för ogiltiga indata. Till exempel:

[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]

Internt anropar attributen String.Format med en platshållare för fältnamnet och ibland ytterligare platshållare. Till exempel:

[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]

När det tillämpas på en Name egenskap är felmeddelandet som skapades av föregående kod "Namnlängden måste vara mellan 6 och 8".

Information om vilka parametrar som skickas till String.Format för ett visst attributs felmeddelande finns i DataAnnotations-källkoden.

Referenstyper som inte kan ogiltigförklaras och attributet [Krävs]

Valideringssystemet behandlar icke-nullbara parametrar eller bundna egenskaper som om de hade ett [Required(AllowEmptyStrings = true)] attribut. Genom att aktivera Nullable kontexter börjar MVC implicit validera icke-nullbara egenskaper eller parametrar som om de hade tillskrivits [Required(AllowEmptyStrings = true)] attributet. Överväg följande kod:

public class Person
{
    public string Name { get; set; }
}

Om appen har skapats med <Nullable>enable</Nullable>resulterar ett värde som saknas för Name i ett JSON- eller formulärinlägg i ett valideringsfel. Använd en nullbar referenstyp för att tillåta att null- eller saknade värden anges för Name egenskapen:

public class Person
{
    public string? Name { get; set; }
}

Det här beteendet kan inaktiveras genom att SuppressImplicitRequiredAttributeForNonNullableReferenceTypes konfigurera i Startup.ConfigureServices:

services.AddControllers(options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

[Obligatorisk] validering på servern

På servern anses ett obligatoriskt värde saknas om egenskapen är null. Ett fält som inte är nullbart är alltid giltigt och [Required] attributets felmeddelande visas aldrig.

Modellbindningen för en icke-nullbar egenskap kan dock misslyckas, vilket resulterar i ett felmeddelande som The value '' is invalid. Om du vill ange ett anpassat felmeddelande för validering på serversidan av icke-nullbara typer har du följande alternativ:

  • Gör fältet null (till exempel decimal? i stället för decimal). Nullbar<T-värdetyper> behandlas som standardtyper som kan ogiltigas.

  • Ange standardfelmeddelandet som ska användas av modellbindningen, enligt följande exempel:

    services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                _ => "The field is required.");
        });
    
    services.AddSingleton<IValidationAttributeAdapterProvider,
        CustomValidationAttributeAdapterProvider>();
    

    Mer information om modellbindningsfel som du kan ange standardmeddelanden för finns i DefaultModelBindingMessageProvider.

[Krävs] validering på klienten

Icke-nullbara typer och strängar hanteras på olika sätt på klienten jämfört med servern. På klienten:

  • Ett värde anses vara närvarande endast om indata anges för det. Därför hanterar validering på klientsidan icke-nullbara typer på samma sätt som nullbara typer.
  • Blanksteg i ett strängfält anses vara giltiga indata med den metod som krävs för jQuery-validering. Verifiering på serversidan anser att ett obligatoriskt strängfält är ogiltigt om endast blanksteg anges.

Som tidigare nämnts behandlas icke-nullbara typer som om de hade ett [Required(AllowEmptyStrings = true)] attribut. Det innebär att du får validering på klientsidan även om du inte tillämpar attributet [Required(AllowEmptyStrings = true)] . Men om du inte använder attributet får du ett standardfelmeddelande. Om du vill ange ett anpassat felmeddelande använder du attributet.

[Fjärr]-attribut

Attributet [Remote] implementerar validering på klientsidan som kräver att en metod anropas på servern för att avgöra om fältindata är giltiga. Appen kan till exempel behöva kontrollera om ett användarnamn redan används.

Så här implementerar du fjärrverifiering:

  1. Skapa en åtgärdsmetod för JavaScript att anropa. Fjärrmetoden jQuery-validering förväntar sig ett JSON-svar:

    • true innebär att indata är giltiga.
    • false, undefined, eller null betyder att indata är ogiltiga. Visa standardfelmeddelandet.
    • Alla andra strängar innebär att indata är ogiltiga. Visa strängen som ett anpassat felmeddelande.

    Här är ett exempel på en åtgärdsmetod som returnerar ett anpassat felmeddelande:

    [AcceptVerbs("GET", "POST")]
    public IActionResult VerifyEmail(string email)
    {
        if (!_userService.VerifyEmail(email))
        {
            return Json($"Email {email} is already in use.");
        }
    
        return Json(true);
    }
    
  2. I modellklassen kommenterar du egenskapen med ett [Remote] attribut som pekar på verifieringsåtgärdsmetoden, som du ser i följande exempel:

    [Remote(action: "VerifyEmail", controller: "Users")]
    public string Email { get; set; }
    

Ytterligare fält

Med AdditionalFields egenskapen för [Remote] attributet kan du verifiera kombinationer av fält mot data på servern. Om User modellen till exempel hade FirstName och LastName egenskaper kanske du vill kontrollera att inga befintliga användare redan har det namnparet. I följande exempel visas hur du använder AdditionalFields:

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(LastName))]
[Display(Name = "First Name")]
public string FirstName { get; set; }

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName))]
[Display(Name = "Last Name")]
public string LastName { get; set; }

AdditionalFields kan explicit sättas till strängarna "FirstName" och "LastName", men med hjälp av nameof-operatorn förenklas senare refaktorisering. Åtgärdsmetoden för den här valideringen måste acceptera både firstName argument och lastName argument:

[AcceptVerbs("GET", "POST")]
public IActionResult VerifyName(string firstName, string lastName)
{
    if (!_userService.VerifyName(firstName, lastName))
    {
        return Json($"A user named {firstName} {lastName} already exists.");
    }

    return Json(true);
}

När användaren anger ett för- eller efternamn gör JavaScript ett fjärranrop för att se om det par med namn har tagits.

Om du vill verifiera två eller flera ytterligare fält anger du dem som en kommaavgränsad lista. Om du till exempel vill lägga till en MiddleName egenskap i modellen anger du [Remote] attributet enligt följande exempel:

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName) + "," + nameof(LastName))]
public string MiddleName { get; set; }

AdditionalFields, liksom alla attributargument, måste vara ett konstant uttryck. Använd därför inte en interpolerad sträng eller ett anrop Join för att initiera AdditionalFields.

Alternativ till inbyggda attribut

Om du behöver verifiering som inte tillhandahålls av inbyggda attribut kan du:

Anpassade attribut

För scenarier som de inbyggda valideringsattributen inte hanterar kan du skapa anpassade valideringsattribut. Skapa en klass som ärver från ValidationAttributeoch åsidosätt IsValid metoden.

Metoden IsValid accepterar ett objekt med namnet value, vilket är indata som ska verifieras. En överlagring accepterar också ett ValidationContext objekt som innehåller ytterligare information, till exempel modellinstansen som skapas av modellbindningen.

I följande exempel verifieras att utgivningsdatumet för en film i den klassiska genren inte är senare än ett angivet år. Attributet [ClassicMovie] :

  • Körs bara på servern.
  • För klassiska filmer validerar du utgivningsdatumet:
public class ClassicMovieAttribute : ValidationAttribute
{
    public ClassicMovieAttribute(int year)
    {
        Year = year;
    }

    public int Year { get; }

    public string GetErrorMessage() =>
        $"Classic movies must have a release year no later than {Year}.";

    protected override ValidationResult IsValid(object value,
        ValidationContext validationContext)
    {
        var movie = (Movie)validationContext.ObjectInstance;
        var releaseYear = ((DateTime)value).Year;

        if (movie.Genre == Genre.Classic && releaseYear > Year)
        {
            return new ValidationResult(GetErrorMessage());
        }

        return ValidationResult.Success;
    }
}

Variabeln movie i föregående exempel representerar ett Movie objekt som innehåller data från formuläröverföringen. När verifieringen misslyckas returneras ett ValidationResult med ett felmeddelande.

IValidatableObject

Föregående exempel fungerar endast med Movie typer. Ett annat alternativ för validering på klassnivå är att implementera IValidatableObject i modellklassen, som du ser i följande exempel:

public class ValidatableMovie : IValidatableObject
{
    private const int _classicYear = 1960;

    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; }

    [DataType(DataType.Date)]
    [Display(Name = "Release Date")]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; }

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    public Genre Genre { get; set; }

    public bool Preorder { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Genre == Genre.Classic && ReleaseDate.Year > _classicYear)
        {
            yield return new ValidationResult(
                $"Classic movies must have a release year no later than {_classicYear}.",
                new[] { nameof(ReleaseDate) });
        }
    }
}

Nodvalidering på toppnivå

Noder på den översta nivån är:

  • Åtgärdsparameter
  • Egenskaper för styrenhet
  • Sidhanterarparametrar
  • Egenskaper för sidmodell

Modellbundna noder på den översta nivån verifieras utöver validering av modellegenskaper. I följande exempel från exempelappenVerifyPhone använder RegularExpressionAttribute metoden för att verifiera åtgärdsparameternphone:

[AcceptVerbs("GET", "POST")]
public IActionResult VerifyPhone(
    [RegularExpression(@"^\d{3}-\d{3}-\d{4}$")] string phone)
{
    if (!ModelState.IsValid)
    {
        return Json($"Phone {phone} has an invalid format. Format: ###-###-####");
    }

    return Json(true);
}

Noder på högsta nivån kan använda BindRequiredAttribute med valideringsattribut. I följande exempel från exempelappenCheckAge anger metoden att parametern age måste vara bunden från frågesträngen när formuläret skickas:

[HttpPost]
public IActionResult CheckAge([BindRequired, FromQuery] int age)
{

På sidan Kontrollera ålder (CheckAge.cshtml) finns det två formulär. Det första formuläret skickar ett Age värde för 99 som en frågesträngsparameter: https://localhost:5001/Users/CheckAge?Age=99.

När en korrekt formaterad age parameter från frågesträngen skickas verifieras formuläret.

Det andra formuläret på sidan Kontrollera ålder skickar Age värdet i begärans brödtext, och verifieringen misslyckas. Bindningen misslyckas eftersom parametern age måste komma från en frågesträng.

Maximalt antal fel

Verifieringen stoppas när det maximala antalet fel har nåtts (200 som standard). Du kan konfigurera det här numret med följande kod i Startup.ConfigureServices:

services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.MaxModelValidationErrors = 50;
        options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
            _ => "The field is required.");
    });

services.AddSingleton<IValidationAttributeAdapterProvider,
    CustomValidationAttributeAdapterProvider>();

Maximum rekursion

ValidationVisitor bläddrar igenom objektdiagrammet för modellen som verifieras. För modeller som är djupa eller oändligt rekursiva kan validering resultera i stacköverflöde. MvcOptions.MaxValidationDepth ger ett sätt att stoppa valideringen tidigt om besökarens rekursion överskrider ett konfigurerat djup. Standardvärdet MvcOptions.MaxValidationDepth för är 32.

Automatisk kortslutning

Valideringen kortsluts automatiskt (hoppas över) om modelldiagrammet inte kräver validering. Objekt som körningen hoppar över validering för inkluderar samlingar av primitiver (till exempel byte[], string[], Dictionary<string, string>) och komplexa objektdiagram som inte har några validatorer.

Validering på klientsidan

Validering på klientsidan förhindrar överföring tills formuläret är giltigt. Knappen Skicka kör JavaScript som antingen skickar formuläret eller visar felmeddelanden.

Validering på klientsidan undviker en onödig förfrågan till servern när det finns felaktiga uppgifter i ett formulär. Följande skriptreferenser i _Layout.cshtml och _ValidationScriptsPartial.cshtml stöder validering på klientsidan:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.1/jquery.validate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.js"></script>

JQuery Unobtrusive Validation-skriptet är ett anpassat Microsoft-klientdelsbibliotek som bygger på det populära plugin-programmet jQuery Validation. Utan jQuery Unobtrusive Validation skulle du behöva koda samma valideringslogik på två platser: en gång i valideringsattributen på serversidan för modellegenskaper och sedan igen i skript på klientsidan. I stället använder Tag Helpers och HTML-hjälparna valideringsattributen och skriver metadata från modellegenskaper för att återge HTML 5-attribut data- för de formulärelement som behöver validering. jQuery Diskret validering tolkar attributen data- och vidarebefordrar logiken till jQuery Validation, vilket effektivt "kopierar" valideringens logik på serversidan till klienten. Du kan visa valideringsfel på klienten med hjälp av tagghjälpare som visas här:

<div class="form-group">
    <label asp-for="Movie.ReleaseDate" class="control-label"></label>
    <input asp-for="Movie.ReleaseDate" class="form-control" />
    <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
</div>

De föregående tagghjälparna återger följande HTML:

<div class="form-group">
    <label class="control-label" for="Movie_ReleaseDate">Release Date</label>
    <input class="form-control" type="date" data-val="true"
        data-val-required="The Release Date field is required."
        id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
    <span class="text-danger field-validation-valid"
        data-valmsg-for="Movie.ReleaseDate" data-valmsg-replace="true"></span>
</div>

Observera att attributen data- i HTML-utdata motsvarar verifieringsattributen Movie.ReleaseDate för egenskapen. Attributet data-val-required innehåller ett felmeddelande som visas om användaren inte fyller i publiceringsdatumfältet. jQuery Unobtrusive Validation skickar det här värdet till metoden jQuery Validation required(), som sedan visar meddelandet i det medföljande <span-elementet> .

Verifiering av datatyp baseras på .NET-typen för en egenskap, såvida den inte åsidosätts av ett [DataType]- attribut. Webbläsare har sina egna standardfelmeddelanden, men jQuery Validation Unobtrusive Validation-paketet kan åsidosätta dessa meddelanden. [DataType] med attribut och underklasser som [EmailAddress] kan du ange felmeddelandet.

Oansträngd validering

Information om diskret validering finns i det här GitHub-problemet.

Lägga till validering i dynamiska formulär

jQuery Unobtrusive Validation förmedlar valideringslogik och parametrar till jQuery Validation när sidan först läses in. Valideringen fungerar därför inte automatiskt på dynamiskt genererade formulär. Om du vill aktivera validering ber du jQuery Unobtrusive Validation att parsa det dynamiska formuläret direkt efter att du har skapat det. Följande kod konfigurerar till exempel validering på klientsidan i ett formulär som lagts till via AJAX.

$.get({
    url: "https://url/that/returns/a/form",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add form. " + errorThrown);
    },
    success: function(newFormHTML) {
        var container = document.getElementById("form-container");
        container.insertAdjacentHTML("beforeend", newFormHTML);
        var forms = container.getElementsByTagName("form");
        var newForm = forms[forms.length - 1];
        $.validator.unobtrusive.parse(newForm);
    }
})

Metoden $.validator.unobtrusive.parse() accepterar en jQuery-väljare för sitt enda argument. Den här metoden instruerar jQuery Unobtrusive Validation att parsa attributen för formulär inom den valda selektorn. Värdena för dessa attribut skickas sedan till plugin-programmet jQuery Validation.

Lägga till validering i dynamiska kontroller

Metoden $.validator.unobtrusive.parse() fungerar i ett helt formulär, inte på enskilda dynamiskt genererade kontroller, till exempel <input> och <select/>. Om du vill skriva om formuläret tar du bort valideringsdata som lades till när formuläret parsades tidigare, som du ser i följande exempel:

$.get({
    url: "https://url/that/returns/a/control",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add control. " + errorThrown);
    },
    success: function(newInputHTML) {
        var form = document.getElementById("my-form");
        form.insertAdjacentHTML("beforeend", newInputHTML);
        $(form).removeData("validator")    // Added by jQuery Validation
               .removeData("unobtrusiveValidation");   // Added by jQuery Unobtrusive Validation
        $.validator.unobtrusive.parse(form);
    }
})

Anpassad validering på klientsidan

Anpassad validering på klientsidan görs genom att generera data- HTML-attribut som fungerar med ett anpassat jQuery-valideringskort. Följande exempeladapterkod skrevs för [ClassicMovie] och [ClassicMovieWithClientValidator]-attributen som presenterades tidigare i den här artikeln:

$.validator.addMethod('classicmovie', function (value, element, params) {
    var genre = $(params[0]).val(), year = params[1], date = new Date(value);

    // The Classic genre has a value of '0'.
    if (genre && genre.length > 0 && genre[0] === '0') {
        // The release date for a Classic is valid if it's no greater than the given year.
        return date.getUTCFullYear() <= year;
    }

    return true;
});

$.validator.unobtrusive.adapters.add('classicmovie', ['year'], function (options) {
    var element = $(options.form).find('select#Movie_Genre')[0];

    options.rules['classicmovie'] = [element, parseInt(options.params['year'])];
    options.messages['classicmovie'] = options.message;
});

Information om hur du skriver adaptrar finns i dokumentationen för jQuery-validering.

Användningen av en adapter för ett givet fält utlöses av data- attribut som:

  • Flagga fältet som föremål för validering (data-val="true").
  • Identifiera ett namn på verifieringsregeln och felmeddelandetexten (till exempel data-val-rulename="Error message.").
  • Ange eventuella ytterligare parametrar som validatorn behöver (till exempel data-val-rulename-param1="value").

I följande exempel visas attributen data- för exempelappensClassicMovie attribut:

<input class="form-control" type="date"
    data-val="true"
    data-val-classicmovie="Classic movies must have a release year no later than 1960."
    data-val-classicmovie-year="1960"
    data-val-required="The Release Date field is required."
    id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">

Som tidigare nämnts använder Tag Helpers och HTML-hjälpare information från valideringsattribut för att återge data- attribut. Det finns två alternativ för att skriva kod som resulterar i skapandet av anpassade data- HTML-attribut:

AttributeAdapter för validering på klientsidan

Den här metoden för renderingsattribut data- i HTML används av ClassicMovie attributet i exempelappen. Så här lägger du till klientverifiering med hjälp av den här metoden:

  1. Skapa en adapterklass för det anpassade valideringsattributet. Härled klassen från AttributeAdapterBase<TAttribute>. Skapa en AddValidation-metod som lägger till data--attribut till de renderingar som visas i detta exempel.

    public class ClassicMovieAttributeAdapter : AttributeAdapterBase<ClassicMovieAttribute>
    {
        public ClassicMovieAttributeAdapter(ClassicMovieAttribute attribute,
            IStringLocalizer stringLocalizer)
            : base(attribute, stringLocalizer)
        {
    
        }
    
        public override void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage(context));
    
            var year = Attribute.Year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
    
        public override string GetErrorMessage(ModelValidationContextBase validationContext) =>
            Attribute.GetErrorMessage();
    }
    
  2. Skapa en adapterproviderklass som implementerar IValidationAttributeAdapterProvider. GetAttributeAdapter I metoden skickar du det anpassade attributet till adapterns konstruktor, som du ser i det här exemplet:

    public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider
    {
        private readonly IValidationAttributeAdapterProvider baseProvider =
            new ValidationAttributeAdapterProvider();
    
        public IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute,
            IStringLocalizer stringLocalizer)
        {
            if (attribute is ClassicMovieAttribute classicMovieAttribute)
            {
                return new ClassicMovieAttributeAdapter(classicMovieAttribute, stringLocalizer);
            }
    
            return baseProvider.GetAttributeAdapter(attribute, stringLocalizer);
        }
    }
    
  3. Registrera adapterprovidern för DI i Startup.ConfigureServices:

    services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                _ => "The field is required.");
        });
    
    services.AddSingleton<IValidationAttributeAdapterProvider,
        CustomValidationAttributeAdapterProvider>();
    

IClientModelValidator för validering på klientsidan

Den här metoden för renderingsattribut data- i HTML används av ClassicMovieWithClientValidator attributet i exempelappen. Så här lägger du till klientverifiering med hjälp av den här metoden:

  • I det anpassade valideringsattributet implementerar du IClientModelValidator gränssnittet och skapar en AddValidation metod. AddValidation I metoden lägger du till data- attribut för validering, som du ser i följande exempel:

    public class ClassicMovieWithClientValidatorAttribute :
        ValidationAttribute, IClientModelValidator
    {
        public ClassicMovieWithClientValidatorAttribute(int year)
        {
            Year = year;
        }
    
        public int Year { get; }
    
        public void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage());
    
            var year = Year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
    
        public string GetErrorMessage() =>
            $"Classic movies must have a release year no later than {Year}.";
    
        protected override ValidationResult IsValid(object value,
            ValidationContext validationContext)
        {
            var movie = (Movie)validationContext.ObjectInstance;
            var releaseYear = ((DateTime)value).Year;
    
            if (movie.Genre == Genre.Classic && releaseYear > Year)
            {
                return new ValidationResult(GetErrorMessage());
            }
    
            return ValidationResult.Success;
        }
    
        private bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
        {
            if (attributes.ContainsKey(key))
            {
                return false;
            }
    
            attributes.Add(key, value);
            return true;
        }
    }
    

Inaktivera validering på klientsidan

Följande kod inaktiverar klientverifiering på Razor sidor:

services.AddRazorPages()
    .AddViewOptions(options =>
    {
        options.HtmlHelperOptions.ClientValidationEnabled = false;
    });

Andra alternativ för att inaktivera validering på klientsidan:

  • Kommentera ut referensen till _ValidationScriptsPartial i alla .cshtml filer.
  • Ta bort innehållet i filen Pages\Shared_ValidationScriptsPartial.cshtml .

Föregående metod förhindrar inte validering på klientsidan av ASP.NET Core-klassbibliotek IdentityRazor . Mer information finns i Scaffold Identity i ASP.NET Core-projekt.

Ytterligare resurser