Sdílet prostřednictvím


Vazba modelu v ASP.NET Core

Poznámka:

Toto není nejnovější verze tohoto článku. Aktuální verzi najdete v tomto článku ve verzi .NET 9.

Upozorňující

Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v zásadách podpory .NET a .NET Core. Aktuální verzi najdete v tomto článku ve verzi .NET 9.

Důležité

Tyto informace se týkají předběžného vydání produktu, který může být podstatně změněn před komerčním vydáním. Microsoft neposkytuje žádné záruky, výslovné ani předpokládané, týkající se zde uváděných informací.

Aktuální verzi najdete v tomto článku ve verzi .NET 9.

Tento článek vysvětluje, co je vazba modelu, jak funguje a jak přizpůsobit její chování.

Co je vazba modelu

Kontrolery a Razor stránky pracují s daty pocházejícími z požadavků HTTP. Například směrovací data můžou poskytnout klíč záznamu a zaúčtované pole formuláře mohou poskytovat hodnoty pro vlastnosti modelu. Psaní kódu pro načtení každé z těchto hodnot a převodu z řetězců na typy .NET by bylo zdlouhavé a náchylné k chybám. Vazba modelu tento proces automatizuje. Systém vazeb modelu:

  • Načte data z různých zdrojů, jako jsou směrování dat, pole formulářů a řetězce dotazů.
  • Poskytuje data pro kontrolery a Razor stránky v parametrech metody a veřejných vlastnostech.
  • Převede řetězcová data na typy .NET.
  • Aktualizuje vlastnosti komplexních typů.

Příklad

Předpokládejme, že máte následující metodu akce:

[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)

Aplikace obdrží požadavek s touto adresou URL:

https://contoso.com/api/pets/2?DogsOnly=true

Vazba modelu prochází následujícími kroky poté, co systém směrování vybere metodu akce:

  • Najde první parametr GetById, celé číslo s názvem id.
  • Projde dostupné zdroje v požadavku HTTP a vyhledá id ve směrovacích datech = "2".
  • Převede řetězec "2" na celé číslo 2.
  • Najde další parametr logické hodnoty GetByIds názvem dogsOnly.
  • Projde zdroje a v řetězci dotazu najde "DogsOnly=true". Porovnávání názvů nerozlišuje malá a velká písmena.
  • Převede řetězec "true" na logickou truehodnotu .

Architektura pak volá metodu GetById , předává 2 pro id parametr a true parametr dogsOnly .

V předchozím příkladu jsou cílové vazby modelu parametry metody, které jsou jednoduché typy. Cíle mohou být také vlastnosti komplexního typu. Po úspěšném vázaní každé vlastnosti dojde k ověření modelu pro tuto vlastnost. Záznam dat, která jsou svázaná s modelem, a všechny chyby vazby nebo ověření jsou uloženy v ControllerBase.ModelState nebo PageModel.ModelState. Pokud chcete zjistit, jestli byl tento proces úspěšný, aplikace zkontroluje příznak ModelState.IsValid .

Cíle

Vazba modelu se pokouší najít hodnoty pro následující typy cílů:

  • Parametry metody akce kontroleru, do které je požadavek směrován.
  • Razor Parametry metody obslužné rutiny Pages, do které je požadavek směrován.
  • Veřejné vlastnosti kontroleru nebo PageModel třídy, pokud jsou zadány atributy.

Atribut [BindProperty]

Lze použít u veřejné vlastnosti kontroleru nebo PageModel třídy, aby byla vazba modelu na cílovou tuto vlastnost:

public class EditModel : PageModel
{
    [BindProperty]
    public Instructor? Instructor { get; set; }

    // ...
}

Atribut [BindProperties]

Lze použít u kontroleru nebo PageModel třídy, aby bylo možné určit vazbu modelu tak, aby cílila na všechny veřejné vlastnosti třídy:

[BindProperties]
public class CreateModel : PageModel
{
    public Instructor? Instructor { get; set; }

    // ...
}

Vazba modelu pro požadavky HTTP GET

Ve výchozím nastavení nejsou vlastnosti vázány pro požadavky HTTP GET. Obvykle stačí k požadavku GET parametr ID záznamu. ID záznamu slouží k vyhledání položky v databázi. Proto není nutné svázat vlastnost, která obsahuje instanci modelu. Ve scénářích, ve kterých chcete vlastnosti vázané na data z požadavků GET, nastavte SupportsGet vlastnost na true:

[BindProperty(Name = "ai_user", SupportsGet = true)]
public string? ApplicationInsightsCookie { get; set; }

Jednoduché a komplexní typy vazby modelu

Vazba modelu používá pro typy, na kterých pracuje, konkrétní definice. Jednoduchý typ je převeden z jednoho řetězce pomocí TypeConverter nebo TryParse metody. Komplexní typ je převeden z více vstupních hodnot. Rámec určuje rozdíl na základě existence nebo TypeConverter TryParse. Doporučujeme vytvořit převaděč typů nebo použít TryParse převod string SomeType , který nevyžaduje externí prostředky nebo více vstupů.

Zdroje

Vazba modelu ve výchozím nastavení získává data ve formě párů klíč-hodnota z následujících zdrojů v požadavku HTTP:

  1. Pole formuláře
  2. Text požadavku (pro kontrolery, které mají atribut [ApiController].)
  3. Směrování dat
  4. Parametry řetězce dotazu
  5. Nahrané soubory

Pro každý cílový parametr nebo vlastnost jsou zdroje prohledávány v pořadí uvedeném v předchozím seznamu. Existuje několik výjimek:

  • Směrovací data a řetězcové hodnoty dotazu se používají jenom pro jednoduché typy.
  • Nahrané soubory jsou vázány pouze na cílové typy, které implementují IFormFile nebo IEnumerable<IFormFile>.

Pokud výchozí zdroj není správný, zadejte zdroj jedním z následujících atributů:

  • [FromQuery] – Získá hodnoty z řetězce dotazu.
  • [FromRoute] – Získá hodnoty ze směrovacích dat.
  • [FromForm] – Získá hodnoty z polí publikovaného formuláře.
  • [FromBody] – Získá hodnoty z textu požadavku.
  • [FromHeader] – Získá hodnoty z hlaviček HTTP.

Tyto atributy:

  • Jsou přidány do vlastností modelu jednotlivě, a ne do třídy modelu, jako v následujícím příkladu:

    public class Instructor
    {
        public int Id { get; set; }
    
        [FromQuery(Name = "Note")]
        public string? NoteFromQueryString { get; set; }
    
        // ...
    }
    
  • Volitelně můžete v konstruktoru přijmout hodnotu názvu modelu. Tato možnost je k dispozici v případě, že název vlastnosti neodpovídá hodnotě v požadavku. Například hodnota v požadavku může být hlavička s pomlčkou v názvu, jak je znázorněno v následujícím příkladu:

    public void OnGet([FromHeader(Name = "Accept-Language")] string language)
    

Atribut [FromBody]

[FromBody] Použijte atribut na parametr k naplnění jeho vlastností z textu požadavku HTTP. Modul runtime ASP.NET Core deleguje odpovědnost za čtení textu na vstupní formátovač. Vstupní formátovací moduly jsou vysvětleny dále v tomto článku.

Při [FromBody] použití u parametru komplexního typu se všechny atributy zdroje vazby použité na jeho vlastnosti ignorují. Například následující Create akce určuje, že se jeho pet parametr naplní z těla:

public ActionResult<Pet> Create([FromBody] Pet pet)

Třída Pet určuje, že jeho Breed vlastnost je naplněna z parametru řetězce dotazu:

public class Pet
{
    public string Name { get; set; } = null!;

    [FromQuery] // Attribute is ignored.
    public string Breed { get; set; } = null!;
}

V předchozím příkladu:

  • Atribut [FromQuery] je ignorován.
  • Vlastnost Breed není naplněna parametrem řetězce dotazu.

Vstupní formátovací moduly čtou pouze tělo a nerozumí atributům zdroje vazby. Pokud je v těle nalezena vhodná hodnota, tato hodnota se použije k naplnění Breed vlastnosti.

Nevztahuje [FromBody] se na více než jeden parametr na metodu akce. Jakmile stream požadavku načte vstupní formátovací modul, nebude už k dispozici ke čtení pro vazbu dalších [FromBody] parametrů.

Další zdroje

Zdrojová data jsou poskytována systému vazby modelu poskytovateli hodnot. Můžete napsat a zaregistrovat vlastní zprostředkovatele hodnot, kteří získávají data pro vazbu modelu z jiných zdrojů. Můžete například chtít data ze souborů cookie nebo stavu relace. Získání dat z nového zdroje:

  • Vytvořte třídu, která implementuje IValueProvider.
  • Vytvořte třídu, která implementuje IValueProviderFactory.
  • Zaregistrujte třídu továrny v Program.cssouboru .

Ukázka obsahuje zprostředkovatele hodnot a příklad továrny, který získává hodnoty ze souborů cookie. Registrace továren zprostředkovatele vlastních hodnot v Program.cs:

builder.Services.AddControllers(options =>
{
    options.ValueProviderFactories.Add(new CookieValueProviderFactory());
});

Předchozí kód umístí vlastního zprostředkovatele hodnot za všechny předdefinované zprostředkovatele hodnot. Pokud chcete, aby byl první v seznamu, volejte Insert(0, new CookieValueProviderFactory()) místo Add.

Žádný zdroj vlastnosti modelu

Ve výchozím nastavení se chyba stavu modelu nevytvořila, pokud pro vlastnost modelu nebyla nalezena žádná hodnota. Vlastnost je nastavena na hodnotu null nebo výchozí hodnotu:

  • Jednoduché typy s možnou hodnotou null jsou nastaveny na nullhodnotu .
  • Typy hodnot, které nemají hodnotu null, jsou nastaveny na default(T)hodnotu . Například parametr int id je nastaven na hodnotu 0.
  • V případě komplexních typů vytvoří vazba modelu instanci pomocí výchozího konstruktoru bez nastavení vlastností.
  • Matice jsou nastaveny na Array.Empty<T>(), s tím rozdílem, že byte[] pole jsou nastavena na null.

Pokud by stav modelu měl být neplatný, pokud se v polích formuláře pro vlastnost modelu nenajde nic, použijte [BindRequired] atribut.

Všimněte si, že toto [BindRequired] chování platí pro vazbu modelu z publikovaných dat formuláře, ne pro data JSON nebo XML v textu požadavku. Základní data požadavku se zpracovávají vstupními formátovacími moduly.

Chyby převodu typů

Pokud se najde zdroj, ale nejde ho převést na cílový typ, stav modelu se označí jako neplatný. Cílový parametr nebo vlastnost je nastaven na hodnotu null nebo výchozí hodnotu, jak je uvedeno v předchozí části.

V kontroleru rozhraní API, který má [ApiController] atribut, výsledkem neplatného stavu modelu je automatická odpověď HTTP 400.

Razor Na stránce znovu zobrazíte stránku s chybovou zprávou:

public IActionResult OnPost()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    // ...

    return RedirectToPage("./Index");
}

Při opětovném zobrazení stránky předchozím kódem se v poli formuláře nezobrazí neplatný vstup. Důvodem je to, že vlastnost modelu byla nastavena na hodnotu null nebo výchozí hodnotu. Neplatný vstup se zobrazí v chybové zprávě. Pokud chcete znovu zobrazit špatná data v poli formuláře, zvažte, jestli je vlastnost modelu řetězec a provést převod dat ručně.

Stejná strategie se doporučuje, pokud nechcete, aby chyby převodu typů způsobily chyby stavu modelu. V takovém případě nastavte vlastnost modelu jako řetězec.

Jednoduché typy

Vysvětlení jednoduchých a složitých typů vazeb modelu najdete v tématu Jednoduché a komplexní typy.

Mezi jednoduché typy, na které může pořadač modelů převést zdrojové řetězce, patří následující:

Vytvořit vazbu s IParsable<T>.TryParse

Rozhraní IParsable<TSelf>.TryParse API podporuje hodnoty parametrů akce kontroleru vazby:

public static bool TryParse (string? s, IFormatProvider? provider, out TSelf result);

Následující DateRange třída implementuje podporu vazby IParsable<TSelf> rozsahu dat:

public class DateRange : IParsable<DateRange>
{
    public DateOnly? From { get; init; }
    public DateOnly? To { get; init; }

    public static DateRange Parse(string value, IFormatProvider? provider)
    {
        if (!TryParse(value, provider, out var result))
        {
           throw new ArgumentException("Could not parse supplied value.", nameof(value));
        }

        return result;
    }

    public static bool TryParse(string? value,
                                IFormatProvider? provider, out DateRange dateRange)
    {
        var segments = value?.Split(',', StringSplitOptions.RemoveEmptyEntries 
                                       | StringSplitOptions.TrimEntries);

        if (segments?.Length == 2
            && DateOnly.TryParse(segments[0], provider, out var fromDate)
            && DateOnly.TryParse(segments[1], provider, out var toDate))
        {
            dateRange = new DateRange { From = fromDate, To = toDate };
            return true;
        }

        dateRange = new DateRange { From = default, To = default };
        return false;
    }
}

Předchozí kód:

  • Převede řetězec představující dvě kalendářní data na DateRange objekt.
  • Pořadač modelu používá metodu IParsable<TSelf>.TryParse k vytvoření vazby DateRange.

Následující akce kontroleru používá DateRange třídu k vytvoření vazby rozsahu dat:

// GET /WeatherForecast/ByRange?range=7/24/2022,07/26/2022
public IActionResult ByRange([FromQuery] DateRange range)
{
    if (!ModelState.IsValid)
        return View("Error", ModelState.Values.SelectMany(v => v.Errors));

    var weatherForecasts = Enumerable
        .Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .Where(wf => DateOnly.FromDateTime(wf.Date) >= range.From
                     && DateOnly.FromDateTime(wf.Date) <= range.To)
        .Select(wf => new WeatherForecastViewModel
        {
            Date = wf.Date.ToString("d"),
            TemperatureC = wf.TemperatureC,
            TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556),
            Summary = wf.Summary
        });

    return View("Index", weatherForecasts);
}

Následující Locale třída implementuje podporu vazby IParsable<TSelf> na CultureInfo:

public class Locale : CultureInfo, IParsable<Locale>
{
    public Locale(string culture) : base(culture)
    {
    }

    public static Locale Parse(string value, IFormatProvider? provider)
    {
        if (!TryParse(value, provider, out var result))
        {
           throw new ArgumentException("Could not parse supplied value.", nameof(value));
        }

        return result;
    }

    public static bool TryParse([NotNullWhen(true)] string? value,
                                IFormatProvider? provider, out Locale locale)
    {
        if (value is null)
        {
            locale = new Locale(CurrentCulture.Name);
            return false;
        }
        
        try
        {
            locale = new Locale(value);
            return true;
        }
        catch (CultureNotFoundException)
        {
            locale = new Locale(CurrentCulture.Name);
            return false;
        }
    }
}

Následující akce kontroleru používá Locale třídu k vytvoření vazby CultureInfo řetězce:

// GET /en-GB/WeatherForecast
public IActionResult Index([FromRoute] Locale locale)
{
    var weatherForecasts = Enumerable
        .Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .Select(wf => new WeatherForecastViewModel
        {
            Date = wf.Date.ToString("d", locale),
            TemperatureC = wf.TemperatureC,
            TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556),
            Summary = wf.Summary
        });

    return View(weatherForecasts);
}

Následující akce kontroleru používá DateRange a Locale třídy k vytvoření vazby rozsahu dat s CultureInfo:

// GET /af-ZA/WeatherForecast/RangeByLocale?range=2022-07-24,2022-07-29
public IActionResult RangeByLocale([FromRoute] Locale locale, [FromQuery] string range)
{
    if (!ModelState.IsValid)
        return View("Error", ModelState.Values.SelectMany(v => v.Errors));

    if (!DateRange.TryParse(range, locale, out DateRange rangeResult))
    {
        ModelState.TryAddModelError(nameof(range),
            $"Invalid date range: {range} for locale {locale.DisplayName}");

        return View("Error", ModelState.Values.SelectMany(v => v.Errors));
    }

    var weatherForecasts = Enumerable
        .Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .Where(wf => DateOnly.FromDateTime(wf.Date) >= rangeResult.From
                     && DateOnly.FromDateTime(wf.Date) <= rangeResult.To)
        .Select(wf => new WeatherForecastViewModel
        {
            Date = wf.Date.ToString("d", locale),
            TemperatureC = wf.TemperatureC,
            TemperatureF = 32 + (int) (wf.TemperatureC / 0.5556),
            Summary = wf.Summary
        });

    return View("Index", weatherForecasts);
}

Ukázková aplikace API na GitHubu ukazuje předchozí ukázku kontroleru rozhraní API.

Vytvořit vazbu s TryParse

Rozhraní TryParse API podporuje hodnoty parametrů akce kontroleru vazby:

public static bool TryParse(string value, T out result);
public static bool TryParse(string value, IFormatProvider provider, T out result);

IParsable<T>.TryParse je doporučeným přístupem pro vazbu parametrů, protože na rozdíl od TryParseněj nezávisí na reflexi.

Následující DateRangeTP třída implementuje TryParse:

public class DateRangeTP
{
    public DateOnly? From { get; }
    public DateOnly? To { get; }

    public DateRangeTP(string from, string to)
    {
        if (string.IsNullOrEmpty(from))
            throw new ArgumentNullException(nameof(from));
        if (string.IsNullOrEmpty(to))
            throw new ArgumentNullException(nameof(to));

        From = DateOnly.Parse(from);
        To = DateOnly.Parse(to);
    }

    public static bool TryParse(string? value, out DateRangeTP? result)
    {
        var range = value?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
        if (range?.Length != 2)
        {
            result = default;
            return false;
        }

        result = new DateRangeTP(range[0], range[1]);
        return true;
    }
}

Následující akce kontroleru používá DateRangeTP třídu k vytvoření vazby rozsahu dat:

// GET /WeatherForecast/ByRangeTP?range=7/24/2022,07/26/2022
public IActionResult ByRangeTP([FromQuery] DateRangeTP range)
{
    if (!ModelState.IsValid)
        return View("Error", ModelState.Values.SelectMany(v => v.Errors));

    var weatherForecasts = Enumerable
        .Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .Where(wf => DateOnly.FromDateTime(wf.Date) >= range.From
                     && DateOnly.FromDateTime(wf.Date) <= range.To)
        .Select(wf => new WeatherForecastViewModel
        {
            Date = wf.Date.ToString("d"),
            TemperatureC = wf.TemperatureC,
            TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556),
            Summary = wf.Summary
        });

    return View("Index", weatherForecasts);
}

Komplexní typy

Komplexní typ musí mít veřejné výchozí konstruktor a veřejné zapisovatelné vlastnosti pro vytvoření vazby. Když dojde k vytvoření vazby modelu, vytvoří se instance třídy pomocí veřejného výchozího konstruktoru.

Pro každou vlastnost komplexního typu vazba modelu hledá prostřednictvím zdrojů vzor názvů prefix.property_name. Pokud se nic nenajde, hledá jenom property_name bez předpony. Rozhodnutí o použití předpony není provedeno pro každou vlastnost. Například s dotazem obsahujícím ?Instructor.Id=100&Name=foometodu s vazbou na metodu OnGet(Instructor instructor)obsahuje výsledný objekt typu Instructor :

  • Id nastaveno na 100hodnotu .
  • Name nastaveno na nullhodnotu . Vazba modelu očekává Instructor.Name , protože Instructor.Id byla použita v předchozím parametru dotazu.

Pro vazbu na parametr je předpona název parametru. Pro vazbu na PageModel veřejnou vlastnost je předpona název veřejné vlastnosti. Některé atributy mají Prefix vlastnost, která umožňuje přepsat výchozí použití parametru nebo názvu vlastnosti.

Předpokládejme například, že komplexní typ je následující Instructor třída:

public class Instructor
{
    public int ID { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
}

Předpona = název parametru

Pokud je model, který má být vázán, parametr s názvem instructorToUpdate:

public IActionResult OnPost(int? id, Instructor instructorToUpdate)

Vazba modelu začíná procházením zdrojů klíče instructorToUpdate.ID. Pokud se nenajde, vyhledá ID se bez předpony.

Prefix = název vlastnosti

Pokud je model, který má být vázán, vlastnost s názvem Instructor kontroleru nebo PageModel třídy:

[BindProperty]
public Instructor Instructor { get; set; }

Vazba modelu začíná procházením zdrojů klíče Instructor.ID. Pokud se nenajde, vyhledá ID se bez předpony.

Vlastní předpona

Pokud je model, který má být vázán, parametr s názvem instructorToUpdate a Bind atribut určuje Instructor jako předponu:

public IActionResult OnPost(
    int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)

Vazba modelu začíná procházením zdrojů klíče Instructor.ID. Pokud se nenajde, vyhledá ID se bez předpony.

Atributy pro cíle komplexního typu

Pro řízení vazby modelu komplexních typů je k dispozici několik předdefinovaných atributů:

Upozorňující

Tyto atributy ovlivňují vazbu modelu při publikování dat formuláře je zdrojem hodnot. Nemají vliv na vstupní formátovací moduly, které proces publikoval tělo požadavků JSON a XML. Vstupní formátovací moduly jsou vysvětleny dále v tomto článku.

[Bind] – atribut

Lze použít na třídu nebo parametr metody. Určuje, které vlastnosti modelu mají být zahrnuty do vazby modelu. [Bind]nemá vliv na vstupní formátovací moduly.

V následujícím příkladu jsou vázány pouze zadané vlastnosti Instructor modelu, pokud je volána jakákoli obslužná rutina nebo metoda akce:

[Bind("LastName,FirstMidName,HireDate")]
public class Instructor

V následujícím příkladu jsou při zavolání metody vázány OnPost pouze zadané vlastnosti Instructor modelu:

[HttpPost]
public IActionResult OnPost(
    [Bind("LastName,FirstMidName,HireDate")] Instructor instructor)

Atribut [Bind] lze použít k ochraně proti nadměrnému umístění ve scénářích vytváření . Nefunguje dobře ve scénářích úprav, protože vyloučené vlastnosti jsou nastavené na hodnotu null nebo výchozí hodnotu, místo aby zůstaly beze změny. Pro ochranu před nadměrném umístěním se místo atributu [Bind] doporučuje zobrazit modely. Další informace naleznete v tématu Bezpečnostní poznámka o nadměrném příspěvku.

Atribut [ModelBinder]

ModelBinderAttribute lze použít u typů, vlastností nebo parametrů. Umožňuje zadat typ pořadače modelu použitého k vytvoření vazby konkrétní instance nebo typu. Příklad:

[HttpPost]
public IActionResult OnPost(
    [ModelBinder<MyInstructorModelBinder>] Instructor instructor)

Atribut [ModelBinder] lze také použít ke změně názvu vlastnosti nebo parametru při vazbě modelu:

public class Instructor
{
    [ModelBinder(Name = "instructor_id")]
    public string Id { get; set; }

    // ...
}

Atribut [BindRequired]

Způsobí, že vazba modelu přidá chybu stavu modelu, pokud vazba nemůže nastat pro vlastnost modelu. Tady je příklad:

public class InstructorBindRequired
{
    // ...

    [BindRequired]
    public DateTime HireDate { get; set; }
}

Projděte si také diskuzi o atributu [Required] v ověření modelu.

Atribut [BindNever]

Lze použít u vlastnosti nebo typu. Zabraňuje vazbě modelu v nastavení vlastnosti modelu. Při použití na typ systém vazeb modelu vyloučí všechny vlastnosti, které typ definuje. Tady je příklad:

public class InstructorBindNever
{
    [BindNever]
    public int Id { get; set; }

    // ...
}

Kolekce

U cílů, které jsou kolekcemi jednoduchých typů, vazba modelu hledá shody s parameter_name nebo property_name. Pokud se nenajde žádná shoda, vyhledá jeden z podporovaných formátů bez předpony. Příklad:

  • Předpokládejme, že parametr, který má být vázán, je pole s názvem selectedCourses:

    public IActionResult OnPost(int? id, int[] selectedCourses)
    
  • Data řetězce formuláře nebo dotazu můžou být v jednom z následujících formátů:

    selectedCourses=1050&selectedCourses=2000 
    
    selectedCourses[0]=1050&selectedCourses[1]=2000
    
    [0]=1050&[1]=2000
    
    selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
    
    [a]=1050&[b]=2000&index=a&index=b
    

    Vyhněte se vytvoření vazby parametru nebo vlastnosti s názvem index nebo Index pokud sousedí s hodnotou kolekce. Vazby modelu se pokusí použít index jako index pro kolekci, což může vést k nesprávné vazbě. Představte si například následující akci:

    public IActionResult Post(string index, List<Product> products)
    

    V předchozím kódu index se parametr řetězce dotazu sváže s parametrem index metody a také slouží k vytvoření vazby kolekce produktů. Přejmenování parametru nebo použití atributu index vazby modelu ke konfiguraci vazby zabrání tomuto problému:

    public IActionResult Post(string productIndex, List<Product> products)
    
  • Následující formát je podporován pouze v datech formuláře:

    selectedCourses[]=1050&selectedCourses[]=2000
    
  • Pro všechny předchozí ukázkové formáty předá vazba modelu do parametru selectedCourses pole dvou položek:

    • selectedCourses[0]=1050
    • selectedCourses[1]=2000

    Formáty dat, které používají čísla dolního indexu (... [0] ... [1] ...) musí zajistit, aby byly číslony postupně počínaje nulou. Pokud jsou v číslování dolního indexu nějaké mezery, budou všechny položky po mezerě ignorovány. Pokud jsou například dolní indexy 0 a 2 místo 0 a 1, druhá položka se ignoruje.

Slovníky

V případě Dictionary cílů vazba modelu hledá shody s parameter_name nebo property_name. Pokud se nenajde žádná shoda, vyhledá jeden z podporovaných formátů bez předpony. Příklad:

  • Předpokládejme, že cílový parametr je pojmenovaný Dictionary<int, string> selectedCourses:

    public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
    
  • Data publikovaného formuláře nebo řetězce dotazu můžou vypadat jako jeden z následujících příkladů:

    selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics
    
    [1050]=Chemistry&selectedCourses[2000]=Economics
    
    selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry&
    selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
    
    [0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
    
  • Pro všechny předchozí ukázkové formáty předá vazba modelu slovník se dvěma položkami parametru selectedCourses :

    • selectedCourses["1050"]="Chemie"
    • selectedCourses["2000"]="Ekonomika"

Vazby konstruktoru a typy záznamů

Vazba modelu vyžaduje, aby složité typy měly konstruktor bez parametrů. System.Text.Json Vstupní formátovací moduly i Newtonsoft.Json na základě podporují deserializaci tříd, které nemají konstruktor bez parametrů.

Typy záznamů představují skvělý způsob, jak stručně znázorňovat data v síti. ASP.NET Core podporuje vazbu modelu a ověřování typů záznamů pomocí jednoho konstruktoru:

public record Person(
    [Required] string Name, [Range(0, 150)] int Age, [BindNever] int Id);

public class PersonController
{
    public IActionResult Index() => View();

    [HttpPost]
    public IActionResult Index(Person person)
    {
        // ...
    }
}

Person/Index.cshtml:

@model Person

<label>Name: <input asp-for="Name" /></label>
<br />
<label>Age: <input asp-for="Age" /></label>

Při ověřování typů záznamů modul runtime vyhledává metadata vazby a ověřování, a to konkrétně u parametrů, nikoli u vlastností.

Architektura umožňuje vazbu na typy záznamů a jejich ověřování:

public record Person([Required] string Name, [Range(0, 100)] int Age);

Aby předchozí funkce fungovala, musí typ:

  • Být typem záznamu.
  • Máte přesně jeden veřejný konstruktor.
  • Obsahují parametry, které mají vlastnost se stejným názvem a typem. Názvy se nesmí lišit písmeny.

PoCOs bez konstruktorů bez parametrů

PoCOs, které nemají konstruktory bez parametrů, nelze svázat.

Výsledkem následujícího kódu je výjimka, která říká, že typ musí mít konstruktor bez parametrů:

public class Person(string Name)

public record Person([Required] string Name, [Range(0, 100)] int Age)
{
    public Person(string Name) : this (Name, 0);
}

Typy záznamů s ručně vytvořenými konstruktory

Typy záznamů s ručně vytvořenými konstruktory, které vypadají jako primární konstruktory

public record Person
{
    public Person([Required] string Name, [Range(0, 100)] int Age)
        => (this.Name, this.Age) = (Name, Age);

    public string Name { get; set; }
    public int Age { get; set; }
}

Typy záznamů, ověřování a metadata vazby

U typů záznamů se používají metadata ověřování a vazby parametrů. Všechna metadata vlastností se ignorují.

public record Person (string Name, int Age)
{
   [BindProperty(Name = "SomeName")] // This does not get used
   [Required] // This does not get used
   public string Name { get; init; }
}

Ověřování a metadata

Ověřování používá metadata parametru, ale používá vlastnost ke čtení hodnoty. V běžném případě s primárními konstruktory by oba byly identické. Existují však způsoby, jak ji porazit:

public record Person([Required] string Name)
{
    private readonly string _name;

    // The following property is never null.
    // However this object could have been constructed as "new Person(null)".
    public string Name { get; init => _name = value ?? string.Empty; }
}

TryUpdateModel neaktualizuje parametry u typu záznamu

public record Person(string Name)
{
    public int Age { get; set; }
}

var person = new Person("initial-name");
TryUpdateModel(person, ...);

V tomto případě se MVC nebude pokoušet o vytvoření vazby Name znovu. Age Je však možné je aktualizovat.

Chování globalizace vazby modelu směruje data a řetězce dotazů

Zprostředkovatel hodnoty trasy ASP.NET Core a zprostředkovatel hodnot řetězce dotazu:

  • Nakládá s hodnotami jako s neutrální jazykovou verzí.
  • Počítejte s tím, že adresy URL jsou invariantní pro jazykovou verzi.

Naproti tomu hodnoty pocházející z dat formuláře procházejí převodem citlivým na jazykovou verzi. Je to záměrně tak, aby adresy URL byly sdíleny napříč národními prostředími.

Aby poskytovatel hodnoty trasy ASP.NET Core a poskytovatel hodnot řetězce dotazu prošly převodem citlivým na jazykovou verzi:

public class CultureQueryStringValueProviderFactory : IValueProviderFactory
{
    public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
    {
        _ = context ?? throw new ArgumentNullException(nameof(context));

        var query = context.ActionContext.HttpContext.Request.Query;
        if (query?.Count > 0)
        {
            context.ValueProviders.Add(
                new QueryStringValueProvider(
                    BindingSource.Query,
                    query,
                    CultureInfo.CurrentCulture));
        }

        return Task.CompletedTask;
    }
}
builder.Services.AddControllers(options =>
{
    var index = options.ValueProviderFactories.IndexOf(
        options.ValueProviderFactories.OfType<QueryStringValueProviderFactory>()
            .Single());

    options.ValueProviderFactories[index] =
        new CultureQueryStringValueProviderFactory();
});

Speciální datové typy

Existuje několik speciálních datových typů, které lze zpracovat vazbou modelu.

IFormFile a IFormFileCollection

Nahraný soubor zahrnutý v požadavku HTTP. Podporuje se IEnumerable<IFormFile> také více souborů.

CancellationToken

Akce můžou volitelně svázat CancellationToken jako parametr. Tím se prováže RequestAborted signál, když dojde k přerušení připojení, které je základem požadavku HTTP. Akce můžou tento parametr použít ke zrušení dlouhotrvajících asynchronních operací, které se spouští jako součást akcí kontroleru.

FormCollection

Slouží k načtení všech hodnot z publikovaných dat formuláře.

Vstupní formátovací moduly

Data v textu požadavku můžou být ve formátu JSON, XML nebo jiném formátu. K analýze těchto dat používá vazba modelu vstupní formátovací modul , který je nakonfigurovaný pro zpracování konkrétního typu obsahu. Ve výchozím nastavení ASP.NET Core obsahuje vstupní formátovací moduly založené na FORMÁTU JSON pro zpracování dat JSON. Můžete přidat další formátovací moduly pro jiné typy obsahu.

ASP.NET Core vybere vstupní formátovací moduly na základě atributu Consumes . Pokud neexistuje žádný atribut, použije hlavičku Content-Type.

Použití předdefinovaných formátovacích souborů XML:

Přizpůsobení vazby modelu pomocí vstupních formátovacích nástrojů

Vstupní formátovací modul přebírá plnou odpovědnost za čtení dat z textu požadavku. Pokud chcete tento proces přizpůsobit, nakonfigurujte rozhraní API používaná vstupním formátovacím modulem. Tato část popisuje, jak přizpůsobit System.Text.Jsonvstupní formátovací modul založený na základech pro pochopení vlastního typu s názvem ObjectId.

Představte si následující model, který obsahuje vlastní ObjectId vlastnost:

public class InstructorObjectId
{
    [Required]
    public ObjectId ObjectId { get; set; } = null!;
}

Chcete-li přizpůsobit proces vazby modelu při použití System.Text.Json, vytvořte třídu odvozenou z JsonConverter<T>:

internal class ObjectIdConverter : JsonConverter<ObjectId>
{
    public override ObjectId Read(
        ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        => new(JsonSerializer.Deserialize<int>(ref reader, options));

    public override void Write(
        Utf8JsonWriter writer, ObjectId value, JsonSerializerOptions options)
        => writer.WriteNumberValue(value.Id);
}

Chcete-li použít vlastní převaděč, použijte JsonConverterAttribute atribut na typ. V následujícím příkladu ObjectId je typ nakonfigurován ObjectIdConverter jako vlastní převaděč:

[JsonConverter(typeof(ObjectIdConverter))]
public record ObjectId(int Id);

Další informace naleznete v tématu Jak psát vlastní převaděče.

Vyloučení zadaných typů z vazby modelu

Chování vazeb modelu a ověřovacích systémů je řízeno ModelMetadata. Úpravou můžete přidat ModelMetadata zprostředkovatele podrobností do MvcOptions.ModelMetadataDetailsProviders. Zprostředkovatelé předdefinovaných podrobností jsou k dispozici pro zakázání vazby nebo ověřování modelu pro zadané typy.

Chcete-li zakázat vazbu modelu u všech modelů zadaného typu, přidejte do ExcludeBindingMetadataProvider Program.cssouboru . Chcete-li například zakázat vazbu modelu u všech modelů typu System.Version:

builder.Services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.ModelMetadataDetailsProviders.Add(
            new ExcludeBindingMetadataProvider(typeof(Version)));
        options.ModelMetadataDetailsProviders.Add(
            new SuppressChildValidationMetadataProvider(typeof(Guid)));
    });

Chcete-li zakázat ověřování u vlastností zadaného typu, přidejte SuppressChildValidationMetadataProvider do Program.cssouboru . Chcete-li například zakázat ověřování u vlastností typu System.Guid:

builder.Services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.ModelMetadataDetailsProviders.Add(
            new ExcludeBindingMetadataProvider(typeof(Version)));
        options.ModelMetadataDetailsProviders.Add(
            new SuppressChildValidationMetadataProvider(typeof(Guid)));
    });

Vlastní pořadače modelů

Vazby modelu můžete rozšířit tak, že napíšete vlastní pořadač modelů a pomocí atributu [ModelBinder] ho vyberete pro daný cíl. Přečtěte si další informace o vazbě vlastního modelu.

Ruční vazba modelu

Vazbu modelu lze vyvolat ručně pomocí TryUpdateModelAsync metody. Metoda je definována v obou ControllerBase třídách PageModel . Přetížení metody umožňují zadat předponu a zprostředkovatele hodnot, které se mají použít. Metoda vrátí false , pokud se nezdaří vazba modelu. Tady je příklad:

if (await TryUpdateModelAsync(
    newInstructor,
    "Instructor",
    x => x.Name, x => x.HireDate!))
{
    _instructorStore.Add(newInstructor);
    return RedirectToPage("./Index");
}

return Page();

TryUpdateModelAsync používá zprostředkovatele hodnot k získání dat z textu formuláře, řetězce dotazu a směrování dat. TryUpdateModelAsync je obvykle:

  • Používá se s aplikacemi Razor Pages a MVC pomocí kontrolerů a zobrazení, aby se zabránilo nadměrnému publikování.
  • Nepoužívá se s webovým rozhraním API, pokud se nepoužívá z dat formulářů, řetězců dotazů a směruje data. Koncové body webového rozhraní API, které využívají JSON, používají k deserializaci textu požadavku do objektu vstupní formátovací moduly.

Další informace naleznete v tématu TryUpdateModelAsync.

Atribut [FromServices]

Název tohoto atributu se řídí vzorem atributů vazby modelu, které určují zdroj dat. Nejedná se ale o vazbu dat z poskytovatele hodnot. Získá instanci typu z kontejneru injektáže závislostí. Jejím účelem je poskytnout alternativu k injektáži konstruktoru, pokud potřebujete službu pouze v případě, že je volána konkrétní metoda.

Pokud instance typu není zaregistrovaná v kontejneru injektáže závislostí, aplikace vyvolá výjimku při pokusu o vytvoření vazby parametru. Pokud chcete parametr nastavit jako volitelný, použijte jeden z následujících přístupů:

  • Nastavení parametru s možnou hodnotou null
  • Nastavte výchozí hodnotu parametru.

V případě parametrů s možnou hodnotou null se ujistěte, že parametr není null před přístupem k němu.

Další materiály

Tento článek vysvětluje, co je vazba modelu, jak funguje a jak přizpůsobit její chování.

Co je vazba modelu

Kontrolery a Razor stránky pracují s daty pocházejícími z požadavků HTTP. Například směrovací data můžou poskytnout klíč záznamu a zaúčtované pole formuláře mohou poskytovat hodnoty pro vlastnosti modelu. Psaní kódu pro načtení každé z těchto hodnot a převodu z řetězců na typy .NET by bylo zdlouhavé a náchylné k chybám. Vazba modelu tento proces automatizuje. Systém vazeb modelu:

  • Načte data z různých zdrojů, jako jsou směrování dat, pole formulářů a řetězce dotazů.
  • Poskytuje data pro kontrolery a Razor stránky v parametrech metody a veřejných vlastnostech.
  • Převede řetězcová data na typy .NET.
  • Aktualizuje vlastnosti komplexních typů.

Příklad

Předpokládejme, že máte následující metodu akce:

[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)

Aplikace obdrží požadavek s touto adresou URL:

https://contoso.com/api/pets/2?DogsOnly=true

Vazba modelu prochází následujícími kroky poté, co systém směrování vybere metodu akce:

  • Najde první parametr GetById, celé číslo s názvem id.
  • Projde dostupné zdroje v požadavku HTTP a vyhledá id ve směrovacích datech = "2".
  • Převede řetězec "2" na celé číslo 2.
  • Najde další parametr logické hodnoty GetByIds názvem dogsOnly.
  • Projde zdroje a v řetězci dotazu najde "DogsOnly=true". Porovnávání názvů nerozlišuje malá a velká písmena.
  • Převede řetězec "true" na logickou truehodnotu .

Architektura pak volá metodu GetById , předává 2 pro id parametr a true parametr dogsOnly .

V předchozím příkladu jsou cílové vazby modelu parametry metody, které jsou jednoduché typy. Cíle mohou být také vlastnosti komplexního typu. Po úspěšném vázaní každé vlastnosti dojde k ověření modelu pro tuto vlastnost. Záznam dat, která jsou svázaná s modelem, a všechny chyby vazby nebo ověření jsou uloženy v ControllerBase.ModelState nebo PageModel.ModelState. Pokud chcete zjistit, jestli byl tento proces úspěšný, aplikace zkontroluje příznak ModelState.IsValid .

Cíle

Vazba modelu se pokouší najít hodnoty pro následující typy cílů:

  • Parametry metody akce kontroleru, do které je požadavek směrován.
  • Razor Parametry metody obslužné rutiny Pages, do které je požadavek směrován.
  • Veřejné vlastnosti kontroleru nebo PageModel třídy, pokud jsou zadány atributy.

Atribut [BindProperty]

Lze použít u veřejné vlastnosti kontroleru nebo PageModel třídy, aby byla vazba modelu na cílovou tuto vlastnost:

public class EditModel : PageModel
{
    [BindProperty]
    public Instructor? Instructor { get; set; }

    // ...
}

Atribut [BindProperties]

Lze použít u kontroleru nebo PageModel třídy, aby bylo možné určit vazbu modelu tak, aby cílila na všechny veřejné vlastnosti třídy:

[BindProperties]
public class CreateModel : PageModel
{
    public Instructor? Instructor { get; set; }

    // ...
}

Vazba modelu pro požadavky HTTP GET

Ve výchozím nastavení nejsou vlastnosti vázány pro požadavky HTTP GET. Obvykle stačí k požadavku GET parametr ID záznamu. ID záznamu slouží k vyhledání položky v databázi. Proto není nutné svázat vlastnost, která obsahuje instanci modelu. Ve scénářích, ve kterých chcete vlastnosti vázané na data z požadavků GET, nastavte SupportsGet vlastnost na true:

[BindProperty(Name = "ai_user", SupportsGet = true)]
public string? ApplicationInsightsCookie { get; set; }

Jednoduché a komplexní typy vazby modelu

Vazba modelu používá pro typy, na kterých pracuje, konkrétní definice. Jednoduchý typ je převeden z jednoho řetězce pomocí TypeConverter nebo TryParse metody. Komplexní typ je převeden z více vstupních hodnot. Rámec určuje rozdíl na základě existence nebo TypeConverter TryParse. Doporučujeme vytvořit převaděč typů nebo použít TryParse převod string SomeType , který nevyžaduje externí prostředky nebo více vstupů.

Zdroje

Vazba modelu ve výchozím nastavení získává data ve formě párů klíč-hodnota z následujících zdrojů v požadavku HTTP:

  1. Pole formuláře
  2. Text požadavku (pro kontrolery, které mají atribut [ApiController].)
  3. Směrování dat
  4. Parametry řetězce dotazu
  5. Nahrané soubory

Pro každý cílový parametr nebo vlastnost jsou zdroje prohledávány v pořadí uvedeném v předchozím seznamu. Existuje několik výjimek:

  • Směrovací data a řetězcové hodnoty dotazu se používají jenom pro jednoduché typy.
  • Nahrané soubory jsou vázány pouze na cílové typy, které implementují IFormFile nebo IEnumerable<IFormFile>.

Pokud výchozí zdroj není správný, zadejte zdroj jedním z následujících atributů:

  • [FromQuery] – Získá hodnoty z řetězce dotazu.
  • [FromRoute] – Získá hodnoty ze směrovacích dat.
  • [FromForm] – Získá hodnoty z polí publikovaného formuláře.
  • [FromBody] – Získá hodnoty z textu požadavku.
  • [FromHeader] – Získá hodnoty z hlaviček HTTP.

Tyto atributy:

  • Jsou přidány do vlastností modelu jednotlivě, a ne do třídy modelu, jako v následujícím příkladu:

    public class Instructor
    {
        public int Id { get; set; }
    
        [FromQuery(Name = "Note")]
        public string? NoteFromQueryString { get; set; }
    
        // ...
    }
    
  • Volitelně můžete v konstruktoru přijmout hodnotu názvu modelu. Tato možnost je k dispozici v případě, že název vlastnosti neodpovídá hodnotě v požadavku. Například hodnota v požadavku může být hlavička s pomlčkou v názvu, jak je znázorněno v následujícím příkladu:

    public void OnGet([FromHeader(Name = "Accept-Language")] string language)
    

Atribut [FromBody]

[FromBody] Použijte atribut na parametr k naplnění jeho vlastností z textu požadavku HTTP. Modul runtime ASP.NET Core deleguje odpovědnost za čtení textu na vstupní formátovač. Vstupní formátovací moduly jsou vysvětleny dále v tomto článku.

Při [FromBody] použití u parametru komplexního typu se všechny atributy zdroje vazby použité na jeho vlastnosti ignorují. Například následující Create akce určuje, že se jeho pet parametr naplní z těla:

public ActionResult<Pet> Create([FromBody] Pet pet)

Třída Pet určuje, že jeho Breed vlastnost je naplněna z parametru řetězce dotazu:

public class Pet
{
    public string Name { get; set; } = null!;

    [FromQuery] // Attribute is ignored.
    public string Breed { get; set; } = null!;
}

V předchozím příkladu:

  • Atribut [FromQuery] je ignorován.
  • Vlastnost Breed není naplněna parametrem řetězce dotazu.

Vstupní formátovací moduly čtou pouze tělo a nerozumí atributům zdroje vazby. Pokud je v těle nalezena vhodná hodnota, tato hodnota se použije k naplnění Breed vlastnosti.

Nevztahuje [FromBody] se na více než jeden parametr na metodu akce. Jakmile stream požadavku načte vstupní formátovací modul, nebude už k dispozici ke čtení pro vazbu dalších [FromBody] parametrů.

Další zdroje

Zdrojová data jsou poskytována systému vazby modelu poskytovateli hodnot. Můžete napsat a zaregistrovat vlastní zprostředkovatele hodnot, kteří získávají data pro vazbu modelu z jiných zdrojů. Můžete například chtít data ze souborů cookie nebo stavu relace. Získání dat z nového zdroje:

  • Vytvořte třídu, která implementuje IValueProvider.
  • Vytvořte třídu, která implementuje IValueProviderFactory.
  • Zaregistrujte třídu továrny v Program.cssouboru .

Ukázka obsahuje zprostředkovatele hodnot a příklad továrny, který získává hodnoty ze souborů cookie. Registrace továren zprostředkovatele vlastních hodnot v Program.cs:

builder.Services.AddControllers(options =>
{
    options.ValueProviderFactories.Add(new CookieValueProviderFactory());
});

Předchozí kód umístí vlastního zprostředkovatele hodnot za všechny předdefinované zprostředkovatele hodnot. Pokud chcete, aby byl první v seznamu, volejte Insert(0, new CookieValueProviderFactory()) místo Add.

Žádný zdroj vlastnosti modelu

Ve výchozím nastavení se chyba stavu modelu nevytvořila, pokud pro vlastnost modelu nebyla nalezena žádná hodnota. Vlastnost je nastavena na hodnotu null nebo výchozí hodnotu:

  • Jednoduché typy s možnou hodnotou null jsou nastaveny na nullhodnotu .
  • Typy hodnot, které nemají hodnotu null, jsou nastaveny na default(T)hodnotu . Například parametr int id je nastaven na hodnotu 0.
  • V případě komplexních typů vytvoří vazba modelu instanci pomocí výchozího konstruktoru bez nastavení vlastností.
  • Matice jsou nastaveny na Array.Empty<T>(), s tím rozdílem, že byte[] pole jsou nastavena na null.

Pokud by stav modelu měl být neplatný, pokud se v polích formuláře pro vlastnost modelu nenajde nic, použijte [BindRequired] atribut.

Všimněte si, že toto [BindRequired] chování platí pro vazbu modelu z publikovaných dat formuláře, ne pro data JSON nebo XML v textu požadavku. Základní data požadavku se zpracovávají vstupními formátovacími moduly.

Chyby převodu typů

Pokud se najde zdroj, ale nejde ho převést na cílový typ, stav modelu se označí jako neplatný. Cílový parametr nebo vlastnost je nastaven na hodnotu null nebo výchozí hodnotu, jak je uvedeno v předchozí části.

V kontroleru rozhraní API, který má [ApiController] atribut, výsledkem neplatného stavu modelu je automatická odpověď HTTP 400.

Razor Na stránce znovu zobrazíte stránku s chybovou zprávou:

public IActionResult OnPost()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    // ...

    return RedirectToPage("./Index");
}

Při opětovném zobrazení stránky předchozím kódem se v poli formuláře nezobrazí neplatný vstup. Důvodem je to, že vlastnost modelu byla nastavena na hodnotu null nebo výchozí hodnotu. Neplatný vstup se zobrazí v chybové zprávě. Pokud chcete znovu zobrazit špatná data v poli formuláře, zvažte, jestli je vlastnost modelu řetězec a provést převod dat ručně.

Stejná strategie se doporučuje, pokud nechcete, aby chyby převodu typů způsobily chyby stavu modelu. V takovém případě nastavte vlastnost modelu jako řetězec.

Jednoduché typy

Vysvětlení jednoduchých a složitých typů vazeb modelu najdete v tématu Jednoduché a komplexní typy.

Mezi jednoduché typy, na které může pořadač modelů převést zdrojové řetězce, patří následující:

Vytvořit vazbu s IParsable<T>.TryParse

Rozhraní IParsable<TSelf>.TryParse API podporuje hodnoty parametrů akce kontroleru vazby:

public static bool TryParse (string? s, IFormatProvider? provider, out TSelf result);

Následující DateRange třída implementuje podporu vazby IParsable<TSelf> rozsahu dat:

public class DateRange : IParsable<DateRange>
{
    public DateOnly? From { get; init; }
    public DateOnly? To { get; init; }

    public static DateRange Parse(string value, IFormatProvider? provider)
    {
        if (!TryParse(value, provider, out var result))
        {
           throw new ArgumentException("Could not parse supplied value.", nameof(value));
        }

        return result;
    }

    public static bool TryParse(string? value,
                                IFormatProvider? provider, out DateRange dateRange)
    {
        var segments = value?.Split(',', StringSplitOptions.RemoveEmptyEntries 
                                       | StringSplitOptions.TrimEntries);

        if (segments?.Length == 2
            && DateOnly.TryParse(segments[0], provider, out var fromDate)
            && DateOnly.TryParse(segments[1], provider, out var toDate))
        {
            dateRange = new DateRange { From = fromDate, To = toDate };
            return true;
        }

        dateRange = new DateRange { From = default, To = default };
        return false;
    }
}

Předchozí kód:

  • Převede řetězec představující dvě kalendářní data na DateRange objekt.
  • Pořadač modelu používá metodu IParsable<TSelf>.TryParse k vytvoření vazby DateRange.

Následující akce kontroleru používá DateRange třídu k vytvoření vazby rozsahu dat:

// GET /WeatherForecast/ByRange?range=7/24/2022,07/26/2022
public IActionResult ByRange([FromQuery] DateRange range)
{
    if (!ModelState.IsValid)
        return View("Error", ModelState.Values.SelectMany(v => v.Errors));

    var weatherForecasts = Enumerable
        .Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .Where(wf => DateOnly.FromDateTime(wf.Date) >= range.From
                     && DateOnly.FromDateTime(wf.Date) <= range.To)
        .Select(wf => new WeatherForecastViewModel
        {
            Date = wf.Date.ToString("d"),
            TemperatureC = wf.TemperatureC,
            TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556),
            Summary = wf.Summary
        });

    return View("Index", weatherForecasts);
}

Následující Locale třída implementuje podporu vazby IParsable<TSelf> na CultureInfo:

public class Locale : CultureInfo, IParsable<Locale>
{
    public Locale(string culture) : base(culture)
    {
    }

    public static Locale Parse(string value, IFormatProvider? provider)
    {
        if (!TryParse(value, provider, out var result))
        {
           throw new ArgumentException("Could not parse supplied value.", nameof(value));
        }

        return result;
    }

    public static bool TryParse([NotNullWhen(true)] string? value,
                                IFormatProvider? provider, out Locale locale)
    {
        if (value is null)
        {
            locale = new Locale(CurrentCulture.Name);
            return false;
        }
        
        try
        {
            locale = new Locale(value);
            return true;
        }
        catch (CultureNotFoundException)
        {
            locale = new Locale(CurrentCulture.Name);
            return false;
        }
    }
}

Následující akce kontroleru používá Locale třídu k vytvoření vazby CultureInfo řetězce:

// GET /en-GB/WeatherForecast
public IActionResult Index([FromRoute] Locale locale)
{
    var weatherForecasts = Enumerable
        .Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .Select(wf => new WeatherForecastViewModel
        {
            Date = wf.Date.ToString("d", locale),
            TemperatureC = wf.TemperatureC,
            TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556),
            Summary = wf.Summary
        });

    return View(weatherForecasts);
}

Následující akce kontroleru používá DateRange a Locale třídy k vytvoření vazby rozsahu dat s CultureInfo:

// GET /af-ZA/WeatherForecast/RangeByLocale?range=2022-07-24,2022-07-29
public IActionResult RangeByLocale([FromRoute] Locale locale, [FromQuery] string range)
{
    if (!ModelState.IsValid)
        return View("Error", ModelState.Values.SelectMany(v => v.Errors));

    if (!DateRange.TryParse(range, locale, out DateRange rangeResult))
    {
        ModelState.TryAddModelError(nameof(range),
            $"Invalid date range: {range} for locale {locale.DisplayName}");

        return View("Error", ModelState.Values.SelectMany(v => v.Errors));
    }

    var weatherForecasts = Enumerable
        .Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .Where(wf => DateOnly.FromDateTime(wf.Date) >= rangeResult.From
                     && DateOnly.FromDateTime(wf.Date) <= rangeResult.To)
        .Select(wf => new WeatherForecastViewModel
        {
            Date = wf.Date.ToString("d", locale),
            TemperatureC = wf.TemperatureC,
            TemperatureF = 32 + (int) (wf.TemperatureC / 0.5556),
            Summary = wf.Summary
        });

    return View("Index", weatherForecasts);
}

Ukázková aplikace API na GitHubu ukazuje předchozí ukázku kontroleru rozhraní API.

Vytvořit vazbu s TryParse

Rozhraní TryParse API podporuje hodnoty parametrů akce kontroleru vazby:

public static bool TryParse(string value, T out result);
public static bool TryParse(string value, IFormatProvider provider, T out result);

IParsable<T>.TryParse je doporučeným přístupem pro vazbu parametrů, protože na rozdíl od TryParseněj nezávisí na reflexi.

Následující DateRangeTP třída implementuje TryParse:

public class DateRangeTP
{
    public DateOnly? From { get; }
    public DateOnly? To { get; }

    public DateRangeTP(string from, string to)
    {
        if (string.IsNullOrEmpty(from))
            throw new ArgumentNullException(nameof(from));
        if (string.IsNullOrEmpty(to))
            throw new ArgumentNullException(nameof(to));

        From = DateOnly.Parse(from);
        To = DateOnly.Parse(to);
    }

    public static bool TryParse(string? value, out DateRangeTP? result)
    {
        var range = value?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
        if (range?.Length != 2)
        {
            result = default;
            return false;
        }

        result = new DateRangeTP(range[0], range[1]);
        return true;
    }
}

Následující akce kontroleru používá DateRangeTP třídu k vytvoření vazby rozsahu dat:

// GET /WeatherForecast/ByRangeTP?range=7/24/2022,07/26/2022
public IActionResult ByRangeTP([FromQuery] DateRangeTP range)
{
    if (!ModelState.IsValid)
        return View("Error", ModelState.Values.SelectMany(v => v.Errors));

    var weatherForecasts = Enumerable
        .Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .Where(wf => DateOnly.FromDateTime(wf.Date) >= range.From
                     && DateOnly.FromDateTime(wf.Date) <= range.To)
        .Select(wf => new WeatherForecastViewModel
        {
            Date = wf.Date.ToString("d"),
            TemperatureC = wf.TemperatureC,
            TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556),
            Summary = wf.Summary
        });

    return View("Index", weatherForecasts);
}

Komplexní typy

Komplexní typ musí mít veřejné výchozí konstruktor a veřejné zapisovatelné vlastnosti pro vytvoření vazby. Když dojde k vytvoření vazby modelu, vytvoří se instance třídy pomocí veřejného výchozího konstruktoru.

Pro každou vlastnost komplexního typu vazba modelu hledá prostřednictvím zdrojů vzor názvů prefix.property_name. Pokud se nic nenajde, hledá jenom property_name bez předpony. Rozhodnutí o použití předpony není provedeno pro každou vlastnost. Například s dotazem obsahujícím ?Instructor.Id=100&Name=foometodu s vazbou na metodu OnGet(Instructor instructor)obsahuje výsledný objekt typu Instructor :

  • Id nastaveno na 100hodnotu .
  • Name nastaveno na nullhodnotu . Vazba modelu očekává Instructor.Name , protože Instructor.Id byla použita v předchozím parametru dotazu.

Pro vazbu na parametr je předpona název parametru. Pro vazbu na PageModel veřejnou vlastnost je předpona název veřejné vlastnosti. Některé atributy mají Prefix vlastnost, která umožňuje přepsat výchozí použití parametru nebo názvu vlastnosti.

Předpokládejme například, že komplexní typ je následující Instructor třída:

public class Instructor
{
    public int ID { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
}

Předpona = název parametru

Pokud je model, který má být vázán, parametr s názvem instructorToUpdate:

public IActionResult OnPost(int? id, Instructor instructorToUpdate)

Vazba modelu začíná procházením zdrojů klíče instructorToUpdate.ID. Pokud se nenajde, vyhledá ID se bez předpony.

Prefix = název vlastnosti

Pokud je model, který má být vázán, vlastnost s názvem Instructor kontroleru nebo PageModel třídy:

[BindProperty]
public Instructor Instructor { get; set; }

Vazba modelu začíná procházením zdrojů klíče Instructor.ID. Pokud se nenajde, vyhledá ID se bez předpony.

Vlastní předpona

Pokud je model, který má být vázán, parametr s názvem instructorToUpdate a Bind atribut určuje Instructor jako předponu:

public IActionResult OnPost(
    int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)

Vazba modelu začíná procházením zdrojů klíče Instructor.ID. Pokud se nenajde, vyhledá ID se bez předpony.

Atributy pro cíle komplexního typu

Pro řízení vazby modelu komplexních typů je k dispozici několik předdefinovaných atributů:

Upozorňující

Tyto atributy ovlivňují vazbu modelu při publikování dat formuláře je zdrojem hodnot. Nemají vliv na vstupní formátovací moduly, které proces publikoval tělo požadavků JSON a XML. Vstupní formátovací moduly jsou vysvětleny dále v tomto článku.

[Bind] – atribut

Lze použít na třídu nebo parametr metody. Určuje, které vlastnosti modelu mají být zahrnuty do vazby modelu. [Bind]nemá vliv na vstupní formátovací moduly.

V následujícím příkladu jsou vázány pouze zadané vlastnosti Instructor modelu, pokud je volána jakákoli obslužná rutina nebo metoda akce:

[Bind("LastName,FirstMidName,HireDate")]
public class Instructor

V následujícím příkladu jsou při zavolání metody vázány OnPost pouze zadané vlastnosti Instructor modelu:

[HttpPost]
public IActionResult OnPost(
    [Bind("LastName,FirstMidName,HireDate")] Instructor instructor)

Atribut [Bind] lze použít k ochraně proti nadměrnému umístění ve scénářích vytváření . Nefunguje dobře ve scénářích úprav, protože vyloučené vlastnosti jsou nastavené na hodnotu null nebo výchozí hodnotu, místo aby zůstaly beze změny. Pro ochranu před nadměrném umístěním se místo atributu [Bind] doporučuje zobrazit modely. Další informace naleznete v tématu Bezpečnostní poznámka o nadměrném příspěvku.

Atribut [ModelBinder]

ModelBinderAttribute lze použít u typů, vlastností nebo parametrů. Umožňuje zadat typ pořadače modelu použitého k vytvoření vazby konkrétní instance nebo typu. Příklad:

[HttpPost]
public IActionResult OnPost(
    [ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)

Atribut [ModelBinder] lze také použít ke změně názvu vlastnosti nebo parametru při vazbě modelu:

public class Instructor
{
    [ModelBinder(Name = "instructor_id")]
    public string Id { get; set; }

    // ...
}

Atribut [BindRequired]

Způsobí, že vazba modelu přidá chybu stavu modelu, pokud vazba nemůže nastat pro vlastnost modelu. Tady je příklad:

public class InstructorBindRequired
{
    // ...

    [BindRequired]
    public DateTime HireDate { get; set; }
}

Projděte si také diskuzi o atributu [Required] v ověření modelu.

Atribut [BindNever]

Lze použít u vlastnosti nebo typu. Zabraňuje vazbě modelu v nastavení vlastnosti modelu. Při použití na typ systém vazeb modelu vyloučí všechny vlastnosti, které typ definuje. Tady je příklad:

public class InstructorBindNever
{
    [BindNever]
    public int Id { get; set; }

    // ...
}

Kolekce

U cílů, které jsou kolekcemi jednoduchých typů, vazba modelu hledá shody s parameter_name nebo property_name. Pokud se nenajde žádná shoda, vyhledá jeden z podporovaných formátů bez předpony. Příklad:

  • Předpokládejme, že parametr, který má být vázán, je pole s názvem selectedCourses:

    public IActionResult OnPost(int? id, int[] selectedCourses)
    
  • Data řetězce formuláře nebo dotazu můžou být v jednom z následujících formátů:

    selectedCourses=1050&selectedCourses=2000 
    
    selectedCourses[0]=1050&selectedCourses[1]=2000
    
    [0]=1050&[1]=2000
    
    selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
    
    [a]=1050&[b]=2000&index=a&index=b
    

    Vyhněte se vytvoření vazby parametru nebo vlastnosti s názvem index nebo Index pokud sousedí s hodnotou kolekce. Vazby modelu se pokusí použít index jako index pro kolekci, což může vést k nesprávné vazbě. Představte si například následující akci:

    public IActionResult Post(string index, List<Product> products)
    

    V předchozím kódu index se parametr řetězce dotazu sváže s parametrem index metody a také slouží k vytvoření vazby kolekce produktů. Přejmenování parametru nebo použití atributu index vazby modelu ke konfiguraci vazby zabrání tomuto problému:

    public IActionResult Post(string productIndex, List<Product> products)
    
  • Následující formát je podporován pouze v datech formuláře:

    selectedCourses[]=1050&selectedCourses[]=2000
    
  • Pro všechny předchozí ukázkové formáty předá vazba modelu do parametru selectedCourses pole dvou položek:

    • selectedCourses[0]=1050
    • selectedCourses[1]=2000

    Formáty dat, které používají čísla dolního indexu (... [0] ... [1] ...) musí zajistit, aby byly číslony postupně počínaje nulou. Pokud jsou v číslování dolního indexu nějaké mezery, budou všechny položky po mezerě ignorovány. Pokud jsou například dolní indexy 0 a 2 místo 0 a 1, druhá položka se ignoruje.

Slovníky

V případě Dictionary cílů vazba modelu hledá shody s parameter_name nebo property_name. Pokud se nenajde žádná shoda, vyhledá jeden z podporovaných formátů bez předpony. Příklad:

  • Předpokládejme, že cílový parametr je pojmenovaný Dictionary<int, string> selectedCourses:

    public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
    
  • Data publikovaného formuláře nebo řetězce dotazu můžou vypadat jako jeden z následujících příkladů:

    selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics
    
    [1050]=Chemistry&selectedCourses[2000]=Economics
    
    selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry&
    selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
    
    [0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
    
  • Pro všechny předchozí ukázkové formáty předá vazba modelu slovník se dvěma položkami parametru selectedCourses :

    • selectedCourses["1050"]="Chemie"
    • selectedCourses["2000"]="Ekonomika"

Vazby konstruktoru a typy záznamů

Vazba modelu vyžaduje, aby složité typy měly konstruktor bez parametrů. System.Text.Json Vstupní formátovací moduly i Newtonsoft.Json na základě podporují deserializaci tříd, které nemají konstruktor bez parametrů.

Typy záznamů představují skvělý způsob, jak stručně znázorňovat data v síti. ASP.NET Core podporuje vazbu modelu a ověřování typů záznamů pomocí jednoho konstruktoru:

public record Person(
    [Required] string Name, [Range(0, 150)] int Age, [BindNever] int Id);

public class PersonController
{
    public IActionResult Index() => View();

    [HttpPost]
    public IActionResult Index(Person person)
    {
        // ...
    }
}

Person/Index.cshtml:

@model Person

<label>Name: <input asp-for="Name" /></label>
<br />
<label>Age: <input asp-for="Age" /></label>

Při ověřování typů záznamů modul runtime vyhledává metadata vazby a ověřování, a to konkrétně u parametrů, nikoli u vlastností.

Architektura umožňuje vazbu na typy záznamů a jejich ověřování:

public record Person([Required] string Name, [Range(0, 100)] int Age);

Aby předchozí funkce fungovala, musí typ:

  • Být typem záznamu.
  • Máte přesně jeden veřejný konstruktor.
  • Obsahují parametry, které mají vlastnost se stejným názvem a typem. Názvy se nesmí lišit písmeny.

PoCOs bez konstruktorů bez parametrů

PoCOs, které nemají konstruktory bez parametrů, nelze svázat.

Výsledkem následujícího kódu je výjimka, která říká, že typ musí mít konstruktor bez parametrů:

public class Person(string Name)

public record Person([Required] string Name, [Range(0, 100)] int Age)
{
    public Person(string Name) : this (Name, 0);
}

Typy záznamů s ručně vytvořenými konstruktory

Typy záznamů s ručně vytvořenými konstruktory, které vypadají jako primární konstruktory

public record Person
{
    public Person([Required] string Name, [Range(0, 100)] int Age)
        => (this.Name, this.Age) = (Name, Age);

    public string Name { get; set; }
    public int Age { get; set; }
}

Typy záznamů, ověřování a metadata vazby

U typů záznamů se používají metadata ověřování a vazby parametrů. Všechna metadata vlastností se ignorují.

public record Person (string Name, int Age)
{
   [BindProperty(Name = "SomeName")] // This does not get used
   [Required] // This does not get used
   public string Name { get; init; }
}

Ověřování a metadata

Ověřování používá metadata parametru, ale používá vlastnost ke čtení hodnoty. V běžném případě s primárními konstruktory by oba byly identické. Existují však způsoby, jak ji porazit:

public record Person([Required] string Name)
{
    private readonly string _name;

    // The following property is never null.
    // However this object could have been constructed as "new Person(null)".
    public string Name { get; init => _name = value ?? string.Empty; }
}

TryUpdateModel neaktualizuje parametry u typu záznamu

public record Person(string Name)
{
    public int Age { get; set; }
}

var person = new Person("initial-name");
TryUpdateModel(person, ...);

V tomto případě se MVC nebude pokoušet o vytvoření vazby Name znovu. Age Je však možné je aktualizovat.

Chování globalizace vazby modelu směruje data a řetězce dotazů

Zprostředkovatel hodnoty trasy ASP.NET Core a zprostředkovatel hodnot řetězce dotazu:

  • Nakládá s hodnotami jako s neutrální jazykovou verzí.
  • Počítejte s tím, že adresy URL jsou invariantní pro jazykovou verzi.

Naproti tomu hodnoty pocházející z dat formuláře procházejí převodem citlivým na jazykovou verzi. Je to záměrně tak, aby adresy URL byly sdíleny napříč národními prostředími.

Aby poskytovatel hodnoty trasy ASP.NET Core a poskytovatel hodnot řetězce dotazu prošly převodem citlivým na jazykovou verzi:

public class CultureQueryStringValueProviderFactory : IValueProviderFactory
{
    public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
    {
        _ = context ?? throw new ArgumentNullException(nameof(context));

        var query = context.ActionContext.HttpContext.Request.Query;
        if (query?.Count > 0)
        {
            context.ValueProviders.Add(
                new QueryStringValueProvider(
                    BindingSource.Query,
                    query,
                    CultureInfo.CurrentCulture));
        }

        return Task.CompletedTask;
    }
}
builder.Services.AddControllers(options =>
{
    var index = options.ValueProviderFactories.IndexOf(
        options.ValueProviderFactories.OfType<QueryStringValueProviderFactory>()
            .Single());

    options.ValueProviderFactories[index] =
        new CultureQueryStringValueProviderFactory();
});

Speciální datové typy

Existuje několik speciálních datových typů, které lze zpracovat vazbou modelu.

IFormFile a IFormFileCollection

Nahraný soubor zahrnutý v požadavku HTTP. Podporuje se IEnumerable<IFormFile> také více souborů.

CancellationToken

Akce můžou volitelně svázat CancellationToken jako parametr. Tím se prováže RequestAborted signál, když dojde k přerušení připojení, které je základem požadavku HTTP. Akce můžou tento parametr použít ke zrušení dlouhotrvajících asynchronních operací, které se spouští jako součást akcí kontroleru.

FormCollection

Slouží k načtení všech hodnot z publikovaných dat formuláře.

Vstupní formátovací moduly

Data v textu požadavku můžou být ve formátu JSON, XML nebo jiném formátu. K analýze těchto dat používá vazba modelu vstupní formátovací modul , který je nakonfigurovaný pro zpracování konkrétního typu obsahu. Ve výchozím nastavení ASP.NET Core obsahuje vstupní formátovací moduly založené na FORMÁTU JSON pro zpracování dat JSON. Můžete přidat další formátovací moduly pro jiné typy obsahu.

ASP.NET Core vybere vstupní formátovací moduly na základě atributu Consumes . Pokud neexistuje žádný atribut, použije hlavičku Content-Type.

Použití předdefinovaných formátovacích souborů XML:

Přizpůsobení vazby modelu pomocí vstupních formátovacích nástrojů

Vstupní formátovací modul přebírá plnou odpovědnost za čtení dat z textu požadavku. Pokud chcete tento proces přizpůsobit, nakonfigurujte rozhraní API používaná vstupním formátovacím modulem. Tato část popisuje, jak přizpůsobit System.Text.Jsonvstupní formátovací modul založený na základech pro pochopení vlastního typu s názvem ObjectId.

Představte si následující model, který obsahuje vlastní ObjectId vlastnost:

public class InstructorObjectId
{
    [Required]
    public ObjectId ObjectId { get; set; } = null!;
}

Chcete-li přizpůsobit proces vazby modelu při použití System.Text.Json, vytvořte třídu odvozenou z JsonConverter<T>:

internal class ObjectIdConverter : JsonConverter<ObjectId>
{
    public override ObjectId Read(
        ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        => new(JsonSerializer.Deserialize<int>(ref reader, options));

    public override void Write(
        Utf8JsonWriter writer, ObjectId value, JsonSerializerOptions options)
        => writer.WriteNumberValue(value.Id);
}

Chcete-li použít vlastní převaděč, použijte JsonConverterAttribute atribut na typ. V následujícím příkladu ObjectId je typ nakonfigurován ObjectIdConverter jako vlastní převaděč:

[JsonConverter(typeof(ObjectIdConverter))]
public record ObjectId(int Id);

Další informace naleznete v tématu Jak psát vlastní převaděče.

Vyloučení zadaných typů z vazby modelu

Chování vazeb modelu a ověřovacích systémů je řízeno ModelMetadata. Úpravou můžete přidat ModelMetadata zprostředkovatele podrobností do MvcOptions.ModelMetadataDetailsProviders. Zprostředkovatelé předdefinovaných podrobností jsou k dispozici pro zakázání vazby nebo ověřování modelu pro zadané typy.

Chcete-li zakázat vazbu modelu u všech modelů zadaného typu, přidejte do ExcludeBindingMetadataProvider Program.cssouboru . Chcete-li například zakázat vazbu modelu u všech modelů typu System.Version:

builder.Services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.ModelMetadataDetailsProviders.Add(
            new ExcludeBindingMetadataProvider(typeof(Version)));
        options.ModelMetadataDetailsProviders.Add(
            new SuppressChildValidationMetadataProvider(typeof(Guid)));
    });

Chcete-li zakázat ověřování u vlastností zadaného typu, přidejte SuppressChildValidationMetadataProvider do Program.cssouboru . Chcete-li například zakázat ověřování u vlastností typu System.Guid:

builder.Services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.ModelMetadataDetailsProviders.Add(
            new ExcludeBindingMetadataProvider(typeof(Version)));
        options.ModelMetadataDetailsProviders.Add(
            new SuppressChildValidationMetadataProvider(typeof(Guid)));
    });

Vlastní pořadače modelů

Vazby modelu můžete rozšířit tak, že napíšete vlastní pořadač modelů a pomocí atributu [ModelBinder] ho vyberete pro daný cíl. Přečtěte si další informace o vazbě vlastního modelu.

Ruční vazba modelu

Vazbu modelu lze vyvolat ručně pomocí TryUpdateModelAsync metody. Metoda je definována v obou ControllerBase třídách PageModel . Přetížení metody umožňují zadat předponu a zprostředkovatele hodnot, které se mají použít. Metoda vrátí false , pokud se nezdaří vazba modelu. Tady je příklad:

if (await TryUpdateModelAsync(
    newInstructor,
    "Instructor",
    x => x.Name, x => x.HireDate!))
{
    _instructorStore.Add(newInstructor);
    return RedirectToPage("./Index");
}

return Page();

TryUpdateModelAsync používá zprostředkovatele hodnot k získání dat z textu formuláře, řetězce dotazu a směrování dat. TryUpdateModelAsync je obvykle:

  • Používá se s aplikacemi Razor Pages a MVC pomocí kontrolerů a zobrazení, aby se zabránilo nadměrnému publikování.
  • Nepoužívá se s webovým rozhraním API, pokud se nepoužívá z dat formulářů, řetězců dotazů a směruje data. Koncové body webového rozhraní API, které využívají JSON, používají k deserializaci textu požadavku do objektu vstupní formátovací moduly.

Další informace naleznete v tématu TryUpdateModelAsync.

Atribut [FromServices]

Název tohoto atributu se řídí vzorem atributů vazby modelu, které určují zdroj dat. Nejedná se ale o vazbu dat z poskytovatele hodnot. Získá instanci typu z kontejneru injektáže závislostí. Jejím účelem je poskytnout alternativu k injektáži konstruktoru, pokud potřebujete službu pouze v případě, že je volána konkrétní metoda.

Pokud instance typu není zaregistrovaná v kontejneru injektáže závislostí, aplikace vyvolá výjimku při pokusu o vytvoření vazby parametru. Pokud chcete parametr nastavit jako volitelný, použijte jeden z následujících přístupů:

  • Nastavení parametru s možnou hodnotou null
  • Nastavte výchozí hodnotu parametru.

V případě parametrů s možnou hodnotou null se ujistěte, že parametr není null před přístupem k němu.

Další materiály

Tento článek vysvětluje, co je vazba modelu, jak funguje a jak přizpůsobit její chování.

Co je vazba modelu

Kontrolery a Razor stránky pracují s daty pocházejícími z požadavků HTTP. Například směrovací data můžou poskytnout klíč záznamu a zaúčtované pole formuláře mohou poskytovat hodnoty pro vlastnosti modelu. Psaní kódu pro načtení každé z těchto hodnot a převodu z řetězců na typy .NET by bylo zdlouhavé a náchylné k chybám. Vazba modelu tento proces automatizuje. Systém vazeb modelu:

  • Načte data z různých zdrojů, jako jsou směrování dat, pole formulářů a řetězce dotazů.
  • Poskytuje data pro kontrolery a Razor stránky v parametrech metody a veřejných vlastnostech.
  • Převede řetězcová data na typy .NET.
  • Aktualizuje vlastnosti komplexních typů.

Příklad

Předpokládejme, že máte následující metodu akce:

[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)

Aplikace obdrží požadavek s touto adresou URL:

https://contoso.com/api/pets/2?DogsOnly=true

Vazba modelu prochází následujícími kroky poté, co systém směrování vybere metodu akce:

  • Najde první parametr GetById, celé číslo s názvem id.
  • Projde dostupné zdroje v požadavku HTTP a vyhledá id ve směrovacích datech = "2".
  • Převede řetězec "2" na celé číslo 2.
  • Najde další parametr logické hodnoty GetByIds názvem dogsOnly.
  • Projde zdroje a v řetězci dotazu najde "DogsOnly=true". Porovnávání názvů nerozlišuje malá a velká písmena.
  • Převede řetězec "true" na logickou truehodnotu .

Architektura pak volá metodu GetById , předává 2 pro id parametr a true parametr dogsOnly .

V předchozím příkladu jsou cílové vazby modelu parametry metody, které jsou jednoduché typy. Cíle mohou být také vlastnosti komplexního typu. Po úspěšném vázaní každé vlastnosti dojde k ověření modelu pro tuto vlastnost. Záznam dat, která jsou svázaná s modelem, a všechny chyby vazby nebo ověření jsou uloženy v ControllerBase.ModelState nebo PageModel.ModelState. Pokud chcete zjistit, jestli byl tento proces úspěšný, aplikace zkontroluje příznak ModelState.IsValid .

Cíle

Vazba modelu se pokouší najít hodnoty pro následující typy cílů:

  • Parametry metody akce kontroleru, do které je požadavek směrován.
  • Razor Parametry metody obslužné rutiny Pages, do které je požadavek směrován.
  • Veřejné vlastnosti kontroleru nebo PageModel třídy, pokud jsou zadány atributy.

Atribut [BindProperty]

Lze použít u veřejné vlastnosti kontroleru nebo PageModel třídy, aby byla vazba modelu na cílovou tuto vlastnost:

public class EditModel : PageModel
{
    [BindProperty]
    public Instructor? Instructor { get; set; }

    // ...
}

Atribut [BindProperties]

Lze použít u kontroleru nebo PageModel třídy, aby bylo možné určit vazbu modelu tak, aby cílila na všechny veřejné vlastnosti třídy:

[BindProperties]
public class CreateModel : PageModel
{
    public Instructor? Instructor { get; set; }

    // ...
}

Vazba modelu pro požadavky HTTP GET

Ve výchozím nastavení nejsou vlastnosti vázány pro požadavky HTTP GET. Obvykle stačí k požadavku GET parametr ID záznamu. ID záznamu slouží k vyhledání položky v databázi. Proto není nutné svázat vlastnost, která obsahuje instanci modelu. Ve scénářích, ve kterých chcete vlastnosti vázané na data z požadavků GET, nastavte SupportsGet vlastnost na true:

[BindProperty(Name = "ai_user", SupportsGet = true)]
public string? ApplicationInsightsCookie { get; set; }

Zdroje

Vazba modelu ve výchozím nastavení získává data ve formě párů klíč-hodnota z následujících zdrojů v požadavku HTTP:

  1. Pole formuláře
  2. Text požadavku (pro kontrolery, které mají atribut [ApiController].)
  3. Směrování dat
  4. Parametry řetězce dotazu
  5. Nahrané soubory

Pro každý cílový parametr nebo vlastnost jsou zdroje prohledávány v pořadí uvedeném v předchozím seznamu. Existuje několik výjimek:

  • Směrovací data a řetězcové hodnoty dotazu se používají jenom pro jednoduché typy.
  • Nahrané soubory jsou vázány pouze na cílové typy, které implementují IFormFile nebo IEnumerable<IFormFile>.

Pokud výchozí zdroj není správný, zadejte zdroj jedním z následujících atributů:

  • [FromQuery] – Získá hodnoty z řetězce dotazu.
  • [FromRoute] – Získá hodnoty ze směrovacích dat.
  • [FromForm] – Získá hodnoty z polí publikovaného formuláře.
  • [FromBody] – Získá hodnoty z textu požadavku.
  • [FromHeader] – Získá hodnoty z hlaviček HTTP.

Tyto atributy:

  • Jsou přidány do vlastností modelu jednotlivě, a ne do třídy modelu, jako v následujícím příkladu:

    public class Instructor
    {
        public int Id { get; set; }
    
        [FromQuery(Name = "Note")]
        public string? NoteFromQueryString { get; set; }
    
        // ...
    }
    
  • Volitelně můžete v konstruktoru přijmout hodnotu názvu modelu. Tato možnost je k dispozici v případě, že název vlastnosti neodpovídá hodnotě v požadavku. Například hodnota v požadavku může být hlavička s pomlčkou v názvu, jak je znázorněno v následujícím příkladu:

    public void OnGet([FromHeader(Name = "Accept-Language")] string language)
    

Atribut [FromBody]

[FromBody] Použijte atribut na parametr k naplnění jeho vlastností z textu požadavku HTTP. Modul runtime ASP.NET Core deleguje odpovědnost za čtení textu na vstupní formátovač. Vstupní formátovací moduly jsou vysvětleny dále v tomto článku.

Při [FromBody] použití u parametru komplexního typu se všechny atributy zdroje vazby použité na jeho vlastnosti ignorují. Například následující Create akce určuje, že se jeho pet parametr naplní z těla:

public ActionResult<Pet> Create([FromBody] Pet pet)

Třída Pet určuje, že jeho Breed vlastnost je naplněna z parametru řetězce dotazu:

public class Pet
{
    public string Name { get; set; } = null!;

    [FromQuery] // Attribute is ignored.
    public string Breed { get; set; } = null!;
}

V předchozím příkladu:

  • Atribut [FromQuery] je ignorován.
  • Vlastnost Breed není naplněna parametrem řetězce dotazu.

Vstupní formátovací moduly čtou pouze tělo a nerozumí atributům zdroje vazby. Pokud je v těle nalezena vhodná hodnota, tato hodnota se použije k naplnění Breed vlastnosti.

Nevztahuje [FromBody] se na více než jeden parametr na metodu akce. Jakmile stream požadavku načte vstupní formátovací modul, nebude už k dispozici ke čtení pro vazbu dalších [FromBody] parametrů.

Další zdroje

Zdrojová data jsou poskytována systému vazby modelu poskytovateli hodnot. Můžete napsat a zaregistrovat vlastní zprostředkovatele hodnot, kteří získávají data pro vazbu modelu z jiných zdrojů. Můžete například chtít data ze souborů cookie nebo stavu relace. Získání dat z nového zdroje:

  • Vytvořte třídu, která implementuje IValueProvider.
  • Vytvořte třídu, která implementuje IValueProviderFactory.
  • Zaregistrujte třídu továrny v Program.cssouboru .

Ukázka obsahuje zprostředkovatele hodnot a příklad továrny, který získává hodnoty ze souborů cookie. Registrace továren zprostředkovatele vlastních hodnot v Program.cs:

builder.Services.AddControllers(options =>
{
    options.ValueProviderFactories.Add(new CookieValueProviderFactory());
});

Předchozí kód umístí vlastního zprostředkovatele hodnot za všechny předdefinované zprostředkovatele hodnot. Pokud chcete, aby byl první v seznamu, volejte Insert(0, new CookieValueProviderFactory()) místo Add.

Žádný zdroj vlastnosti modelu

Ve výchozím nastavení se chyba stavu modelu nevytvořila, pokud pro vlastnost modelu nebyla nalezena žádná hodnota. Vlastnost je nastavena na hodnotu null nebo výchozí hodnotu:

  • Jednoduché typy s možnou hodnotou null jsou nastaveny na nullhodnotu .
  • Typy hodnot, které nemají hodnotu null, jsou nastaveny na default(T)hodnotu . Například parametr int id je nastaven na hodnotu 0.
  • V případě komplexních typů vytvoří vazba modelu instanci pomocí výchozího konstruktoru bez nastavení vlastností.
  • Matice jsou nastaveny na Array.Empty<T>(), s tím rozdílem, že byte[] pole jsou nastavena na null.

Pokud by stav modelu měl být neplatný, pokud se v polích formuláře pro vlastnost modelu nenajde nic, použijte [BindRequired] atribut.

Všimněte si, že toto [BindRequired] chování platí pro vazbu modelu z publikovaných dat formuláře, ne pro data JSON nebo XML v textu požadavku. Základní data požadavku se zpracovávají vstupními formátovacími moduly.

Chyby převodu typů

Pokud se najde zdroj, ale nejde ho převést na cílový typ, stav modelu se označí jako neplatný. Cílový parametr nebo vlastnost je nastaven na hodnotu null nebo výchozí hodnotu, jak je uvedeno v předchozí části.

V kontroleru rozhraní API, který má [ApiController] atribut, výsledkem neplatného stavu modelu je automatická odpověď HTTP 400.

Razor Na stránce znovu zobrazíte stránku s chybovou zprávou:

public IActionResult OnPost()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    // ...

    return RedirectToPage("./Index");
}

Při opětovném zobrazení stránky předchozím kódem se v poli formuláře nezobrazí neplatný vstup. Důvodem je to, že vlastnost modelu byla nastavena na hodnotu null nebo výchozí hodnotu. Neplatný vstup se zobrazí v chybové zprávě. Pokud chcete znovu zobrazit špatná data v poli formuláře, zvažte, jestli je vlastnost modelu řetězec a provést převod dat ručně.

Stejná strategie se doporučuje, pokud nechcete, aby chyby převodu typů způsobily chyby stavu modelu. V takovém případě nastavte vlastnost modelu jako řetězec.

Jednoduché typy

Mezi jednoduché typy, na které může pořadač modelů převést zdrojové řetězce, patří následující:

Komplexní typy

Komplexní typ musí mít veřejné výchozí konstruktor a veřejné zapisovatelné vlastnosti pro vytvoření vazby. Když dojde k vytvoření vazby modelu, vytvoří se instance třídy pomocí veřejného výchozího konstruktoru.

Pro každou vlastnost komplexního typu vazba modelu hledá prostřednictvím zdrojů vzor názvů prefix.property_name. Pokud se nic nenajde, hledá jenom property_name bez předpony. Rozhodnutí o použití předpony není provedeno pro každou vlastnost. Například s dotazem obsahujícím ?Instructor.Id=100&Name=foometodu s vazbou na metodu OnGet(Instructor instructor)obsahuje výsledný objekt typu Instructor :

  • Id nastaveno na 100hodnotu .
  • Name nastaveno na nullhodnotu . Vazba modelu očekává Instructor.Name , protože Instructor.Id byla použita v předchozím parametru dotazu.

Pro vazbu na parametr je předpona název parametru. Pro vazbu na PageModel veřejnou vlastnost je předpona název veřejné vlastnosti. Některé atributy mají Prefix vlastnost, která umožňuje přepsat výchozí použití parametru nebo názvu vlastnosti.

Předpokládejme například, že komplexní typ je následující Instructor třída:

public class Instructor
{
    public int ID { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
}

Předpona = název parametru

Pokud je model, který má být vázán, parametr s názvem instructorToUpdate:

public IActionResult OnPost(int? id, Instructor instructorToUpdate)

Vazba modelu začíná procházením zdrojů klíče instructorToUpdate.ID. Pokud se nenajde, vyhledá ID se bez předpony.

Prefix = název vlastnosti

Pokud je model, který má být vázán, vlastnost s názvem Instructor kontroleru nebo PageModel třídy:

[BindProperty]
public Instructor Instructor { get; set; }

Vazba modelu začíná procházením zdrojů klíče Instructor.ID. Pokud se nenajde, vyhledá ID se bez předpony.

Vlastní předpona

Pokud je model, který má být vázán, parametr s názvem instructorToUpdate a Bind atribut určuje Instructor jako předponu:

public IActionResult OnPost(
    int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)

Vazba modelu začíná procházením zdrojů klíče Instructor.ID. Pokud se nenajde, vyhledá ID se bez předpony.

Atributy pro cíle komplexního typu

Pro řízení vazby modelu komplexních typů je k dispozici několik předdefinovaných atributů:

Upozorňující

Tyto atributy ovlivňují vazbu modelu při publikování dat formuláře je zdrojem hodnot. Nemají vliv na vstupní formátovací moduly, které proces publikoval tělo požadavků JSON a XML. Vstupní formátovací moduly jsou vysvětleny dále v tomto článku.

[Bind] – atribut

Lze použít na třídu nebo parametr metody. Určuje, které vlastnosti modelu mají být zahrnuty do vazby modelu. [Bind]nemá vliv na vstupní formátovací moduly.

V následujícím příkladu jsou vázány pouze zadané vlastnosti Instructor modelu, pokud je volána jakákoli obslužná rutina nebo metoda akce:

[Bind("LastName,FirstMidName,HireDate")]
public class Instructor

V následujícím příkladu jsou při zavolání metody vázány OnPost pouze zadané vlastnosti Instructor modelu:

[HttpPost]
public IActionResult OnPost(
    [Bind("LastName,FirstMidName,HireDate")] Instructor instructor)

Atribut [Bind] lze použít k ochraně proti nadměrnému umístění ve scénářích vytváření . Nefunguje dobře ve scénářích úprav, protože vyloučené vlastnosti jsou nastavené na hodnotu null nebo výchozí hodnotu, místo aby zůstaly beze změny. Pro ochranu před nadměrném umístěním se místo atributu [Bind] doporučuje zobrazit modely. Další informace naleznete v tématu Bezpečnostní poznámka o nadměrném příspěvku.

Atribut [ModelBinder]

ModelBinderAttribute lze použít u typů, vlastností nebo parametrů. Umožňuje zadat typ pořadače modelu použitého k vytvoření vazby konkrétní instance nebo typu. Příklad:

[HttpPost]
public IActionResult OnPost(
    [ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)

Atribut [ModelBinder] lze také použít ke změně názvu vlastnosti nebo parametru při vazbě modelu:

public class Instructor
{
    [ModelBinder(Name = "instructor_id")]
    public string Id { get; set; }

    // ...
}

Atribut [BindRequired]

Způsobí, že vazba modelu přidá chybu stavu modelu, pokud vazba nemůže nastat pro vlastnost modelu. Tady je příklad:

public class InstructorBindRequired
{
    // ...

    [BindRequired]
    public DateTime HireDate { get; set; }
}

Projděte si také diskuzi o atributu [Required] v ověření modelu.

Atribut [BindNever]

Lze použít u vlastnosti nebo typu. Zabraňuje vazbě modelu v nastavení vlastnosti modelu. Při použití na typ systém vazeb modelu vyloučí všechny vlastnosti, které typ definuje. Tady je příklad:

public class InstructorBindNever
{
    [BindNever]
    public int Id { get; set; }

    // ...
}

Kolekce

U cílů, které jsou kolekcemi jednoduchých typů, vazba modelu hledá shody s parameter_name nebo property_name. Pokud se nenajde žádná shoda, vyhledá jeden z podporovaných formátů bez předpony. Příklad:

  • Předpokládejme, že parametr, který má být vázán, je pole s názvem selectedCourses:

    public IActionResult OnPost(int? id, int[] selectedCourses)
    
  • Data řetězce formuláře nebo dotazu můžou být v jednom z následujících formátů:

    selectedCourses=1050&selectedCourses=2000 
    
    selectedCourses[0]=1050&selectedCourses[1]=2000
    
    [0]=1050&[1]=2000
    
    selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
    
    [a]=1050&[b]=2000&index=a&index=b
    

    Vyhněte se vytvoření vazby parametru nebo vlastnosti s názvem index nebo Index pokud sousedí s hodnotou kolekce. Vazby modelu se pokusí použít index jako index pro kolekci, což může vést k nesprávné vazbě. Představte si například následující akci:

    public IActionResult Post(string index, List<Product> products)
    

    V předchozím kódu index se parametr řetězce dotazu sváže s parametrem index metody a také slouží k vytvoření vazby kolekce produktů. Přejmenování parametru nebo použití atributu index vazby modelu ke konfiguraci vazby zabrání tomuto problému:

    public IActionResult Post(string productIndex, List<Product> products)
    
  • Následující formát je podporován pouze v datech formuláře:

    selectedCourses[]=1050&selectedCourses[]=2000
    
  • Pro všechny předchozí ukázkové formáty předá vazba modelu do parametru selectedCourses pole dvou položek:

    • selectedCourses[0]=1050
    • selectedCourses[1]=2000

    Formáty dat, které používají čísla dolního indexu (... [0] ... [1] ...) musí zajistit, aby byly číslony postupně počínaje nulou. Pokud jsou v číslování dolního indexu nějaké mezery, budou všechny položky po mezerě ignorovány. Pokud jsou například dolní indexy 0 a 2 místo 0 a 1, druhá položka se ignoruje.

Slovníky

V případě Dictionary cílů vazba modelu hledá shody s parameter_name nebo property_name. Pokud se nenajde žádná shoda, vyhledá jeden z podporovaných formátů bez předpony. Příklad:

  • Předpokládejme, že cílový parametr je pojmenovaný Dictionary<int, string> selectedCourses:

    public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
    
  • Data publikovaného formuláře nebo řetězce dotazu můžou vypadat jako jeden z následujících příkladů:

    selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics
    
    [1050]=Chemistry&selectedCourses[2000]=Economics
    
    selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry&
    selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
    
    [0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
    
  • Pro všechny předchozí ukázkové formáty předá vazba modelu slovník se dvěma položkami parametru selectedCourses :

    • selectedCourses["1050"]="Chemie"
    • selectedCourses["2000"]="Ekonomika"

Vazby konstruktoru a typy záznamů

Vazba modelu vyžaduje, aby složité typy měly konstruktor bez parametrů. System.Text.Json Vstupní formátovací moduly i Newtonsoft.Json na základě podporují deserializaci tříd, které nemají konstruktor bez parametrů.

Typy záznamů představují skvělý způsob, jak stručně znázorňovat data v síti. ASP.NET Core podporuje vazbu modelu a ověřování typů záznamů pomocí jednoho konstruktoru:

public record Person(
    [Required] string Name, [Range(0, 150)] int Age, [BindNever] int Id);

public class PersonController
{
    public IActionResult Index() => View();

    [HttpPost]
    public IActionResult Index(Person person)
    {
        // ...
    }
}

Person/Index.cshtml:

@model Person

<label>Name: <input asp-for="Name" /></label>
<br />
<label>Age: <input asp-for="Age" /></label>

Při ověřování typů záznamů modul runtime vyhledává metadata vazby a ověřování, a to konkrétně u parametrů, nikoli u vlastností.

Architektura umožňuje vazbu na typy záznamů a jejich ověřování:

public record Person([Required] string Name, [Range(0, 100)] int Age);

Aby předchozí funkce fungovala, musí typ:

  • Být typem záznamu.
  • Máte přesně jeden veřejný konstruktor.
  • Obsahují parametry, které mají vlastnost se stejným názvem a typem. Názvy se nesmí lišit písmeny.

PoCOs bez konstruktorů bez parametrů

PoCOs, které nemají konstruktory bez parametrů, nelze svázat.

Výsledkem následujícího kódu je výjimka, která říká, že typ musí mít konstruktor bez parametrů:

public class Person(string Name)

public record Person([Required] string Name, [Range(0, 100)] int Age)
{
    public Person(string Name) : this (Name, 0);
}

Typy záznamů s ručně vytvořenými konstruktory

Typy záznamů s ručně vytvořenými konstruktory, které vypadají jako primární konstruktory

public record Person
{
    public Person([Required] string Name, [Range(0, 100)] int Age)
        => (this.Name, this.Age) = (Name, Age);

    public string Name { get; set; }
    public int Age { get; set; }
}

Typy záznamů, ověřování a metadata vazby

U typů záznamů se používají metadata ověřování a vazby parametrů. Všechna metadata vlastností se ignorují.

public record Person (string Name, int Age)
{
   [BindProperty(Name = "SomeName")] // This does not get used
   [Required] // This does not get used
   public string Name { get; init; }
}

Ověřování a metadata

Ověřování používá metadata parametru, ale používá vlastnost ke čtení hodnoty. V běžném případě s primárními konstruktory by oba byly identické. Existují však způsoby, jak ji porazit:

public record Person([Required] string Name)
{
    private readonly string _name;

    // The following property is never null.
    // However this object could have been constructed as "new Person(null)".
    public string Name { get; init => _name = value ?? string.Empty; }
}

TryUpdateModel neaktualizuje parametry u typu záznamu

public record Person(string Name)
{
    public int Age { get; set; }
}

var person = new Person("initial-name");
TryUpdateModel(person, ...);

V tomto případě se MVC nebude pokoušet o vytvoření vazby Name znovu. Age Je však možné je aktualizovat.

Chování globalizace vazby modelu směruje data a řetězce dotazů

Zprostředkovatel hodnoty trasy ASP.NET Core a zprostředkovatel hodnot řetězce dotazu:

  • Nakládá s hodnotami jako s neutrální jazykovou verzí.
  • Počítejte s tím, že adresy URL jsou invariantní pro jazykovou verzi.

Naproti tomu hodnoty pocházející z dat formuláře procházejí převodem citlivým na jazykovou verzi. Je to záměrně tak, aby adresy URL byly sdíleny napříč národními prostředími.

Aby poskytovatel hodnoty trasy ASP.NET Core a poskytovatel hodnot řetězce dotazu prošly převodem citlivým na jazykovou verzi:

public class CultureQueryStringValueProviderFactory : IValueProviderFactory
{
    public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
    {
        _ = context ?? throw new ArgumentNullException(nameof(context));

        var query = context.ActionContext.HttpContext.Request.Query;
        if (query?.Count > 0)
        {
            context.ValueProviders.Add(
                new QueryStringValueProvider(
                    BindingSource.Query,
                    query,
                    CultureInfo.CurrentCulture));
        }

        return Task.CompletedTask;
    }
}
builder.Services.AddControllers(options =>
{
    var index = options.ValueProviderFactories.IndexOf(
        options.ValueProviderFactories.OfType<QueryStringValueProviderFactory>()
            .Single());

    options.ValueProviderFactories[index] =
        new CultureQueryStringValueProviderFactory();
});

Speciální datové typy

Existuje několik speciálních datových typů, které lze zpracovat vazbou modelu.

IFormFile a IFormFileCollection

Nahraný soubor zahrnutý v požadavku HTTP. Podporuje se IEnumerable<IFormFile> také více souborů.

CancellationToken

Akce můžou volitelně svázat CancellationToken jako parametr. Tím se prováže RequestAborted signál, když dojde k přerušení připojení, které je základem požadavku HTTP. Akce můžou tento parametr použít ke zrušení dlouhotrvajících asynchronních operací, které se spouští jako součást akcí kontroleru.

FormCollection

Slouží k načtení všech hodnot z publikovaných dat formuláře.

Vstupní formátovací moduly

Data v textu požadavku můžou být ve formátu JSON, XML nebo jiném formátu. K analýze těchto dat používá vazba modelu vstupní formátovací modul , který je nakonfigurovaný pro zpracování konkrétního typu obsahu. Ve výchozím nastavení ASP.NET Core obsahuje vstupní formátovací moduly založené na FORMÁTU JSON pro zpracování dat JSON. Můžete přidat další formátovací moduly pro jiné typy obsahu.

ASP.NET Core vybere vstupní formátovací moduly na základě atributu Consumes . Pokud neexistuje žádný atribut, použije hlavičku Content-Type.

Použití předdefinovaných formátovacích souborů XML:

Přizpůsobení vazby modelu pomocí vstupních formátovacích nástrojů

Vstupní formátovací modul přebírá plnou odpovědnost za čtení dat z textu požadavku. Pokud chcete tento proces přizpůsobit, nakonfigurujte rozhraní API používaná vstupním formátovacím modulem. Tato část popisuje, jak přizpůsobit System.Text.Jsonvstupní formátovací modul založený na základech pro pochopení vlastního typu s názvem ObjectId.

Představte si následující model, který obsahuje vlastní ObjectId vlastnost:

public class InstructorObjectId
{
    [Required]
    public ObjectId ObjectId { get; set; } = null!;
}

Chcete-li přizpůsobit proces vazby modelu při použití System.Text.Json, vytvořte třídu odvozenou z JsonConverter<T>:

internal class ObjectIdConverter : JsonConverter<ObjectId>
{
    public override ObjectId Read(
        ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        => new(JsonSerializer.Deserialize<int>(ref reader, options));

    public override void Write(
        Utf8JsonWriter writer, ObjectId value, JsonSerializerOptions options)
        => writer.WriteNumberValue(value.Id);
}

Chcete-li použít vlastní převaděč, použijte JsonConverterAttribute atribut na typ. V následujícím příkladu ObjectId je typ nakonfigurován ObjectIdConverter jako vlastní převaděč:

[JsonConverter(typeof(ObjectIdConverter))]
public record ObjectId(int Id);

Další informace naleznete v tématu Jak psát vlastní převaděče.

Vyloučení zadaných typů z vazby modelu

Chování vazeb modelu a ověřovacích systémů je řízeno ModelMetadata. Úpravou můžete přidat ModelMetadata zprostředkovatele podrobností do MvcOptions.ModelMetadataDetailsProviders. Zprostředkovatelé předdefinovaných podrobností jsou k dispozici pro zakázání vazby nebo ověřování modelu pro zadané typy.

Chcete-li zakázat vazbu modelu u všech modelů zadaného typu, přidejte do ExcludeBindingMetadataProvider Program.cssouboru . Chcete-li například zakázat vazbu modelu u všech modelů typu System.Version:

builder.Services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.ModelMetadataDetailsProviders.Add(
            new ExcludeBindingMetadataProvider(typeof(Version)));
        options.ModelMetadataDetailsProviders.Add(
            new SuppressChildValidationMetadataProvider(typeof(Guid)));
    });

Chcete-li zakázat ověřování u vlastností zadaného typu, přidejte SuppressChildValidationMetadataProvider do Program.cssouboru . Chcete-li například zakázat ověřování u vlastností typu System.Guid:

builder.Services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.ModelMetadataDetailsProviders.Add(
            new ExcludeBindingMetadataProvider(typeof(Version)));
        options.ModelMetadataDetailsProviders.Add(
            new SuppressChildValidationMetadataProvider(typeof(Guid)));
    });

Vlastní pořadače modelů

Vazby modelu můžete rozšířit tak, že napíšete vlastní pořadač modelů a pomocí atributu [ModelBinder] ho vyberete pro daný cíl. Přečtěte si další informace o vazbě vlastního modelu.

Ruční vazba modelu

Vazbu modelu lze vyvolat ručně pomocí TryUpdateModelAsync metody. Metoda je definována v obou ControllerBase třídách PageModel . Přetížení metody umožňují zadat předponu a zprostředkovatele hodnot, které se mají použít. Metoda vrátí false , pokud se nezdaří vazba modelu. Tady je příklad:

if (await TryUpdateModelAsync(
    newInstructor,
    "Instructor",
    x => x.Name, x => x.HireDate!))
{
    _instructorStore.Add(newInstructor);
    return RedirectToPage("./Index");
}

return Page();

TryUpdateModelAsync používá zprostředkovatele hodnot k získání dat z textu formuláře, řetězce dotazu a směrování dat. TryUpdateModelAsync je obvykle:

  • Používá se s aplikacemi Razor Pages a MVC pomocí kontrolerů a zobrazení, aby se zabránilo nadměrnému publikování.
  • Nepoužívá se s webovým rozhraním API, pokud se nepoužívá z dat formulářů, řetězců dotazů a směruje data. Koncové body webového rozhraní API, které využívají JSON, používají k deserializaci textu požadavku do objektu vstupní formátovací moduly.

Další informace naleznete v tématu TryUpdateModelAsync.

Atribut [FromServices]

Název tohoto atributu se řídí vzorem atributů vazby modelu, které určují zdroj dat. Nejedná se ale o vazbu dat z poskytovatele hodnot. Získá instanci typu z kontejneru injektáže závislostí. Jejím účelem je poskytnout alternativu k injektáži konstruktoru, pokud potřebujete službu pouze v případě, že je volána konkrétní metoda.

Pokud instance typu není zaregistrovaná v kontejneru injektáže závislostí, aplikace vyvolá výjimku při pokusu o vytvoření vazby parametru. Pokud chcete parametr nastavit jako volitelný, použijte jeden z následujících přístupů:

  • Nastavení parametru s možnou hodnotou null
  • Nastavte výchozí hodnotu parametru.

V případě parametrů s možnou hodnotou null se ujistěte, že parametr není null před přístupem k němu.

Další materiály

Tento článek vysvětluje, co je vazba modelu, jak funguje a jak přizpůsobit její chování.

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

Co je vazba modelu

Kontrolery a Razor stránky pracují s daty pocházejícími z požadavků HTTP. Například směrovací data můžou poskytnout klíč záznamu a zaúčtované pole formuláře mohou poskytovat hodnoty pro vlastnosti modelu. Psaní kódu pro načtení každé z těchto hodnot a převodu z řetězců na typy .NET by bylo zdlouhavé a náchylné k chybám. Vazba modelu tento proces automatizuje. Systém vazeb modelu:

  • Načte data z různých zdrojů, jako jsou směrování dat, pole formulářů a řetězce dotazů.
  • Poskytuje data pro kontrolery a Razor stránky v parametrech metody a veřejných vlastnostech.
  • Převede řetězcová data na typy .NET.
  • Aktualizuje vlastnosti komplexních typů.

Příklad

Předpokládejme, že máte následující metodu akce:

[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)

Aplikace obdrží požadavek s touto adresou URL:

http://contoso.com/api/pets/2?DogsOnly=true

Vazba modelu prochází následujícími kroky poté, co systém směrování vybere metodu akce:

  • Najde první parametr GetById, celé číslo s názvem id.
  • Projde dostupné zdroje v požadavku HTTP a vyhledá id ve směrovacích datech = "2".
  • Převede řetězec "2" na celé číslo 2.
  • Najde další parametr logické hodnoty GetByIds názvem dogsOnly.
  • Projde zdroje a v řetězci dotazu najde "DogsOnly=true". Porovnávání názvů nerozlišuje malá a velká písmena.
  • Převede řetězec "true" na logickou truehodnotu .

Architektura pak volá metodu GetById , předává 2 pro id parametr a true parametr dogsOnly .

V předchozím příkladu jsou cílové vazby modelu parametry metody, které jsou jednoduché typy. Cíle mohou být také vlastnosti komplexního typu. Po úspěšném vázaní každé vlastnosti dojde k ověření modelu pro tuto vlastnost. Záznam dat, která jsou svázaná s modelem, a všechny chyby vazby nebo ověření jsou uloženy v ControllerBase.ModelState nebo PageModel.ModelState. Pokud chcete zjistit, jestli byl tento proces úspěšný, aplikace zkontroluje příznak ModelState.IsValid .

Cíle

Vazba modelu se pokouší najít hodnoty pro následující typy cílů:

  • Parametry metody akce kontroleru, do které je požadavek směrován.
  • Razor Parametry metody obslužné rutiny Pages, do které je požadavek směrován.
  • Veřejné vlastnosti kontroleru nebo PageModel třídy, pokud jsou zadány atributy.

Atribut [BindProperty]

Lze použít u veřejné vlastnosti kontroleru nebo PageModel třídy, aby byla vazba modelu na cílovou tuto vlastnost:

public class EditModel : InstructorsPageModel
{
    [BindProperty]
    public Instructor Instructor { get; set; }

Atribut [BindProperties]

K dispozici v ASP.NET Core 2.1 a novějším. Lze použít u kontroleru nebo PageModel třídy, aby bylo možné určit vazbu modelu tak, aby cílila na všechny veřejné vlastnosti třídy:

[BindProperties(SupportsGet = true)]
public class CreateModel : InstructorsPageModel
{
    public Instructor Instructor { get; set; }

Vazba modelu pro požadavky HTTP GET

Ve výchozím nastavení nejsou vlastnosti vázány pro požadavky HTTP GET. Obvykle stačí k požadavku GET parametr ID záznamu. ID záznamu slouží k vyhledání položky v databázi. Proto není nutné svázat vlastnost, která obsahuje instanci modelu. Ve scénářích, ve kterých chcete vlastnosti vázané na data z požadavků GET, nastavte SupportsGet vlastnost na true:

[BindProperty(Name = "ai_user", SupportsGet = true)]
public string ApplicationInsightsCookie { get; set; }

Zdroje

Vazba modelu ve výchozím nastavení získává data ve formě párů klíč-hodnota z následujících zdrojů v požadavku HTTP:

  1. Pole formuláře
  2. Text požadavku (pro kontrolery, které mají atribut [ApiController].)
  3. Směrování dat
  4. Parametry řetězce dotazu
  5. Nahrané soubory

Pro každý cílový parametr nebo vlastnost jsou zdroje prohledávány v pořadí uvedeném v předchozím seznamu. Existuje několik výjimek:

  • Směrovací data a řetězcové hodnoty dotazu se používají jenom pro jednoduché typy.
  • Nahrané soubory jsou vázány pouze na cílové typy, které implementují IFormFile nebo IEnumerable<IFormFile>.

Pokud výchozí zdroj není správný, zadejte zdroj jedním z následujících atributů:

  • [FromQuery] – Získá hodnoty z řetězce dotazu.
  • [FromRoute] – Získá hodnoty ze směrovacích dat.
  • [FromForm] – Získá hodnoty z polí publikovaného formuláře.
  • [FromBody] – Získá hodnoty z textu požadavku.
  • [FromHeader] – Získá hodnoty z hlaviček HTTP.

Tyto atributy:

  • Jsou přidány do vlastností modelu jednotlivě (nikoli do třídy modelu), jak je znázorněno v následujícím příkladu:

    public class Instructor
    {
        public int ID { get; set; }
    
        [FromQuery(Name = "Note")]
        public string NoteFromQueryString { get; set; }
    
  • Volitelně můžete v konstruktoru přijmout hodnotu názvu modelu. Tato možnost je k dispozici v případě, že název vlastnosti neodpovídá hodnotě v požadavku. Například hodnota v požadavku může být hlavička s pomlčkou v názvu, jak je znázorněno v následujícím příkladu:

    public void OnGet([FromHeader(Name = "Accept-Language")] string language)
    

Atribut [FromBody]

[FromBody] Použijte atribut na parametr k naplnění jeho vlastností z textu požadavku HTTP. Modul runtime ASP.NET Core deleguje odpovědnost za čtení textu na vstupní formátovač. Vstupní formátovací moduly jsou vysvětleny dále v tomto článku.

Při [FromBody] použití u parametru komplexního typu se všechny atributy zdroje vazby použité na jeho vlastnosti ignorují. Například následující Create akce určuje, že se jeho pet parametr naplní z těla:

public ActionResult<Pet> Create([FromBody] Pet pet)

Třída Pet určuje, že jeho Breed vlastnost je naplněna z parametru řetězce dotazu:

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

    [FromQuery] // Attribute is ignored.
    public string Breed { get; set; }
}

V předchozím příkladu:

  • Atribut [FromQuery] je ignorován.
  • Vlastnost Breed není naplněna parametrem řetězce dotazu.

Vstupní formátovací moduly čtou pouze tělo a nerozumí atributům zdroje vazby. Pokud je v těle nalezena vhodná hodnota, tato hodnota se použije k naplnění Breed vlastnosti.

Nevztahuje [FromBody] se na více než jeden parametr na metodu akce. Jakmile stream požadavku načte vstupní formátovací modul, nebude už k dispozici ke čtení pro vazbu dalších [FromBody] parametrů.

Další zdroje

Zdrojová data jsou poskytována systému vazby modelu poskytovateli hodnot. Můžete napsat a zaregistrovat vlastní zprostředkovatele hodnot, kteří získávají data pro vazbu modelu z jiných zdrojů. Můžete například chtít data ze souborů cookie nebo stavu relace. Získání dat z nového zdroje:

  • Vytvořte třídu, která implementuje IValueProvider.
  • Vytvořte třídu, která implementuje IValueProviderFactory.
  • Zaregistrujte třídu továrny v Startup.ConfigureServicessouboru .

Ukázková aplikace obsahuje zprostředkovatele hodnot a příklad továrny, který získává hodnoty ze souborů cookie. Tady je registrační kód v Startup.ConfigureServices:

services.AddRazorPages()
    .AddMvcOptions(options =>
{
    options.ValueProviderFactories.Add(new CookieValueProviderFactory());
    options.ModelMetadataDetailsProviders.Add(
        new ExcludeBindingMetadataProvider(typeof(System.Version)));
    options.ModelMetadataDetailsProviders.Add(
        new SuppressChildValidationMetadataProvider(typeof(System.Guid)));
})
.AddXmlSerializerFormatters();

Zobrazený kód umístí vlastního zprostředkovatele hodnot za všechny předdefinované zprostředkovatele hodnot. Pokud chcete, aby byl první v seznamu, volejte Insert(0, new CookieValueProviderFactory()) místo Add.

Žádný zdroj vlastnosti modelu

Ve výchozím nastavení se chyba stavu modelu nevytvořila, pokud pro vlastnost modelu nebyla nalezena žádná hodnota. Vlastnost je nastavena na hodnotu null nebo výchozí hodnotu:

  • Jednoduché typy s možnou hodnotou null jsou nastaveny na nullhodnotu .
  • Typy hodnot, které nemají hodnotu null, jsou nastaveny na default(T)hodnotu . Například parametr int id je nastaven na hodnotu 0.
  • V případě komplexních typů vytvoří vazba modelu instanci pomocí výchozího konstruktoru bez nastavení vlastností.
  • Matice jsou nastaveny na Array.Empty<T>(), s tím rozdílem, že byte[] pole jsou nastavena na null.

Pokud by stav modelu měl být neplatný, pokud se v polích formuláře pro vlastnost modelu nenajde nic, použijte [BindRequired] atribut.

Všimněte si, že toto [BindRequired] chování platí pro vazbu modelu z publikovaných dat formuláře, ne pro data JSON nebo XML v textu požadavku. Základní data požadavku se zpracovávají vstupními formátovacími moduly.

Chyby převodu typů

Pokud se najde zdroj, ale nejde ho převést na cílový typ, stav modelu se označí jako neplatný. Cílový parametr nebo vlastnost je nastaven na hodnotu null nebo výchozí hodnotu, jak je uvedeno v předchozí části.

V kontroleru rozhraní API, který má [ApiController] atribut, výsledkem neplatného stavu modelu je automatická odpověď HTTP 400.

Razor Na stránce znovu zobrazíte stránku s chybovou zprávou:

public IActionResult OnPost()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _instructorsInMemoryStore.Add(Instructor);
    return RedirectToPage("./Index");
}

Ověření na straně klienta zachytává nejvíce chybná data, která by jinak byla odeslána Razor do formuláře Stránky. Toto ověření znesnadňuje aktivaci předchozího zvýrazněného kódu. Ukázková aplikace obsahuje tlačítko Odeslat s neplatným datem , které vloží chybná data do pole Datum přijetí a odešle formulář. Toto tlačítko ukazuje, jak kód pro opětovné zobrazení stránky funguje, když dojde k chybám převodu dat.

Při opětovném zobrazení stránky předchozím kódem se v poli formuláře nezobrazí neplatný vstup. Důvodem je to, že vlastnost modelu byla nastavena na hodnotu null nebo výchozí hodnotu. Neplatný vstup se zobrazí v chybové zprávě. Pokud ale chcete znovu zobrazit špatná data v poli formuláře, zvažte, že vlastnost modelu vytvoří řetězec a provede převod dat ručně.

Stejná strategie se doporučuje, pokud nechcete, aby chyby převodu typů způsobily chyby stavu modelu. V takovém případě nastavte vlastnost modelu jako řetězec.

Jednoduché typy

Mezi jednoduché typy, na které může pořadač modelů převést zdrojové řetězce, patří následující:

Komplexní typy

Komplexní typ musí mít veřejné výchozí konstruktor a veřejné zapisovatelné vlastnosti pro vytvoření vazby. Když dojde k vytvoření vazby modelu, vytvoří se instance třídy pomocí veřejného výchozího konstruktoru.

Pro každou vlastnost komplexního typu vazba modelu hledá prostřednictvím zdrojů vzor názvů prefix.property_name. Pokud se nic nenajde, hledá jenom property_name bez předpony.

Pro vazbu na parametr je předpona název parametru. Pro vazbu na PageModel veřejnou vlastnost je předpona název veřejné vlastnosti. Některé atributy mají Prefix vlastnost, která umožňuje přepsat výchozí použití parametru nebo názvu vlastnosti.

Předpokládejme například, že komplexní typ je následující Instructor třída:

public class Instructor
{
    public int ID { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
}

Předpona = název parametru

Pokud je model, který má být vázán, parametr s názvem instructorToUpdate:

public IActionResult OnPost(int? id, Instructor instructorToUpdate)

Vazba modelu začíná procházením zdrojů klíče instructorToUpdate.ID. Pokud se nenajde, vyhledá ID se bez předpony.

Prefix = název vlastnosti

Pokud je model, který má být vázán, vlastnost s názvem Instructor kontroleru nebo PageModel třídy:

[BindProperty]
public Instructor Instructor { get; set; }

Vazba modelu začíná procházením zdrojů klíče Instructor.ID. Pokud se nenajde, vyhledá ID se bez předpony.

Vlastní předpona

Pokud je model, který má být vázán, parametr s názvem instructorToUpdate a Bind atribut určuje Instructor jako předponu:

public IActionResult OnPost(
    int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)

Vazba modelu začíná procházením zdrojů klíče Instructor.ID. Pokud se nenajde, vyhledá ID se bez předpony.

Atributy pro cíle komplexního typu

Pro řízení vazby modelu komplexních typů je k dispozici několik předdefinovaných atributů:

  • [Bind]
  • [BindRequired]
  • [BindNever]

Upozorňující

Tyto atributy ovlivňují vazbu modelu při publikování dat formuláře je zdrojem hodnot. Nemají vliv na vstupní formátovací moduly, které proces publikoval tělo požadavků JSON a XML. Vstupní formátovací moduly jsou vysvětleny dále v tomto článku.

[Bind] – atribut

Lze použít na třídu nebo parametr metody. Určuje, které vlastnosti modelu mají být zahrnuty do vazby modelu. [Bind]nemá vliv na vstupní formátovací moduly.

V následujícím příkladu jsou vázány pouze zadané vlastnosti Instructor modelu, pokud je volána jakákoli obslužná rutina nebo metoda akce:

[Bind("LastName,FirstMidName,HireDate")]
public class Instructor

V následujícím příkladu jsou při zavolání metody vázány OnPost pouze zadané vlastnosti Instructor modelu:

[HttpPost]
public IActionResult OnPost([Bind("LastName,FirstMidName,HireDate")] Instructor instructor)

Atribut [Bind] lze použít k ochraně proti nadměrnému umístění ve scénářích vytváření . Nefunguje dobře ve scénářích úprav, protože vyloučené vlastnosti jsou nastavené na hodnotu null nebo výchozí hodnotu, místo aby zůstaly beze změny. Pro ochranu před nadměrném umístěním se místo atributu [Bind] doporučuje zobrazit modely. Další informace naleznete v tématu Bezpečnostní poznámka o nadměrném příspěvku.

Atribut [ModelBinder]

ModelBinderAttribute lze použít u typů, vlastností nebo parametrů. Umožňuje zadat typ pořadače modelu použitého k vytvoření vazby konkrétní instance nebo typu. Příklad:

[HttpPost]
public IActionResult OnPost([ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)

Atribut [ModelBinder] lze také použít ke změně názvu vlastnosti nebo parametru při vazbě modelu:

public class Instructor
{
    [ModelBinder(Name = "instructor_id")]
    public string Id { get; set; }

    public string Name { get; set; }
}

Atribut [BindRequired]

Lze použít pouze u vlastností modelu, ne na parametry metody. Způsobí, že vazba modelu přidá chybu stavu modelu, pokud vazba nemůže nastat pro vlastnost modelu. Tady je příklad:

public class InstructorWithCollection
{
    public int ID { get; set; }

    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
    [Display(Name = "Hire Date")]
    [BindRequired]
    public DateTime HireDate { get; set; }

Projděte si také diskuzi o atributu [Required] v ověření modelu.

Atribut [BindNever]

Lze použít pouze u vlastností modelu, ne na parametry metody. Zabraňuje vazbě modelu v nastavení vlastnosti modelu. Tady je příklad:

public class InstructorWithDictionary
{
    [BindNever]
    public int ID { get; set; }

Kolekce

U cílů, které jsou kolekcemi jednoduchých typů, vazba modelu hledá shody s parameter_name nebo property_name. Pokud se nenajde žádná shoda, vyhledá jeden z podporovaných formátů bez předpony. Příklad:

  • Předpokládejme, že parametr, který má být vázán, je pole s názvem selectedCourses:

    public IActionResult OnPost(int? id, int[] selectedCourses)
    
  • Data řetězce formuláře nebo dotazu můžou být v jednom z následujících formátů:

    selectedCourses=1050&selectedCourses=2000 
    
    selectedCourses[0]=1050&selectedCourses[1]=2000
    
    [0]=1050&[1]=2000
    
    selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
    
    [a]=1050&[b]=2000&index=a&index=b
    

    Vyhněte se vytvoření vazby parametru nebo vlastnosti s názvem index nebo Index pokud sousedí s hodnotou kolekce. Vazby modelu se pokusí použít index jako index pro kolekci, což může vést k nesprávné vazbě. Představte si například následující akci:

    public IActionResult Post(string index, List<Product> products)
    

    V předchozím kódu index se parametr řetězce dotazu sváže s parametrem index metody a také slouží k vytvoření vazby kolekce produktů. Přejmenování parametru nebo použití atributu index vazby modelu ke konfiguraci vazby zabrání tomuto problému:

    public IActionResult Post(string productIndex, List<Product> products)
    
  • Následující formát je podporován pouze v datech formuláře:

    selectedCourses[]=1050&selectedCourses[]=2000
    
  • Pro všechny předchozí ukázkové formáty předá vazba modelu do parametru selectedCourses pole dvou položek:

    • selectedCourses[0]=1050
    • selectedCourses[1]=2000

    Formáty dat, které používají čísla dolního indexu (... [0] ... [1] ...) musí zajistit, aby byly číslony postupně počínaje nulou. Pokud jsou v číslování dolního indexu nějaké mezery, budou všechny položky po mezerě ignorovány. Pokud jsou například dolní indexy 0 a 2 místo 0 a 1, druhá položka se ignoruje.

Slovníky

V případě Dictionary cílů vazba modelu hledá shody s parameter_name nebo property_name. Pokud se nenajde žádná shoda, vyhledá jeden z podporovaných formátů bez předpony. Příklad:

  • Předpokládejme, že cílový parametr je pojmenovaný Dictionary<int, string> selectedCourses:

    public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
    
  • Data publikovaného formuláře nebo řetězce dotazu můžou vypadat jako jeden z následujících příkladů:

    selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics
    
    [1050]=Chemistry&selectedCourses[2000]=Economics
    
    selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry&
    selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
    
    [0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
    
  • Pro všechny předchozí ukázkové formáty předá vazba modelu slovník se dvěma položkami parametru selectedCourses :

    • selectedCourses["1050"]="Chemie"
    • selectedCourses["2000"]="Ekonomika"

Vazby konstruktoru a typy záznamů

Vazba modelu vyžaduje, aby složité typy měly konstruktor bez parametrů. System.Text.Json Vstupní formátovací moduly i Newtonsoft.Json na základě podporují deserializaci tříd, které nemají konstruktor bez parametrů.

C# 9 představuje typy záznamů, které představují skvělý způsob, jak stručně znázorňovat data v síti. ASP.NET Core přidává podporu pro vazbu modelu a ověřování typů záznamů pomocí jednoho konstruktoru:

public record Person([Required] string Name, [Range(0, 150)] int Age, [BindNever] int Id);

public class PersonController
{
   public IActionResult Index() => View();

   [HttpPost]
   public IActionResult Index(Person person)
   {
       ...
   }
}

Person/Index.cshtml:

@model Person

<label>Name: <input asp-for="Name" /></label>
...
<label>Age: <input asp-for="Age" /></label>

Při ověřování typů záznamů modul runtime vyhledává metadata vazby a ověřování, a to konkrétně u parametrů, nikoli u vlastností.

Architektura umožňuje vazbu na typy záznamů a jejich ověřování:

public record Person([Required] string Name, [Range(0, 100)] int Age);

Aby předchozí funkce fungovala, musí typ:

  • Být typem záznamu.
  • Máte přesně jeden veřejný konstruktor.
  • Obsahují parametry, které mají vlastnost se stejným názvem a typem. Názvy se nesmí lišit písmeny.

PoCOs bez konstruktorů bez parametrů

PoCOs, které nemají konstruktory bez parametrů, nelze svázat.

Výsledkem následujícího kódu je výjimka, která říká, že typ musí mít konstruktor bez parametrů:

public class Person(string Name)

public record Person([Required] string Name, [Range(0, 100)] int Age)
{
   public Person(string Name) : this (Name, 0);
}

Typy záznamů s ručně vytvořenými konstruktory

Typy záznamů s ručně vytvořenými konstruktory, které vypadají jako primární konstruktory

public record Person
{
   public Person([Required] string Name, [Range(0, 100)] int Age) => (this.Name, this.Age) = (Name, Age);

   public string Name { get; set; }
   public int Age { get; set; }
}

Typy záznamů, ověřování a metadata vazby

U typů záznamů se používají metadata ověřování a vazby parametrů. Všechna metadata vlastností se ignorují.

public record Person (string Name, int Age)
{
   [BindProperty(Name = "SomeName")] // This does not get used
   [Required] // This does not get used
   public string Name { get; init; }
}

Ověřování a metadata

Ověřování používá metadata parametru, ale používá vlastnost ke čtení hodnoty. V běžném případě s primárními konstruktory by oba byly identické. Existují však způsoby, jak ji porazit:

public record Person([Required] string Name)
{
   private readonly string _name;
   public Name { get; init => _name = value ?? string.Empty; } // Now this property is never null. However this object could have been constructed as `new Person(null);`
}

TryUpdateModel neaktualizuje parametry u typu záznamu

public record Person(string Name)
{
   public int Age { get; set; }
}

var person = new Person("initial-name");
TryUpdateModel(person, ...);

V tomto případě se MVC nebude pokoušet o vytvoření vazby Name znovu. Age Je však možné je aktualizovat.

Chování globalizace vazby modelu směruje data a řetězce dotazů

Zprostředkovatel hodnoty trasy ASP.NET Core a zprostředkovatel hodnot řetězce dotazu:

  • Nakládá s hodnotami jako s neutrální jazykovou verzí.
  • Počítejte s tím, že adresy URL jsou invariantní pro jazykovou verzi.

Naproti tomu hodnoty pocházející z dat formuláře procházejí převodem citlivým na jazykovou verzi. Je to záměrně tak, aby adresy URL byly sdíleny napříč národními prostředími.

Aby poskytovatel hodnoty trasy ASP.NET Core a poskytovatel hodnot řetězce dotazu prošly převodem citlivým na jazykovou verzi:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
    {
        var index = options.ValueProviderFactories.IndexOf(
            options.ValueProviderFactories.OfType<QueryStringValueProviderFactory>().Single());
        options.ValueProviderFactories[index] = new CulturedQueryStringValueProviderFactory();
    });
}
public class CulturedQueryStringValueProviderFactory : IValueProviderFactory
{
    public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        var query = context.ActionContext.HttpContext.Request.Query;
        if (query != null && query.Count > 0)
        {
            var valueProvider = new QueryStringValueProvider(
                BindingSource.Query,
                query,
                CultureInfo.CurrentCulture);

            context.ValueProviders.Add(valueProvider);
        }

        return Task.CompletedTask;
    }
}

Speciální datové typy

Existuje několik speciálních datových typů, které lze zpracovat vazbou modelu.

IFormFile a IFormFileCollection

Nahraný soubor zahrnutý v požadavku HTTP. Podporuje se IEnumerable<IFormFile> také více souborů.

CancellationToken

Akce můžou volitelně svázat CancellationToken jako parametr. Tím se prováže RequestAborted signál, když dojde k přerušení připojení, které je základem požadavku HTTP. Akce můžou tento parametr použít ke zrušení dlouhotrvajících asynchronních operací, které se spouští jako součást akcí kontroleru.

FormCollection

Slouží k načtení všech hodnot z publikovaných dat formuláře.

Vstupní formátovací moduly

Data v textu požadavku můžou být ve formátu JSON, XML nebo jiném formátu. K analýze těchto dat používá vazba modelu vstupní formátovací modul , který je nakonfigurovaný pro zpracování konkrétního typu obsahu. Ve výchozím nastavení ASP.NET Core obsahuje vstupní formátovací moduly založené na FORMÁTU JSON pro zpracování dat JSON. Můžete přidat další formátovací moduly pro jiné typy obsahu.

ASP.NET Core vybere vstupní formátovací moduly na základě atributu Consumes . Pokud neexistuje žádný atribut, použije hlavičku Content-Type.

Použití předdefinovaných formátovacích souborů XML:

  • Microsoft.AspNetCore.Mvc.Formatters.Xml Nainstalujte balíček NuGet.

  • Startup.ConfigureServicesVolat AddXmlSerializerFormatters nebo AddXmlDataContractSerializerFormatters.

    services.AddRazorPages()
        .AddMvcOptions(options =>
    {
        options.ValueProviderFactories.Add(new CookieValueProviderFactory());
        options.ModelMetadataDetailsProviders.Add(
            new ExcludeBindingMetadataProvider(typeof(System.Version)));
        options.ModelMetadataDetailsProviders.Add(
            new SuppressChildValidationMetadataProvider(typeof(System.Guid)));
    })
    .AddXmlSerializerFormatters();
    
  • Consumes Použijte atribut na třídy kontroleru nebo metody akcí, které by měly očekávat KÓD XML v textu požadavku.

    [HttpPost]
    [Consumes("application/xml")]
    public ActionResult<Pet> Create(Pet pet)
    

    Další informace naleznete v tématu Představení serializace XML.

Přizpůsobení vazby modelu pomocí vstupních formátovacích nástrojů

Vstupní formátovací modul přebírá plnou odpovědnost za čtení dat z textu požadavku. Pokud chcete tento proces přizpůsobit, nakonfigurujte rozhraní API používaná vstupním formátovacím modulem. Tato část popisuje, jak přizpůsobit System.Text.Jsonvstupní formátovací modul založený na základech pro pochopení vlastního typu s názvem ObjectId.

Představte si následující model, který obsahuje vlastní ObjectId vlastnost s názvem Id:

public class ModelWithObjectId
{
    public ObjectId Id { get; set; }
}

Chcete-li přizpůsobit proces vazby modelu při použití System.Text.Json, vytvořte třídu odvozenou z JsonConverter<T>:

internal class ObjectIdConverter : JsonConverter<ObjectId>
{
    public override ObjectId Read(
        ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return new ObjectId(JsonSerializer.Deserialize<int>(ref reader, options));
    }

    public override void Write(
        Utf8JsonWriter writer, ObjectId value, JsonSerializerOptions options)
    {
        writer.WriteNumberValue(value.Id);
    }
}

Chcete-li použít vlastní převaděč, použijte JsonConverterAttribute atribut na typ. V následujícím příkladu ObjectId je typ nakonfigurován ObjectIdConverter jako vlastní převaděč:

[JsonConverter(typeof(ObjectIdConverter))]
public struct ObjectId
{
    public ObjectId(int id) =>
        Id = id;

    public int Id { get; }
}

Další informace naleznete v tématu Jak psát vlastní převaděče.

Vyloučení zadaných typů z vazby modelu

Chování vazeb modelu a ověřovacích systémů je řízeno ModelMetadata. Úpravou můžete přidat ModelMetadata zprostředkovatele podrobností do MvcOptions.ModelMetadataDetailsProviders. Zprostředkovatelé předdefinovaných podrobností jsou k dispozici pro zakázání vazby nebo ověřování modelu pro zadané typy.

Chcete-li zakázat vazbu modelu u všech modelů zadaného typu, přidejte do ExcludeBindingMetadataProvider Startup.ConfigureServicessouboru . Chcete-li například zakázat vazbu modelu u všech modelů typu System.Version:

services.AddRazorPages()
    .AddMvcOptions(options =>
{
    options.ValueProviderFactories.Add(new CookieValueProviderFactory());
    options.ModelMetadataDetailsProviders.Add(
        new ExcludeBindingMetadataProvider(typeof(System.Version)));
    options.ModelMetadataDetailsProviders.Add(
        new SuppressChildValidationMetadataProvider(typeof(System.Guid)));
})
.AddXmlSerializerFormatters();

Chcete-li zakázat ověřování u vlastností zadaného typu, přidejte SuppressChildValidationMetadataProvider do Startup.ConfigureServicessouboru . Chcete-li například zakázat ověřování u vlastností typu System.Guid:

services.AddRazorPages()
    .AddMvcOptions(options =>
{
    options.ValueProviderFactories.Add(new CookieValueProviderFactory());
    options.ModelMetadataDetailsProviders.Add(
        new ExcludeBindingMetadataProvider(typeof(System.Version)));
    options.ModelMetadataDetailsProviders.Add(
        new SuppressChildValidationMetadataProvider(typeof(System.Guid)));
})
.AddXmlSerializerFormatters();

Vlastní pořadače modelů

Vazby modelu můžete rozšířit tak, že napíšete vlastní pořadač modelů a pomocí atributu [ModelBinder] ho vyberete pro daný cíl. Přečtěte si další informace o vazbě vlastního modelu.

Ruční vazba modelu

Vazbu modelu lze vyvolat ručně pomocí TryUpdateModelAsync metody. Metoda je definována v obou ControllerBase třídách PageModel . Přetížení metody umožňují zadat předponu a zprostředkovatele hodnot, které se mají použít. Metoda vrátí false , pokud se nezdaří vazba modelu. Tady je příklad:

if (await TryUpdateModelAsync<InstructorWithCollection>(
    newInstructor,
    "Instructor",
    i => i.FirstMidName, i => i.LastName, i => i.HireDate))
{
    _instructorsInMemoryStore.Add(newInstructor);
    return RedirectToPage("./Index");
}
PopulateAssignedCourseData(newInstructor);
return Page();

TryUpdateModelAsync používá zprostředkovatele hodnot k získání dat z textu formuláře, řetězce dotazu a směrování dat. TryUpdateModelAsync je obvykle:

  • Používá se s aplikacemi Razor Pages a MVC pomocí kontrolerů a zobrazení, aby se zabránilo nadměrnému publikování.
  • Nepoužívá se s webovým rozhraním API, pokud se nepoužívá z dat formulářů, řetězců dotazů a směruje data. Koncové body webového rozhraní API, které využívají JSON, používají k deserializaci textu požadavku do objektu vstupní formátovací moduly.

Další informace naleznete v tématu TryUpdateModelAsync.

Atribut [FromServices]

Název tohoto atributu se řídí vzorem atributů vazby modelu, které určují zdroj dat. Nejedná se ale o vazbu dat z poskytovatele hodnot. Získá instanci typu z kontejneru injektáže závislostí. Jejím účelem je poskytnout alternativu k injektáži konstruktoru, pokud potřebujete službu pouze v případě, že je volána konkrétní metoda.

Další materiály