Walidacja modelu w usłudze ASP.NET Core MVC i Razor pages

W tym artykule wyjaśniono, jak zweryfikować dane wejściowe użytkownika w aplikacji ASP.NET Core MVC lub Razor Pages.

Wyświetl lub pobierz przykładowy kod (jak pobrać).

Stan modelu

Stan modelu reprezentuje błędy pochodzące z dwóch podsystemów: powiązanie modelu i walidację modelu. Błędy pochodzące z powiązania modelu to zazwyczaj błędy konwersji danych. Na przykład znak "x" jest wprowadzany w polu liczby całkowitej. Walidacja modelu odbywa się po powiązaniu modelu i zgłasza błędy, w których dane nie są zgodne z regułami biznesowymi. Na przykład wartość 0 jest wprowadzana w polu, które oczekuje oceny z zakresu od 1 do 5.

Zarówno powiązanie modelu, jak i walidacja modelu są wykonywane przed wykonaniem akcji kontrolera lub Razor metody obsługi stron. W przypadku aplikacji internetowych obowiązkiem aplikacji jest odpowiednie sprawdzenie ModelState.IsValid i reagowanie na nie. Aplikacje internetowe zwykle redisplay strony z komunikatem o błędzie, jak pokazano w poniższym Razor przykładzie stron:

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

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

    return RedirectToPage("./Index");
}

W przypadku ASP.NET Core MVC z kontrolerami i widokami w poniższym przykładzie pokazano, jak sprawdzić ModelState.IsValid wewnątrz akcji kontrolera:

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 internetowego interfejsu API nie muszą sprawdzaćModelState.IsValid, czy mają atrybut [ApiController]. W takim przypadku zwracana jest automatyczna odpowiedź HTTP 400 zawierająca szczegóły błędu, gdy stan modelu jest nieprawidłowy. Aby uzyskać więcej informacji, zobacz Automatyczne odpowiedzi HTTP 400.

Ponowne uruchamianie walidacji

Walidacja jest automatyczna, ale warto ją powtórzyć ręcznie. Na przykład można obliczyć wartość właściwości i chcieć ponownie uruchomić walidację po ustawieniu właściwości na obliczoną wartość. Aby ponownie uruchomić walidację, wywołaj polecenie ModelStateDictionary.ClearValidationState , aby wyczyścić walidację specyficzną dla weryfikowanego modelu, a następnie :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");
}

Atrybuty weryfikacji

Atrybuty weryfikacji umożliwiają określenie reguł walidacji dla właściwości modelu. Poniższy przykład z przykładowej aplikacji przedstawia klasę modelu z adnotacjami z atrybutami walidacji. Atrybut [ClassicMovie] jest atrybutem weryfikacji niestandardowej, a pozostałe są wbudowane. Nie pokazano elementu , [ClassicMovieWithClientValidator]który pokazuje alternatywny sposób implementowania atrybutu niestandardowego.

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

Atrybuty wbudowane

Oto niektóre wbudowane atrybuty weryfikacji:

  • [ValidateNever]: Wskazuje, że właściwość lub parametr powinien zostać wykluczony z walidacji.
  • [CreditCard]: Sprawdza, czy właściwość ma format karty kredytowej. Wymaga dodatkowych metod weryfikacji jQuery.
  • [Porównaj]: Sprawdza, czy dwie właściwości w modelu są zgodne.
  • [EmailAddress]: Sprawdza, czy właściwość ma format wiadomości e-mail.
  • [Telefon]: Sprawdza, czy właściwość ma format numeru telefonu.
  • [Zakres]: Sprawdza, czy wartość właściwości mieści się w określonym zakresie.
  • [RegularExpression]: Sprawdza, czy wartość właściwości jest zgodna z określonym wyrażeniem regularnym.
  • [Wymagane]: sprawdza, czy pole nie ma wartości null. Zobacz [Required] atrybut , aby uzyskać szczegółowe informacje o zachowaniu tego atrybutu.
  • [StringLength]: Sprawdza, czy wartość właściwości ciągu nie przekracza określonego limitu długości.
  • [Url]: sprawdza, czy właściwość ma format adresu URL.
  • [Zdalne]: Weryfikuje dane wejściowe na kliencie przez wywołanie metody akcji na serwerze. Zobacz [Remote] atrybut , aby uzyskać szczegółowe informacje o zachowaniu tego atrybutu.

Pełną listę atrybutów weryfikacji można znaleźć w System.ComponentModel.DataAnnotations przestrzeni nazw.

Komunikaty o błędach

Atrybuty weryfikacji umożliwiają określenie komunikatu o błędzie, który ma być wyświetlany dla nieprawidłowych danych wejściowych. Przykład:

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

Wewnętrznie atrybuty są wywoływane String.Format z symbolem zastępczym nazwy pola, a czasami dodatkowymi symbolami zastępczymi. Przykład:

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

Po zastosowaniu Name do właściwości komunikat o błędzie utworzony przez poprzedni kod będzie miał wartość "Długość nazwy musi należeć do przedziału od 6 do 8".

Aby dowiedzieć się, które parametry są przekazywane dla String.Format komunikatu o błędzie określonego atrybutu, zobacz kod źródłowy DataAnnotations.

Używanie JSnazw właściwości ON w błędach walidacji

Domyślnie, gdy wystąpi błąd weryfikacji, walidacja modelu generuje ModelStateDictionary element o nazwie właściwości jako klucz błędu. Niektóre aplikacje, takie jak aplikacje jednostronicowe, korzystają z używania JSnazw właściwości ON na potrzeby błędów walidacji generowanych przez internetowe interfejsy API. Poniższy kod konfiguruje walidację, aby używać SystemTextJsonValidationMetadataProviderJSnazwy właściwości 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();

Poniższy kod konfiguruje walidację tak, aby używała NewtonsoftJsonValidationMetadataProviderJSnazwy właściwości ON w przypadku używania Json.NET:

using Microsoft.AspNetCore.Mvc.NewtonsoftJson;

var builder = WebApplication.CreateBuilder(args);

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

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Aby zapoznać się z przykładem zasad, które mają być używane wielbłąda, zobacz Program.cs w witrynie GitHub.

Typy odwołań bez wartości null i atrybut [Wymagane]

System sprawdzania poprawności traktuje parametry niepuste lub powiązane właściwości tak, jakby miały [Required(AllowEmptyStrings = true)] atrybut. Włączając Nullable konteksty, MVC niejawnie rozpoczyna weryfikowanie właściwości lub parametrów, które nie mogą mieć wartości null, tak jakby zostały przypisane do atrybutu[Required(AllowEmptyStrings = true)]. Rozważ następujący kod:

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

Jeśli aplikacja została skompilowana przy użyciu <Nullable>enable</Nullable>polecenia , brakująca wartość w NameJSobszarze WŁ. lub wpis formularza spowoduje wystąpienie błędu weryfikacji. Może to wydawać się sprzeczne, ponieważ [Required(AllowEmptyStrings = true)] atrybut jest implikowany, ale jest to oczekiwane zachowanie, ponieważ puste ciągi są domyślnie konwertowane na wartość null. Użyj typu odwołania dopuszczanego do wartości null, aby zezwolić na określanie wartości null lub brakujących dla Name właściwości:

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

To zachowanie można wyłączyć, konfigurując w Program.csprogramie SuppressImplicitRequiredAttributeForNonNullableReferenceTypes :

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

[Wymagane] walidacja na serwerze

Na serwerze wymagana wartość jest uważana za brakującą, jeśli właściwość ma wartość null. Pole bez wartości null jest zawsze prawidłowe, a [Required] komunikat o błędzie atrybutu nigdy nie jest wyświetlany.

Jednak powiązanie modelu dla właściwości innej niż null może zakończyć się niepowodzeniem, co spowoduje wyświetlenie komunikatu o błędzie, takiego jak The value '' is invalid. Aby określić niestandardowy komunikat o błędzie na potrzeby weryfikacji po stronie serwera typów niezwiązanych z wartościami null, dostępne są następujące opcje:

  • Ustaw pole jako dopuszczane do wartości null (na przykład decimal? zamiast decimal). Typy wartości T> dopuszczane<do wartości null są traktowane jak standardowe typy dopuszczane do wartości null.

  • Określ domyślny komunikat o błędzie, który ma być używany przez powiązanie modelu, jak pokazano w poniższym przykładzie:

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

    Aby uzyskać więcej informacji na temat błędów powiązań modelu, dla których można ustawić komunikaty domyślne, zobacz DefaultModelBindingMessageProvider.

[Wymagane] walidacja na kliencie

Typy i ciągi bez wartości null są obsługiwane inaczej na kliencie w porównaniu z serwerem. Na kliencie:

  • Wartość jest uważana za obecną tylko wtedy, gdy wprowadzono dla niej dane wejściowe. W związku z tym walidacja po stronie klienta obsługuje typy niepuste takie same jak typy dopuszczane do wartości null.
  • Odstępy w polu ciągu są uznawane za prawidłowe dane wejściowe przez wymaganą metodę weryfikacji jQuery. Walidacja po stronie serwera uwzględnia pole wymaganego ciągu nieprawidłowe, jeśli wprowadzono tylko białe znaki.

Jak wspomniano wcześniej, typy niepuste są traktowane tak, jakby miały [Required(AllowEmptyStrings = true)] atrybut. Oznacza to, że uzyskujesz walidację po stronie klienta, nawet jeśli nie zastosujesz atrybutu [Required(AllowEmptyStrings = true)] . Jeśli jednak nie używasz atrybutu, zostanie wyświetlony domyślny komunikat o błędzie. Aby określić niestandardowy komunikat o błędzie, użyj atrybutu .

[Remote] , atrybut

Atrybut [Remote] implementuje walidację po stronie klienta, która wymaga wywołania metody na serwerze w celu określenia, czy dane wejściowe pola są prawidłowe. Na przykład aplikacja może wymagać sprawdzenia, czy nazwa użytkownika jest już używana.

Aby zaimplementować zdalną walidację:

  1. Utwórz metodę akcji dla języka JavaScript do wywołania. Metoda zdalna weryfikacji jQuery oczekuje JSodpowiedzi ON:

    • true oznacza, że dane wejściowe są prawidłowe.
    • false, lub undefinednull oznacza, że dane wejściowe są nieprawidłowe. Wyświetl domyślny komunikat o błędzie.
    • Każdy inny ciąg oznacza, że dane wejściowe są nieprawidłowe. Wyświetl ciąg jako niestandardowy komunikat o błędzie.

    Oto przykład metody akcji zwracającej niestandardowy komunikat o błędzie:

    [AcceptVerbs("GET", "POST")]
    public IActionResult VerifyEmail(string email)
    {
        if (!_userService.VerifyEmail(email))
        {
            return Json($"Email {email} is already in use.");
        }
    
        return Json(true);
    }
    
  2. W klasie modelu dodaj adnotację do właściwości z atrybutem wskazującym [Remote] metodę akcji walidacji, jak pokazano w poniższym przykładzie:

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

Walidacja po stronie serwera musi być również zaimplementowana dla klientów, którzy wyłączyli język JavaScript.

Dodatkowe pola

Właściwość AdditionalFields atrybutu [Remote] umożliwia weryfikowanie kombinacji pól względem danych na serwerze. Jeśli na przykład User model miał FirstName i LastName właściwości, możesz sprawdzić, czy żadni istniejący użytkownicy nie mają już tej pary nazw. W poniższym przykładzie pokazano, jak używać polecenia 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 Można jawnie ustawić ciągi "FirstName" i "LastName", ale użycie operatora nameof upraszcza późniejsze refaktoryzacja. Metoda akcji dla tej weryfikacji musi akceptować zarówno argumenty, jak firstName i lastName :

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

Gdy użytkownik wprowadzi imię lub nazwisko, język JavaScript wykonuje zdalne wywołanie, aby sprawdzić, czy ta para nazw została podjęta.

Aby zweryfikować co najmniej dwa dodatkowe pola, podaj je jako listę rozdzielaną przecinkami. Aby na przykład dodać MiddleName właściwość do modelu, ustaw [Remote] atrybut, jak pokazano w poniższym przykładzie:

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

AdditionalFields, podobnie jak wszystkie argumenty atrybutów, musi być wyrażeniem stałym. W związku z tym nie używaj ciągu interpolowanego ani wywołania Join w celu zainicjowania AdditionalFieldsmetody .

Alternatywy dla wbudowanych atrybutów

Jeśli potrzebujesz weryfikacji, która nie jest dostarczana przez wbudowane atrybuty, możesz:

Atrybuty niestandardowe

W przypadku scenariuszy, w których wbudowane atrybuty weryfikacji nie są obsługiwane, można utworzyć niestandardowe atrybuty weryfikacji. Utwórz klasę dziedziczą z ValidationAttributeklasy , a następnie przesłaniaj metodę IsValid .

Metoda IsValid akceptuje obiekt o nazwie value, czyli dane wejściowe do zweryfikowania. Przeciążenie akceptuje ValidationContext również obiekt, który udostępnia dodatkowe informacje, takie jak wystąpienie modelu utworzone przez powiązanie modelu.

Poniższy przykład sprawdza, czy data wydania filmu w gatunku Classic nie jest późniejsza niż określony rok. Atrybut [ClassicMovie] :

  • Jest uruchamiany tylko na serwerze.
  • W przypadku filmów klasycznych zweryfikuje datę wydania:
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;
    }
}

Zmienna movie w poprzednim przykładzie reprezentuje Movie obiekt zawierający dane z przesyłania formularza. W przypadku niepowodzenia ValidationResult walidacji zwracany jest komunikat o błędzie.

IValidatableObject

Powyższy przykład działa tylko z typami Movie . Inną opcją weryfikacji na poziomie klasy jest zaimplementowanie IValidatableObject w klasie modelu, jak pokazano w poniższym przykładzie:

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

Walidacja niestandardowa

Poniższy kod pokazuje, jak dodać błąd modelu po zbadaniu modelu:

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

Poniższy kod implementuje test weryfikacji w kontrolerze:

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

Poniższy kod sprawdza, czy numer telefonu i adres e-mail są unikatowe:

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

Poniższy kod implementuje test weryfikacji w kontrolerze:

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

Sprawdzanie unikatowego numeru telefonu lub wiadomości e-mail zwykle odbywa się również przy użyciu zdalnego sprawdzania poprawności.

Validationresult

Rozważmy następujące niestandardowe elementy 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;
    }
}

W poniższym kodzie jest stosowany atrybut niestandardowy [ValidateName] :

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

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

Gdy model zawiera zzelement , zostanie zwrócony nowy ValidationResult .

Walidacja węzła najwyższego poziomu

Węzły najwyższego poziomu obejmują:

  • Parametry akcji
  • Właściwości kontrolera
  • Parametry procedury obsługi stron
  • Właściwości modelu strony

Węzły najwyższego poziomu powiązane z modelem są weryfikowane oprócz sprawdzania poprawności właściwości modelu. W poniższym przykładzie z przykładowej aplikacjiVerifyPhone metoda używa RegularExpressionAttribute metody , aby zweryfikować phone parametr akcji:

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

Węzły najwyższego poziomu mogą używać BindRequiredAttribute z atrybutami walidacji. W poniższym przykładzie z przykładowej aplikacji metoda określa, CheckAge że age parametr musi być powiązany z ciągu zapytania po przesłaniu formularza:

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

Na stronie Sprawdzanie wieku (CheckAge.cshtml) znajdują się dwie formularze. Pierwszy formularz przesyła Age wartość jako 99 parametr ciągu zapytania: https://localhost:5001/Users/CheckAge?Age=99.

Po przesłaniu poprawnie sformatowanego age parametru z ciągu zapytania formularz sprawdza poprawność.

Drugi formularz na stronie Sprawdzanie wieku przesyła Age wartość w treści żądania i walidacja kończy się niepowodzeniem. Powiązanie kończy się niepowodzeniem, ponieważ age parametr musi pochodzić z ciągu zapytania.

Maksymalna liczba błędów

Walidacja zostaje zatrzymana po osiągnięciu maksymalnej liczby błędów (domyślnie 200). Tę liczbę można skonfigurować przy użyciu następującego kodu w pliku Program.cs:

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

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

Maksymalna rekursja

ValidationVisitor przechodzi przez graf obiektu weryfikowanego modelu. W przypadku modeli, które są głębokie lub nieskończenie rekursywne, walidacja może spowodować przepełnienie stosu. MvcOptions.MaxValidationDepth zapewnia sposób na zatrzymanie weryfikacji na wczesnym etapie, jeśli rekursja gościa przekroczy skonfigurowaną głębokość. Wartość domyślna to MvcOptions.MaxValidationDepth 32.

Automatyczne zwarcie

Sprawdzanie poprawności jest automatycznie zwarcie (pomijane), jeśli graf modelu nie wymaga weryfikacji. Obiekty pomijają walidację dla kolekcji elementów pierwotnych (takich jak byte[], , string[]Dictionary<string, string>) i złożone grafy obiektów, które nie mają żadnych modułów sprawdzania poprawności.

Walidacja po stronie klienta

Walidacja po stronie klienta uniemożliwia przesyłanie do momentu, aż formularz będzie prawidłowy. Przycisk Prześlij uruchamia kod JavaScript, który przesyła formularz lub wyświetla komunikaty o błędach.

Walidacja po stronie klienta pozwala uniknąć niepotrzebnej rundy na serwerze, gdy w formularzu występują błędy wejściowe. Następujące odwołania do skryptu w _Layout.cshtml systemie i _ValidationScriptsPartial.cshtml obsługują weryfikację po stronie 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>

Skrypt jQuery Unobtrusive Validation to niestandardowa biblioteka frontonu firmy Microsoft oparta na popularnej wtyczce weryfikacji jQuery. Bez weryfikacji nieuprawdzonej metody jQuery należy kodować tę samą logikę walidacji w dwóch miejscach: raz w atrybutach weryfikacji po stronie serwera we właściwościach modelu, a następnie ponownie w skryptach po stronie klienta. Zamiast tego pomocnicy tagów i pomocnicy HTML używają atrybutów weryfikacji i metadanych typu z właściwości modelu do renderowania atrybutów HTML 5 data- dla elementów formularza, które wymagają weryfikacji. jQuery Unobtrusive Validation analizuje data- atrybuty i przekazuje logikę do weryfikacji jQuery, skutecznie "kopiowanie" logiki weryfikacji po stronie serwera do klienta. Błędy weryfikacji można wyświetlić na kliencie przy użyciu pomocników tagów, jak pokazano poniżej:

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

Poprzednie pomocniki tagów renderuje następujący kod 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>

Zwróć uwagę, że data- atrybuty w danych wyjściowych HTML odpowiadają atrybutom weryfikacji właściwości Movie.ReleaseDate . Atrybut data-val-required zawiera komunikat o błędzie wyświetlany, jeśli użytkownik nie wypełni pola data wydania. Funkcja jQuery Unobtrusive Validation przekazuje tę wartość do metody jQuery Validation required(), która następnie wyświetla ten komunikat w towarzyszącym <elemecie span> .

Walidacja typu danych jest oparta na typie platformy .NET właściwości, chyba że jest ona zastępowana przez atrybut [DataType]. Przeglądarki mają własne domyślne komunikaty o błędach, ale pakiet weryfikacji jQuery Unobtrusive Validation może zastąpić te komunikaty. [DataType] atrybuty i podklasy, takie jak [EmailAddress], umożliwiają określenie komunikatu o błędzie.

Nieuprawdniająca walidacja

Aby uzyskać informacje na temat nieprawdziwej weryfikacji, zobacz ten problem z usługą GitHub.

Dodawanie walidacji do formularzy dynamicznych

jQuery Unobtrusive Validation przechodzi logikę walidacji i parametry do weryfikacji jQuery po pierwszym załadowaniu strony. W związku z tym walidacja nie działa automatycznie na dynamicznie generowanych formularzach. Aby włączyć walidację, poinformuj jQuery Unobtrusive Validation (Sprawdzanie poprawności nieprawdziwej), aby przeanalizować formularz dynamiczny natychmiast po jego utworzeniu. Na przykład poniższy kod konfiguruje weryfikację po stronie klienta w formularzu dodanym za pośrednictwem 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() akceptuje selektor jQuery dla jednego argumentu. Ta metoda nakazuje jQuery Unobtrusive Validation analizowanie data- atrybutów formularzy w tym selektorze. Wartości tych atrybutów są następnie przekazywane do wtyczki jQuery Validation.

Dodawanie walidacji do kontrolek dynamicznych

Metoda $.validator.unobtrusive.parse() działa na całym formularzu, a nie na poszczególnych dynamicznie generowanych kontrolkach, takich jak <input> i <select/>. Aby ponownie przeanalizować formularz, usuń dane weryfikacji, które zostały dodane podczas analizowania formularza wcześniej, jak pokazano w poniższym przykładzie:

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

Niestandardowa weryfikacja po stronie klienta

Niestandardowa weryfikacja po stronie klienta jest wykonywana przez generowanie data- atrybutów HTML, które współpracują z niestandardową kartą walidacji jQuery. Poniższy przykładowy kod adaptera został napisany dla [ClassicMovie] atrybutów i [ClassicMovieWithClientValidator] , które zostały wprowadzone wcześniej w tym artykule:

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

Aby uzyskać informacje na temat pisania kart, zobacz dokumentację weryfikacji jQuery.

Użycie adaptera dla danego pola jest wyzwalane przez data- atrybuty, które:

  • Flaguj pole jako podlegające weryfikacji (data-val="true").
  • Zidentyfikuj nazwę reguły walidacji i tekst komunikatu o błędzie (na przykład data-val-rulename="Error message.").
  • Podaj wszelkie dodatkowe parametry wymagane przez moduł sprawdzania poprawności (na przykład data-val-rulename-param1="value").

W poniższym przykładzie przedstawiono data- atrybuty atrybutu przykładowej ClassicMovie aplikacji:

<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 wspomniano wcześniej, pomocnicy tagów i pomocnicy HTML używają informacji z atrybutów weryfikacji do renderowania data- atrybutów. Istnieją dwie opcje pisania kodu, które skutkują tworzeniem niestandardowych data- atrybutów HTML:

  • Utwórz klasę, która pochodzi z AttributeAdapterBase<TAttribute> klasy i , która implementuje IValidationAttributeAdapterProviderelement , i zarejestruj atrybut i jego adapter w di. Ta metoda jest zgodna z jedną zasadą odpowiedzialności w tym kodzie weryfikacji powiązanym z serwerem i klientem jest w oddzielnych klasach. Adapter ma również zaletę, że ponieważ jest zarejestrowany w di, inne usługi w DI są dostępne dla niego w razie potrzeby.
  • Zaimplementuj IClientModelValidatorValidationAttribute w klasie. Ta metoda może być odpowiednia, jeśli atrybut nie wykonuje walidacji po stronie serwera i nie wymaga żadnych usług z di.

AttributeAdapter na potrzeby weryfikacji po stronie klienta

Ta metoda renderowania data- atrybutów w kodzie HTML jest używana przez ClassicMovie atrybut w przykładowej aplikacji. Aby dodać walidację klienta przy użyciu tej metody:

  1. Utwórz klasę adaptera atrybutów dla niestandardowego atrybutu weryfikacji. Utwórz pochodną klasę z AttributeAdapterBase<TAttribute>klasy . Utwórz metodę AddValidation , która dodaje data- atrybuty do renderowanych danych wyjściowych, jak pokazano w tym przykładzie:

    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. Utwórz klasę dostawcy adaptera, która implementuje IValidationAttributeAdapterProviderelement . W metodzie GetAttributeAdapter przekaż atrybut niestandardowy do konstruktora adaptera, jak pokazano w tym przykładzie:

    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. Zarejestruj dostawcę adaptera dla di w programie Program.cs:

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

IClientModelValidator na potrzeby weryfikacji po stronie klienta

Ta metoda renderowania data- atrybutów w kodzie HTML jest używana przez ClassicMovieWithClientValidator atrybut w przykładowej aplikacji. Aby dodać walidację klienta przy użyciu tej metody:

  • W niestandardowym atrybucie walidacji zaimplementuj IClientModelValidator interfejs i utwórz metodę AddValidation . W metodzie AddValidation dodaj data- atrybuty do weryfikacji, jak pokazano w poniższym przykładzie:

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

Wyłączanie weryfikacji po stronie klienta

Poniższy kod wyłącza walidację klienta na stronach Razor :

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

Inne opcje wyłączania weryfikacji po stronie klienta:

  • Oznacz jako komentarz odwołanie _ValidationScriptsPartial we wszystkich plikach .cshtml .
  • Usuń zawartość pliku Pages\Shared_ValidationScriptsPartial.cshtml .

Powyższe podejście nie uniemożliwi weryfikacji po stronie klienta biblioteki klas podstawowych IdentityRazor ASP.NET. Aby uzyskać więcej informacji, zobacz Szkielet Identity w projektach ASP.NET Core.

Szczegóły problemu

Szczegóły problemu nie są jedynym formatem odpowiedzi opisujący błąd interfejsu API HTTP, jednak są one często używane do zgłaszania błędów dla interfejsów API HTTP.

Usługa szczegółów problemu IProblemDetailsService implementuje interfejs, który obsługuje tworzenie szczegółów problemu w ASP.NET Core. Metoda AddProblemDetails rozszerzenia w systemie IServiceCollection rejestruje domyślną IProblemDetailsService implementację.

W aplikacjach ASP.NET Core następujące oprogramowanie pośredniczące generuje szczegóły problemów odpowiedzi HTTP podczas AddProblemDetails wywoływana, z wyjątkiem sytuacji, gdyAcceptnagłówek HTTP żądania nie zawiera jednego z typów zawartości obsługiwanych przez zarejestrowane IProblemDetailsWriter (domyślnie: application/json):

Dodatkowe zasoby

W tym artykule wyjaśniono, jak zweryfikować dane wejściowe użytkownika w aplikacji ASP.NET Core MVC lub Razor Pages.

Wyświetl lub pobierz przykładowy kod (jak pobrać).

Stan modelu

Stan modelu reprezentuje błędy pochodzące z dwóch podsystemów: powiązanie modelu i walidację modelu. Błędy pochodzące z powiązania modelu to zazwyczaj błędy konwersji danych. Na przykład znak "x" jest wprowadzany w polu liczby całkowitej. Walidacja modelu odbywa się po powiązaniu modelu i zgłasza błędy, w których dane nie są zgodne z regułami biznesowymi. Na przykład wartość 0 jest wprowadzana w polu, które oczekuje oceny z zakresu od 1 do 5.

Zarówno powiązanie modelu, jak i walidacja modelu są wykonywane przed wykonaniem akcji kontrolera lub Razor metody obsługi stron. W przypadku aplikacji internetowych obowiązkiem aplikacji jest odpowiednie sprawdzenie ModelState.IsValid i reagowanie na nie. Aplikacje internetowe zwykle redisplay strony z komunikatem o błędzie, jak pokazano w poniższym Razor przykładzie stron:

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

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

    return RedirectToPage("./Index");
}

W przypadku ASP.NET Core MVC z kontrolerami i widokami w poniższym przykładzie pokazano, jak sprawdzić ModelState.IsValid wewnątrz akcji kontrolera:

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 internetowego interfejsu API nie muszą sprawdzaćModelState.IsValid, czy mają atrybut [ApiController]. W takim przypadku zwracana jest automatyczna odpowiedź HTTP 400 zawierająca szczegóły błędu, gdy stan modelu jest nieprawidłowy. Aby uzyskać więcej informacji, zobacz Automatyczne odpowiedzi HTTP 400.

Ponowne uruchamianie walidacji

Walidacja jest automatyczna, ale warto ją powtórzyć ręcznie. Na przykład można obliczyć wartość właściwości i chcieć ponownie uruchomić walidację po ustawieniu właściwości na obliczoną wartość. Aby ponownie uruchomić walidację, wywołaj polecenie ModelStateDictionary.ClearValidationState , aby wyczyścić walidację specyficzną dla weryfikowanego modelu, a następnie :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");
}

Atrybuty weryfikacji

Atrybuty weryfikacji umożliwiają określenie reguł walidacji dla właściwości modelu. Poniższy przykład z przykładowej aplikacji przedstawia klasę modelu z adnotacjami z atrybutami walidacji. Atrybut [ClassicMovie] jest atrybutem weryfikacji niestandardowej, a pozostałe są wbudowane. Nie pokazano elementu , [ClassicMovieWithClientValidator]który pokazuje alternatywny sposób implementowania atrybutu niestandardowego.

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

Atrybuty wbudowane

Oto niektóre wbudowane atrybuty weryfikacji:

  • [ValidateNever]: Wskazuje, że właściwość lub parametr powinien zostać wykluczony z walidacji.
  • [CreditCard]: Sprawdza, czy właściwość ma format karty kredytowej. Wymaga dodatkowych metod weryfikacji jQuery.
  • [Porównaj]: Sprawdza, czy dwie właściwości w modelu są zgodne.
  • [EmailAddress]: Sprawdza, czy właściwość ma format wiadomości e-mail.
  • [Telefon]: Sprawdza, czy właściwość ma format numeru telefonu.
  • [Zakres]: Sprawdza, czy wartość właściwości mieści się w określonym zakresie.
  • [RegularExpression]: Sprawdza, czy wartość właściwości jest zgodna z określonym wyrażeniem regularnym.
  • [Wymagane]: sprawdza, czy pole nie ma wartości null. Zobacz [Required] atrybut , aby uzyskać szczegółowe informacje o zachowaniu tego atrybutu.
  • [StringLength]: Sprawdza, czy wartość właściwości ciągu nie przekracza określonego limitu długości.
  • [Url]: sprawdza, czy właściwość ma format adresu URL.
  • [Zdalne]: Weryfikuje dane wejściowe na kliencie przez wywołanie metody akcji na serwerze. Zobacz [Remote] atrybut , aby uzyskać szczegółowe informacje o zachowaniu tego atrybutu.

Pełną listę atrybutów weryfikacji można znaleźć w System.ComponentModel.DataAnnotations przestrzeni nazw.

Komunikaty o błędach

Atrybuty weryfikacji umożliwiają określenie komunikatu o błędzie, który ma być wyświetlany dla nieprawidłowych danych wejściowych. Przykład:

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

Wewnętrznie atrybuty są wywoływane String.Format z symbolem zastępczym nazwy pola, a czasami dodatkowymi symbolami zastępczymi. Przykład:

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

Po zastosowaniu Name do właściwości komunikat o błędzie utworzony przez poprzedni kod będzie miał wartość "Długość nazwy musi należeć do przedziału od 6 do 8".

Aby dowiedzieć się, które parametry są przekazywane dla String.Format komunikatu o błędzie określonego atrybutu, zobacz kod źródłowy DataAnnotations.

Typy odwołań bez wartości null i atrybut [Wymagane]

System sprawdzania poprawności traktuje parametry niepuste lub powiązane właściwości tak, jakby miały [Required(AllowEmptyStrings = true)] atrybut. Włączając Nullable konteksty, MVC niejawnie rozpoczyna weryfikowanie właściwości niezwiązanych z wartościami null w typach niegenerycznych lub parametrach, tak jakby zostały przypisane atrybutowi [Required(AllowEmptyStrings = true)] . Rozważ następujący kod:

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

Jeśli aplikacja została skompilowana przy użyciu <Nullable>enable</Nullable>polecenia , brakująca wartość w NameJSobszarze WŁ. lub wpis formularza spowoduje wystąpienie błędu weryfikacji. Użyj typu odwołania dopuszczanego do wartości null, aby zezwolić na określanie wartości null lub brakujących dla Name właściwości:

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

To zachowanie można wyłączyć, konfigurując w Program.csprogramie SuppressImplicitRequiredAttributeForNonNullableReferenceTypes :

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

Właściwości niepuste dla typów ogólnych i atrybutu [Wymagane]

Właściwości niepuste dla typów ogólnych muszą zawierać [Required] atrybut, gdy typ jest wymagany. W poniższym kodzie TestRequired nie jest wymagany:

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

W poniższym kodzie TestRequired jest jawnie oznaczony jako wymagany:

using System.ComponentModel.DataAnnotations;

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

[Wymagane] walidacja na serwerze

Na serwerze wymagana wartość jest uważana za brakującą, jeśli właściwość ma wartość null. Pole bez wartości null jest zawsze prawidłowe, a [Required] komunikat o błędzie atrybutu nigdy nie jest wyświetlany.

Jednak powiązanie modelu dla właściwości innej niż null może zakończyć się niepowodzeniem, co spowoduje wyświetlenie komunikatu o błędzie, takiego jak The value '' is invalid. Aby określić niestandardowy komunikat o błędzie na potrzeby weryfikacji po stronie serwera typów niezwiązanych z wartościami null, dostępne są następujące opcje:

  • Ustaw pole jako dopuszczane do wartości null (na przykład decimal? zamiast decimal). Typy wartości T> dopuszczane<do wartości null są traktowane jak standardowe typy dopuszczane do wartości null.

  • Określ domyślny komunikat o błędzie, który ma być używany przez powiązanie modelu, jak pokazano w poniższym przykładzie:

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

    Aby uzyskać więcej informacji na temat błędów powiązań modelu, dla których można ustawić komunikaty domyślne, zobacz DefaultModelBindingMessageProvider.

[Wymagane] walidacja na kliencie

Typy i ciągi bez wartości null są obsługiwane inaczej na kliencie w porównaniu z serwerem. Na kliencie:

  • Wartość jest uważana za obecną tylko wtedy, gdy wprowadzono dla niej dane wejściowe. W związku z tym walidacja po stronie klienta obsługuje typy niepuste takie same jak typy dopuszczane do wartości null.
  • Odstępy w polu ciągu są uznawane za prawidłowe dane wejściowe przez wymaganą metodę weryfikacji jQuery. Walidacja po stronie serwera uwzględnia pole wymaganego ciągu nieprawidłowe, jeśli wprowadzono tylko białe znaki.

Jak wspomniano wcześniej, typy niepuste są traktowane tak, jakby miały [Required(AllowEmptyStrings = true)] atrybut. Oznacza to, że uzyskujesz walidację po stronie klienta, nawet jeśli nie zastosujesz atrybutu [Required(AllowEmptyStrings = true)] . Jeśli jednak nie używasz atrybutu, zostanie wyświetlony domyślny komunikat o błędzie. Aby określić niestandardowy komunikat o błędzie, użyj atrybutu .

[Remote] , atrybut

Atrybut [Remote] implementuje walidację po stronie klienta, która wymaga wywołania metody na serwerze w celu określenia, czy dane wejściowe pola są prawidłowe. Na przykład aplikacja może wymagać sprawdzenia, czy nazwa użytkownika jest już używana.

Aby zaimplementować zdalną walidację:

  1. Utwórz metodę akcji dla języka JavaScript do wywołania. Metoda zdalna weryfikacji jQuery oczekuje JSodpowiedzi ON:

    • true oznacza, że dane wejściowe są prawidłowe.
    • false, lub undefinednull oznacza, że dane wejściowe są nieprawidłowe. Wyświetl domyślny komunikat o błędzie.
    • Każdy inny ciąg oznacza, że dane wejściowe są nieprawidłowe. Wyświetl ciąg jako niestandardowy komunikat o błędzie.

    Oto przykład metody akcji zwracającej niestandardowy komunikat o błędzie:

    [AcceptVerbs("GET", "POST")]
    public IActionResult VerifyEmail(string email)
    {
        if (!_userService.VerifyEmail(email))
        {
            return Json($"Email {email} is already in use.");
        }
    
        return Json(true);
    }
    
  2. W klasie modelu dodaj adnotację do właściwości z atrybutem wskazującym [Remote] metodę akcji walidacji, jak pokazano w poniższym przykładzie:

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

Dodatkowe pola

Właściwość AdditionalFields atrybutu [Remote] umożliwia weryfikowanie kombinacji pól względem danych na serwerze. Jeśli na przykład User model miał FirstName i LastName właściwości, możesz sprawdzić, czy żadni istniejący użytkownicy nie mają już tej pary nazw. W poniższym przykładzie pokazano, jak używać polecenia 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 Można jawnie ustawić ciągi "FirstName" i "LastName", ale użycie operatora nameof upraszcza późniejsze refaktoryzacja. Metoda akcji dla tej weryfikacji musi akceptować zarówno argumenty, jak firstName i lastName :

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

Gdy użytkownik wprowadzi imię lub nazwisko, język JavaScript wykonuje zdalne wywołanie, aby sprawdzić, czy ta para nazw została podjęta.

Aby zweryfikować co najmniej dwa dodatkowe pola, podaj je jako listę rozdzielaną przecinkami. Aby na przykład dodać MiddleName właściwość do modelu, ustaw [Remote] atrybut, jak pokazano w poniższym przykładzie:

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

AdditionalFields, podobnie jak wszystkie argumenty atrybutów, musi być wyrażeniem stałym. W związku z tym nie używaj ciągu interpolowanego ani wywołania Join w celu zainicjowania AdditionalFieldsmetody .

Alternatywy dla wbudowanych atrybutów

Jeśli potrzebujesz weryfikacji, która nie jest dostarczana przez wbudowane atrybuty, możesz:

Atrybuty niestandardowe

W przypadku scenariuszy, w których wbudowane atrybuty weryfikacji nie są obsługiwane, można utworzyć niestandardowe atrybuty weryfikacji. Utwórz klasę dziedziczą z ValidationAttributeklasy , a następnie przesłaniaj metodę IsValid .

Metoda IsValid akceptuje obiekt o nazwie value, czyli dane wejściowe do zweryfikowania. Przeciążenie akceptuje ValidationContext również obiekt, który udostępnia dodatkowe informacje, takie jak wystąpienie modelu utworzone przez powiązanie modelu.

Poniższy przykład sprawdza, czy data wydania filmu w gatunku Classic nie jest późniejsza niż określony rok. Atrybut [ClassicMovie] :

  • Jest uruchamiany tylko na serwerze.
  • W przypadku filmów klasycznych zweryfikuje datę wydania:
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;
    }
}

Zmienna movie w poprzednim przykładzie reprezentuje Movie obiekt zawierający dane z przesyłania formularza. W przypadku niepowodzenia ValidationResult walidacji zwracany jest komunikat o błędzie.

IValidatableObject

Powyższy przykład działa tylko z typami Movie . Inną opcją weryfikacji na poziomie klasy jest zaimplementowanie IValidatableObject w klasie modelu, jak pokazano w poniższym przykładzie:

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

Walidacja węzła najwyższego poziomu

Węzły najwyższego poziomu obejmują:

  • Parametry akcji
  • Właściwości kontrolera
  • Parametry procedury obsługi stron
  • Właściwości modelu strony

Węzły najwyższego poziomu powiązane z modelem są weryfikowane oprócz sprawdzania poprawności właściwości modelu. W poniższym przykładzie z przykładowej aplikacjiVerifyPhone metoda używa RegularExpressionAttribute metody , aby zweryfikować phone parametr akcji:

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

Węzły najwyższego poziomu mogą używać BindRequiredAttribute z atrybutami walidacji. W poniższym przykładzie z przykładowej aplikacji metoda określa, CheckAge że age parametr musi być powiązany z ciągu zapytania po przesłaniu formularza:

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

Na stronie Sprawdzanie wieku (CheckAge.cshtml) znajdują się dwie formularze. Pierwszy formularz przesyła Age wartość jako 99 parametr ciągu zapytania: https://localhost:5001/Users/CheckAge?Age=99.

Po przesłaniu poprawnie sformatowanego age parametru z ciągu zapytania formularz sprawdza poprawność.

Drugi formularz na stronie Sprawdzanie wieku przesyła Age wartość w treści żądania i walidacja kończy się niepowodzeniem. Powiązanie kończy się niepowodzeniem, ponieważ age parametr musi pochodzić z ciągu zapytania.

Maksymalna liczba błędów

Walidacja zostaje zatrzymana po osiągnięciu maksymalnej liczby błędów (domyślnie 200). Tę liczbę można skonfigurować przy użyciu następującego kodu w pliku Program.cs:

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

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

Maksymalna rekursja

ValidationVisitor przechodzi przez graf obiektu weryfikowanego modelu. W przypadku modeli, które są głębokie lub nieskończenie rekursywne, walidacja może spowodować przepełnienie stosu. MvcOptions.MaxValidationDepth zapewnia sposób na zatrzymanie weryfikacji na wczesnym etapie, jeśli rekursja gościa przekroczy skonfigurowaną głębokość. Wartość domyślna to MvcOptions.MaxValidationDepth 32.

Automatyczne zwarcie

Sprawdzanie poprawności jest automatycznie zwarcie (pomijane), jeśli graf modelu nie wymaga weryfikacji. Obiekty pomijają walidację dla kolekcji elementów pierwotnych (takich jak byte[], , string[]Dictionary<string, string>) i złożone grafy obiektów, które nie mają żadnych modułów sprawdzania poprawności.

Walidacja po stronie klienta

Walidacja po stronie klienta uniemożliwia przesyłanie do momentu, aż formularz będzie prawidłowy. Przycisk Prześlij uruchamia kod JavaScript, który przesyła formularz lub wyświetla komunikaty o błędach.

Walidacja po stronie klienta pozwala uniknąć niepotrzebnej rundy na serwerze, gdy w formularzu występują błędy wejściowe. Następujące odwołania do skryptu w _Layout.cshtml systemie i _ValidationScriptsPartial.cshtml obsługują weryfikację po stronie 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>

Skrypt jQuery Unobtrusive Validation to niestandardowa biblioteka frontonu firmy Microsoft oparta na popularnej wtyczce weryfikacji jQuery. Bez weryfikacji nieuprawdzonej metody jQuery należy kodować tę samą logikę walidacji w dwóch miejscach: raz w atrybutach weryfikacji po stronie serwera we właściwościach modelu, a następnie ponownie w skryptach po stronie klienta. Zamiast tego pomocnicy tagów i pomocnicy HTML używają atrybutów weryfikacji i metadanych typu z właściwości modelu do renderowania atrybutów HTML 5 data- dla elementów formularza, które wymagają weryfikacji. jQuery Unobtrusive Validation analizuje data- atrybuty i przekazuje logikę do weryfikacji jQuery, skutecznie "kopiowanie" logiki weryfikacji po stronie serwera do klienta. Błędy weryfikacji można wyświetlić na kliencie przy użyciu pomocników tagów, jak pokazano poniżej:

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

Poprzednie pomocniki tagów renderuje następujący kod 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>

Zwróć uwagę, że data- atrybuty w danych wyjściowych HTML odpowiadają atrybutom weryfikacji właściwości Movie.ReleaseDate . Atrybut data-val-required zawiera komunikat o błędzie wyświetlany, jeśli użytkownik nie wypełni pola data wydania. Funkcja jQuery Unobtrusive Validation przekazuje tę wartość do metody jQuery Validation required(), która następnie wyświetla ten komunikat w towarzyszącym <elemecie span> .

Walidacja typu danych jest oparta na typie platformy .NET właściwości, chyba że jest ona zastępowana przez atrybut [DataType]. Przeglądarki mają własne domyślne komunikaty o błędach, ale pakiet weryfikacji jQuery Unobtrusive Validation może zastąpić te komunikaty. [DataType] atrybuty i podklasy, takie jak [EmailAddress], umożliwiają określenie komunikatu o błędzie.

Nieuprawdniająca walidacja

Aby uzyskać informacje na temat nieprawdziwej weryfikacji, zobacz ten problem z usługą GitHub.

Dodawanie walidacji do formularzy dynamicznych

jQuery Unobtrusive Validation przechodzi logikę walidacji i parametry do weryfikacji jQuery po pierwszym załadowaniu strony. W związku z tym walidacja nie działa automatycznie na dynamicznie generowanych formularzach. Aby włączyć walidację, poinformuj jQuery Unobtrusive Validation (Sprawdzanie poprawności nieprawdziwej), aby przeanalizować formularz dynamiczny natychmiast po jego utworzeniu. Na przykład poniższy kod konfiguruje weryfikację po stronie klienta w formularzu dodanym za pośrednictwem 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() akceptuje selektor jQuery dla jednego argumentu. Ta metoda nakazuje jQuery Unobtrusive Validation analizowanie data- atrybutów formularzy w tym selektorze. Wartości tych atrybutów są następnie przekazywane do wtyczki jQuery Validation.

Dodawanie walidacji do kontrolek dynamicznych

Metoda $.validator.unobtrusive.parse() działa na całym formularzu, a nie na poszczególnych dynamicznie generowanych kontrolkach, takich jak <input> i <select/>. Aby ponownie przeanalizować formularz, usuń dane weryfikacji, które zostały dodane podczas analizowania formularza wcześniej, jak pokazano w poniższym przykładzie:

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

Niestandardowa weryfikacja po stronie klienta

Niestandardowa weryfikacja po stronie klienta jest wykonywana przez generowanie data- atrybutów HTML, które współpracują z niestandardową kartą walidacji jQuery. Poniższy przykładowy kod adaptera został napisany dla [ClassicMovie] atrybutów i [ClassicMovieWithClientValidator] , które zostały wprowadzone wcześniej w tym artykule:

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

Aby uzyskać informacje na temat pisania kart, zobacz dokumentację weryfikacji jQuery.

Użycie adaptera dla danego pola jest wyzwalane przez data- atrybuty, które:

  • Flaguj pole jako podlegające weryfikacji (data-val="true").
  • Zidentyfikuj nazwę reguły walidacji i tekst komunikatu o błędzie (na przykład data-val-rulename="Error message.").
  • Podaj wszelkie dodatkowe parametry wymagane przez moduł sprawdzania poprawności (na przykład data-val-rulename-param1="value").

W poniższym przykładzie przedstawiono data- atrybuty atrybutu przykładowej ClassicMovie aplikacji:

<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 wspomniano wcześniej, pomocnicy tagów i pomocnicy HTML używają informacji z atrybutów weryfikacji do renderowania data- atrybutów. Istnieją dwie opcje pisania kodu, które skutkują tworzeniem niestandardowych data- atrybutów HTML:

  • Utwórz klasę, która pochodzi z AttributeAdapterBase<TAttribute> klasy i , która implementuje IValidationAttributeAdapterProviderelement , i zarejestruj atrybut i jego adapter w di. Ta metoda jest zgodna z jedną zasadą odpowiedzialności w tym kodzie weryfikacji powiązanym z serwerem i klientem jest w oddzielnych klasach. Adapter ma również zaletę, że ponieważ jest zarejestrowany w di, inne usługi w DI są dostępne dla niego w razie potrzeby.
  • Zaimplementuj IClientModelValidatorValidationAttribute w klasie. Ta metoda może być odpowiednia, jeśli atrybut nie wykonuje walidacji po stronie serwera i nie wymaga żadnych usług z di.

AttributeAdapter na potrzeby weryfikacji po stronie klienta

Ta metoda renderowania data- atrybutów w kodzie HTML jest używana przez ClassicMovie atrybut w przykładowej aplikacji. Aby dodać walidację klienta przy użyciu tej metody:

  1. Utwórz klasę adaptera atrybutów dla niestandardowego atrybutu weryfikacji. Utwórz pochodną klasę z AttributeAdapterBase<TAttribute>klasy . Utwórz metodę AddValidation , która dodaje data- atrybuty do renderowanych danych wyjściowych, jak pokazano w tym przykładzie:

    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. Utwórz klasę dostawcy adaptera, która implementuje IValidationAttributeAdapterProviderelement . W metodzie GetAttributeAdapter przekaż atrybut niestandardowy do konstruktora adaptera, jak pokazano w tym przykładzie:

    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. Zarejestruj dostawcę adaptera dla di w programie Program.cs:

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

IClientModelValidator na potrzeby weryfikacji po stronie klienta

Ta metoda renderowania data- atrybutów w kodzie HTML jest używana przez ClassicMovieWithClientValidator atrybut w przykładowej aplikacji. Aby dodać walidację klienta przy użyciu tej metody:

  • W niestandardowym atrybucie walidacji zaimplementuj IClientModelValidator interfejs i utwórz metodę AddValidation . W metodzie AddValidation dodaj data- atrybuty do weryfikacji, jak pokazano w poniższym przykładzie:

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

Wyłączanie weryfikacji po stronie klienta

Poniższy kod wyłącza walidację klienta na stronach Razor :

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

Inne opcje wyłączania weryfikacji po stronie klienta:

  • Oznacz jako komentarz odwołanie _ValidationScriptsPartial we wszystkich plikach .cshtml .
  • Usuń zawartość pliku Pages\Shared_ValidationScriptsPartial.cshtml .

Powyższe podejście nie uniemożliwi weryfikacji po stronie klienta biblioteki klas podstawowych IdentityRazor ASP.NET. Aby uzyskać więcej informacji, zobacz Szkielet Identity w projektach ASP.NET Core.

Dodatkowe zasoby

W tym artykule wyjaśniono, jak zweryfikować dane wejściowe użytkownika w aplikacji ASP.NET Core MVC lub Razor Pages.

Wyświetl lub pobierz przykładowy kod (jak pobrać).

Stan modelu

Stan modelu reprezentuje błędy pochodzące z dwóch podsystemów: powiązanie modelu i walidację modelu. Błędy pochodzące z powiązania modelu to zazwyczaj błędy konwersji danych. Na przykład znak "x" jest wprowadzany w polu liczby całkowitej. Walidacja modelu odbywa się po powiązaniu modelu i zgłasza błędy, w których dane nie są zgodne z regułami biznesowymi. Na przykład wartość 0 jest wprowadzana w polu, które oczekuje oceny z zakresu od 1 do 5.

Zarówno powiązanie modelu, jak i walidacja modelu są wykonywane przed wykonaniem akcji kontrolera lub Razor metody obsługi stron. W przypadku aplikacji internetowych obowiązkiem aplikacji jest odpowiednie sprawdzenie ModelState.IsValid i reagowanie na nie. Aplikacje internetowe zwykle redisplay strony z komunikatem o błędzie:

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

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

    return RedirectToPage("./Index");
}

Kontrolery internetowego interfejsu API nie muszą sprawdzaćModelState.IsValid, czy mają atrybut [ApiController]. W takim przypadku zwracana jest automatyczna odpowiedź HTTP 400 zawierająca szczegóły błędu, gdy stan modelu jest nieprawidłowy. Aby uzyskać więcej informacji, zobacz Automatyczne odpowiedzi HTTP 400.

Ponowne uruchamianie walidacji

Walidacja jest automatyczna, ale warto ją powtórzyć ręcznie. Na przykład można obliczyć wartość właściwości i chcieć ponownie uruchomić walidację po ustawieniu właściwości na obliczoną wartość. Aby ponownie uruchomić walidację, wywołaj polecenie ModelStateDictionary.ClearValidationState , aby wyczyścić walidację specyficzną dla weryfikowanego modelu, a następnie :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");
}

Atrybuty weryfikacji

Atrybuty weryfikacji umożliwiają określenie reguł walidacji dla właściwości modelu. Poniższy przykład z przykładowej aplikacji przedstawia klasę modelu z adnotacjami z atrybutami walidacji. Atrybut [ClassicMovie] jest atrybutem weryfikacji niestandardowej, a pozostałe są wbudowane. Nie pokazano elementu , [ClassicMovieWithClientValidator]który pokazuje alternatywny sposób implementowania atrybutu niestandardowego.

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

Atrybuty wbudowane

Oto niektóre wbudowane atrybuty weryfikacji:

  • [ValidateNever]: Wskazuje, że właściwość lub parametr powinien zostać wykluczony z walidacji.
  • [CreditCard]: Sprawdza, czy właściwość ma format karty kredytowej. Wymaga dodatkowych metod weryfikacji jQuery.
  • [Porównaj]: Sprawdza, czy dwie właściwości w modelu są zgodne.
  • [EmailAddress]: Sprawdza, czy właściwość ma format wiadomości e-mail.
  • [Telefon]: Sprawdza, czy właściwość ma format numeru telefonu.
  • [Zakres]: Sprawdza, czy wartość właściwości mieści się w określonym zakresie.
  • [RegularExpression]: Sprawdza, czy wartość właściwości jest zgodna z określonym wyrażeniem regularnym.
  • [Wymagane]: sprawdza, czy pole nie ma wartości null. Zobacz [Required] atrybut , aby uzyskać szczegółowe informacje o zachowaniu tego atrybutu.
  • [StringLength]: Sprawdza, czy wartość właściwości ciągu nie przekracza określonego limitu długości.
  • [Url]: sprawdza, czy właściwość ma format adresu URL.
  • [Zdalne]: Weryfikuje dane wejściowe na kliencie przez wywołanie metody akcji na serwerze. Zobacz [Remote] atrybut , aby uzyskać szczegółowe informacje o zachowaniu tego atrybutu.

Pełną listę atrybutów weryfikacji można znaleźć w System.ComponentModel.DataAnnotations przestrzeni nazw.

Komunikaty o błędach

Atrybuty weryfikacji umożliwiają określenie komunikatu o błędzie, który ma być wyświetlany dla nieprawidłowych danych wejściowych. Przykład:

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

Wewnętrznie atrybuty są wywoływane String.Format z symbolem zastępczym nazwy pola, a czasami dodatkowymi symbolami zastępczymi. Przykład:

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

Po zastosowaniu Name do właściwości komunikat o błędzie utworzony przez poprzedni kod będzie miał wartość "Długość nazwy musi należeć do przedziału od 6 do 8".

Aby dowiedzieć się, które parametry są przekazywane dla String.Format komunikatu o błędzie określonego atrybutu, zobacz kod źródłowy DataAnnotations.

Typy odwołań bez wartości null i atrybut [Wymagane]

System sprawdzania poprawności traktuje parametry niepuste lub powiązane właściwości tak, jakby miały [Required(AllowEmptyStrings = true)] atrybut. Włączając Nullable konteksty, MVC niejawnie rozpoczyna weryfikowanie właściwości lub parametrów, które nie mogą mieć wartości null, tak jakby zostały przypisane do atrybutu[Required(AllowEmptyStrings = true)]. Rozważ następujący kod:

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

Jeśli aplikacja została skompilowana przy użyciu <Nullable>enable</Nullable>polecenia , brakująca wartość w NameJSobszarze WŁ. lub wpis formularza spowoduje wystąpienie błędu weryfikacji. Użyj typu odwołania dopuszczanego do wartości null, aby zezwolić na określanie wartości null lub brakujących dla Name właściwości:

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

To zachowanie można wyłączyć, konfigurując w Startup.ConfigureServicesprogramie SuppressImplicitRequiredAttributeForNonNullableReferenceTypes :

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

[Wymagane] walidacja na serwerze

Na serwerze wymagana wartość jest uważana za brakującą, jeśli właściwość ma wartość null. Pole bez wartości null jest zawsze prawidłowe, a [Required] komunikat o błędzie atrybutu nigdy nie jest wyświetlany.

Jednak powiązanie modelu dla właściwości innej niż null może zakończyć się niepowodzeniem, co spowoduje wyświetlenie komunikatu o błędzie, takiego jak The value '' is invalid. Aby określić niestandardowy komunikat o błędzie na potrzeby weryfikacji po stronie serwera typów niezwiązanych z wartościami null, dostępne są następujące opcje:

  • Ustaw pole jako dopuszczane do wartości null (na przykład decimal? zamiast decimal). Typy wartości T> dopuszczane<do wartości null są traktowane jak standardowe typy dopuszczane do wartości null.

  • Określ domyślny komunikat o błędzie, który ma być używany przez powiązanie modelu, jak pokazano w poniższym przykładzie:

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

    Aby uzyskać więcej informacji na temat błędów powiązań modelu, dla których można ustawić komunikaty domyślne, zobacz DefaultModelBindingMessageProvider.

[Wymagane] walidacja na kliencie

Typy i ciągi bez wartości null są obsługiwane inaczej na kliencie w porównaniu z serwerem. Na kliencie:

  • Wartość jest uważana za obecną tylko wtedy, gdy wprowadzono dla niej dane wejściowe. W związku z tym walidacja po stronie klienta obsługuje typy niepuste takie same jak typy dopuszczane do wartości null.
  • Odstępy w polu ciągu są uznawane za prawidłowe dane wejściowe przez wymaganą metodę weryfikacji jQuery. Walidacja po stronie serwera uwzględnia pole wymaganego ciągu nieprawidłowe, jeśli wprowadzono tylko białe znaki.

Jak wspomniano wcześniej, typy niepuste są traktowane tak, jakby miały [Required(AllowEmptyStrings = true)] atrybut. Oznacza to, że uzyskujesz walidację po stronie klienta, nawet jeśli nie zastosujesz atrybutu [Required(AllowEmptyStrings = true)] . Jeśli jednak nie używasz atrybutu, zostanie wyświetlony domyślny komunikat o błędzie. Aby określić niestandardowy komunikat o błędzie, użyj atrybutu .

[Remote] , atrybut

Atrybut [Remote] implementuje walidację po stronie klienta, która wymaga wywołania metody na serwerze w celu określenia, czy dane wejściowe pola są prawidłowe. Na przykład aplikacja może wymagać sprawdzenia, czy nazwa użytkownika jest już używana.

Aby zaimplementować zdalną walidację:

  1. Utwórz metodę akcji dla języka JavaScript do wywołania. Metoda zdalna weryfikacji jQuery oczekuje JSodpowiedzi ON:

    • true oznacza, że dane wejściowe są prawidłowe.
    • false, lub undefinednull oznacza, że dane wejściowe są nieprawidłowe. Wyświetl domyślny komunikat o błędzie.
    • Każdy inny ciąg oznacza, że dane wejściowe są nieprawidłowe. Wyświetl ciąg jako niestandardowy komunikat o błędzie.

    Oto przykład metody akcji zwracającej niestandardowy komunikat o błędzie:

    [AcceptVerbs("GET", "POST")]
    public IActionResult VerifyEmail(string email)
    {
        if (!_userService.VerifyEmail(email))
        {
            return Json($"Email {email} is already in use.");
        }
    
        return Json(true);
    }
    
  2. W klasie modelu dodaj adnotację do właściwości z atrybutem wskazującym [Remote] metodę akcji walidacji, jak pokazano w poniższym przykładzie:

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

Dodatkowe pola

Właściwość AdditionalFields atrybutu [Remote] umożliwia weryfikowanie kombinacji pól względem danych na serwerze. Jeśli na przykład User model miał FirstName i LastName właściwości, możesz sprawdzić, czy żadni istniejący użytkownicy nie mają już tej pary nazw. W poniższym przykładzie pokazano, jak używać polecenia 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 Można jawnie ustawić ciągi "FirstName" i "LastName", ale użycie operatora nameof upraszcza późniejsze refaktoryzacja. Metoda akcji dla tej weryfikacji musi akceptować zarówno argumenty, jak firstName i lastName :

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

Gdy użytkownik wprowadzi imię lub nazwisko, język JavaScript wykonuje zdalne wywołanie, aby sprawdzić, czy ta para nazw została podjęta.

Aby zweryfikować co najmniej dwa dodatkowe pola, podaj je jako listę rozdzielaną przecinkami. Aby na przykład dodać MiddleName właściwość do modelu, ustaw [Remote] atrybut, jak pokazano w poniższym przykładzie:

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

AdditionalFields, podobnie jak wszystkie argumenty atrybutów, musi być wyrażeniem stałym. W związku z tym nie używaj ciągu interpolowanego ani wywołania Join w celu zainicjowania AdditionalFieldsmetody .

Alternatywy dla wbudowanych atrybutów

Jeśli potrzebujesz weryfikacji, która nie jest dostarczana przez wbudowane atrybuty, możesz:

Atrybuty niestandardowe

W przypadku scenariuszy, w których wbudowane atrybuty weryfikacji nie są obsługiwane, można utworzyć niestandardowe atrybuty weryfikacji. Utwórz klasę dziedziczą z ValidationAttributeklasy , a następnie przesłaniaj metodę IsValid .

Metoda IsValid akceptuje obiekt o nazwie value, czyli dane wejściowe do zweryfikowania. Przeciążenie akceptuje ValidationContext również obiekt, który udostępnia dodatkowe informacje, takie jak wystąpienie modelu utworzone przez powiązanie modelu.

Poniższy przykład sprawdza, czy data wydania filmu w gatunku Classic nie jest późniejsza niż określony rok. Atrybut [ClassicMovie] :

  • Jest uruchamiany tylko na serwerze.
  • W przypadku filmów klasycznych zweryfikuje datę wydania:
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;
    }
}

Zmienna movie w poprzednim przykładzie reprezentuje Movie obiekt zawierający dane z przesyłania formularza. W przypadku niepowodzenia ValidationResult walidacji zwracany jest komunikat o błędzie.

IValidatableObject

Powyższy przykład działa tylko z typami Movie . Inną opcją weryfikacji na poziomie klasy jest zaimplementowanie IValidatableObject w klasie modelu, jak pokazano w poniższym przykładzie:

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

Walidacja węzła najwyższego poziomu

Węzły najwyższego poziomu obejmują:

  • Parametry akcji
  • Właściwości kontrolera
  • Parametry procedury obsługi stron
  • Właściwości modelu strony

Węzły najwyższego poziomu powiązane z modelem są weryfikowane oprócz sprawdzania poprawności właściwości modelu. W poniższym przykładzie z przykładowej aplikacjiVerifyPhone metoda używa RegularExpressionAttribute metody , aby zweryfikować phone parametr akcji:

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

Węzły najwyższego poziomu mogą używać BindRequiredAttribute z atrybutami walidacji. W poniższym przykładzie z przykładowej aplikacji metoda określa, CheckAge że age parametr musi być powiązany z ciągu zapytania po przesłaniu formularza:

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

Na stronie Sprawdzanie wieku (CheckAge.cshtml) znajdują się dwie formularze. Pierwszy formularz przesyła Age wartość jako 99 parametr ciągu zapytania: https://localhost:5001/Users/CheckAge?Age=99.

Po przesłaniu poprawnie sformatowanego age parametru z ciągu zapytania formularz sprawdza poprawność.

Drugi formularz na stronie Sprawdzanie wieku przesyła Age wartość w treści żądania i walidacja kończy się niepowodzeniem. Powiązanie kończy się niepowodzeniem, ponieważ age parametr musi pochodzić z ciągu zapytania.

Maksymalna liczba błędów

Walidacja zostaje zatrzymana po osiągnięciu maksymalnej liczby błędów (domyślnie 200). Tę liczbę można skonfigurować przy użyciu następującego kodu w pliku Startup.ConfigureServices:

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

services.AddSingleton<IValidationAttributeAdapterProvider,
    CustomValidationAttributeAdapterProvider>();

Maksymalna rekursja

ValidationVisitor przechodzi przez graf obiektu weryfikowanego modelu. W przypadku modeli, które są głębokie lub nieskończenie rekursywne, walidacja może spowodować przepełnienie stosu. MvcOptions.MaxValidationDepth zapewnia sposób na zatrzymanie weryfikacji na wczesnym etapie, jeśli rekursja gościa przekroczy skonfigurowaną głębokość. Wartość domyślna to MvcOptions.MaxValidationDepth 32.

Automatyczne zwarcie

Sprawdzanie poprawności jest automatycznie zwarcie (pomijane), jeśli graf modelu nie wymaga weryfikacji. Obiekty pomijają walidację dla kolekcji elementów pierwotnych (takich jak byte[], , string[]Dictionary<string, string>) i złożone grafy obiektów, które nie mają żadnych modułów sprawdzania poprawności.

Walidacja po stronie klienta

Walidacja po stronie klienta uniemożliwia przesyłanie do momentu, aż formularz będzie prawidłowy. Przycisk Prześlij uruchamia kod JavaScript, który przesyła formularz lub wyświetla komunikaty o błędach.

Walidacja po stronie klienta pozwala uniknąć niepotrzebnej rundy na serwerze, gdy w formularzu występują błędy wejściowe. Następujące odwołania do skryptu w _Layout.cshtml systemie i _ValidationScriptsPartial.cshtml obsługują weryfikację po stronie 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>

Skrypt jQuery Unobtrusive Validation to niestandardowa biblioteka frontonu firmy Microsoft oparta na popularnej wtyczce weryfikacji jQuery. Bez weryfikacji nieuprawdzonej metody jQuery należy kodować tę samą logikę walidacji w dwóch miejscach: raz w atrybutach weryfikacji po stronie serwera we właściwościach modelu, a następnie ponownie w skryptach po stronie klienta. Zamiast tego pomocnicy tagów i pomocnicy HTML używają atrybutów weryfikacji i metadanych typu z właściwości modelu do renderowania atrybutów HTML 5 data- dla elementów formularza, które wymagają weryfikacji. jQuery Unobtrusive Validation analizuje data- atrybuty i przekazuje logikę do weryfikacji jQuery, skutecznie "kopiowanie" logiki weryfikacji po stronie serwera do klienta. Błędy weryfikacji można wyświetlić na kliencie przy użyciu pomocników tagów, jak pokazano poniżej:

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

Poprzednie pomocniki tagów renderuje następujący kod 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>

Zwróć uwagę, że data- atrybuty w danych wyjściowych HTML odpowiadają atrybutom weryfikacji właściwości Movie.ReleaseDate . Atrybut data-val-required zawiera komunikat o błędzie wyświetlany, jeśli użytkownik nie wypełni pola data wydania. Funkcja jQuery Unobtrusive Validation przekazuje tę wartość do metody jQuery Validation required(), która następnie wyświetla ten komunikat w towarzyszącym <elemecie span> .

Walidacja typu danych jest oparta na typie platformy .NET właściwości, chyba że jest ona zastępowana przez atrybut [DataType]. Przeglądarki mają własne domyślne komunikaty o błędach, ale pakiet weryfikacji jQuery Unobtrusive Validation może zastąpić te komunikaty. [DataType] atrybuty i podklasy, takie jak [EmailAddress], umożliwiają określenie komunikatu o błędzie.

Nieuprawdniająca walidacja

Aby uzyskać informacje na temat nieprawdziwej weryfikacji, zobacz ten problem z usługą GitHub.

Dodawanie walidacji do formularzy dynamicznych

jQuery Unobtrusive Validation przechodzi logikę walidacji i parametry do weryfikacji jQuery po pierwszym załadowaniu strony. W związku z tym walidacja nie działa automatycznie na dynamicznie generowanych formularzach. Aby włączyć walidację, poinformuj jQuery Unobtrusive Validation (Sprawdzanie poprawności nieprawdziwej), aby przeanalizować formularz dynamiczny natychmiast po jego utworzeniu. Na przykład poniższy kod konfiguruje weryfikację po stronie klienta w formularzu dodanym za pośrednictwem 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() akceptuje selektor jQuery dla jednego argumentu. Ta metoda nakazuje jQuery Unobtrusive Validation analizowanie data- atrybutów formularzy w tym selektorze. Wartości tych atrybutów są następnie przekazywane do wtyczki jQuery Validation.

Dodawanie walidacji do kontrolek dynamicznych

Metoda $.validator.unobtrusive.parse() działa na całym formularzu, a nie na poszczególnych dynamicznie generowanych kontrolkach, takich jak <input> i <select/>. Aby ponownie przeanalizować formularz, usuń dane weryfikacji, które zostały dodane podczas analizowania formularza wcześniej, jak pokazano w poniższym przykładzie:

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

Niestandardowa weryfikacja po stronie klienta

Niestandardowa weryfikacja po stronie klienta jest wykonywana przez generowanie data- atrybutów HTML, które współpracują z niestandardową kartą walidacji jQuery. Poniższy przykładowy kod adaptera został napisany dla [ClassicMovie] atrybutów i [ClassicMovieWithClientValidator] , które zostały wprowadzone wcześniej w tym artykule:

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

Aby uzyskać informacje na temat pisania kart, zobacz dokumentację weryfikacji jQuery.

Użycie adaptera dla danego pola jest wyzwalane przez data- atrybuty, które:

  • Flaguj pole jako podlegające weryfikacji (data-val="true").
  • Zidentyfikuj nazwę reguły walidacji i tekst komunikatu o błędzie (na przykład data-val-rulename="Error message.").
  • Podaj wszelkie dodatkowe parametry wymagane przez moduł sprawdzania poprawności (na przykład data-val-rulename-param1="value").

W poniższym przykładzie przedstawiono data- atrybuty atrybutu przykładowej ClassicMovie aplikacji:

<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 wspomniano wcześniej, pomocnicy tagów i pomocnicy HTML używają informacji z atrybutów weryfikacji do renderowania data- atrybutów. Istnieją dwie opcje pisania kodu, które skutkują tworzeniem niestandardowych data- atrybutów HTML:

  • Utwórz klasę, która pochodzi z AttributeAdapterBase<TAttribute> klasy i , która implementuje IValidationAttributeAdapterProviderelement , i zarejestruj atrybut i jego adapter w di. Ta metoda jest zgodna z jedną zasadą odpowiedzialności w tym kodzie weryfikacji powiązanym z serwerem i klientem jest w oddzielnych klasach. Adapter ma również zaletę, że ponieważ jest zarejestrowany w di, inne usługi w DI są dostępne dla niego w razie potrzeby.
  • Zaimplementuj IClientModelValidatorValidationAttribute w klasie. Ta metoda może być odpowiednia, jeśli atrybut nie wykonuje walidacji po stronie serwera i nie wymaga żadnych usług z di.

AttributeAdapter na potrzeby weryfikacji po stronie klienta

Ta metoda renderowania data- atrybutów w kodzie HTML jest używana przez ClassicMovie atrybut w przykładowej aplikacji. Aby dodać walidację klienta przy użyciu tej metody:

  1. Utwórz klasę adaptera atrybutów dla niestandardowego atrybutu weryfikacji. Utwórz pochodną klasę z AttributeAdapterBase<TAttribute>klasy . Utwórz metodę AddValidation , która dodaje data- atrybuty do renderowanych danych wyjściowych, jak pokazano w tym przykładzie:

    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. Utwórz klasę dostawcy adaptera, która implementuje IValidationAttributeAdapterProviderelement . W metodzie GetAttributeAdapter przekaż atrybut niestandardowy do konstruktora adaptera, jak pokazano w tym przykładzie:

    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. Zarejestruj dostawcę adaptera dla di w programie Startup.ConfigureServices:

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

IClientModelValidator na potrzeby weryfikacji po stronie klienta

Ta metoda renderowania data- atrybutów w kodzie HTML jest używana przez ClassicMovieWithClientValidator atrybut w przykładowej aplikacji. Aby dodać walidację klienta przy użyciu tej metody:

  • W niestandardowym atrybucie walidacji zaimplementuj IClientModelValidator interfejs i utwórz metodę AddValidation . W metodzie AddValidation dodaj data- atrybuty do weryfikacji, jak pokazano w poniższym przykładzie:

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

Wyłączanie weryfikacji po stronie klienta

Poniższy kod wyłącza walidację klienta na stronach Razor :

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

Inne opcje wyłączania weryfikacji po stronie klienta:

  • Oznacz jako komentarz odwołanie _ValidationScriptsPartial we wszystkich plikach .cshtml .
  • Usuń zawartość pliku Pages\Shared_ValidationScriptsPartial.cshtml .

Powyższe podejście nie uniemożliwi weryfikacji po stronie klienta biblioteki klas podstawowych IdentityRazor ASP.NET. Aby uzyskać więcej informacji, zobacz Szkielet Identity w projektach ASP.NET Core.

Dodatkowe zasoby