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ázvemid
. - 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
GetById
s názvemdogsOnly
. - 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
true
hodnotu .
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:
- Pole formuláře
- Text požadavku (pro kontrolery, které mají atribut [ApiController].)
- Směrování dat
- Parametry řetězce dotazu
- 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
neboIEnumerable<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.cs
souboru .
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
null
hodnotu . - Typy hodnot, které nemají hodnotu null, jsou nastaveny na
default(T)
hodnotu . Například parametrint 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, žebyte[]
pole jsou nastavena nanull
.
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í:
- Logická hodnota
- Bajt, SByte
- Char
- DateOnly
- DateTime
- DateTimeOffset
- Desetinné číslo
- Dvojité desetinné číslo
- Výčet
- Identifikátor guid
- Int16, Int32, Int64
- Jednotlivý
- TimeOnly
- Časový rozsah
- UInt16, UInt32, UInt64
- Identifikátor URI
- Verze
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í vazbyDateRange
.
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 TryParse
ně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=foo
metodu s vazbou na metodu OnGet(Instructor instructor)
obsahuje výsledný objekt typu Instructor
:
Id
nastaveno na100
hodnotu .Name
nastaveno nanull
hodnotu . Vazba modelu očekáváInstructor.Name
, protožeInstructor.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
neboIndex
pokud sousedí s hodnotou kolekce. Vazby modelu se pokusí použítindex
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 parametremindex
metody a také slouží k vytvoření vazby kolekce produktů. Přejmenování parametru nebo použití atributuindex
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:
- Dědit z IValueProviderFactory
- Zkopírování kódu z QueryStringValueProviderFactory nebo RouteValueValueProviderFactory
- Nahraďte hodnotu jazykové verze předanou konstruktoru zprostředkovatele hodnot cultureInfo.CurrentCulture.
- Nahraďte výchozí objekt pro vytváření zprostředkovatelů hodnot v možnostech MVC novým objektem:
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:
Program.cs
Volat AddXmlSerializerFormatters nebo AddXmlDataContractSerializerFormatters.builder.Services.AddControllers() .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.Json
vstupní 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.cs
souboru . 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.cs
souboru . 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ázvemid
. - 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
GetById
s názvemdogsOnly
. - 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
true
hodnotu .
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:
- Pole formuláře
- Text požadavku (pro kontrolery, které mají atribut [ApiController].)
- Směrování dat
- Parametry řetězce dotazu
- 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
neboIEnumerable<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.cs
souboru .
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
null
hodnotu . - Typy hodnot, které nemají hodnotu null, jsou nastaveny na
default(T)
hodnotu . Například parametrint 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, žebyte[]
pole jsou nastavena nanull
.
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í:
- Logická hodnota
- Bajt, SByte
- Char
- DateOnly
- DateTime
- DateTimeOffset
- Desetinné číslo
- Dvojité desetinné číslo
- Výčet
- Identifikátor guid
- Int16, Int32, Int64
- Jednotlivý
- TimeOnly
- Časový rozsah
- UInt16, UInt32, UInt64
- Identifikátor URI
- Verze
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í vazbyDateRange
.
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 TryParse
ně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=foo
metodu s vazbou na metodu OnGet(Instructor instructor)
obsahuje výsledný objekt typu Instructor
:
Id
nastaveno na100
hodnotu .Name
nastaveno nanull
hodnotu . Vazba modelu očekáváInstructor.Name
, protožeInstructor.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
neboIndex
pokud sousedí s hodnotou kolekce. Vazby modelu se pokusí použítindex
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 parametremindex
metody a také slouží k vytvoření vazby kolekce produktů. Přejmenování parametru nebo použití atributuindex
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:
- Dědit z IValueProviderFactory
- Zkopírování kódu z QueryStringValueProviderFactory nebo RouteValueValueProviderFactory
- Nahraďte hodnotu jazykové verze předanou konstruktoru zprostředkovatele hodnot cultureInfo.CurrentCulture.
- Nahraďte výchozí objekt pro vytváření zprostředkovatelů hodnot v možnostech MVC novým objektem:
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:
Program.cs
Volat AddXmlSerializerFormatters nebo AddXmlDataContractSerializerFormatters.builder.Services.AddControllers() .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.Json
vstupní 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.cs
souboru . 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.cs
souboru . 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ázvemid
. - 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
GetById
s názvemdogsOnly
. - 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
true
hodnotu .
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:
- Pole formuláře
- Text požadavku (pro kontrolery, které mají atribut [ApiController].)
- Směrování dat
- Parametry řetězce dotazu
- 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
neboIEnumerable<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.cs
souboru .
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
null
hodnotu . - Typy hodnot, které nemají hodnotu null, jsou nastaveny na
default(T)
hodnotu . Například parametrint 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, žebyte[]
pole jsou nastavena nanull
.
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í:
- Logická hodnota
- Bajt, SByte
- Char
- DateTime
- DateTimeOffset
- Desetinné číslo
- Dvojité desetinné číslo
- Výčet
- Identifikátor guid
- Int16, Int32, Int64
- Jednotlivý
- Časový rozsah
- UInt16, UInt32, UInt64
- Identifikátor URI
- Verze
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=foo
metodu s vazbou na metodu OnGet(Instructor instructor)
obsahuje výsledný objekt typu Instructor
:
Id
nastaveno na100
hodnotu .Name
nastaveno nanull
hodnotu . Vazba modelu očekáváInstructor.Name
, protožeInstructor.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
neboIndex
pokud sousedí s hodnotou kolekce. Vazby modelu se pokusí použítindex
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 parametremindex
metody a také slouží k vytvoření vazby kolekce produktů. Přejmenování parametru nebo použití atributuindex
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:
- Dědit z IValueProviderFactory
- Zkopírování kódu z QueryStringValueProviderFactory nebo RouteValueValueProviderFactory
- Nahraďte hodnotu jazykové verze předanou konstruktoru zprostředkovatele hodnot cultureInfo.CurrentCulture.
- Nahraďte výchozí objekt pro vytváření zprostředkovatelů hodnot v možnostech MVC novým objektem:
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:
Program.cs
Volat AddXmlSerializerFormatters nebo AddXmlDataContractSerializerFormatters.builder.Services.AddControllers() .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.Json
vstupní 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.cs
souboru . 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.cs
souboru . 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ázvemid
. - 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
GetById
s názvemdogsOnly
. - 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
true
hodnotu .
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:
- Pole formuláře
- Text požadavku (pro kontrolery, které mají atribut [ApiController].)
- Směrování dat
- Parametry řetězce dotazu
- 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
neboIEnumerable<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.ConfigureServices
souboru .
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
null
hodnotu . - Typy hodnot, které nemají hodnotu null, jsou nastaveny na
default(T)
hodnotu . Například parametrint 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, žebyte[]
pole jsou nastavena nanull
.
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í:
- Logická hodnota
- Bajt, SByte
- Char
- DateTime
- DateTimeOffset
- Desetinné číslo
- Dvojité desetinné číslo
- Výčet
- Identifikátor guid
- Int16, Int32, Int64
- Jednotlivý
- Časový rozsah
- UInt16, UInt32, UInt64
- Identifikátor URI
- Verze
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
neboIndex
pokud sousedí s hodnotou kolekce. Vazby modelu se pokusí použítindex
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 parametremindex
metody a také slouží k vytvoření vazby kolekce produktů. Přejmenování parametru nebo použití atributuindex
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:
- Dědit z IValueProviderFactory
- Zkopírování kódu z QueryStringValueProviderFactory nebo RouteValueValueProviderFactory
- Nahraďte hodnotu jazykové verze předanou konstruktoru zprostředkovatele hodnot cultureInfo.CurrentCulture.
- Nahraďte výchozí objekt pro vytváření zprostředkovatelů hodnot v možnostech MVC novým objektem:
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.ConfigureServices
Volat 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.Json
vstupní 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.ConfigureServices
souboru . 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.ConfigureServices
souboru . 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.