Ověření modelu na ASP.NET Core MVC a Razor stránkách

Tento článek vysvětluje, jak ověřit vstup uživatele v aplikaci ASP.NET Core MVC nebo Razor Pages.

Zobrazení nebo stažení vzorového kódu (postup stažení)

Stav modelu

Stav modelu představuje chyby, které pocházejí ze dvou subsystémů: vazby modelu a ověření modelu. Chyby, které pocházejí z vazby modelu, jsou obecně chyby převodu dat. Do celočíselného pole se například zadává "x". K ověření modelu dochází po vazbě modelu a hlásí chyby, kdy data nevyhovují obchodním pravidlům. Do pole, které očekává hodnocení od 1 do 5, je například zadáno číslo 0.

Vazby modelu i ověření modelu probíhají před spuštěním akce kontroleru nebo Razor metody obslužné rutiny Pages. U webových aplikací je tato aplikace zodpovědná za kontrolu ModelState.IsValid a správné reakce. Webové aplikace obvykle znovu zobrazí stránku chybovou zprávou, jak je znázorněno v následujícím Razor příkladu stránky:

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

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

    return RedirectToPage("./Index");
}

Pro ASP.NET Core MVC s řadiči a zobrazeními ukazuje následující příklad, jak zkontrolovat ModelState.IsValid uvnitř akce kontroleru:

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

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

    return RedirectToAction(nameof(Index));
}

Kontrolery webového rozhraní API nemusí kontrolovatModelState.IsValid, jestli mají atribut [ApiController]. V takovém případě se vrátí automatická odpověď HTTP 400 obsahující podrobnosti o chybě, když je stav modelu neplatný. Další informace naleznete v tématu Automatické odpovědi HTTP 400.

Znovu spustit ověření

Ověření je automatické, ale možná ho budete chtít opakovat ručně. Můžete například vypočítat hodnotu vlastnosti a po nastavení vlastnosti na vypočítanou hodnotu znovu spustit ověření. Pokud chcete znovu spustit ověření, zavolejte ModelStateDictionary.ClearValidationState na ověření specifické pro model, za nímž následuje 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");
}

Ověřovací atributy

Ověřovací atributy umožňují zadat ověřovací pravidla pro vlastnosti modelu. Následující příklad z ukázkové aplikace ukazuje třídu modelu, která je opatřena poznámkami s ověřovacími atributy. Atribut [ClassicMovie] je vlastní ověřovací atribut a ostatní jsou integrované. Nezobrazuje se , [ClassicMovieWithClientValidator]což ukazuje alternativní způsob implementace vlastního atributu.

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; }
}

Předdefinované atributy

Tady jsou některé předdefinované ověřovací atributy:

Úplný seznam ověřovacích atributů najdete v System.ComponentModel.DataAnnotations oboru názvů.

Chybové zprávy

Ověřovací atributy umožňují zadat chybovou zprávu, která se má zobrazit pro neplatný vstup. Příklad:

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

Atributy interně volají String.Format zástupný symbol pro název pole a někdy i další zástupné symboly. Příklad:

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

Při použití na Name vlastnost by chybová zpráva vytvořená předchozím kódem byla "Délka názvu musí být v rozmezí 6 až 8".

Pokud chcete zjistit, které parametry se předávají String.Format pro chybovou zprávu konkrétního atributu, přečtěte si zdrojový kód DataAnnotations.

Použití JSnázvů vlastností ON v chybách ověřování

Ve výchozím nastavení při výskytu chyby ověření vytvoří ověření ModelStateDictionary modelu název vlastnosti jako klíč chyby. Některé aplikace, například jednostrákové aplikace, můžou využívat JSnázvy vlastností ON pro chyby ověřování vygenerované z webových rozhraní API. Následující kód konfiguruje ověřování tak, aby používal SystemTextJsonValidationMetadataProviderJSnázvy vlastností ON:

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();

Následující kód konfiguruje ověřování tak, aby při použití Json.NET používal NewtonsoftJsonValidationMetadataProviderJSnázev vlastnosti ON:

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();

Příklad zásad pro použití camel-casing najdete Program.cs na GitHubu.

Odkazové typy bez hodnoty null a atribut [Povinné]

Ověřovací systém zpracovává parametry bez hodnoty null nebo vázané vlastnosti, jako by měly [Required(AllowEmptyStrings = true)] atribut. Když povolíte Nullable kontexty, MVC implicitně začne ověřovat nenulové vlastnosti nebo parametry, jako by byly přiřazeny atributem[Required(AllowEmptyStrings = true)]. Vezměte v úvahu následující kód:

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

Pokud byla aplikace sestavena s <Nullable>enable</Nullable>chybějící hodnotou Name v příspěvku JSo zapnutí nebo formuláři, dojde k chybě ověření. To se může zdát v rozporu, protože [Required(AllowEmptyStrings = true)] atribut je implicitní, ale toto chování je očekávané, protože prázdné řetězce jsou ve výchozím nastavení převedeny na hodnotu null. Chcete-li povolit zadání hodnot null nebo chybějících hodnot pro vlastnost, použijte typ odkazu s možnou Name hodnotou null:

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

Toto chování lze zakázat konfigurací SuppressImplicitRequiredAttributeForNonNullableReferenceTypes v Program.cs:

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

[Povinné] Ověření na serveru

Na serveru je požadovaná hodnota považována za chybějící, pokud je vlastnost null. Pole bez hodnoty null je vždy platné a chybová zpráva atributu [Required] se nikdy nezobrazí.

Vazba modelu pro vlastnost, která není nullable však může selhat, což vede k chybové zprávě, například The value '' is invalid. Pokud chcete zadat vlastní chybovou zprávu pro ověřování typů, které nejsou s možnou hodnotou null, máte následující možnosti:

  • Nastavení pole s možnou hodnotou null (například decimal? místo decimal). Typy hodnot T> s možnou<hodnotou null jsou považovány za standardní typy s možnou hodnotou null.

  • Zadejte výchozí chybovou zprávu, kterou má použít vazba modelu, jak je znázorněno v následujícím příkladu:

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

    Další informace o chybách vazby modelu, pro které můžete nastavit výchozí zprávy, naleznete v tématu DefaultModelBindingMessageProvider.

[Požadováno] Ověření v klientovi

Typy a řetězce, které nemají hodnotu null, se na klientovi zpracovávají jinak než na serveru. Na klientovi:

  • Hodnota je považována za přítomnou pouze v případě, že je pro ni zadán vstup. Ověření na straně klienta proto zpracovává typy bez hodnoty null stejné jako typy s možnou hodnotou null.
  • Prázdné znaky v řetězcovém poli se považují za platný vstup požadovanou metodou jQuery Validation. Ověření na straně serveru považuje požadované pole řetězce za neplatné, pokud je zadáno pouze prázdné znaky.

Jak jsme uvedli dříve, typy, které nemají hodnotu null, se považují za [Required(AllowEmptyStrings = true)] atributy. To znamená, že získáte ověření na straně klienta, i když atribut nepoužijete [Required(AllowEmptyStrings = true)] . Pokud ale atribut nepoužíváte, zobrazí se výchozí chybová zpráva. Pokud chcete zadat vlastní chybovou zprávu, použijte atribut.

[Vzdálený] atribut

Atribut [Remote] implementuje ověření na straně klienta, které vyžaduje volání metody na serveru k určení, zda vstup pole je platný. Aplikace může například potřebovat ověřit, jestli se už používá uživatelské jméno.

Implementace vzdáleného ověřování:

  1. Vytvořte metodu akce pro volání JavaScriptu. Vzdálená metoda jQuery Validation očekává JSodpověď ON:

    • true znamená, že vstupní data jsou platná.
    • false, undefinednebo null znamená, že vstup je neplatný. Zobrazí výchozí chybovou zprávu.
    • Jakýkoli jiný řetězec znamená, že vstup je neplatný. Zobrazí řetězec jako vlastní chybovou zprávu.

    Tady je příklad metody akce, která vrací vlastní chybovou zprávu:

    [AcceptVerbs("GET", "POST")]
    public IActionResult VerifyEmail(string email)
    {
        if (!_userService.VerifyEmail(email))
        {
            return Json($"Email {email} is already in use.");
        }
    
        return Json(true);
    }
    
  2. Ve třídě modelu označte vlastnost atributem [Remote] , který odkazuje na metodu ověřovací akce, jak je znázorněno v následujícím příkladu:

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

Pro klienty, kteří zakázali JavaScript, je potřeba implementovat také ověření na straně serveru.

Dodatečná pole

Vlastnost AdditionalFields atributu [Remote] umožňuje ověřit kombinace polí s daty na serveru. Pokud User má model například vlastnosti a LastName vlastnostiFirstName, můžete chtít ověřit, jestli už žádný existující uživatel nemá tuto dvojici jmen. Následující příklad ukazuje, jak používat 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 můžou být explicitně nastaveny na řetězce "FirstName" a "LastName", ale použití operátoru nameof zjednodušuje pozdější refaktoring. Metoda akce pro toto ověření musí přijímat oba firstName argumenty i lastName argumenty:

[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);
}

Když uživatel zadá křestní jméno nebo příjmení, JavaScript provede vzdálené volání, aby zjistil, jestli byla pořízena dvojice jmen.

Pokud chcete ověřit dvě nebo více dalších polí, zadejte je jako seznam oddělený čárkami. Pokud například chcete do modelu přidat MiddleName vlastnost, nastavte [Remote] atribut, jak je znázorněno v následujícím příkladu:

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

AdditionalFields, stejně jako všechny argumenty atributů, musí být konstantní výraz. Proto nepoužívejte interpolovaný řetězec ani volání Join k inicializaci AdditionalFields.

Alternativy k integrovaným atributům

Pokud potřebujete ověření, které neposkytuje předdefinované atributy, můžete:

Vlastní atributy

Ve scénářích, které předdefinované ověřovací atributy nezpracují, můžete vytvořit vlastní ověřovací atributy. Vytvořte třídu, která dědí z ValidationAttribute, a přepsat metodu IsValid .

Metoda IsValid přijímá objekt pojmenovanou hodnotu, což je vstup, který se má ověřit. Přetížení také přijímá ValidationContext objekt, který poskytuje další informace, jako je instance modelu vytvořená vazbou modelu.

Následující příklad ověří, že datum vydání filmu v klasickém žánru není pozdější než zadaný rok. Atribut [ClassicMovie] :

  • Běží jenom na serveru.
  • U klasických filmů ověří datum vydání:
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;
    }
}

Proměnná movie v předchozím příkladu představuje Movie objekt, který obsahuje data z odeslání formuláře. Pokud se ověření nezdaří, ValidationResult vrátí se chybová zpráva.

IValidatableObject

Předchozí příklad funguje pouze s Movie typy. Další možností ověřování na úrovni třídy je implementace IValidatableObject ve třídě modelu, jak je znázorněno v následujícím příkladu:

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) });
        }
    }
}

Vlastní ověření

Následující kód ukazuje, jak přidat chybu modelu po prozkoumání modelu:

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

Následující kód implementuje ověřovací test v kontroleru:

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

Následující kód ověří, že telefonní číslo a e-mail jsou jedinečné:

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");
}

Následující kód implementuje ověřovací test v kontroleru:

[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);
}

Kontrola jedinečného telefonního čísla nebo e-mailu se obvykle provádí také pomocí vzdáleného ověření.

Validationresult

Zvažte následující vlastní 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;
    }
}

V následujícím kódu se použije vlastní [ValidateName] atribut:

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; }
}

Pokud model obsahuje zz, vrátí se nový ValidationResult .

Ověření uzlu nejvyšší úrovně

Mezi uzly nejvyšší úrovně patří:

  • Parametry akce
  • Vlastnosti kontroleru
  • Parametry obslužné rutiny stránky
  • Vlastnosti modelu stránky

Uzly nejvyšší úrovně vázané na model se kromě ověřování vlastností modelu ověřují. V následujícím příkladu z ukázkové aplikaceVerifyPhone metoda použije RegularExpressionAttribute k ověření parametru phone akce:

[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);
}

Uzly nejvyšší úrovně se můžou používat BindRequiredAttribute s ověřovacími atributy. V následujícím příkladu z ukázkové aplikace metoda určuje, CheckAge že age parametr musí být při odeslání formuláře vázán z řetězce dotazu:

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

Na stránce Check Age (CheckAge.cshtml) jsou dva formuláře. První formulář odešle Age hodnotu 99 jako parametr řetězce dotazu: https://localhost:5001/Users/CheckAge?Age=99.

Při odeslání správně naformátovaného age parametru z řetězce dotazu se formulář ověří.

Druhý formulář na stránce Kontrola věku odešle Age hodnotu v textu požadavku a ověření selže. Vazba selže, protože age parametr musí pocházet z řetězce dotazu.

Maximální počet chyb

Ověření se zastaví, když se dosáhne maximálního počtu chyb (ve výchozím nastavení 200). Toto číslo můžete nakonfigurovat následujícím kódem:Program.cs

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

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

Maximální rekurze

ValidationVisitor prochází graf objektů modelu, který se ověřuje. U modelů, které jsou hluboké nebo nekonečně rekurzivní, může ověření vést k přetečení zásobníku. MvcOptions.MaxValidationDepth poskytuje způsob, jak včas zastavit ověřování, pokud rekurze návštěvníka překročí nakonfigurovanou hloubku. Výchozí hodnota MvcOptions.MaxValidationDepth je 32.

Automatický zkrat

Pokud graf modelu nevyžaduje ověření, je ověření automaticky zkrácené (vynecháno). Objekty, které modul runtime přeskočí ověřování, zahrnují kolekce primitiv (například byte[], string[]), Dictionary<string, string>a komplexní objektové grafy, které nemají žádné validátory.

Ověřování na straně klienta

Ověření na straně klienta zabraňuje odeslání, dokud formulář není platný. Tlačítko Odeslat spustí JavaScript, který odešle formulář nebo zobrazí chybové zprávy.

Ověřování na straně klienta zabraňuje zbytečnému zaokrouhlení na server, pokud ve formuláři dojde k chybám vstupu. Následující odkazy na skripty podporují _Layout.cshtml_ValidationScriptsPartial.cshtml ověřování na straně klienta:

<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>

Skript jQuery Unobtrusive Validation je vlastní front-end knihovna Microsoftu, která je založená na oblíbeném modulu plug-in jQuery Validation. Bez ověřování jQuery Byste museli kódovat stejnou logiku ověřování na dvou místech: jednou v atributech ověřování na straně serveru ve vlastnostech modelu a pak znovu ve skriptech na straně klienta. Místo toho pomocné rutiny značek a pomocné rutiny HTML používají ověřovací atributy a zapisují metadata z vlastností modelu k vykreslení atributů HTML 5 data- pro prvky formuláře, které potřebují ověření. jQuery Unobtrusive Validation parsuje data- atributy a předává logiku do jQuery Validation, a to efektivně "kopírování" logiky ověření na straně serveru klientovi. Chyby ověřování můžete v klientovi zobrazit pomocí pomocných rutin značek, jak je znázorněno tady:

<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>

Předchozí pomocné rutiny značek vykreslují následující kód 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>

Všimněte si, že data- atributy ve výstupu HTML odpovídají ověřovacím atributům Movie.ReleaseDate vlastnosti. Atribut data-val-required obsahuje chybovou zprávu, která se zobrazí, pokud uživatel nevyplní pole data vydání. jQuery Unobtrusive Validation předá tuto hodnotu do metody jQuery Validation required(), která pak zobrazí tuto zprávu v doprovodném <span> elementu.

Ověření datového typu je založeno na typu .NET vlastnosti, pokud není přepsán atributem [DataType]. Prohlížeče mají vlastní výchozí chybové zprávy, ale balíček jQuery Validation Unobsive Validation může tyto zprávy přepsat. [DataType] atributy a podtřídy, například [EmailAddress] umožňují zadat chybovou zprávu.

Nerušivé ověřování

Informace o nerušivém ověřování najdete v tomto problému na GitHubu.

Přidání ověřování do dynamických formulářů

jQuery Unobtrusive Validation předá ověřovací logiku a parametry do jQuery Validation při prvním načtení stránky. Proto ověřování nefunguje automaticky na dynamicky generovaných formulářích. Pokud chcete povolit ověřování, řekněte jQuery Unobtrusive Validation, aby dynamický formulář parsovat hned po jeho vytvoření. Například následující kód nastaví ověřování na straně klienta ve formuláři přidaném přes 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);
    }
})

Metoda $.validator.unobtrusive.parse() přijímá pro svůj jeden argument selektor jQuery. Tato metoda říká jQuery Unobtrusive Validation parsovat data- atributy formulářů v rámci tohoto selektoru. Hodnoty těchto atributů se pak předají modulu plug-in jQuery Validation.

Přidání ověření do dynamických ovládacích prvků

Metoda $.validator.unobtrusive.parse() funguje na celém formuláři, ne na jednotlivých dynamicky generovaných ovládacích prvcích, například <input> a <select/>. Chcete-li formulář znovu provést, odeberte ověřovací data přidaná při analýze formuláře dříve, jak je znázorněno v následujícím příkladu:

$.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);
    }
})

Vlastní ověřování na straně klienta

Vlastní ověřování na straně klienta se provádí generováním data- atributů HTML, které pracují s vlastním adaptérem ověřování jQuery. Následující ukázkový kód adaptéru byl napsán pro [ClassicMovie][ClassicMovieWithClientValidator] atributy, které byly zavedeny dříve v tomto článku:

$.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;
});

Informace o zápisu adaptérů najdete v dokumentaci k ověřování jQuery.

Použití adaptéru pro dané pole je aktivováno data- atributy, které:

  • Označte pole příznakem jako předmětem ověření (data-val="true").
  • Identifikujte název ověřovacího pravidla a text chybové zprávy (například data-val-rulename="Error message.").
  • Zadejte všechny další parametry, které validátor potřebuje (například data-val-rulename-param1="value").

Následující příklad ukazuje data- atributy atributu ukázkové aplikaceClassicMovie :

<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="">

Jak jsme si poznamenali dříve, pomocné rutiny značek a pomocné rutiny HTML používají k vykreslení data- atributů informace z ověřovacích atributů. Existují dvě možnosti pro psaní kódu, které mají za následek vytvoření vlastních data- atributů HTML:

  • Vytvořte třídu odvozenou od AttributeAdapterBase<TAttribute> třídy a třídy, která implementuje IValidationAttributeAdapterProvider, a zaregistrujte atribut a jeho adaptér v DI. Tato metoda se řídí principem jedné odpovědnosti v tom, že ověřovací kód související se serverem a klientem je v samostatných třídách. Adaptér má také výhodu, že vzhledem k tomu, že je zaregistrovaný v DI, jsou pro něj v případě potřeby k dispozici další služby v DI.
  • Implementujte IClientModelValidator ve své ValidationAttribute třídě. Tato metoda může být vhodná, pokud atribut neprovádí žádné ověřování na straně serveru a nepotřebuje žádné služby z DI.

AtributAdapter pro ověřování na straně klienta

Tato metoda vykreslování data- atributů v HTML se používá ClassicMovie atributem v ukázkové aplikaci. Pokud chcete přidat ověření klienta pomocí této metody:

  1. Vytvořte třídu adaptéru atributu pro vlastní ověřovací atribut. Odvození třídy z AttributeAdapterBase<TAttribute>. Vytvořte metodu AddValidation , která přidá data- atributy do vykresleného výstupu, jak je znázorněno v tomto příkladu:

    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. Vytvořte třídu zprostředkovatele adaptéru, která implementuje IValidationAttributeAdapterProvider. GetAttributeAdapter V metodě předejte vlastní atribut konstruktoru adaptéru, jak je znázorněno v tomto příkladu:

    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. Zaregistrujte poskytovatele adaptéru pro DI v Program.cs:

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

IClientModelValidator pro ověřování na straně klienta

Tato metoda vykreslování data- atributů v HTML se používá ClassicMovieWithClientValidator atributem v ukázkové aplikaci. Pokud chcete přidat ověření klienta pomocí této metody:

  • Ve vlastním ověřovacím atributu IClientModelValidator implementujte rozhraní a vytvořte metodu AddValidation . AddValidation V metodě přidejte data- atributy pro ověření, jak je znázorněno v následujícím příkladu:

    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;
        }
    }
    

Zakázání ověřování na straně klienta

Následující kód zakáže ověření klienta na Razor stránkách:

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

Další možnosti zakázání ověřování na straně klienta:

  • Zakomentujte odkaz na _ValidationScriptsPartial všechny .cshtml soubory.
  • Odeberte obsah souboru Pages\Shared_ValidationScriptsPartial.cshtml .

Předchozí přístup nezabrání ověřování ASP.NET knihovny základních IdentityRazor tříd na straně klienta. Další informace najdete v tématu Generování uživatelského rozhraní Identity v projektech ASP.NET Core.

Podrobnosti o problému

Podrobnosti o problému nejsou jediným formátem odpovědi, který popisuje chybu rozhraní HTTP API, ale běžně se používají k hlášení chyb pro rozhraní HTTP API.

Služba podrobností problému implementuje IProblemDetailsService rozhraní, které podporuje vytváření podrobností o problému v ASP.NET Core. Metoda AddProblemDetails rozšíření při IServiceCollection registraci výchozí IProblemDetailsService implementace.

V aplikacích ASP.NET Core generuje následující middleware při zavolání podrobnosti o problému odpovědi AddProblemDetails HTTP, s výjimkou případů, kdy Accept hlavička HTTP požadavku neobsahuje jeden z typů obsahu podporovaných zaregistrovaným IProblemDetailsWriter (výchozí: application/json):

Další prostředky

Tento článek vysvětluje, jak ověřit vstup uživatele v aplikaci ASP.NET Core MVC nebo Razor Pages.

Zobrazení nebo stažení vzorového kódu (postup stažení)

Stav modelu

Stav modelu představuje chyby, které pocházejí ze dvou subsystémů: vazby modelu a ověření modelu. Chyby, které pocházejí z vazby modelu, jsou obecně chyby převodu dat. Do celočíselného pole se například zadává "x". K ověření modelu dochází po vazbě modelu a hlásí chyby, kdy data nevyhovují obchodním pravidlům. Do pole, které očekává hodnocení od 1 do 5, je například zadáno číslo 0.

Vazby modelu i ověření modelu probíhají před spuštěním akce kontroleru nebo Razor metody obslužné rutiny Pages. U webových aplikací je tato aplikace zodpovědná za kontrolu ModelState.IsValid a správné reakce. Webové aplikace obvykle znovu zobrazí stránku chybovou zprávou, jak je znázorněno v následujícím Razor příkladu stránky:

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

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

    return RedirectToPage("./Index");
}

Pro ASP.NET Core MVC s řadiči a zobrazeními ukazuje následující příklad, jak zkontrolovat ModelState.IsValid uvnitř akce kontroleru:

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

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

    return RedirectToAction(nameof(Index));
}

Kontrolery webového rozhraní API nemusí kontrolovatModelState.IsValid, jestli mají atribut [ApiController]. V takovém případě se vrátí automatická odpověď HTTP 400 obsahující podrobnosti o chybě, když je stav modelu neplatný. Další informace naleznete v tématu Automatické odpovědi HTTP 400.

Znovu spustit ověření

Ověření je automatické, ale možná ho budete chtít opakovat ručně. Můžete například vypočítat hodnotu vlastnosti a po nastavení vlastnosti na vypočítanou hodnotu znovu spustit ověření. Pokud chcete znovu spustit ověření, zavolejte ModelStateDictionary.ClearValidationState na ověření specifické pro model, za nímž následuje 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");
}

Ověřovací atributy

Ověřovací atributy umožňují zadat ověřovací pravidla pro vlastnosti modelu. Následující příklad z ukázkové aplikace ukazuje třídu modelu, která je opatřena poznámkami s ověřovacími atributy. Atribut [ClassicMovie] je vlastní ověřovací atribut a ostatní jsou integrované. Nezobrazuje se , [ClassicMovieWithClientValidator]což ukazuje alternativní způsob implementace vlastního atributu.

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; }
}

Předdefinované atributy

Tady jsou některé předdefinované ověřovací atributy:

Úplný seznam ověřovacích atributů najdete v System.ComponentModel.DataAnnotations oboru názvů.

Chybové zprávy

Ověřovací atributy umožňují zadat chybovou zprávu, která se má zobrazit pro neplatný vstup. Příklad:

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

Atributy interně volají String.Format zástupný symbol pro název pole a někdy i další zástupné symboly. Příklad:

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

Při použití na Name vlastnost by chybová zpráva vytvořená předchozím kódem byla "Délka názvu musí být v rozmezí 6 až 8".

Pokud chcete zjistit, které parametry se předávají String.Format pro chybovou zprávu konkrétního atributu, přečtěte si zdrojový kód DataAnnotations.

Odkazové typy bez hodnoty null a atribut [Povinné]

Ověřovací systém zpracovává parametry bez hodnoty null nebo vázané vlastnosti, jako by měly [Required(AllowEmptyStrings = true)] atribut. Když povolíte Nullable kontexty, MVC implicitně spustí ověřování nenulových vlastností u jiných než obecných typů nebo parametrů, jako by byly přiřazeny atributem [Required(AllowEmptyStrings = true)] . Vezměte v úvahu následující kód:

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

Pokud byla aplikace sestavena s <Nullable>enable</Nullable>chybějící hodnotou Name v příspěvku JSo zapnutí nebo formuláři, dojde k chybě ověření. Chcete-li povolit zadání hodnot null nebo chybějících hodnot pro vlastnost, použijte typ odkazu s možnou Name hodnotou null:

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

Toto chování lze zakázat konfigurací SuppressImplicitRequiredAttributeForNonNullableReferenceTypes v Program.cs:

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

Nenulové vlastnosti u obecných typů a atribut [Povinné]

Vlastnosti bez hodnoty null u obecných typů musí obsahovat [Required] atribut, pokud je typ povinný. V následujícím kódu TestRequired se nevyžaduje:

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

V následujícím kódu TestRequired je explicitně označen jako povinný:

using System.ComponentModel.DataAnnotations;

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

[Povinné] Ověření na serveru

Na serveru je požadovaná hodnota považována za chybějící, pokud je vlastnost null. Pole bez hodnoty null je vždy platné a chybová zpráva atributu [Required] se nikdy nezobrazí.

Vazba modelu pro vlastnost, která není nullable však může selhat, což vede k chybové zprávě, například The value '' is invalid. Pokud chcete zadat vlastní chybovou zprávu pro ověřování typů, které nejsou s možnou hodnotou null, máte následující možnosti:

  • Nastavení pole s možnou hodnotou null (například decimal? místo decimal). Typy hodnot T> s možnou<hodnotou null jsou považovány za standardní typy s možnou hodnotou null.

  • Zadejte výchozí chybovou zprávu, kterou má použít vazba modelu, jak je znázorněno v následujícím příkladu:

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

    Další informace o chybách vazby modelu, pro které můžete nastavit výchozí zprávy, naleznete v tématu DefaultModelBindingMessageProvider.

[Požadováno] Ověření v klientovi

Typy a řetězce, které nemají hodnotu null, se na klientovi zpracovávají jinak než na serveru. Na klientovi:

  • Hodnota je považována za přítomnou pouze v případě, že je pro ni zadán vstup. Ověření na straně klienta proto zpracovává typy bez hodnoty null stejné jako typy s možnou hodnotou null.
  • Prázdné znaky v řetězcovém poli se považují za platný vstup požadovanou metodou jQuery Validation. Ověření na straně serveru považuje požadované pole řetězce za neplatné, pokud je zadáno pouze prázdné znaky.

Jak jsme uvedli dříve, typy, které nemají hodnotu null, se považují za [Required(AllowEmptyStrings = true)] atributy. To znamená, že získáte ověření na straně klienta, i když atribut nepoužijete [Required(AllowEmptyStrings = true)] . Pokud ale atribut nepoužíváte, zobrazí se výchozí chybová zpráva. Pokud chcete zadat vlastní chybovou zprávu, použijte atribut.

[Vzdálený] atribut

Atribut [Remote] implementuje ověření na straně klienta, které vyžaduje volání metody na serveru k určení, zda vstup pole je platný. Aplikace může například potřebovat ověřit, jestli se už používá uživatelské jméno.

Implementace vzdáleného ověřování:

  1. Vytvořte metodu akce pro volání JavaScriptu. Vzdálená metoda jQuery Validation očekává JSodpověď ON:

    • true znamená, že vstupní data jsou platná.
    • false, undefinednebo null znamená, že vstup je neplatný. Zobrazí výchozí chybovou zprávu.
    • Jakýkoli jiný řetězec znamená, že vstup je neplatný. Zobrazí řetězec jako vlastní chybovou zprávu.

    Tady je příklad metody akce, která vrací vlastní chybovou zprávu:

    [AcceptVerbs("GET", "POST")]
    public IActionResult VerifyEmail(string email)
    {
        if (!_userService.VerifyEmail(email))
        {
            return Json($"Email {email} is already in use.");
        }
    
        return Json(true);
    }
    
  2. Ve třídě modelu označte vlastnost atributem [Remote] , který odkazuje na metodu ověřovací akce, jak je znázorněno v následujícím příkladu:

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

Dodatečná pole

Vlastnost AdditionalFields atributu [Remote] umožňuje ověřit kombinace polí s daty na serveru. Pokud User má model například vlastnosti a LastName vlastnostiFirstName, můžete chtít ověřit, jestli už žádný existující uživatel nemá tuto dvojici jmen. Následující příklad ukazuje, jak používat 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 můžou být explicitně nastaveny na řetězce "FirstName" a "LastName", ale použití operátoru nameof zjednodušuje pozdější refaktoring. Metoda akce pro toto ověření musí přijímat oba firstName argumenty i lastName argumenty:

[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);
}

Když uživatel zadá křestní jméno nebo příjmení, JavaScript provede vzdálené volání, aby zjistil, jestli byla pořízena dvojice jmen.

Pokud chcete ověřit dvě nebo více dalších polí, zadejte je jako seznam oddělený čárkami. Pokud například chcete do modelu přidat MiddleName vlastnost, nastavte [Remote] atribut, jak je znázorněno v následujícím příkladu:

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

AdditionalFields, stejně jako všechny argumenty atributů, musí být konstantní výraz. Proto nepoužívejte interpolovaný řetězec ani volání Join k inicializaci AdditionalFields.

Alternativy k integrovaným atributům

Pokud potřebujete ověření, které neposkytuje předdefinované atributy, můžete:

Vlastní atributy

Ve scénářích, které předdefinované ověřovací atributy nezpracují, můžete vytvořit vlastní ověřovací atributy. Vytvořte třídu, která dědí z ValidationAttribute, a přepsat metodu IsValid .

Metoda IsValid přijímá objekt pojmenovanou hodnotu, což je vstup, který se má ověřit. Přetížení také přijímá ValidationContext objekt, který poskytuje další informace, jako je instance modelu vytvořená vazbou modelu.

Následující příklad ověří, že datum vydání filmu v klasickém žánru není pozdější než zadaný rok. Atribut [ClassicMovie] :

  • Běží jenom na serveru.
  • U klasických filmů ověří datum vydání:
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;
    }
}

Proměnná movie v předchozím příkladu představuje Movie objekt, který obsahuje data z odeslání formuláře. Pokud se ověření nezdaří, ValidationResult vrátí se chybová zpráva.

IValidatableObject

Předchozí příklad funguje pouze s Movie typy. Další možností ověřování na úrovni třídy je implementace IValidatableObject ve třídě modelu, jak je znázorněno v následujícím příkladu:

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) });
        }
    }
}

Ověření uzlu nejvyšší úrovně

Mezi uzly nejvyšší úrovně patří:

  • Parametry akce
  • Vlastnosti kontroleru
  • Parametry obslužné rutiny stránky
  • Vlastnosti modelu stránky

Uzly nejvyšší úrovně vázané na model se kromě ověřování vlastností modelu ověřují. V následujícím příkladu z ukázkové aplikaceVerifyPhone metoda použije RegularExpressionAttribute k ověření parametru phone akce:

[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);
}

Uzly nejvyšší úrovně se můžou používat BindRequiredAttribute s ověřovacími atributy. V následujícím příkladu z ukázkové aplikace metoda určuje, CheckAge že age parametr musí být při odeslání formuláře vázán z řetězce dotazu:

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

Na stránce Check Age (CheckAge.cshtml) jsou dva formuláře. První formulář odešle Age hodnotu 99 jako parametr řetězce dotazu: https://localhost:5001/Users/CheckAge?Age=99.

Při odeslání správně naformátovaného age parametru z řetězce dotazu se formulář ověří.

Druhý formulář na stránce Kontrola věku odešle Age hodnotu v textu požadavku a ověření selže. Vazba selže, protože age parametr musí pocházet z řetězce dotazu.

Maximální počet chyb

Ověření se zastaví, když se dosáhne maximálního počtu chyb (ve výchozím nastavení 200). Toto číslo můžete nakonfigurovat následujícím kódem:Program.cs

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

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

Maximální rekurze

ValidationVisitor prochází graf objektů modelu, který se ověřuje. U modelů, které jsou hluboké nebo nekonečně rekurzivní, může ověření vést k přetečení zásobníku. MvcOptions.MaxValidationDepth poskytuje způsob, jak včas zastavit ověřování, pokud rekurze návštěvníka překročí nakonfigurovanou hloubku. Výchozí hodnota MvcOptions.MaxValidationDepth je 32.

Automatický zkrat

Pokud graf modelu nevyžaduje ověření, je ověření automaticky zkrácené (vynecháno). Objekty, které modul runtime přeskočí ověřování, zahrnují kolekce primitiv (například byte[], string[]), Dictionary<string, string>a komplexní objektové grafy, které nemají žádné validátory.

Ověřování na straně klienta

Ověření na straně klienta zabraňuje odeslání, dokud formulář není platný. Tlačítko Odeslat spustí JavaScript, který odešle formulář nebo zobrazí chybové zprávy.

Ověřování na straně klienta zabraňuje zbytečnému zaokrouhlení na server, pokud ve formuláři dojde k chybám vstupu. Následující odkazy na skripty podporují _Layout.cshtml_ValidationScriptsPartial.cshtml ověřování na straně klienta:

<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>

Skript jQuery Unobtrusive Validation je vlastní front-end knihovna Microsoftu, která je založená na oblíbeném modulu plug-in jQuery Validation. Bez ověřování jQuery Byste museli kódovat stejnou logiku ověřování na dvou místech: jednou v atributech ověřování na straně serveru ve vlastnostech modelu a pak znovu ve skriptech na straně klienta. Místo toho pomocné rutiny značek a pomocné rutiny HTML používají ověřovací atributy a zapisují metadata z vlastností modelu k vykreslení atributů HTML 5 data- pro prvky formuláře, které potřebují ověření. jQuery Unobtrusive Validation parsuje data- atributy a předává logiku do jQuery Validation, a to efektivně "kopírování" logiky ověření na straně serveru klientovi. Chyby ověřování můžete v klientovi zobrazit pomocí pomocných rutin značek, jak je znázorněno tady:

<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>

Předchozí pomocné rutiny značek vykreslují následující kód 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>

Všimněte si, že data- atributy ve výstupu HTML odpovídají ověřovacím atributům Movie.ReleaseDate vlastnosti. Atribut data-val-required obsahuje chybovou zprávu, která se zobrazí, pokud uživatel nevyplní pole data vydání. jQuery Unobtrusive Validation předá tuto hodnotu do metody jQuery Validation required(), která pak zobrazí tuto zprávu v doprovodném <span> elementu.

Ověření datového typu je založeno na typu .NET vlastnosti, pokud není přepsán atributem [DataType]. Prohlížeče mají vlastní výchozí chybové zprávy, ale balíček jQuery Validation Unobsive Validation může tyto zprávy přepsat. [DataType] atributy a podtřídy, například [EmailAddress] umožňují zadat chybovou zprávu.

Nerušivé ověřování

Informace o nerušivém ověřování najdete v tomto problému na GitHubu.

Přidání ověřování do dynamických formulářů

jQuery Unobtrusive Validation předá ověřovací logiku a parametry do jQuery Validation při prvním načtení stránky. Proto ověřování nefunguje automaticky na dynamicky generovaných formulářích. Pokud chcete povolit ověřování, řekněte jQuery Unobtrusive Validation, aby dynamický formulář parsovat hned po jeho vytvoření. Například následující kód nastaví ověřování na straně klienta ve formuláři přidaném přes 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);
    }
})

Metoda $.validator.unobtrusive.parse() přijímá pro svůj jeden argument selektor jQuery. Tato metoda říká jQuery Unobtrusive Validation parsovat data- atributy formulářů v rámci tohoto selektoru. Hodnoty těchto atributů se pak předají modulu plug-in jQuery Validation.

Přidání ověření do dynamických ovládacích prvků

Metoda $.validator.unobtrusive.parse() funguje na celém formuláři, ne na jednotlivých dynamicky generovaných ovládacích prvcích, například <input> a <select/>. Chcete-li formulář znovu provést, odeberte ověřovací data přidaná při analýze formuláře dříve, jak je znázorněno v následujícím příkladu:

$.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);
    }
})

Vlastní ověřování na straně klienta

Vlastní ověřování na straně klienta se provádí generováním data- atributů HTML, které pracují s vlastním adaptérem ověřování jQuery. Následující ukázkový kód adaptéru byl napsán pro [ClassicMovie][ClassicMovieWithClientValidator] atributy, které byly zavedeny dříve v tomto článku:

$.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;
});

Informace o zápisu adaptérů najdete v dokumentaci k ověřování jQuery.

Použití adaptéru pro dané pole je aktivováno data- atributy, které:

  • Označte pole příznakem jako předmětem ověření (data-val="true").
  • Identifikujte název ověřovacího pravidla a text chybové zprávy (například data-val-rulename="Error message.").
  • Zadejte všechny další parametry, které validátor potřebuje (například data-val-rulename-param1="value").

Následující příklad ukazuje data- atributy atributu ukázkové aplikaceClassicMovie :

<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="">

Jak jsme si poznamenali dříve, pomocné rutiny značek a pomocné rutiny HTML používají k vykreslení data- atributů informace z ověřovacích atributů. Existují dvě možnosti pro psaní kódu, které mají za následek vytvoření vlastních data- atributů HTML:

  • Vytvořte třídu odvozenou od AttributeAdapterBase<TAttribute> třídy a třídy, která implementuje IValidationAttributeAdapterProvider, a zaregistrujte atribut a jeho adaptér v DI. Tato metoda se řídí principem jedné odpovědnosti v tom, že ověřovací kód související se serverem a klientem je v samostatných třídách. Adaptér má také výhodu, že vzhledem k tomu, že je zaregistrovaný v DI, jsou pro něj v případě potřeby k dispozici další služby v DI.
  • Implementujte IClientModelValidator ve své ValidationAttribute třídě. Tato metoda může být vhodná, pokud atribut neprovádí žádné ověřování na straně serveru a nepotřebuje žádné služby z DI.

AtributAdapter pro ověřování na straně klienta

Tato metoda vykreslování data- atributů v HTML se používá ClassicMovie atributem v ukázkové aplikaci. Pokud chcete přidat ověření klienta pomocí této metody:

  1. Vytvořte třídu adaptéru atributu pro vlastní ověřovací atribut. Odvození třídy z AttributeAdapterBase<TAttribute>. Vytvořte metodu AddValidation , která přidá data- atributy do vykresleného výstupu, jak je znázorněno v tomto příkladu:

    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. Vytvořte třídu zprostředkovatele adaptéru, která implementuje IValidationAttributeAdapterProvider. GetAttributeAdapter V metodě předejte vlastní atribut konstruktoru adaptéru, jak je znázorněno v tomto příkladu:

    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. Zaregistrujte poskytovatele adaptéru pro DI v Program.cs:

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

IClientModelValidator pro ověřování na straně klienta

Tato metoda vykreslování data- atributů v HTML se používá ClassicMovieWithClientValidator atributem v ukázkové aplikaci. Pokud chcete přidat ověření klienta pomocí této metody:

  • Ve vlastním ověřovacím atributu IClientModelValidator implementujte rozhraní a vytvořte metodu AddValidation . AddValidation V metodě přidejte data- atributy pro ověření, jak je znázorněno v následujícím příkladu:

    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;
        }
    }
    

Zakázání ověřování na straně klienta

Následující kód zakáže ověření klienta na Razor stránkách:

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

Další možnosti zakázání ověřování na straně klienta:

  • Zakomentujte odkaz na _ValidationScriptsPartial všechny .cshtml soubory.
  • Odeberte obsah souboru Pages\Shared_ValidationScriptsPartial.cshtml .

Předchozí přístup nezabrání ověřování ASP.NET knihovny základních IdentityRazor tříd na straně klienta. Další informace najdete v tématu Generování uživatelského rozhraní Identity v projektech ASP.NET Core.

Další prostředky

Tento článek vysvětluje, jak ověřit vstup uživatele v aplikaci ASP.NET Core MVC nebo Razor Pages.

Zobrazení nebo stažení vzorového kódu (postup stažení)

Stav modelu

Stav modelu představuje chyby, které pocházejí ze dvou subsystémů: vazby modelu a ověření modelu. Chyby, které pocházejí z vazby modelu, jsou obecně chyby převodu dat. Do celočíselného pole se například zadává "x". K ověření modelu dochází po vazbě modelu a hlásí chyby, kdy data nevyhovují obchodním pravidlům. Do pole, které očekává hodnocení od 1 do 5, je například zadáno číslo 0.

Vazby modelu i ověření modelu probíhají před spuštěním akce kontroleru nebo Razor metody obslužné rutiny Pages. U webových aplikací je tato aplikace zodpovědná za kontrolu ModelState.IsValid a správné reakce. Webové aplikace obvykle znovu zobrazí stránku s chybovou zprávou:

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

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

    return RedirectToPage("./Index");
}

Kontrolery webového rozhraní API nemusí kontrolovatModelState.IsValid, jestli mají atribut [ApiController]. V takovém případě se vrátí automatická odpověď HTTP 400 obsahující podrobnosti o chybě, když je stav modelu neplatný. Další informace naleznete v tématu Automatické odpovědi HTTP 400.

Znovu spustit ověření

Ověření je automatické, ale možná ho budete chtít opakovat ručně. Můžete například vypočítat hodnotu vlastnosti a po nastavení vlastnosti na vypočítanou hodnotu znovu spustit ověření. Pokud chcete znovu spustit ověření, zavolejte ModelStateDictionary.ClearValidationState na ověření specifické pro model, za nímž následuje 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");
}

Ověřovací atributy

Ověřovací atributy umožňují zadat ověřovací pravidla pro vlastnosti modelu. Následující příklad z ukázkové aplikace ukazuje třídu modelu, která je opatřena poznámkami s ověřovacími atributy. Atribut [ClassicMovie] je vlastní ověřovací atribut a ostatní jsou integrované. Nezobrazuje se , [ClassicMovieWithClientValidator]což ukazuje alternativní způsob implementace vlastního atributu.

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; }
}

Předdefinované atributy

Tady jsou některé předdefinované ověřovací atributy:

Úplný seznam ověřovacích atributů najdete v System.ComponentModel.DataAnnotations oboru názvů.

Chybové zprávy

Ověřovací atributy umožňují zadat chybovou zprávu, která se má zobrazit pro neplatný vstup. Příklad:

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

Atributy interně volají String.Format zástupný symbol pro název pole a někdy i další zástupné symboly. Příklad:

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

Při použití na Name vlastnost by chybová zpráva vytvořená předchozím kódem byla "Délka názvu musí být v rozmezí 6 až 8".

Pokud chcete zjistit, které parametry se předávají String.Format pro chybovou zprávu konkrétního atributu, přečtěte si zdrojový kód DataAnnotations.

Odkazové typy bez hodnoty null a atribut [Povinné]

Ověřovací systém zpracovává parametry bez hodnoty null nebo vázané vlastnosti, jako by měly [Required(AllowEmptyStrings = true)] atribut. Když povolíte Nullable kontexty, MVC implicitně začne ověřovat nenulové vlastnosti nebo parametry, jako by byly přiřazeny atributem[Required(AllowEmptyStrings = true)]. Vezměte v úvahu následující kód:

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

Pokud byla aplikace sestavena s <Nullable>enable</Nullable>chybějící hodnotou Name v příspěvku JSo zapnutí nebo formuláři, dojde k chybě ověření. Chcete-li povolit zadání hodnot null nebo chybějících hodnot pro vlastnost, použijte typ odkazu s možnou Name hodnotou null:

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

Toto chování lze zakázat konfigurací SuppressImplicitRequiredAttributeForNonNullableReferenceTypes v Startup.ConfigureServices:

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

[Povinné] Ověření na serveru

Na serveru je požadovaná hodnota považována za chybějící, pokud je vlastnost null. Pole bez hodnoty null je vždy platné a chybová zpráva atributu [Required] se nikdy nezobrazí.

Vazba modelu pro vlastnost, která není nullable však může selhat, což vede k chybové zprávě, například The value '' is invalid. Pokud chcete zadat vlastní chybovou zprávu pro ověřování typů, které nejsou s možnou hodnotou null, máte následující možnosti:

  • Nastavení pole s možnou hodnotou null (například decimal? místo decimal). Typy hodnot T> s možnou<hodnotou null jsou považovány za standardní typy s možnou hodnotou null.

  • Zadejte výchozí chybovou zprávu, kterou má použít vazba modelu, jak je znázorněno v následujícím příkladu:

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

    Další informace o chybách vazby modelu, pro které můžete nastavit výchozí zprávy, naleznete v tématu DefaultModelBindingMessageProvider.

[Požadováno] Ověření v klientovi

Typy a řetězce, které nemají hodnotu null, se na klientovi zpracovávají jinak než na serveru. Na klientovi:

  • Hodnota je považována za přítomnou pouze v případě, že je pro ni zadán vstup. Ověření na straně klienta proto zpracovává typy bez hodnoty null stejné jako typy s možnou hodnotou null.
  • Prázdné znaky v řetězcovém poli se považují za platný vstup požadovanou metodou jQuery Validation. Ověření na straně serveru považuje požadované pole řetězce za neplatné, pokud je zadáno pouze prázdné znaky.

Jak jsme uvedli dříve, typy, které nemají hodnotu null, se považují za [Required(AllowEmptyStrings = true)] atributy. To znamená, že získáte ověření na straně klienta, i když atribut nepoužijete [Required(AllowEmptyStrings = true)] . Pokud ale atribut nepoužíváte, zobrazí se výchozí chybová zpráva. Pokud chcete zadat vlastní chybovou zprávu, použijte atribut.

[Vzdálený] atribut

Atribut [Remote] implementuje ověření na straně klienta, které vyžaduje volání metody na serveru k určení, zda vstup pole je platný. Aplikace může například potřebovat ověřit, jestli se už používá uživatelské jméno.

Implementace vzdáleného ověřování:

  1. Vytvořte metodu akce pro volání JavaScriptu. Vzdálená metoda jQuery Validation očekává JSodpověď ON:

    • true znamená, že vstupní data jsou platná.
    • false, undefinednebo null znamená, že vstup je neplatný. Zobrazí výchozí chybovou zprávu.
    • Jakýkoli jiný řetězec znamená, že vstup je neplatný. Zobrazí řetězec jako vlastní chybovou zprávu.

    Tady je příklad metody akce, která vrací vlastní chybovou zprávu:

    [AcceptVerbs("GET", "POST")]
    public IActionResult VerifyEmail(string email)
    {
        if (!_userService.VerifyEmail(email))
        {
            return Json($"Email {email} is already in use.");
        }
    
        return Json(true);
    }
    
  2. Ve třídě modelu označte vlastnost atributem [Remote] , který odkazuje na metodu ověřovací akce, jak je znázorněno v následujícím příkladu:

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

Dodatečná pole

Vlastnost AdditionalFields atributu [Remote] umožňuje ověřit kombinace polí s daty na serveru. Pokud User má model například vlastnosti a LastName vlastnostiFirstName, můžete chtít ověřit, jestli už žádný existující uživatel nemá tuto dvojici jmen. Následující příklad ukazuje, jak používat 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 můžou být explicitně nastaveny na řetězce "FirstName" a "LastName", ale použití operátoru nameof zjednodušuje pozdější refaktoring. Metoda akce pro toto ověření musí přijímat oba firstName argumenty i lastName argumenty:

[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);
}

Když uživatel zadá křestní jméno nebo příjmení, JavaScript provede vzdálené volání, aby zjistil, jestli byla pořízena dvojice jmen.

Pokud chcete ověřit dvě nebo více dalších polí, zadejte je jako seznam oddělený čárkami. Pokud například chcete do modelu přidat MiddleName vlastnost, nastavte [Remote] atribut, jak je znázorněno v následujícím příkladu:

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

AdditionalFields, stejně jako všechny argumenty atributů, musí být konstantní výraz. Proto nepoužívejte interpolovaný řetězec ani volání Join k inicializaci AdditionalFields.

Alternativy k integrovaným atributům

Pokud potřebujete ověření, které neposkytuje předdefinované atributy, můžete:

Vlastní atributy

Ve scénářích, které předdefinované ověřovací atributy nezpracují, můžete vytvořit vlastní ověřovací atributy. Vytvořte třídu, která dědí z ValidationAttribute, a přepsat metodu IsValid .

Metoda IsValid přijímá objekt pojmenovanou hodnotu, což je vstup, který se má ověřit. Přetížení také přijímá ValidationContext objekt, který poskytuje další informace, jako je instance modelu vytvořená vazbou modelu.

Následující příklad ověří, že datum vydání filmu v klasickém žánru není pozdější než zadaný rok. Atribut [ClassicMovie] :

  • Běží jenom na serveru.
  • U klasických filmů ověří datum vydání:
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;
    }
}

Proměnná movie v předchozím příkladu představuje Movie objekt, který obsahuje data z odeslání formuláře. Pokud se ověření nezdaří, ValidationResult vrátí se chybová zpráva.

IValidatableObject

Předchozí příklad funguje pouze s Movie typy. Další možností ověřování na úrovni třídy je implementace IValidatableObject ve třídě modelu, jak je znázorněno v následujícím příkladu:

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) });
        }
    }
}

Ověření uzlu nejvyšší úrovně

Mezi uzly nejvyšší úrovně patří:

  • Parametry akce
  • Vlastnosti kontroleru
  • Parametry obslužné rutiny stránky
  • Vlastnosti modelu stránky

Uzly nejvyšší úrovně vázané na model se kromě ověřování vlastností modelu ověřují. V následujícím příkladu z ukázkové aplikaceVerifyPhone metoda použije RegularExpressionAttribute k ověření parametru phone akce:

[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);
}

Uzly nejvyšší úrovně se můžou používat BindRequiredAttribute s ověřovacími atributy. V následujícím příkladu z ukázkové aplikace metoda určuje, CheckAge že age parametr musí být při odeslání formuláře vázán z řetězce dotazu:

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

Na stránce Check Age (CheckAge.cshtml) jsou dva formuláře. První formulář odešle Age hodnotu 99 jako parametr řetězce dotazu: https://localhost:5001/Users/CheckAge?Age=99.

Při odeslání správně naformátovaného age parametru z řetězce dotazu se formulář ověří.

Druhý formulář na stránce Kontrola věku odešle Age hodnotu v textu požadavku a ověření selže. Vazba selže, protože age parametr musí pocházet z řetězce dotazu.

Maximální počet chyb

Ověření se zastaví, když se dosáhne maximálního počtu chyb (ve výchozím nastavení 200). Toto číslo můžete nakonfigurovat následujícím kódem:Startup.ConfigureServices

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

services.AddSingleton<IValidationAttributeAdapterProvider,
    CustomValidationAttributeAdapterProvider>();

Maximální rekurze

ValidationVisitor prochází graf objektů modelu, který se ověřuje. U modelů, které jsou hluboké nebo nekonečně rekurzivní, může ověření vést k přetečení zásobníku. MvcOptions.MaxValidationDepth poskytuje způsob, jak včas zastavit ověřování, pokud rekurze návštěvníka překročí nakonfigurovanou hloubku. Výchozí hodnota MvcOptions.MaxValidationDepth je 32.

Automatický zkrat

Pokud graf modelu nevyžaduje ověření, je ověření automaticky zkrácené (vynecháno). Objekty, které modul runtime přeskočí ověřování, zahrnují kolekce primitiv (například byte[], string[]), Dictionary<string, string>a komplexní objektové grafy, které nemají žádné validátory.

Ověřování na straně klienta

Ověření na straně klienta zabraňuje odeslání, dokud formulář není platný. Tlačítko Odeslat spustí JavaScript, který odešle formulář nebo zobrazí chybové zprávy.

Ověřování na straně klienta zabraňuje zbytečnému zaokrouhlení na server, pokud ve formuláři dojde k chybám vstupu. Následující odkazy na skripty podporují _Layout.cshtml_ValidationScriptsPartial.cshtml ověřování na straně klienta:

<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>

Skript jQuery Unobtrusive Validation je vlastní front-end knihovna Microsoftu, která je založená na oblíbeném modulu plug-in jQuery Validation. Bez ověřování jQuery Byste museli kódovat stejnou logiku ověřování na dvou místech: jednou v atributech ověřování na straně serveru ve vlastnostech modelu a pak znovu ve skriptech na straně klienta. Místo toho pomocné rutiny značek a pomocné rutiny HTML používají ověřovací atributy a zapisují metadata z vlastností modelu k vykreslení atributů HTML 5 data- pro prvky formuláře, které potřebují ověření. jQuery Unobtrusive Validation parsuje data- atributy a předává logiku do jQuery Validation, a to efektivně "kopírování" logiky ověření na straně serveru klientovi. Chyby ověřování můžete v klientovi zobrazit pomocí pomocných rutin značek, jak je znázorněno tady:

<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>

Předchozí pomocné rutiny značek vykreslují následující kód 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>

Všimněte si, že data- atributy ve výstupu HTML odpovídají ověřovacím atributům Movie.ReleaseDate vlastnosti. Atribut data-val-required obsahuje chybovou zprávu, která se zobrazí, pokud uživatel nevyplní pole data vydání. jQuery Unobtrusive Validation předá tuto hodnotu do metody jQuery Validation required(), která pak zobrazí tuto zprávu v doprovodném <span> elementu.

Ověření datového typu je založeno na typu .NET vlastnosti, pokud není přepsán atributem [DataType]. Prohlížeče mají vlastní výchozí chybové zprávy, ale balíček jQuery Validation Unobsive Validation může tyto zprávy přepsat. [DataType] atributy a podtřídy, například [EmailAddress] umožňují zadat chybovou zprávu.

Nerušivé ověřování

Informace o nerušivém ověřování najdete v tomto problému na GitHubu.

Přidání ověřování do dynamických formulářů

jQuery Unobtrusive Validation předá ověřovací logiku a parametry do jQuery Validation při prvním načtení stránky. Proto ověřování nefunguje automaticky na dynamicky generovaných formulářích. Pokud chcete povolit ověřování, řekněte jQuery Unobtrusive Validation, aby dynamický formulář parsovat hned po jeho vytvoření. Například následující kód nastaví ověřování na straně klienta ve formuláři přidaném přes 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);
    }
})

Metoda $.validator.unobtrusive.parse() přijímá pro svůj jeden argument selektor jQuery. Tato metoda říká jQuery Unobtrusive Validation parsovat data- atributy formulářů v rámci tohoto selektoru. Hodnoty těchto atributů se pak předají modulu plug-in jQuery Validation.

Přidání ověření do dynamických ovládacích prvků

Metoda $.validator.unobtrusive.parse() funguje na celém formuláři, ne na jednotlivých dynamicky generovaných ovládacích prvcích, například <input> a <select/>. Chcete-li formulář znovu provést, odeberte ověřovací data přidaná při analýze formuláře dříve, jak je znázorněno v následujícím příkladu:

$.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);
    }
})

Vlastní ověřování na straně klienta

Vlastní ověřování na straně klienta se provádí generováním data- atributů HTML, které pracují s vlastním adaptérem ověřování jQuery. Následující ukázkový kód adaptéru byl napsán pro [ClassicMovie][ClassicMovieWithClientValidator] atributy, které byly zavedeny dříve v tomto článku:

$.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;
});

Informace o zápisu adaptérů najdete v dokumentaci k ověřování jQuery.

Použití adaptéru pro dané pole je aktivováno data- atributy, které:

  • Označte pole příznakem jako předmětem ověření (data-val="true").
  • Identifikujte název ověřovacího pravidla a text chybové zprávy (například data-val-rulename="Error message.").
  • Zadejte všechny další parametry, které validátor potřebuje (například data-val-rulename-param1="value").

Následující příklad ukazuje data- atributy atributu ukázkové aplikaceClassicMovie :

<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="">

Jak jsme si poznamenali dříve, pomocné rutiny značek a pomocné rutiny HTML používají k vykreslení data- atributů informace z ověřovacích atributů. Existují dvě možnosti pro psaní kódu, které mají za následek vytvoření vlastních data- atributů HTML:

  • Vytvořte třídu odvozenou od AttributeAdapterBase<TAttribute> třídy a třídy, která implementuje IValidationAttributeAdapterProvider, a zaregistrujte atribut a jeho adaptér v DI. Tato metoda se řídí principem jedné odpovědnosti v tom, že ověřovací kód související se serverem a klientem je v samostatných třídách. Adaptér má také výhodu, že vzhledem k tomu, že je zaregistrovaný v DI, jsou pro něj v případě potřeby k dispozici další služby v DI.
  • Implementujte IClientModelValidator ve své ValidationAttribute třídě. Tato metoda může být vhodná, pokud atribut neprovádí žádné ověřování na straně serveru a nepotřebuje žádné služby z DI.

AtributAdapter pro ověřování na straně klienta

Tato metoda vykreslování data- atributů v HTML se používá ClassicMovie atributem v ukázkové aplikaci. Pokud chcete přidat ověření klienta pomocí této metody:

  1. Vytvořte třídu adaptéru atributu pro vlastní ověřovací atribut. Odvození třídy z AttributeAdapterBase<TAttribute>. Vytvořte metodu AddValidation , která přidá data- atributy do vykresleného výstupu, jak je znázorněno v tomto příkladu:

    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. Vytvořte třídu zprostředkovatele adaptéru, která implementuje IValidationAttributeAdapterProvider. GetAttributeAdapter V metodě předejte vlastní atribut konstruktoru adaptéru, jak je znázorněno v tomto příkladu:

    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. Zaregistrujte poskytovatele adaptéru pro DI v Startup.ConfigureServices:

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

IClientModelValidator pro ověřování na straně klienta

Tato metoda vykreslování data- atributů v HTML se používá ClassicMovieWithClientValidator atributem v ukázkové aplikaci. Pokud chcete přidat ověření klienta pomocí této metody:

  • Ve vlastním ověřovacím atributu IClientModelValidator implementujte rozhraní a vytvořte metodu AddValidation . AddValidation V metodě přidejte data- atributy pro ověření, jak je znázorněno v následujícím příkladu:

    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;
        }
    }
    

Zakázání ověřování na straně klienta

Následující kód zakáže ověření klienta na Razor stránkách:

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

Další možnosti zakázání ověřování na straně klienta:

  • Zakomentujte odkaz na _ValidationScriptsPartial všechny .cshtml soubory.
  • Odeberte obsah souboru Pages\Shared_ValidationScriptsPartial.cshtml .

Předchozí přístup nezabrání ověřování ASP.NET knihovny základních IdentityRazor tříd na straně klienta. Další informace najdete v tématu Generování uživatelského rozhraní Identity v projektech ASP.NET Core.

Další prostředky