Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Note
Dit is niet de nieuwste versie van dit artikel. Zie de .NET 9-versie van dit artikel voor de huidige release.
Warning
Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie het .NET- en .NET Core-ondersteuningsbeleid voor meer informatie. Zie de .NET 9-versie van dit artikel voor de huidige release.
Important
Deze informatie heeft betrekking op een pre-releaseproduct dat aanzienlijk kan worden gewijzigd voordat het commercieel wordt uitgebracht. Microsoft geeft geen garanties, uitdrukkelijk of impliciet, met betrekking tot de informatie die hier wordt verstrekt.
Zie de .NET 9-versie van dit artikel voor de huidige release.
In dit artikel wordt uitgelegd wat modelbinding is, hoe het werkt en hoe het gedrag ervan kan worden aangepast.
Wat is modelbinding?
Controllers en Razor pagina's werken met gegevens die afkomstig zijn van HTTP-aanvragen. Routegegevens kunnen bijvoorbeeld een recordsleutel bevatten en geplaatste formuliervelden kunnen waarden bieden voor de eigenschappen van het model. Het schrijven van code om elk van deze waarden op te halen en deze te converteren van tekenreeksen naar .NET-typen zou tijdrovend en foutgevoelig zijn. Modelbinding automatiseert dit proces. Het modelbindingssysteem:
- Hiermee worden gegevens opgehaald uit verschillende bronnen, zoals routegegevens, formuliervelden en queryreeksen.
- Stelt de gegevens beschikbaar aan controllers en Razor pagina's in methodeparameters en openbare eigenschappen.
- Converteert tekenreeksgegevens naar .NET-typen.
- Hiermee worden eigenschappen van complexe typen bijgewerkt.
Example
Stel dat u de volgende actiemethode hebt:
[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)
En de app ontvangt een aanvraag met deze URL:
https://contoso.com/api/pets/2?DogsOnly=true
Modelbinding doorloopt de volgende stappen nadat het routeringssysteem de actiemethode heeft geselecteerd:
- Hiermee zoekt u de eerste parameter van
GetById, een geheel getal met de naamid. - Bekijkt de beschikbare bronnen in de HTTP-aanvraag en vindt
id= '2' in routegegevens. - Converteert de tekenreeks '2' naar geheel getal 2.
- Hiermee zoekt u de volgende parameter van
GetById, een Booleaanse parameter genaamddogsOnly. - Bekijkt de bronnen en zoekt 'DogsOnly=true' in de querytekenreeks. Naamkoppeling is niet hoofdlettergevoelig.
- Converteert de tekenreeks "true" naar de booleaanse waarde
true.
Het framework roept vervolgens de GetById methode aan, waarbij 2 wordt doorgegeven voor de id parameter en true voor de dogsOnly parameter.
In het voorgaande voorbeeld zijn de modelbindingsdoelen methodeparameters die eenvoudige typen zijn. Doelen kunnen ook de eigenschappen van een complex type zijn. Nadat elke eigenschap is gebonden, vindt modelvalidatie plaats voor die eigenschap. De record van welke gegevens zijn gebonden aan het model, en eventuele bindings- of validatiefouten, worden opgeslagen in ControllerBase.ModelState of PageModel.ModelState. Om erachter te komen of dit proces is geslaagd, controleert de app de vlag ModelState.IsValid .
Targets
Modelbinding probeert waarden te vinden voor de volgende soorten doelen:
- Parameters van de actiemethode controller waarnaar een aanvraag wordt doorgestuurd.
- Parameters van de Razor handlermethode Pages waarnaar een aanvraag wordt doorgestuurd.
- Openbare eigenschappen van een controller of
PageModelklasse, indien opgegeven door kenmerken.
Kenmerk [BindProperty]
Kan worden toegepast op een openbare eigenschap van een controller of PageModel klasse om modelbinding te veroorzaken voor de doeleigenschap:
public class EditModel : PageModel
{
[BindProperty]
public Instructor? Instructor { get; set; }
// ...
}
[BindProperties] kenmerk
Kan worden toegepast op een controller of de PageModel klasse om aan te geven dat modelbinding zich moet richten op alle openbare eigenschappen van de klasse.
[BindProperties]
public class CreateModel : PageModel
{
public Instructor? Instructor { get; set; }
// ...
}
Modelbinding voor HTTP GET-aanvragen
Eigenschappen zijn standaard niet gebonden voor HTTP GET-aanvragen. Normaal gesproken is alles wat u nodig hebt voor een GET-aanvraag een record-id-parameter. De record-id wordt gebruikt om het item in de database op te zoeken. Daarom is het niet nodig om een eigenschap te binden die een exemplaar van het model bevat. In scenario's waarin u wilt dat eigenschappen zijn gebonden aan gegevens uit GET-aanvragen, stelt u de SupportsGet eigenschap truein op:
[BindProperty(Name = "ai_user", SupportsGet = true)]
public string? ApplicationInsightsCookie { get; set; }
Eenvoudige en complexe typen modelbinding
Modelbinding maakt gebruik van specifieke definities voor de typen waarop deze werkt. Een eenvoudig type wordt geconverteerd van één tekenreeks met behulp van TypeConverter of een TryParse methode. Een complex type wordt geconverteerd van meerdere invoerwaarden. Het framework bepaalt het verschil op basis van het bestaan van een TypeConverter of TryParse. Wij raden aan om een typeconverter te maken of het gebruik van TryParse voor een string naar SomeType conversie waarvoor geen externe middelen of meerdere invoer nodig zijn.
Sources
Modelbinding haalt standaard gegevens op in de vorm van sleutel-waardeparen uit de volgende bronnen in een HTTP-aanvraag:
- Formuliervelden
- De aanvraagbody (voor controllers die het kenmerk [ApiController] hebben.)
- Routegegevens
- Parameters voor zoekreeks
- Geüploade bestanden
Voor elke doelparameter of eigenschap worden de bronnen gescand in de volgorde die wordt aangegeven in de voorgaande lijst. Er zijn enkele uitzonderingen:
- Routegegevens en queryreekswaarden worden alleen gebruikt voor eenvoudige typen.
- Geüploade bestanden zijn alleen gebonden aan doeltypen die implementeren
IFormFileofIEnumerable<IFormFile>.
Als de standaardbron niet juist is, gebruikt u een van de volgende kenmerken om de bron op te geven:
-
[FromQuery]- Haalt waarden op uit de querytekenreeks. -
[FromRoute]- Haalt waarden op uit routegegevens. -
[FromForm]- Haalt waarden op uit geplaatste formuliervelden. -
[FromBody]- Haalt waarden op uit de hoofdtekst van de aanvraag. -
[FromHeader]- Haalt waarden op uit HTTP-headers.
Deze kenmerken:
Worden afzonderlijk toegevoegd aan modeleigenschappen en niet aan de modelklasse, zoals in het volgende voorbeeld:
public class Instructor { public int Id { get; set; } [FromQuery(Name = "Note")] public string? NoteFromQueryString { get; set; } // ... }Accepteer eventueel een modelnaamwaarde in de constructor. Deze optie wordt opgegeven als de eigenschapsnaam niet overeenkomt met de waarde in de aanvraag. De waarde in de aanvraag kan bijvoorbeeld een header zijn met een afbreekstreepje in de naam, zoals in het volgende voorbeeld:
public void OnGet([FromHeader(Name = "Accept-Language")] string language)
Kenmerk [FromBody]
Pas het [FromBody] kenmerk toe op een parameter om de eigenschappen ervan te vullen vanuit de hoofdtekst van een HTTP-aanvraag. De ASP.NET Core-runtime delegeert de verantwoordelijkheid voor het lezen van de body naar een invoerformatter. Invoerindelingen worden verderop in dit artikel uitgelegd.
Wanneer [FromBody] deze wordt toegepast op een complexe typeparameter, worden alle bindingsbronkenmerken die op de eigenschappen ervan worden toegepast, genegeerd. Met de volgende Create actie wordt bijvoorbeeld aangegeven dat de parameter van pet uit de body wordt ingevuld:
public ActionResult<Pet> Create([FromBody] Pet pet)
De Pet klasse geeft aan dat de Breed eigenschap wordt ingevuld vanuit een queryreeksparameter:
public class Pet
{
public string Name { get; set; } = null!;
[FromQuery] // Attribute is ignored.
public string Breed { get; set; } = null!;
}
In het voorgaande voorbeeld:
- Het
[FromQuery]kenmerk wordt genegeerd. - De
Breedeigenschap wordt niet ingevuld vanuit een queryreeksparameter.
Invoerindelingen lezen alleen de hoofdtekst en begrijpen geen bindingsbronkenmerken. Als een geschikte waarde wordt gevonden in de hoofdtekst, wordt die waarde gebruikt om de Breed eigenschap te vullen.
Pas [FromBody] niet toe op meer dan één parameter per actiemethode. Zodra de verzoekstroom is gelezen door een invoerformatter, is deze niet meer beschikbaar om opnieuw te worden gelezen voor het binden van andere [FromBody] parameters.
Aanvullende bronnen
Brongegevens worden door waardeproviders verstrekt aan het modelbindingssysteem. U kunt aangepaste waardeproviders schrijven en registreren die gegevens ophalen voor modelbinding uit andere bronnen. U wilt bijvoorbeeld gegevens uit cookies of sessiestatus. Ga als volgt te werk om gegevens op te halen uit een nieuwe bron:
- Maak een klasse die
IValueProviderimplementeert. - Maak een klasse die
IValueProviderFactoryimplementeert. - Registreer de fabrieksklasse in
Program.cs.
Het voorbeeld bevat een waardeprovider en factory voorbeeld dat waarden ophaalt uit cookies. Registreer aangepaste waardeprovider-fabrieken in Program.cs:
builder.Services.AddControllers(options =>
{
options.ValueProviderFactories.Add(new CookieValueProviderFactory());
});
De voorgaande code plaatst de provider van aangepaste waarden na alle ingebouwde waardeproviders. Om het de eerste in de lijst te maken, gebruik Insert(0, new CookieValueProviderFactory()) in plaats van Add.
Geen bron voor een modeleigenschap
Standaard wordt er geen modelstatusfout gemaakt als er geen waarde wordt gevonden voor een modeleigenschap. De eigenschap is ingesteld op null of een standaardwaarde:
- Nullable simple types zijn ingesteld op
null. - Niet-nullwaardetypen zijn ingesteld op
default(T). Een parameterint idis bijvoorbeeld ingesteld op 0. - Voor complexe typen maakt modelbinding een exemplaar met behulp van de standaardconstructor, zonder eigenschappen in te stellen.
- Matrices zijn ingesteld op
Array.Empty<T>(), behalve datbyte[]matrices zijn ingesteld opnull.
Als de modelstatus ongeldig moet worden gemaakt wanneer er niets wordt gevonden in formuliervelden voor een modeleigenschap, gebruikt u het [BindRequired] kenmerk.
Houd er rekening mee dat dit [BindRequired] gedrag van toepassing is op modelbinding van geposte formuliergegevens, niet van JSON- of XML-gegevens in een aanvraagbody. Hoofdtekstgegevens van de aanvraag worden verwerkt door invoerformatters.
Typeconversiefouten
Als een bron wordt gevonden maar niet kan worden geconverteerd naar het doeltype, wordt de modelstatus gemarkeerd als ongeldig. De doelparameter of -eigenschap is ingesteld op null of een standaardwaarde, zoals vermeld in de vorige sectie.
In een API-controller met het [ApiController] kenmerk resulteert een ongeldige modelstatus in een automatisch HTTP 400-antwoord.
Op een Razor pagina wordt de pagina opnieuw weergegeven met een foutbericht:
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
// ...
return RedirectToPage("./Index");
}
Wanneer de pagina opnieuw wordt weergegeven door de voorgaande code, wordt de ongeldige invoer niet weergegeven in het formulierveld. Dit komt doordat de modeleigenschap is ingesteld op null of een standaardwaarde. De ongeldige invoer wordt weergegeven in een foutbericht. Als u de slechte gegevens in het formulierveld opnieuw wilt weergeven, kunt u overwegen om de modeleigenschap een tekenreeks te maken en de gegevensconversie handmatig uit te voeren.
Dezelfde strategie wordt aanbevolen als u niet wilt dat typeconversiefouten resulteren in modelstatusfouten. In dat geval moet u de modeleigenschap een tekenreeks maken.
Eenvoudige typen
Zie Modelbinding eenvoudige en complexe typen voor uitleg over eenvoudige en complexe typen.
De eenvoudige typen waarmee de modelbindinger brontekenreeksen kan converteren, zijn onder andere:
- Boolean
- Byte, SByte
- Char
- DateOnly
- DateTime
- DateTimeOffset
- Decimal
- Double
- Enum
- Guid
- Int16, Int32, Int64
- Single
- TimeOnly
- TimeSpan
- UInt16, UInt32, UInt64
- Uri
- Version
Binden met IParsable<T>.TryParse
De IParsable<TSelf>.TryParse API ondersteunt parameterwaarden voor bindingscontrolleracties:
public static bool TryParse (string? s, IFormatProvider? provider, out TSelf result);
De volgende DateRange klasse implementeert IParsable<TSelf> ter ondersteuning van het binden van een datumbereik:
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;
}
}
De voorgaande code:
- Converteert een tekenreeks die twee datums vertegenwoordigt naar een
DateRangeobject - De modelbinder gebruikt de
IParsable<TSelf>.TryParsemethode om deDateRangete binden.
De volgende controlleractie maakt gebruik van de DateRange klasse om een datumbereik te binden:
// 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);
}
De volgende Locale klasse implementeert IParsable<TSelf> ter ondersteuning van binding met 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;
}
}
}
De volgende controlleractie maakt gebruik van de Locale klasse om een CultureInfo tekenreeks te binden:
// 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);
}
De volgende controlleractie gebruikt de DateRange en Locale klassen om een datumbereik te binden met 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);
}
In de API-voorbeeld-app op GitHub ziet u het voorgaande voorbeeld voor een API-controller.
Binden met TryParse
De TryParse API ondersteunt parameterwaarden voor bindingscontrolleracties:
public static bool TryParse(string value, T out result);
public static bool TryParse(string value, IFormatProvider provider, T out result);
IParsable<T>.TryParse is de aanbevolen methode voor parameterbinding omdat het, in tegenstelling tot TryParse, niet afhankelijk is van reflectie.
De volgende DateRangeTP klasse implementeert 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;
}
}
De volgende controlleractie maakt gebruik van de DateRangeTP klasse om een datumbereik te binden:
// 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);
}
Complexe typen
Een complex type moet een openbare standaardconstructor en openbare schrijfbare eigenschappen hebben om te binden. Wanneer modelbinding plaatsvindt, wordt de klasse geïnstantieerd met behulp van de openbare standaardconstructor.
Voor elke eigenschap van het complexe type doorzoekt modelbinding de bronnen naar het naampatroonprefix.property_name. Als er niets wordt gevonden, wordt gezocht naar property_name zonder het voorvoegsel. De beslissing om het voorvoegsel te gebruiken wordt niet per eigenschap gemaakt. Bijvoorbeeld, met een query die ?Instructor.Id=100&Name=foo bevat en gebonden is aan de methode OnGet(Instructor instructor), bevat het resulterende object van het type Instructor:
-
Idingesteld op100. -
Nameingesteld opnull. Model binding verwachtInstructor.NameomdatInstructor.Idwerd gebruikt in de voorgaande queryparameter.
Note
Documentatiekoppelingen naar .NET-referentiebron laden meestal de standaardbranch van de opslagplaats, die de huidige ontwikkeling vertegenwoordigt voor de volgende release van .NET. Als u een tag voor een specifieke release wilt selecteren, gebruikt u de Switch branches of tags vervolgkeuzelijst. Zie Een versietag selecteren van ASP.NET Core-broncode (dotnet/AspNetCore.Docs #26205)voor meer informatie.
Voor binding met een parameter is het voorvoegsel de parameternaam. Voor binding met een PageModel openbare eigenschap is het voorvoegsel de naam van de openbare eigenschap. Sommige kenmerken hebben een Prefix eigenschap waarmee u het standaardgebruik van de parameter of eigenschapsnaam kunt overschrijven.
Stel dat het complexe type de volgende Instructor klasse is:
public class Instructor
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
Voorvoegsel = parameternaam
Als het model moet worden gebonden aan een parameter met de naam instructorToUpdate:
public IActionResult OnPost(int? id, Instructor instructorToUpdate)
Modelbinding begint door de bronnen voor de sleutel instructorToUpdate.IDte bekijken. Als dat niet wordt gevonden, wordt gezocht naar ID zonder voorvoegsel.
Voorvoegsel = eigenschapsnaam
Als het model dat moet worden gebonden, een eigenschap is met de naam Instructor van de controller of PageModel klasse:
[BindProperty]
public Instructor Instructor { get; set; }
Modelbinding begint door de bronnen voor de sleutel Instructor.IDte bekijken. Als dat niet wordt gevonden, wordt gezocht naar ID zonder voorvoegsel.
Aangepast voorvoegsel
Als het model dat moet worden gebonden een parameter met de naam instructorToUpdate is en een kenmerk Bind het voorvoegsel Instructor opgeeft.
public IActionResult OnPost(
int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)
Modelbinding begint door de bronnen voor de sleutel Instructor.IDte bekijken. Als dat niet wordt gevonden, wordt gezocht naar ID zonder voorvoegsel.
Kenmerken voor complexe typedoelen
Er zijn verschillende ingebouwde kenmerken beschikbaar voor het beheren van modelbindingen van complexe typen:
Warning
Deze kenmerken zijn van invloed op modelbinding wanneer geplaatste formuliergegevens de bron van waarden zijn. Ze hebben geen invloed op invoerindelingen, die JSON- en XML-aanvraagteksten verwerken. Invoerindelingen worden verderop in dit artikel uitgelegd.
Kenmerk [Bind]
Kan worden toegepast op een klasse- of methodeparameter. Hiermee geeft u op welke eigenschappen van een model moeten worden opgenomen in modelbinding.
[Bind] heeft geen invloed op invoerindelingen.
In het volgende voorbeeld zijn alleen de opgegeven eigenschappen van het Instructor model afhankelijk wanneer een handler of actiemethode wordt aangeroepen:
[Bind("LastName,FirstMidName,HireDate")]
public class Instructor
In het volgende voorbeeld zijn alleen de opgegeven eigenschappen van het Instructor model afhankelijk wanneer de OnPost methode wordt aangeroepen:
[HttpPost]
public IActionResult OnPost(
[Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
Het [Bind] kenmerk kan worden gebruikt om te beschermen tegen overposting in create-scenario's. Het werkt niet goed in bewerkingsscenario's omdat uitgesloten eigenschappen zijn ingesteld op null of een standaardwaarde in plaats van ongewijzigd te laten. Voor bescherming tegen overposting worden weergavemodellen aanbevolen in plaats van het [Bind] kenmerk. Zie Beveiligingsnotitie over overposting voor meer informatie.
[ModelBinder] kenmerk
ModelBinderAttribute kan worden toegepast op typen, eigenschappen of parameters. Hiermee kunt u het type modelbinding opgeven dat wordt gebruikt om het specifieke exemplaar of type te binden. Voorbeeld:
[HttpPost]
public IActionResult OnPost(
[ModelBinder<MyInstructorModelBinder>] Instructor instructor)
Het [ModelBinder] kenmerk kan ook worden gebruikt om de naam van een eigenschap of parameter te wijzigen wanneer het model afhankelijk is:
public class Instructor
{
[ModelBinder(Name = "instructor_id")]
public string Id { get; set; }
// ...
}
Kenmerk [BindRequired]
Zorgt ervoor dat modelbinding een fout in de modelstatus toevoegt indien de binding niet kan plaatsvinden voor een eigenschap van een model. Hier is een voorbeeld:
public class InstructorBindRequired
{
// ...
[BindRequired]
public DateTime HireDate { get; set; }
}
Zie ook de discussie over het [Required] kenmerk in modelvalidatie.
Kenmerk [BindNever]
Kan worden toegepast op een eigenschap of een type. Hiermee voorkomt u dat modelbinding de eigenschap van een model instelt. Wanneer het modelbindingssysteem wordt toegepast op een type, worden alle eigenschappen uitgesloten die door het type worden gedefinieerd. Hier is een voorbeeld:
public class InstructorBindNever
{
[BindNever]
public int Id { get; set; }
// ...
}
Collections
Voor doelen die verzamelingen eenvoudige typen zijn, zoekt modelbinding naar overeenkomsten voor parameter_name of property_name. Als er geen overeenkomst wordt gevonden, wordt gezocht naar een van de ondersteunde indelingen zonder het voorvoegsel. Voorbeeld:
Stel dat de parameter die moet worden gebonden een matrix is met de naam
selectedCourses:public IActionResult OnPost(int? id, int[] selectedCourses)Formulier- of querytekenreeksgegevens kunnen een van de volgende indelingen hebben:
selectedCourses=1050&selectedCourses=2000selectedCourses[0]=1050&selectedCourses[1]=2000[0]=1050&[1]=2000selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b[a]=1050&[b]=2000&index=a&index=bVermijd het binden van een parameter of eigenschap met de naam
indexofIndexals deze grenzen aan een verzamelingswaarde. Modelbinding probeert omindexals de index te gebruiken voor de verzameling, wat kan leiden tot onjuiste binding. Denk bijvoorbeeld aan de volgende actie:public IActionResult Post(string index, List<Product> products)In de voorgaande code wordt de
indexqueryreeksparameter gekoppeld aan deindexmethodeparameter en wordt ook gebruikt om de productverzameling te binden. Als u de naam van deindexparameter wijzigt of een modelbindingskenmerk gebruikt om binding te configureren, voorkomt u dit probleem:public IActionResult Post(string productIndex, List<Product> products)De volgende indeling wordt alleen ondersteund in formuliergegevens:
selectedCourses[]=1050&selectedCourses[]=2000Voor alle voorgaande voorbeeldindelingen geeft modelbinding een matrix van twee items door aan de
selectedCoursesparameter:- selectedCourses[0]=1050
- selectedCourses[1]=2000
Gegevensindelingen die gebruikmaken van subscriptnummers (... [0] ... [1] ...) moet ervoor zorgen dat ze opeenvolgend beginnen bij nul. Als er hiaten zijn in subscriptnummering, worden alle items na de tussenruimte genegeerd. Als de subscripts bijvoorbeeld 0 en 2 zijn in plaats van 0 en 1, wordt het tweede item genegeerd.
Dictionaries
Voor Dictionary doelen zoekt modelbinding naar overeenkomsten voor parameter_name of property_name. Als er geen overeenkomst wordt gevonden, wordt gezocht naar een van de ondersteunde indelingen zonder het voorvoegsel. Voorbeeld:
Stel dat de doelparameter een
Dictionary<int, string>naamselectedCoursesheeft:public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)De geplaatste formulier- of querytekenreeksgegevens kunnen eruitzien als een van de volgende voorbeelden:
selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics[1050]=Chemistry&selectedCourses[2000]=EconomicsselectedCourses[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=EconomicsVoor alle voorgaande voorbeeldindelingen geeft modelbinding een woordenlijst van twee items door aan de
selectedCoursesparameter:- selectedCourses["1050"]="Chemistry"
- selectedCourses["2000"]="Economics"
Constructor-binding en recordtypen
Voor modelbinding is vereist dat complexe typen een parameterloze constructor hebben. Zowel System.Text.Json als Newtonsoft.Json invoerindelingen ondersteunen deserialisatie van klassen die geen parameterloze constructor hebben.
Recordtypen zijn een uitstekende manier om gegevens via het netwerk beknopt weer te geven. ASP.NET Core ondersteunt modelbinding en het valideren van recordtypen met één constructor:
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>
Bij het valideren van recordtypen zoekt de runtime specifiek naar bindings- en validatiemetagegevens op parameters in plaats van op eigenschappen.
Het framework maakt het mogelijk om recordtypen te binden en te valideren:
public record Person([Required] string Name, [Range(0, 100)] int Age);
Voordat het voorgaande werkt, moet het type:
- Wees een recordtype.
- Zorg ervoor dat er precies één publieke constructor is.
- Parameters bevatten die een eigenschap met dezelfde naam en hetzelfde type hebben. De namen mogen niet per geval verschillen.
POCOs zonder parameterloze constructors
POCOs die geen parameterloze constructors hebben, kunnen niet worden gebonden.
De volgende code resulteert in een uitzondering die aangeeft dat het type een parameterloze constructor moet hebben:
public class Person {
public Person(string Name) { }
}
public record Person([Required] string Name, [Range(0, 100)] int Age)
{
public Person(string Name) : this (Name, 0)
{
}
}
Recordtypen met handmatig geschreven constructors
Recordtypen met handmatig geschreven constructors die eruitzien als primaire constructors werken
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; }
}
Recordtypen, validatie en bindingmetagegevens
Voor recordtypen wordt validatie en bindingsmetagegevens op parameters gebruikt. Metagegevens over eigenschappen worden genegeerd
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; }
}
Validatie en metagegevens
Validatie maakt gebruik van metagegevens voor de parameter, maar gebruikt de eigenschap om de waarde te lezen. In het normale geval met primaire constructors zouden de twee identiek zijn. Er zijn echter manieren om het te verslaan:
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 werkt geen parameters bij voor een recordtype
public record Person(string Name)
{
public int Age { get; set; }
}
var person = new Person("initial-name");
TryUpdateModel(person, ...);
In dit geval probeert MVC niet opnieuw te binden Name . Het is echter toegestaan om Age bij te werken.
Globalisatiegedrag van modelbindingsroutegegevens en queryreeksen
De routewaardeprovider en querywaardeprovider van ASP.NET Core:
- Waarden behandelen als invariante cultuur.
- Verwacht dat URL's cultuur-invariant zijn.
Waarden die afkomstig zijn van formuliergegevens, ondergaan daarentegen een cultuurgevoelige conversie. Dit is standaard zo dat URL's kunnen worden gedeeld tussen landinstellingen.
De ASP.NET Core route value provider en query string value provider moeten een cultuurafhankelijke conversie ondergaan.
- Overnemen van IValueProviderFactory
- Kopieer de code uit QueryStringValueProviderFactory of RouteValueValueProviderFactory
- Vervang de cultuurwaarde die is doorgegeven aan de constructor van de waardeprovider door CultureInfo.CurrentCulture
- Vervang de standaardwaardeproviderfabriek in de MVC-opties door uw nieuwe.
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();
});
Speciale gegevenstypen
Er zijn enkele speciale gegevenstypen die modelbinding kunnen verwerken.
IFormFile en IFormFileCollection
Een geüpload bestand dat is opgenomen in de HTTP-aanvraag. Ook ondersteund is IEnumerable<IFormFile> voor meerdere bestanden.
CancellationToken
Acties kunnen desgewenst een CancellationToken als parameter binden. Dit koppelt RequestAborted dat signalen afgeeft wanneer de achterliggende verbinding van het HTTP-verzoek wordt beëindigd. Acties kunnen deze parameter gebruiken om langlopende asynchrone bewerkingen te annuleren die worden uitgevoerd als onderdeel van de controlleracties.
FormCollection
Wordt gebruikt om alle waarden op te halen uit geposte formuliergegevens.
Invoerindelingen
Gegevens in de aanvraagbody kunnen in JSON, XML of een andere indeling zijn. Voor het parseren van deze gegevens maakt modelbinding gebruik van een invoerindeling die is geconfigureerd voor het afhandelen van een bepaald inhoudstype. Standaard bevat ASP.NET Core invoerindelingen op basis van JSON voor het verwerken van JSON-gegevens met behulp van System.Text.Json. U kunt andere formatters toevoegen voor andere inhoudstypen.
De standaardindeling voor JSON-invoer kan worden geconfigureerd met behulp van de AddJsonOptions methode:
builder.Services.AddControllers().AddJsonOptions(options =>
{
// Configure property naming policy (camelCase)
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
// Add enum converter to serialize enums as strings
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
// Configure other JSON options
options.JsonSerializerOptions.WriteIndented = true;
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
});
Veelvoorkomende configuratieopties zijn:
- Naamgevingsbeleid voor eigenschappen - CamelCase of andere naamconventies configureren
- Enum-converters - Behandelt enumeratieserialisatie als strings
- Aangepaste conversieprogramma's - Typespecifieke serialisatielogica toevoegen
ASP.NET Core selecteert invoerindelingen op basis van het kenmerk Verbruiken . Als er geen kenmerk aanwezig is, wordt de header Content-Type gebruikt.
Om de ingebouwde XML-invoerindelingen te gebruiken:
In
Program.cs, bel AddXmlSerializerFormatters of AddXmlDataContractSerializerFormatters.builder.Services.AddControllers() .AddXmlSerializerFormatters();Pas het
Consumeskenmerk toe op controllerklassen of actiemethoden die XML in de hoofdtekst van de aanvraag moeten verwachten.[HttpPost] [Consumes("application/xml")] public ActionResult<Pet> Create(Pet pet)Zie Inleiding tot XML-serialisatie voor meer informatie.
Modelbinding aanpassen met inputformatters
Een invoeropmaaker neemt volledige verantwoordelijkheid voor het lezen van gegevens uit de aanvraagbody. Als u dit proces wilt aanpassen, configureert u de API's die worden gebruikt door de input formatter. In deze sectie wordt beschreven hoe u de op System.Text.Json-gebaseerde invoerindeling aanpast om een aangepast type met de naam ObjectId te kunnen interpreteren.
Overweeg het volgende model, dat een aangepaste ObjectId eigenschap bevat:
public class InstructorObjectId
{
[Required]
public ObjectId ObjectId { get; set; } = null!;
}
Als u het modelbindingsproces wilt aanpassen wanneer u dit gebruikt System.Text.Json, maakt u een klasse die is afgeleid van 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);
}
Als u een aangepast conversieprogramma wilt gebruiken, past u het JsonConverterAttribute kenmerk toe op het type. In het volgende voorbeeld wordt het ObjectId-type geconfigureerd met ObjectIdConverter als zijn aangepaste converter:
[JsonConverter(typeof(ObjectIdConverter))]
public record ObjectId(int Id);
Zie Aangepaste conversieprogramma's schrijven voor meer informatie.
Opgegeven typen uitsluiten van modelbinding
Het gedrag van modelbindingen en validatiesystemen wordt aangestuurd door ModelMetadata. U kunt dit aanpassen ModelMetadata door een gegevensprovider toe te voegen aan MvcOptions.ModelMetadataDetailsProviders. Ingebouwde gegevensproviders zijn beschikbaar voor het uitschakelen van modelbinding of validatie voor opgegeven typen.
Als u modelbinding wilt uitschakelen voor alle modellen van een opgegeven type, voegt u een ExcludeBindingMetadataProvider in Program.cs. Als u bijvoorbeeld modelbinding wilt uitschakelen voor alle modellen van het type System.Version:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
Als u validatie wilt uitschakelen voor eigenschappen van een opgegeven type, voegt u een SuppressChildValidationMetadataProvider in Program.cs. Als u bijvoorbeeld validatie wilt uitschakelen voor eigenschappen van het type System.Guid:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
Aangepaste modelbindingen
U kunt het binden van modellen uitbreiden door een eigen modelbinder te schrijven en dan het kenmerk te gebruiken om deze [ModelBinder] te selecteren voor een gegeven doelwit. Meer informatie over aangepaste modelbinding.
Handmatige modelbinding
Modelbinding kan handmatig worden aangeroepen met behulp van de TryUpdateModelAsync methode. De methode is gedefinieerd op zowel ControllerBase als PageModel klassen. Met overbelasting van methoden kunt u het te gebruiken voorvoegsel en de waardeprovider opgeven. De methode retourneert false als modelbinding mislukt. Hier is een voorbeeld:
if (await TryUpdateModelAsync(
newInstructor,
"Instructor",
x => x.Name, x => x.HireDate!))
{
_instructorStore.Add(newInstructor);
return RedirectToPage("./Index");
}
return Page();
TryUpdateModelAsync maakt gebruik van waardeproviders om gegevens op te halen uit de hoofdtekst van het formulier, queryreeks en routegegevens.
TryUpdateModelAsync is doorgaans:
- Wordt gebruikt met Razor Pagina's en MVC-apps waarbij controllers en weergaven worden gebruikt om over-posting te voorkomen.
- Niet gebruikt met een web-API, tenzij het wordt benut vanuit formuliergegevens, queryreeksen en routegegevens. Web-API-eindpunten die JSON gebruiken, gebruiken input formatters om de hoofdtekst van de aanvraag in een object te deserialiseren.
Zie TryUpdateModelAsync voor meer informatie.
Kenmerk [FromServices]
De naam van dit kenmerk volgt het patroon van modelbindingskenmerken die een gegevensbron opgeven. Maar het gaat niet om bindingsgegevens van een waardeprovider. Er wordt een instance van een type opgehaald uit de container voor afhankelijkheidsinjectie. Het doel is om een alternatief voor constructorinjectie te bieden wanneer u een service alleen nodig hebt als een bepaalde methode wordt aangeroepen.
Als een exemplaar van het type niet is geregistreerd in de container voor afhankelijkheidsinjectie, genereert de app een uitzondering bij het verbinden van de parameter. Als u de parameter optioneel wilt maken, gebruikt u een van de volgende methoden:
- Maak de parameter nullable.
- Stel een standaardwaarde in voor de parameter.
Voor null-parameters moet u ervoor zorgen dat de parameter niet null is voordat u deze opent.
Deserialisatie van Json+PipeReader in MVC
Vanaf .NET 10 maken de volgende functionele gebieden van ASP.NET Core gebruik van overloads van JsonSerializer.DeserializeAsync gebaseerd op PipeReader in plaats van Stream.
- Minimale API's (parameterbinding, hoofdtekst van leesaanvraag)
- MVC (invoerindelingen, model)
- De HttpRequestJsonExtensions extensiemethoden voor het lezen van de verzoekbody als JSON.
Voor de meeste toepassingen biedt een overgang van Stream naar PipeReader betere prestaties zonder dat hiervoor wijzigingen in de toepassingscode nodig zijn. Maar als uw toepassing een aangepast conversieprogramma heeft, kan het conversieprogramma Utf8JsonReader.HasValueSequence mogelijk niet correct verwerken. Als dit niet het probleem is, kan het resultaat fouten zijn, zoals ArgumentOutOfRangeException of ontbrekende gegevens bij het deserialiseren. U hebt de volgende opties om uw conversieprogramma te laten werken zonder PipeReader-gerelateerde fouten.
Optie 1: Tijdelijke tijdelijke oplossing
De snelle tijdelijke oplossing is terug te gaan naar het gebruik van Stream zonder PipeReader-ondersteuning. Als u deze optie wilt implementeren, stelt u de AppContext-switch Microsoft.AspNetCore.UseStreamBasedJsonParsing in op 'true'. We raden u aan dit alleen te doen als tijdelijke tijdelijke oplossing en uw conversieprogramma zo snel mogelijk bij te werken ter ondersteuning HasValueSequence . De switch kan worden verwijderd in .NET 11. Het enige doel was ontwikkelaars tijd te geven om hun conversieprogramma's bijgewerkt te krijgen.
Optie 2: Een snelle oplossing voor JsonConverter implementaties
Voor deze oplossing alloceert u een array vanuit de ReadOnlySequence. In dit voorbeeld ziet u hoe de code eruit zou zien:
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
// previous code
}
Optie 3: Een gecompliceerder maar beter presterende oplossing
Deze oplossing omvat het instellen van een afzonderlijk codepad voor de ReadOnlySequence verwerking:
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.HasValueSequence)
{
reader.ValueSequence;
// ReadOnlySequence optimized path
}
else
{
reader.ValueSpan;
// ReadOnlySpan optimized path
}
}
Zie voor meer informatie
Aanvullende bronnen
In dit artikel wordt uitgelegd wat modelbinding is, hoe het werkt en hoe het gedrag ervan kan worden aangepast.
Wat is modelbinding?
Controllers en Razor pagina's werken met gegevens die afkomstig zijn van HTTP-aanvragen. Routegegevens kunnen bijvoorbeeld een recordsleutel bevatten en geplaatste formuliervelden kunnen waarden bieden voor de eigenschappen van het model. Het schrijven van code om elk van deze waarden op te halen en deze te converteren van tekenreeksen naar .NET-typen zou tijdrovend en foutgevoelig zijn. Modelbinding automatiseert dit proces. Het modelbindingssysteem:
- Hiermee worden gegevens opgehaald uit verschillende bronnen, zoals routegegevens, formuliervelden en queryreeksen.
- Stelt de gegevens beschikbaar aan controllers en Razor pagina's in methodeparameters en openbare eigenschappen.
- Converteert tekenreeksgegevens naar .NET-typen.
- Hiermee worden eigenschappen van complexe typen bijgewerkt.
Example
Stel dat u de volgende actiemethode hebt:
[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)
En de app ontvangt een aanvraag met deze URL:
https://contoso.com/api/pets/2?DogsOnly=true
Modelbinding doorloopt de volgende stappen nadat het routeringssysteem de actiemethode heeft geselecteerd:
- Hiermee zoekt u de eerste parameter van
GetById, een geheel getal met de naamid. - Bekijkt de beschikbare bronnen in de HTTP-aanvraag en vindt
id= '2' in routegegevens. - Converteert de tekenreeks '2' naar geheel getal 2.
- Hiermee zoekt u de volgende parameter van
GetById, een Booleaanse parameter genaamddogsOnly. - Bekijkt de bronnen en zoekt 'DogsOnly=true' in de querytekenreeks. Naamkoppeling is niet hoofdlettergevoelig.
- Converteert de tekenreeks "true" naar de booleaanse waarde
true.
Het framework roept vervolgens de GetById methode aan, waarbij 2 wordt doorgegeven voor de id parameter en true voor de dogsOnly parameter.
In het voorgaande voorbeeld zijn de modelbindingsdoelen methodeparameters die eenvoudige typen zijn. Doelen kunnen ook de eigenschappen van een complex type zijn. Nadat elke eigenschap is gebonden, vindt modelvalidatie plaats voor die eigenschap. De record van welke gegevens zijn gebonden aan het model, en eventuele bindings- of validatiefouten, worden opgeslagen in ControllerBase.ModelState of PageModel.ModelState. Om erachter te komen of dit proces is geslaagd, controleert de app de vlag ModelState.IsValid .
Targets
Modelbinding probeert waarden te vinden voor de volgende soorten doelen:
- Parameters van de actiemethode controller waarnaar een aanvraag wordt doorgestuurd.
- Parameters van de Razor handlermethode Pages waarnaar een aanvraag wordt doorgestuurd.
- Openbare eigenschappen van een controller of
PageModelklasse, indien opgegeven door kenmerken.
Kenmerk [BindProperty]
Kan worden toegepast op een openbare eigenschap van een controller of PageModel klasse om modelbinding te veroorzaken voor de doeleigenschap:
public class EditModel : PageModel
{
[BindProperty]
public Instructor? Instructor { get; set; }
// ...
}
[BindProperties] kenmerk
Kan worden toegepast op een controller of de PageModel klasse om aan te geven dat modelbinding zich moet richten op alle openbare eigenschappen van de klasse.
[BindProperties]
public class CreateModel : PageModel
{
public Instructor? Instructor { get; set; }
// ...
}
Modelbinding voor HTTP GET-aanvragen
Eigenschappen zijn standaard niet gebonden voor HTTP GET-aanvragen. Normaal gesproken is alles wat u nodig hebt voor een GET-aanvraag een record-id-parameter. De record-id wordt gebruikt om het item in de database op te zoeken. Daarom is het niet nodig om een eigenschap te binden die een exemplaar van het model bevat. In scenario's waarin u wilt dat eigenschappen zijn gebonden aan gegevens uit GET-aanvragen, stelt u de SupportsGet eigenschap truein op:
[BindProperty(Name = "ai_user", SupportsGet = true)]
public string? ApplicationInsightsCookie { get; set; }
Eenvoudige en complexe typen modelbinding
Modelbinding maakt gebruik van specifieke definities voor de typen waarop deze werkt. Een eenvoudig type wordt geconverteerd van één tekenreeks met behulp van TypeConverter of een TryParse methode. Een complex type wordt geconverteerd van meerdere invoerwaarden. Het framework bepaalt het verschil op basis van het bestaan van een TypeConverter of TryParse. Wij raden aan om een typeconverter te maken of het gebruik van TryParse voor een string naar SomeType conversie waarvoor geen externe middelen of meerdere invoer nodig zijn.
Sources
Modelbinding haalt standaard gegevens op in de vorm van sleutel-waardeparen uit de volgende bronnen in een HTTP-aanvraag:
- Formuliervelden
- De aanvraagbody (voor controllers die het kenmerk [ApiController] hebben.)
- Routegegevens
- Parameters voor zoekreeks
- Geüploade bestanden
Voor elke doelparameter of eigenschap worden de bronnen gescand in de volgorde die wordt aangegeven in de voorgaande lijst. Er zijn enkele uitzonderingen:
- Routegegevens en queryreekswaarden worden alleen gebruikt voor eenvoudige typen.
- Geüploade bestanden zijn alleen gebonden aan doeltypen die implementeren
IFormFileofIEnumerable<IFormFile>.
Als de standaardbron niet juist is, gebruikt u een van de volgende kenmerken om de bron op te geven:
-
[FromQuery]- Haalt waarden op uit de querytekenreeks. -
[FromRoute]- Haalt waarden op uit routegegevens. -
[FromForm]- Haalt waarden op uit geplaatste formuliervelden. -
[FromBody]- Haalt waarden op uit de hoofdtekst van de aanvraag. -
[FromHeader]- Haalt waarden op uit HTTP-headers.
Deze kenmerken:
Worden afzonderlijk toegevoegd aan modeleigenschappen en niet aan de modelklasse, zoals in het volgende voorbeeld:
public class Instructor { public int Id { get; set; } [FromQuery(Name = "Note")] public string? NoteFromQueryString { get; set; } // ... }Accepteer eventueel een modelnaamwaarde in de constructor. Deze optie wordt opgegeven als de eigenschapsnaam niet overeenkomt met de waarde in de aanvraag. De waarde in de aanvraag kan bijvoorbeeld een header zijn met een afbreekstreepje in de naam, zoals in het volgende voorbeeld:
public void OnGet([FromHeader(Name = "Accept-Language")] string language)
Kenmerk [FromBody]
Pas het [FromBody] kenmerk toe op een parameter om de eigenschappen ervan te vullen vanuit de hoofdtekst van een HTTP-aanvraag. De ASP.NET Core-runtime delegeert de verantwoordelijkheid voor het lezen van de body naar een invoerformatter. Invoerindelingen worden verderop in dit artikel uitgelegd.
Wanneer [FromBody] deze wordt toegepast op een complexe typeparameter, worden alle bindingsbronkenmerken die op de eigenschappen ervan worden toegepast, genegeerd. Met de volgende Create actie wordt bijvoorbeeld aangegeven dat de parameter van pet uit de body wordt ingevuld:
public ActionResult<Pet> Create([FromBody] Pet pet)
De Pet klasse geeft aan dat de Breed eigenschap wordt ingevuld vanuit een queryreeksparameter:
public class Pet
{
public string Name { get; set; } = null!;
[FromQuery] // Attribute is ignored.
public string Breed { get; set; } = null!;
}
In het voorgaande voorbeeld:
- Het
[FromQuery]kenmerk wordt genegeerd. - De
Breedeigenschap wordt niet ingevuld vanuit een queryreeksparameter.
Invoerindelingen lezen alleen de hoofdtekst en begrijpen geen bindingsbronkenmerken. Als een geschikte waarde wordt gevonden in de hoofdtekst, wordt die waarde gebruikt om de Breed eigenschap te vullen.
Pas [FromBody] niet toe op meer dan één parameter per actiemethode. Zodra de verzoekstroom is gelezen door een invoerformatter, is deze niet meer beschikbaar om opnieuw te worden gelezen voor het binden van andere [FromBody] parameters.
Aanvullende bronnen
Brongegevens worden door waardeproviders verstrekt aan het modelbindingssysteem. U kunt aangepaste waardeproviders schrijven en registreren die gegevens ophalen voor modelbinding uit andere bronnen. U wilt bijvoorbeeld gegevens uit cookies of sessiestatus. Ga als volgt te werk om gegevens op te halen uit een nieuwe bron:
- Maak een klasse die
IValueProviderimplementeert. - Maak een klasse die
IValueProviderFactoryimplementeert. - Registreer de fabrieksklasse in
Program.cs.
Het voorbeeld bevat een waardeprovider en factory voorbeeld dat waarden ophaalt uit cookies. Registreer aangepaste waardeprovider-fabrieken in Program.cs:
builder.Services.AddControllers(options =>
{
options.ValueProviderFactories.Add(new CookieValueProviderFactory());
});
De voorgaande code plaatst de provider van aangepaste waarden na alle ingebouwde waardeproviders. Om het de eerste in de lijst te maken, gebruik Insert(0, new CookieValueProviderFactory()) in plaats van Add.
Geen bron voor een modeleigenschap
Standaard wordt er geen modelstatusfout gemaakt als er geen waarde wordt gevonden voor een modeleigenschap. De eigenschap is ingesteld op null of een standaardwaarde:
- Nullable simple types zijn ingesteld op
null. - Niet-nullwaardetypen zijn ingesteld op
default(T). Een parameterint idis bijvoorbeeld ingesteld op 0. - Voor complexe typen maakt modelbinding een exemplaar met behulp van de standaardconstructor, zonder eigenschappen in te stellen.
- Matrices zijn ingesteld op
Array.Empty<T>(), behalve datbyte[]matrices zijn ingesteld opnull.
Als de modelstatus ongeldig moet worden gemaakt wanneer er niets wordt gevonden in formuliervelden voor een modeleigenschap, gebruikt u het [BindRequired] kenmerk.
Houd er rekening mee dat dit [BindRequired] gedrag van toepassing is op modelbinding van geposte formuliergegevens, niet op JSON- of XML-gegevens in een aanvraagbody. Hoofdtekstgegevens van de aanvraag worden verwerkt door invoerformatters.
Typeconversiefouten
Als een bron wordt gevonden maar niet kan worden geconverteerd naar het doeltype, wordt de modelstatus gemarkeerd als ongeldig. De doelparameter of -eigenschap is ingesteld op null of een standaardwaarde, zoals vermeld in de vorige sectie.
In een API-controller met het [ApiController] kenmerk resulteert een ongeldige modelstatus in een automatisch HTTP 400-antwoord.
Op een Razor pagina wordt de pagina opnieuw weergegeven met een foutbericht:
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
// ...
return RedirectToPage("./Index");
}
Wanneer de pagina opnieuw wordt weergegeven door de voorgaande code, wordt de ongeldige invoer niet weergegeven in het formulierveld. Dit komt doordat de modeleigenschap is ingesteld op null of een standaardwaarde. De ongeldige invoer wordt weergegeven in een foutbericht. Als u de slechte gegevens in het formulierveld opnieuw wilt weergeven, kunt u overwegen om de modeleigenschap een tekenreeks te maken en de gegevensconversie handmatig uit te voeren.
Dezelfde strategie wordt aanbevolen als u niet wilt dat typeconversiefouten resulteren in modelstatusfouten. In dat geval moet u de modeleigenschap een tekenreeks maken.
Eenvoudige typen
Zie Modelbinding eenvoudige en complexe typen voor uitleg over eenvoudige en complexe typen.
De eenvoudige typen waarmee de modelbindinger brontekenreeksen kan converteren, zijn onder andere:
- Boolean
- Byte, SByte
- Char
- DateOnly
- DateTime
- DateTimeOffset
- Decimal
- Double
- Enum
- Guid
- Int16, Int32, Int64
- Single
- TimeOnly
- TimeSpan
- UInt16, UInt32, UInt64
- Uri
- Version
Binden met IParsable<T>.TryParse
De IParsable<TSelf>.TryParse API ondersteunt parameterwaarden voor bindingscontrolleracties:
public static bool TryParse (string? s, IFormatProvider? provider, out TSelf result);
De volgende DateRange klasse implementeert IParsable<TSelf> ter ondersteuning van het binden van een datumbereik:
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;
}
}
De voorgaande code:
- Converteert een tekenreeks die twee datums vertegenwoordigt naar een
DateRangeobject - De modelbinder gebruikt de
IParsable<TSelf>.TryParsemethode om deDateRangete binden.
De volgende controlleractie maakt gebruik van de DateRange klasse om een datumbereik te binden:
// 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);
}
De volgende Locale klasse implementeert IParsable<TSelf> ter ondersteuning van binding met 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;
}
}
}
De volgende controlleractie maakt gebruik van de Locale klasse om een CultureInfo tekenreeks te binden:
// 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);
}
De volgende controlleractie gebruikt de DateRange en Locale klassen om een datumbereik te binden met 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);
}
In de API-voorbeeld-app op GitHub ziet u het voorgaande voorbeeld voor een API-controller.
Binden met TryParse
De TryParse API ondersteunt parameterwaarden voor bindingscontrolleracties:
public static bool TryParse(string value, T out result);
public static bool TryParse(string value, IFormatProvider provider, T out result);
IParsable<T>.TryParse is de aanbevolen methode voor parameterbinding omdat het, in tegenstelling tot TryParse, niet afhankelijk is van reflectie.
De volgende DateRangeTP klasse implementeert 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;
}
}
De volgende controlleractie maakt gebruik van de DateRangeTP klasse om een datumbereik te binden:
// 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);
}
Complexe typen
Een complex type moet een openbare standaardconstructor en openbare schrijfbare eigenschappen hebben om te binden. Wanneer modelbinding plaatsvindt, wordt de klasse geïnstantieerd met behulp van de openbare standaardconstructor.
Voor elke eigenschap van het complexe type doorzoekt modelbinding de bronnen naar het naampatroonprefix.property_name. Als er niets wordt gevonden, wordt gezocht naar property_name zonder het voorvoegsel. De beslissing om het voorvoegsel te gebruiken wordt niet per eigenschap gemaakt. Bijvoorbeeld, met een query die ?Instructor.Id=100&Name=foo bevat en gebonden is aan de methode OnGet(Instructor instructor), bevat het resulterende object van het type Instructor:
-
Idingesteld op100. -
Nameingesteld opnull. Model binding verwachtInstructor.NameomdatInstructor.Idwerd gebruikt in de voorgaande queryparameter.
Note
Documentatiekoppelingen naar .NET-referentiebron laden meestal de standaardbranch van de opslagplaats, die de huidige ontwikkeling vertegenwoordigt voor de volgende release van .NET. Als u een tag voor een specifieke release wilt selecteren, gebruikt u de Switch branches of tags vervolgkeuzelijst. Zie Een versietag selecteren van ASP.NET Core-broncode (dotnet/AspNetCore.Docs #26205)voor meer informatie.
Voor binding met een parameter is het voorvoegsel de parameternaam. Voor binding met een PageModel openbare eigenschap is het voorvoegsel de naam van de openbare eigenschap. Sommige kenmerken hebben een Prefix eigenschap waarmee u het standaardgebruik van de parameter of eigenschapsnaam kunt overschrijven.
Stel dat het complexe type de volgende Instructor klasse is:
public class Instructor
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
Voorvoegsel = parameternaam
Als het model moet worden gebonden aan een parameter met de naam instructorToUpdate:
public IActionResult OnPost(int? id, Instructor instructorToUpdate)
Modelbinding begint door de bronnen voor de sleutel instructorToUpdate.IDte bekijken. Als dat niet wordt gevonden, wordt gezocht naar ID zonder voorvoegsel.
Voorvoegsel = eigenschapsnaam
Als het model dat moet worden gebonden, een eigenschap is met de naam Instructor van de controller of PageModel klasse:
[BindProperty]
public Instructor Instructor { get; set; }
Modelbinding begint door de bronnen voor de sleutel Instructor.IDte bekijken. Als dat niet wordt gevonden, wordt gezocht naar ID zonder voorvoegsel.
Aangepast voorvoegsel
Als het model dat moet worden gebonden een parameter met de naam instructorToUpdate is en een kenmerk Bind het voorvoegsel Instructor opgeeft.
public IActionResult OnPost(
int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)
Modelbinding begint door de bronnen voor de sleutel Instructor.IDte bekijken. Als dat niet wordt gevonden, wordt gezocht naar ID zonder voorvoegsel.
Kenmerken voor complexe typedoelen
Er zijn verschillende ingebouwde kenmerken beschikbaar voor het beheren van modelbindingen van complexe typen:
Warning
Deze kenmerken zijn van invloed op modelbinding wanneer geplaatste formuliergegevens de bron van waarden zijn. Ze hebben geen invloed op invoerindelingen, die JSON- en XML-aanvraagteksten verwerken. Invoerindelingen worden verderop in dit artikel uitgelegd.
Kenmerk [Bind]
Kan worden toegepast op een klasse- of methodeparameter. Hiermee geeft u op welke eigenschappen van een model moeten worden opgenomen in modelbinding.
[Bind] heeft geen invloed op invoerindelingen.
In het volgende voorbeeld zijn alleen de opgegeven eigenschappen van het Instructor model afhankelijk wanneer een handler of actiemethode wordt aangeroepen:
[Bind("LastName,FirstMidName,HireDate")]
public class Instructor
In het volgende voorbeeld zijn alleen de opgegeven eigenschappen van het Instructor model afhankelijk wanneer de OnPost methode wordt aangeroepen:
[HttpPost]
public IActionResult OnPost(
[Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
Het [Bind] kenmerk kan worden gebruikt om te beschermen tegen overposting in create-scenario's. Het werkt niet goed in bewerkingsscenario's omdat uitgesloten eigenschappen zijn ingesteld op null of een standaardwaarde in plaats van ongewijzigd te laten. Voor bescherming tegen overposting worden weergavemodellen aanbevolen in plaats van het [Bind] kenmerk. Zie Beveiligingsnotitie over overposting voor meer informatie.
[ModelBinder] kenmerk
ModelBinderAttribute kan worden toegepast op typen, eigenschappen of parameters. Hiermee kunt u het type modelbinding opgeven dat wordt gebruikt om het specifieke exemplaar of type te binden. Voorbeeld:
[HttpPost]
public IActionResult OnPost(
[ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)
Het [ModelBinder] kenmerk kan ook worden gebruikt om de naam van een eigenschap of parameter te wijzigen wanneer het model afhankelijk is:
public class Instructor
{
[ModelBinder(Name = "instructor_id")]
public string Id { get; set; }
// ...
}
Kenmerk [BindRequired]
Zorgt ervoor dat modelbinding een fout in de modelstatus toevoegt indien de binding niet kan plaatsvinden voor een eigenschap van een model. Hier is een voorbeeld:
public class InstructorBindRequired
{
// ...
[BindRequired]
public DateTime HireDate { get; set; }
}
Zie ook de discussie over het [Required] kenmerk in modelvalidatie.
Kenmerk [BindNever]
Kan worden toegepast op een eigenschap of een type. Hiermee voorkomt u dat modelbinding de eigenschap van een model instelt. Wanneer het modelbindingssysteem wordt toegepast op een type, worden alle eigenschappen uitgesloten die door het type worden gedefinieerd. Hier is een voorbeeld:
public class InstructorBindNever
{
[BindNever]
public int Id { get; set; }
// ...
}
Collections
Voor doelen die verzamelingen eenvoudige typen zijn, zoekt modelbinding naar overeenkomsten voor parameter_name of property_name. Als er geen overeenkomst wordt gevonden, wordt gezocht naar een van de ondersteunde indelingen zonder het voorvoegsel. Voorbeeld:
Stel dat de parameter die moet worden gebonden een matrix is met de naam
selectedCourses:public IActionResult OnPost(int? id, int[] selectedCourses)Formulier- of querytekenreeksgegevens kunnen een van de volgende indelingen hebben:
selectedCourses=1050&selectedCourses=2000selectedCourses[0]=1050&selectedCourses[1]=2000[0]=1050&[1]=2000selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b[a]=1050&[b]=2000&index=a&index=bVermijd het binden van een parameter of eigenschap met de naam
indexofIndexals deze grenzen aan een verzamelingswaarde. Modelbinding probeert omindexals de index te gebruiken voor de verzameling, wat kan leiden tot onjuiste binding. Denk bijvoorbeeld aan de volgende actie:public IActionResult Post(string index, List<Product> products)In de voorgaande code wordt de
indexqueryreeksparameter gekoppeld aan deindexmethodeparameter en wordt ook gebruikt om de productverzameling te binden. Als u de naam van deindexparameter wijzigt of een modelbindingskenmerk gebruikt om binding te configureren, voorkomt u dit probleem:public IActionResult Post(string productIndex, List<Product> products)De volgende indeling wordt alleen ondersteund in formuliergegevens:
selectedCourses[]=1050&selectedCourses[]=2000Voor alle voorgaande voorbeeldindelingen geeft modelbinding een matrix van twee items door aan de
selectedCoursesparameter:- selectedCourses[0]=1050
- selectedCourses[1]=2000
Gegevensindelingen die gebruikmaken van subscriptnummers (... [0] ... [1] ...) moet ervoor zorgen dat ze opeenvolgend beginnen bij nul. Als er hiaten zijn in subscriptnummering, worden alle items na de tussenruimte genegeerd. Als de subscripts bijvoorbeeld 0 en 2 zijn in plaats van 0 en 1, wordt het tweede item genegeerd.
Dictionaries
Voor Dictionary doelen zoekt modelbinding naar overeenkomsten voor parameter_name of property_name. Als er geen overeenkomst wordt gevonden, wordt gezocht naar een van de ondersteunde indelingen zonder het voorvoegsel. Voorbeeld:
Stel dat de doelparameter een
Dictionary<int, string>naamselectedCoursesheeft:public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)De geplaatste formulier- of querytekenreeksgegevens kunnen eruitzien als een van de volgende voorbeelden:
selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics[1050]=Chemistry&selectedCourses[2000]=EconomicsselectedCourses[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=EconomicsVoor alle voorgaande voorbeeldindelingen geeft modelbinding een woordenlijst van twee items door aan de
selectedCoursesparameter:- selectedCourses["1050"]="Chemistry"
- selectedCourses["2000"]="Economics"
Constructor-binding en recordtypen
Voor modelbinding is vereist dat complexe typen een parameterloze constructor hebben. Zowel System.Text.Json als Newtonsoft.Json invoerindelingen ondersteunen deserialisatie van klassen die geen parameterloze constructor hebben.
Recordtypen zijn een uitstekende manier om gegevens via het netwerk beknopt weer te geven. ASP.NET Core ondersteunt modelbinding en het valideren van recordtypen met één constructor:
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>
Bij het valideren van recordtypen zoekt de runtime specifiek naar bindings- en validatiemetagegevens op parameters in plaats van op eigenschappen.
Het framework maakt het mogelijk om recordtypen te binden en te valideren:
public record Person([Required] string Name, [Range(0, 100)] int Age);
Voordat het voorgaande werkt, moet het type:
- Wees een recordtype.
- Zorg ervoor dat er precies één publieke constructor is.
- Parameters bevatten die een eigenschap met dezelfde naam en hetzelfde type hebben. De namen mogen niet per geval verschillen.
POCOs zonder parameterloze constructors
POCOs die geen parameterloze constructors hebben, kunnen niet worden gebonden.
De volgende code resulteert in een uitzondering die aangeeft dat het type een parameterloze constructor moet hebben:
public class Person(string Name)
public record Person([Required] string Name, [Range(0, 100)] int Age)
{
public Person(string Name) : this (Name, 0);
}
Recordtypen met handmatig geschreven constructors
Recordtypen met handmatig geschreven constructors die eruitzien als primaire constructors werken
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; }
}
Recordtypen, validatie en bindingmetagegevens
Voor recordtypen wordt validatie en bindingsmetagegevens op parameters gebruikt. Metagegevens over eigenschappen worden genegeerd
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; }
}
Validatie en metagegevens
Validatie maakt gebruik van metagegevens voor de parameter, maar gebruikt de eigenschap om de waarde te lezen. In het normale geval met primaire constructors zouden de twee identiek zijn. Er zijn echter manieren om het te verslaan:
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 werkt geen parameters bij voor een recordtype
public record Person(string Name)
{
public int Age { get; set; }
}
var person = new Person("initial-name");
TryUpdateModel(person, ...);
In dit geval probeert MVC niet opnieuw te binden Name . Het is echter toegestaan om Age bij te werken.
Globalisatiegedrag van modelbindingsroutegegevens en queryreeksen
De routewaardeprovider en querywaardeprovider van ASP.NET Core:
- Waarden behandelen als invariante cultuur.
- Verwacht dat URL's cultuur-invariant zijn.
Waarden die afkomstig zijn van formuliergegevens, ondergaan daarentegen een cultuurgevoelige conversie. Dit is standaard zo dat URL's kunnen worden gedeeld tussen landinstellingen.
De ASP.NET Core route value provider en query string value provider moeten een cultuurafhankelijke conversie ondergaan.
- Overnemen van IValueProviderFactory
- Kopieer de code uit QueryStringValueProviderFactory of RouteValueValueProviderFactory
- Vervang de cultuurwaarde die is doorgegeven aan de constructor van de waardeprovider door CultureInfo.CurrentCulture
- Vervang de standaardwaardeproviderfabriek in de MVC-opties door uw nieuwe.
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();
});
Speciale gegevenstypen
Er zijn enkele speciale gegevenstypen die modelbinding kunnen verwerken.
IFormFile en IFormFileCollection
Een geüpload bestand dat is opgenomen in de HTTP-aanvraag. Ook ondersteund is IEnumerable<IFormFile> voor meerdere bestanden.
CancellationToken
Acties kunnen desgewenst een CancellationToken als parameter binden. Dit koppelt RequestAborted dat signalen afgeeft wanneer de achterliggende verbinding van het HTTP-verzoek wordt beëindigd. Acties kunnen deze parameter gebruiken om langlopende asynchrone bewerkingen te annuleren die worden uitgevoerd als onderdeel van de controlleracties.
FormCollection
Wordt gebruikt om alle waarden op te halen uit geposte formuliergegevens.
Invoerindelingen
Gegevens in de aanvraagbody kunnen in JSON, XML of een andere indeling zijn. Voor het parseren van deze gegevens maakt modelbinding gebruik van een invoerindeling die is geconfigureerd voor het afhandelen van een bepaald inhoudstype. Standaard bevat ASP.NET Core invoerindelingen op basis van JSON voor het verwerken van JSON-gegevens. U kunt andere formatters toevoegen voor andere inhoudstypen.
ASP.NET Core selecteert invoerindelingen op basis van het kenmerk Verbruiken . Als er geen kenmerk aanwezig is, wordt de header Content-Type gebruikt.
Om de ingebouwde XML-invoerindelingen te gebruiken:
In
Program.cs, bel AddXmlSerializerFormatters of AddXmlDataContractSerializerFormatters.builder.Services.AddControllers() .AddXmlSerializerFormatters();Pas het
Consumeskenmerk toe op controllerklassen of actiemethoden die XML in de hoofdtekst van de aanvraag moeten verwachten.[HttpPost] [Consumes("application/xml")] public ActionResult<Pet> Create(Pet pet)Zie Inleiding tot XML-serialisatie voor meer informatie.
Modelbinding aanpassen met inputformatters
Een invoeropmaaker neemt volledige verantwoordelijkheid voor het lezen van gegevens uit de aanvraagbody. Als u dit proces wilt aanpassen, configureert u de API's die worden gebruikt door de input formatter. In deze sectie wordt beschreven hoe u de op System.Text.Json-gebaseerde invoerindeling aanpast om een aangepast type met de naam ObjectId te kunnen interpreteren.
Overweeg het volgende model, dat een aangepaste ObjectId eigenschap bevat:
public class InstructorObjectId
{
[Required]
public ObjectId ObjectId { get; set; } = null!;
}
Als u het modelbindingsproces wilt aanpassen wanneer u dit gebruikt System.Text.Json, maakt u een klasse die is afgeleid van 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);
}
Als u een aangepast conversieprogramma wilt gebruiken, past u het JsonConverterAttribute kenmerk toe op het type. In het volgende voorbeeld wordt het ObjectId-type geconfigureerd met ObjectIdConverter als zijn aangepaste converter:
[JsonConverter(typeof(ObjectIdConverter))]
public record ObjectId(int Id);
Zie Aangepaste conversieprogramma's schrijven voor meer informatie.
Opgegeven typen uitsluiten van modelbinding
Het gedrag van modelbindingen en validatiesystemen wordt aangestuurd door ModelMetadata. U kunt dit aanpassen ModelMetadata door een gegevensprovider toe te voegen aan MvcOptions.ModelMetadataDetailsProviders. Ingebouwde gegevensproviders zijn beschikbaar voor het uitschakelen van modelbinding of validatie voor opgegeven typen.
Als u modelbinding wilt uitschakelen voor alle modellen van een opgegeven type, voegt u een ExcludeBindingMetadataProvider in Program.cs. Als u bijvoorbeeld modelbinding wilt uitschakelen voor alle modellen van het type System.Version:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
Als u validatie wilt uitschakelen voor eigenschappen van een opgegeven type, voegt u een SuppressChildValidationMetadataProvider in Program.cs. Als u bijvoorbeeld validatie wilt uitschakelen voor eigenschappen van het type System.Guid:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
Aangepaste modelbindingen
U kunt het binden van modellen uitbreiden door een eigen modelbinder te schrijven en dan het kenmerk te gebruiken om deze [ModelBinder] te selecteren voor een gegeven doelwit. Meer informatie over aangepaste modelbinding.
Handmatige modelbinding
Modelbinding kan handmatig worden aangeroepen met behulp van de TryUpdateModelAsync methode. De methode is gedefinieerd op zowel ControllerBase als PageModel klassen. Met overbelasting van methoden kunt u het te gebruiken voorvoegsel en de waardeprovider opgeven. De methode retourneert false als modelbinding mislukt. Hier is een voorbeeld:
if (await TryUpdateModelAsync(
newInstructor,
"Instructor",
x => x.Name, x => x.HireDate!))
{
_instructorStore.Add(newInstructor);
return RedirectToPage("./Index");
}
return Page();
TryUpdateModelAsync maakt gebruik van waardeproviders om gegevens op te halen uit de hoofdtekst van het formulier, queryreeks en routegegevens.
TryUpdateModelAsync is doorgaans:
- Wordt gebruikt met Razor Pagina's en MVC-apps waarbij controllers en weergaven worden gebruikt om over-posting te voorkomen.
- Niet gebruikt met een web-API, tenzij het wordt benut vanuit formuliergegevens, queryreeksen en routegegevens. Web-API-eindpunten die JSON gebruiken, gebruiken input formatters om de hoofdtekst van de aanvraag in een object te deserialiseren.
Zie TryUpdateModelAsync voor meer informatie.
Kenmerk [FromServices]
De naam van dit kenmerk volgt het patroon van modelbindingskenmerken die een gegevensbron opgeven. Maar het gaat niet om bindingsgegevens van een waardeprovider. Er wordt een instance van een type opgehaald uit de container voor afhankelijkheidsinjectie. Het doel is om een alternatief voor constructorinjectie te bieden wanneer u een service alleen nodig hebt als een bepaalde methode wordt aangeroepen.
Als een exemplaar van het type niet is geregistreerd in de container voor afhankelijkheidsinjectie, genereert de app een uitzondering bij het verbinden van de parameter. Als u de parameter optioneel wilt maken, gebruikt u een van de volgende methoden:
- Maak de parameter nullable.
- Stel een standaardwaarde in voor de parameter.
Voor null-parameters moet u ervoor zorgen dat de parameter niet null is voordat u deze opent.
Aanvullende bronnen
In dit artikel wordt uitgelegd wat modelbinding is, hoe het werkt en hoe het gedrag ervan kan worden aangepast.
Wat is modelbinding?
Controllers en Razor pagina's werken met gegevens die afkomstig zijn van HTTP-aanvragen. Routegegevens kunnen bijvoorbeeld een recordsleutel bevatten en geplaatste formuliervelden kunnen waarden bieden voor de eigenschappen van het model. Het schrijven van code om elk van deze waarden op te halen en deze te converteren van tekenreeksen naar .NET-typen zou tijdrovend en foutgevoelig zijn. Modelbinding automatiseert dit proces. Het modelbindingssysteem:
- Hiermee worden gegevens opgehaald uit verschillende bronnen, zoals routegegevens, formuliervelden en queryreeksen.
- Stelt de gegevens beschikbaar aan controllers en Razor pagina's in methodeparameters en openbare eigenschappen.
- Converteert tekenreeksgegevens naar .NET-typen.
- Hiermee worden eigenschappen van complexe typen bijgewerkt.
Example
Stel dat u de volgende actiemethode hebt:
[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)
En de app ontvangt een aanvraag met deze URL:
https://contoso.com/api/pets/2?DogsOnly=true
Modelbinding doorloopt de volgende stappen nadat het routeringssysteem de actiemethode heeft geselecteerd:
- Hiermee zoekt u de eerste parameter van
GetById, een geheel getal met de naamid. - Bekijkt de beschikbare bronnen in de HTTP-aanvraag en vindt
id= '2' in routegegevens. - Converteert de tekenreeks '2' naar geheel getal 2.
- Hiermee zoekt u de volgende parameter van
GetById, een Booleaanse parameter genaamddogsOnly. - Bekijkt de bronnen en zoekt 'DogsOnly=true' in de querytekenreeks. Naamkoppeling is niet hoofdlettergevoelig.
- Converteert de tekenreeks "true" naar de booleaanse waarde
true.
Het framework roept vervolgens de GetById methode aan, waarbij 2 wordt doorgegeven voor de id parameter en true voor de dogsOnly parameter.
In het voorgaande voorbeeld zijn de modelbindingsdoelen methodeparameters die eenvoudige typen zijn. Doelen kunnen ook de eigenschappen van een complex type zijn. Nadat elke eigenschap is gebonden, vindt modelvalidatie plaats voor die eigenschap. De record van welke gegevens zijn gebonden aan het model, en eventuele bindings- of validatiefouten, worden opgeslagen in ControllerBase.ModelState of PageModel.ModelState. Om erachter te komen of dit proces is geslaagd, controleert de app de vlag ModelState.IsValid .
Targets
Modelbinding probeert waarden te vinden voor de volgende soorten doelen:
- Parameters van de actiemethode controller waarnaar een aanvraag wordt doorgestuurd.
- Parameters van de Razor handlermethode Pages waarnaar een aanvraag wordt doorgestuurd.
- Openbare eigenschappen van een controller of
PageModelklasse, indien opgegeven door kenmerken.
Kenmerk [BindProperty]
Kan worden toegepast op een openbare eigenschap van een controller of PageModel klasse om modelbinding te veroorzaken voor de doeleigenschap:
public class EditModel : PageModel
{
[BindProperty]
public Instructor? Instructor { get; set; }
// ...
}
[BindProperties] kenmerk
Kan worden toegepast op een controller of de PageModel klasse om aan te geven dat modelbinding zich moet richten op alle openbare eigenschappen van de klasse.
[BindProperties]
public class CreateModel : PageModel
{
public Instructor? Instructor { get; set; }
// ...
}
Modelbinding voor HTTP GET-aanvragen
Eigenschappen zijn standaard niet gebonden voor HTTP GET-aanvragen. Normaal gesproken is alles wat u nodig hebt voor een GET-aanvraag een record-id-parameter. De record-id wordt gebruikt om het item in de database op te zoeken. Daarom is het niet nodig om een eigenschap te binden die een exemplaar van het model bevat. In scenario's waarin u wilt dat eigenschappen zijn gebonden aan gegevens uit GET-aanvragen, stelt u de SupportsGet eigenschap truein op:
[BindProperty(Name = "ai_user", SupportsGet = true)]
public string? ApplicationInsightsCookie { get; set; }
Sources
Modelbinding haalt standaard gegevens op in de vorm van sleutel-waardeparen uit de volgende bronnen in een HTTP-aanvraag:
- Formuliervelden
- De aanvraagbody (voor controllers die het kenmerk [ApiController] hebben.)
- Routegegevens
- Parameters voor zoekreeks
- Geüploade bestanden
Voor elke doelparameter of eigenschap worden de bronnen gescand in de volgorde die wordt aangegeven in de voorgaande lijst. Er zijn enkele uitzonderingen:
- Routegegevens en queryreekswaarden worden alleen gebruikt voor eenvoudige typen.
- Geüploade bestanden zijn alleen gebonden aan doeltypen die implementeren
IFormFileofIEnumerable<IFormFile>.
Als de standaardbron niet juist is, gebruikt u een van de volgende kenmerken om de bron op te geven:
-
[FromQuery]- Haalt waarden op uit de querytekenreeks. -
[FromRoute]- Haalt waarden op uit routegegevens. -
[FromForm]- Haalt waarden op uit geplaatste formuliervelden. -
[FromBody]- Haalt waarden op uit de hoofdtekst van de aanvraag. -
[FromHeader]- Haalt waarden op uit HTTP-headers.
Deze kenmerken:
Worden afzonderlijk toegevoegd aan modeleigenschappen en niet aan de modelklasse, zoals in het volgende voorbeeld:
public class Instructor { public int Id { get; set; } [FromQuery(Name = "Note")] public string? NoteFromQueryString { get; set; } // ... }Accepteer eventueel een modelnaamwaarde in de constructor. Deze optie wordt opgegeven als de eigenschapsnaam niet overeenkomt met de waarde in de aanvraag. De waarde in de aanvraag kan bijvoorbeeld een header zijn met een afbreekstreepje in de naam, zoals in het volgende voorbeeld:
public void OnGet([FromHeader(Name = "Accept-Language")] string language)
Kenmerk [FromBody]
Pas het [FromBody] kenmerk toe op een parameter om de eigenschappen ervan te vullen vanuit de hoofdtekst van een HTTP-aanvraag. De ASP.NET Core-runtime delegeert de verantwoordelijkheid voor het lezen van de body naar een invoerformatter. Invoerindelingen worden verderop in dit artikel uitgelegd.
Wanneer [FromBody] deze wordt toegepast op een complexe typeparameter, worden alle bindingsbronkenmerken die op de eigenschappen ervan worden toegepast, genegeerd. Met de volgende Create actie wordt bijvoorbeeld aangegeven dat de parameter van pet uit de body wordt ingevuld:
public ActionResult<Pet> Create([FromBody] Pet pet)
De Pet klasse geeft aan dat de Breed eigenschap wordt ingevuld vanuit een queryreeksparameter:
public class Pet
{
public string Name { get; set; } = null!;
[FromQuery] // Attribute is ignored.
public string Breed { get; set; } = null!;
}
In het voorgaande voorbeeld:
- Het
[FromQuery]kenmerk wordt genegeerd. - De
Breedeigenschap wordt niet ingevuld vanuit een queryreeksparameter.
Invoerindelingen lezen alleen de hoofdtekst en begrijpen geen bindingsbronkenmerken. Als een geschikte waarde wordt gevonden in de hoofdtekst, wordt die waarde gebruikt om de Breed eigenschap te vullen.
Pas [FromBody] niet toe op meer dan één parameter per actiemethode. Zodra de verzoekstroom is gelezen door een invoerformatter, is deze niet meer beschikbaar om opnieuw te worden gelezen voor het binden van andere [FromBody] parameters.
Aanvullende bronnen
Brongegevens worden door waardeproviders verstrekt aan het modelbindingssysteem. U kunt aangepaste waardeproviders schrijven en registreren die gegevens ophalen voor modelbinding uit andere bronnen. U wilt bijvoorbeeld gegevens uit cookies of sessiestatus. Ga als volgt te werk om gegevens op te halen uit een nieuwe bron:
- Maak een klasse die
IValueProviderimplementeert. - Maak een klasse die
IValueProviderFactoryimplementeert. - Registreer de fabrieksklasse in
Program.cs.
Het voorbeeld bevat een waardeprovider en factory voorbeeld dat waarden ophaalt uit cookies. Registreer aangepaste waardeprovider-fabrieken in Program.cs:
builder.Services.AddControllers(options =>
{
options.ValueProviderFactories.Add(new CookieValueProviderFactory());
});
De voorgaande code plaatst de provider van aangepaste waarden na alle ingebouwde waardeproviders. Om het de eerste in de lijst te maken, gebruik Insert(0, new CookieValueProviderFactory()) in plaats van Add.
Geen bron voor een modeleigenschap
Standaard wordt er geen modelstatusfout gemaakt als er geen waarde wordt gevonden voor een modeleigenschap. De eigenschap is ingesteld op null of een standaardwaarde:
- Nullable simple types zijn ingesteld op
null. - Niet-nullwaardetypen zijn ingesteld op
default(T). Een parameterint idis bijvoorbeeld ingesteld op 0. - Voor complexe typen maakt modelbinding een exemplaar met behulp van de standaardconstructor, zonder eigenschappen in te stellen.
- Matrices zijn ingesteld op
Array.Empty<T>(), behalve datbyte[]matrices zijn ingesteld opnull.
Als de modelstatus ongeldig moet worden gemaakt wanneer er niets wordt gevonden in formuliervelden voor een modeleigenschap, gebruikt u het [BindRequired] kenmerk.
Houd er rekening mee dat dit [BindRequired] gedrag van toepassing is op modelbinding van geposte formuliergegevens, niet op JSON- of XML-gegevens in een aanvraagbody. Hoofdtekstgegevens van de aanvraag worden verwerkt door invoerformatters.
Typeconversiefouten
Als een bron wordt gevonden maar niet kan worden geconverteerd naar het doeltype, wordt de modelstatus gemarkeerd als ongeldig. De doelparameter of -eigenschap is ingesteld op null of een standaardwaarde, zoals vermeld in de vorige sectie.
In een API-controller met het [ApiController] kenmerk resulteert een ongeldige modelstatus in een automatisch HTTP 400-antwoord.
Op een Razor pagina wordt de pagina opnieuw weergegeven met een foutbericht:
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
// ...
return RedirectToPage("./Index");
}
Wanneer de pagina opnieuw wordt weergegeven door de voorgaande code, wordt de ongeldige invoer niet weergegeven in het formulierveld. Dit komt doordat de modeleigenschap is ingesteld op null of een standaardwaarde. De ongeldige invoer wordt weergegeven in een foutbericht. Als u de slechte gegevens in het formulierveld opnieuw wilt weergeven, kunt u overwegen om de modeleigenschap een tekenreeks te maken en de gegevensconversie handmatig uit te voeren.
Dezelfde strategie wordt aanbevolen als u niet wilt dat typeconversiefouten resulteren in modelstatusfouten. In dat geval moet u de modeleigenschap een tekenreeks maken.
Eenvoudige typen
De eenvoudige typen waarmee de modelbindinger brontekenreeksen kan converteren, zijn onder andere:
- Boolean
- Byte, SByte
- Char
- DateTime
- DateTimeOffset
- Decimal
- Double
- Enum
- Guid
- Int16, Int32, Int64
- Single
- TimeSpan
- UInt16, UInt32, UInt64
- Uri
- Version
Complexe typen
Een complex type moet een openbare standaardconstructor en openbare schrijfbare eigenschappen hebben om te binden. Wanneer modelbinding plaatsvindt, wordt de klasse geïnstantieerd met behulp van de openbare standaardconstructor.
Voor elke eigenschap van het complexe type doorzoekt modelbinding de bronnen naar het naampatroonprefix.property_name. Als er niets wordt gevonden, wordt gezocht naar property_name zonder het voorvoegsel. De beslissing om het voorvoegsel te gebruiken wordt niet per eigenschap gemaakt. Bijvoorbeeld, met een query die ?Instructor.Id=100&Name=foo bevat en gebonden is aan de methode OnGet(Instructor instructor), bevat het resulterende object van het type Instructor:
-
Idingesteld op100. -
Nameingesteld opnull. Model binding verwachtInstructor.NameomdatInstructor.Idwerd gebruikt in de voorgaande queryparameter.
Note
Documentatiekoppelingen naar .NET-referentiebron laden meestal de standaardbranch van de opslagplaats, die de huidige ontwikkeling vertegenwoordigt voor de volgende release van .NET. Als u een tag voor een specifieke release wilt selecteren, gebruikt u de Switch branches of tags vervolgkeuzelijst. Zie Een versietag selecteren van ASP.NET Core-broncode (dotnet/AspNetCore.Docs #26205)voor meer informatie.
Voor binding met een parameter is het voorvoegsel de parameternaam. Voor binding met een PageModel openbare eigenschap is het voorvoegsel de naam van de openbare eigenschap. Sommige kenmerken hebben een Prefix eigenschap waarmee u het standaardgebruik van de parameter of eigenschapsnaam kunt overschrijven.
Stel dat het complexe type de volgende Instructor klasse is:
public class Instructor
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
Voorvoegsel = parameternaam
Als het model moet worden gebonden aan een parameter met de naam instructorToUpdate:
public IActionResult OnPost(int? id, Instructor instructorToUpdate)
Modelbinding begint door de bronnen voor de sleutel instructorToUpdate.IDte bekijken. Als dat niet wordt gevonden, wordt gezocht naar ID zonder voorvoegsel.
Voorvoegsel = eigenschapsnaam
Als het model dat moet worden gebonden, een eigenschap is met de naam Instructor van de controller of PageModel klasse:
[BindProperty]
public Instructor Instructor { get; set; }
Modelbinding begint door de bronnen voor de sleutel Instructor.IDte bekijken. Als dat niet wordt gevonden, wordt gezocht naar ID zonder voorvoegsel.
Aangepast voorvoegsel
Als het model dat moet worden gebonden een parameter met de naam instructorToUpdate is en een kenmerk Bind het voorvoegsel Instructor opgeeft.
public IActionResult OnPost(
int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)
Modelbinding begint door de bronnen voor de sleutel Instructor.IDte bekijken. Als dat niet wordt gevonden, wordt gezocht naar ID zonder voorvoegsel.
Kenmerken voor complexe typedoelen
Er zijn verschillende ingebouwde kenmerken beschikbaar voor het beheren van modelbindingen van complexe typen:
Warning
Deze kenmerken zijn van invloed op modelbinding wanneer geplaatste formuliergegevens de bron van waarden zijn. Ze hebben geen invloed op invoerindelingen, die JSON- en XML-aanvraagteksten verwerken. Invoerindelingen worden verderop in dit artikel uitgelegd.
Kenmerk [Bind]
Kan worden toegepast op een klasse- of methodeparameter. Hiermee geeft u op welke eigenschappen van een model moeten worden opgenomen in modelbinding.
[Bind] heeft geen invloed op invoerindelingen.
In het volgende voorbeeld zijn alleen de opgegeven eigenschappen van het Instructor model afhankelijk wanneer een handler of actiemethode wordt aangeroepen:
[Bind("LastName,FirstMidName,HireDate")]
public class Instructor
In het volgende voorbeeld zijn alleen de opgegeven eigenschappen van het Instructor model afhankelijk wanneer de OnPost methode wordt aangeroepen:
[HttpPost]
public IActionResult OnPost(
[Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
Het [Bind] kenmerk kan worden gebruikt om te beschermen tegen overposting in create-scenario's. Het werkt niet goed in bewerkingsscenario's omdat uitgesloten eigenschappen zijn ingesteld op null of een standaardwaarde in plaats van ongewijzigd te laten. Voor bescherming tegen overposting worden weergavemodellen aanbevolen in plaats van het [Bind] kenmerk. Zie Beveiligingsnotitie over overposting voor meer informatie.
[ModelBinder] kenmerk
ModelBinderAttribute kan worden toegepast op typen, eigenschappen of parameters. Hiermee kunt u het type modelbinding opgeven dat wordt gebruikt om het specifieke exemplaar of type te binden. Voorbeeld:
[HttpPost]
public IActionResult OnPost(
[ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)
Het [ModelBinder] kenmerk kan ook worden gebruikt om de naam van een eigenschap of parameter te wijzigen wanneer het model afhankelijk is:
public class Instructor
{
[ModelBinder(Name = "instructor_id")]
public string Id { get; set; }
// ...
}
Kenmerk [BindRequired]
Zorgt ervoor dat modelbinding een fout in de modelstatus toevoegt indien de binding niet kan plaatsvinden voor een eigenschap van een model. Hier is een voorbeeld:
public class InstructorBindRequired
{
// ...
[BindRequired]
public DateTime HireDate { get; set; }
}
Zie ook de discussie over het [Required] kenmerk in modelvalidatie.
Kenmerk [BindNever]
Kan worden toegepast op een eigenschap of een type. Hiermee voorkomt u dat modelbinding de eigenschap van een model instelt. Wanneer het modelbindingssysteem wordt toegepast op een type, worden alle eigenschappen uitgesloten die door het type worden gedefinieerd. Hier is een voorbeeld:
public class InstructorBindNever
{
[BindNever]
public int Id { get; set; }
// ...
}
Collections
Voor doelen die verzamelingen eenvoudige typen zijn, zoekt modelbinding naar overeenkomsten voor parameter_name of property_name. Als er geen overeenkomst wordt gevonden, wordt gezocht naar een van de ondersteunde indelingen zonder het voorvoegsel. Voorbeeld:
Stel dat de parameter die moet worden gebonden een matrix is met de naam
selectedCourses:public IActionResult OnPost(int? id, int[] selectedCourses)Formulier- of querytekenreeksgegevens kunnen een van de volgende indelingen hebben:
selectedCourses=1050&selectedCourses=2000selectedCourses[0]=1050&selectedCourses[1]=2000[0]=1050&[1]=2000selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b[a]=1050&[b]=2000&index=a&index=bVermijd het binden van een parameter of eigenschap met de naam
indexofIndexals deze grenzen aan een verzamelingswaarde. Modelbinding probeert omindexals de index te gebruiken voor de verzameling, wat kan leiden tot onjuiste binding. Denk bijvoorbeeld aan de volgende actie:public IActionResult Post(string index, List<Product> products)In de voorgaande code wordt de
indexqueryreeksparameter gekoppeld aan deindexmethodeparameter en wordt ook gebruikt om de productverzameling te binden. Als u de naam van deindexparameter wijzigt of een modelbindingskenmerk gebruikt om binding te configureren, voorkomt u dit probleem:public IActionResult Post(string productIndex, List<Product> products)De volgende indeling wordt alleen ondersteund in formuliergegevens:
selectedCourses[]=1050&selectedCourses[]=2000Voor alle voorgaande voorbeeldindelingen geeft modelbinding een matrix van twee items door aan de
selectedCoursesparameter:- selectedCourses[0]=1050
- selectedCourses[1]=2000
Gegevensindelingen die gebruikmaken van subscriptnummers (... [0] ... [1] ...) moet ervoor zorgen dat ze opeenvolgend beginnen bij nul. Als er hiaten zijn in subscriptnummering, worden alle items na de tussenruimte genegeerd. Als de subscripts bijvoorbeeld 0 en 2 zijn in plaats van 0 en 1, wordt het tweede item genegeerd.
Dictionaries
Voor Dictionary doelen zoekt modelbinding naar overeenkomsten voor parameter_name of property_name. Als er geen overeenkomst wordt gevonden, wordt gezocht naar een van de ondersteunde indelingen zonder het voorvoegsel. Voorbeeld:
Stel dat de doelparameter een
Dictionary<int, string>naamselectedCoursesheeft:public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)De geplaatste formulier- of querytekenreeksgegevens kunnen eruitzien als een van de volgende voorbeelden:
selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics[1050]=Chemistry&selectedCourses[2000]=EconomicsselectedCourses[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=EconomicsVoor alle voorgaande voorbeeldindelingen geeft modelbinding een woordenlijst van twee items door aan de
selectedCoursesparameter:- selectedCourses["1050"]="Chemistry"
- selectedCourses["2000"]="Economics"
Constructor-binding en recordtypen
Voor modelbinding is vereist dat complexe typen een parameterloze constructor hebben. Zowel System.Text.Json als Newtonsoft.Json invoerindelingen ondersteunen deserialisatie van klassen die geen parameterloze constructor hebben.
Recordtypen zijn een uitstekende manier om gegevens via het netwerk beknopt weer te geven. ASP.NET Core ondersteunt modelbinding en het valideren van recordtypen met één constructor:
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>
Bij het valideren van recordtypen zoekt de runtime specifiek naar bindings- en validatiemetagegevens op parameters in plaats van op eigenschappen.
Het framework maakt het mogelijk om recordtypen te binden en te valideren:
public record Person([Required] string Name, [Range(0, 100)] int Age);
Voordat het voorgaande werkt, moet het type:
- Wees een recordtype.
- Zorg ervoor dat er precies één publieke constructor is.
- Parameters bevatten die een eigenschap met dezelfde naam en hetzelfde type hebben. De namen mogen niet per geval verschillen.
POCOs zonder parameterloze constructors
POCOs die geen parameterloze constructors hebben, kunnen niet worden gebonden.
De volgende code resulteert in een uitzondering die aangeeft dat het type een parameterloze constructor moet hebben:
public class Person(string Name)
public record Person([Required] string Name, [Range(0, 100)] int Age)
{
public Person(string Name) : this (Name, 0);
}
Recordtypen met handmatig geschreven constructors
Recordtypen met handmatig geschreven constructors die eruitzien als primaire constructors werken
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; }
}
Recordtypen, validatie en bindingmetagegevens
Voor recordtypen wordt validatie en bindingsmetagegevens op parameters gebruikt. Metagegevens over eigenschappen worden genegeerd
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; }
}
Validatie en metagegevens
Validatie maakt gebruik van metagegevens voor de parameter, maar gebruikt de eigenschap om de waarde te lezen. In het normale geval met primaire constructors zouden de twee identiek zijn. Er zijn echter manieren om het te verslaan:
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 werkt geen parameters bij voor een recordtype
public record Person(string Name)
{
public int Age { get; set; }
}
var person = new Person("initial-name");
TryUpdateModel(person, ...);
In dit geval probeert MVC niet opnieuw te binden Name . Het is echter toegestaan om Age bij te werken.
Globalisatiegedrag van modelbindingsroutegegevens en queryreeksen
De routewaardeprovider en querywaardeprovider van ASP.NET Core:
- Waarden behandelen als invariante cultuur.
- Verwacht dat URL's cultuur-invariant zijn.
Waarden die afkomstig zijn van formuliergegevens, ondergaan daarentegen een cultuurgevoelige conversie. Dit is standaard zo dat URL's kunnen worden gedeeld tussen landinstellingen.
De ASP.NET Core route value provider en query string value provider moeten een cultuurafhankelijke conversie ondergaan.
- Overnemen van IValueProviderFactory
- Kopieer de code uit QueryStringValueProviderFactory of RouteValueValueProviderFactory
- Vervang de cultuurwaarde die is doorgegeven aan de constructor van de waardeprovider door CultureInfo.CurrentCulture
- Vervang de standaardwaardeproviderfabriek in de MVC-opties door uw nieuwe.
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();
});
Speciale gegevenstypen
Er zijn enkele speciale gegevenstypen die modelbinding kunnen verwerken.
IFormFile en IFormFileCollection
Een geüpload bestand dat is opgenomen in de HTTP-aanvraag. Ook ondersteund is IEnumerable<IFormFile> voor meerdere bestanden.
CancellationToken
Acties kunnen desgewenst een CancellationToken als parameter binden. Dit koppelt RequestAborted dat signalen afgeeft wanneer de achterliggende verbinding van het HTTP-verzoek wordt beëindigd. Acties kunnen deze parameter gebruiken om langlopende asynchrone bewerkingen te annuleren die worden uitgevoerd als onderdeel van de controlleracties.
FormCollection
Wordt gebruikt om alle waarden op te halen uit geposte formuliergegevens.
Invoerindelingen
Gegevens in de aanvraagbody kunnen in JSON, XML of een andere indeling zijn. Voor het parseren van deze gegevens maakt modelbinding gebruik van een invoerindeling die is geconfigureerd voor het afhandelen van een bepaald inhoudstype. Standaard bevat ASP.NET Core invoerindelingen op basis van JSON voor het verwerken van JSON-gegevens. U kunt andere formatters toevoegen voor andere inhoudstypen.
ASP.NET Core selecteert invoerindelingen op basis van het kenmerk Verbruiken . Als er geen kenmerk aanwezig is, wordt de header Content-Type gebruikt.
Om de ingebouwde XML-invoerindelingen te gebruiken:
In
Program.cs, bel AddXmlSerializerFormatters of AddXmlDataContractSerializerFormatters.builder.Services.AddControllers() .AddXmlSerializerFormatters();Pas het
Consumeskenmerk toe op controllerklassen of actiemethoden die XML in de hoofdtekst van de aanvraag moeten verwachten.[HttpPost] [Consumes("application/xml")] public ActionResult<Pet> Create(Pet pet)Zie Inleiding tot XML-serialisatie voor meer informatie.
Modelbinding aanpassen met inputformatters
Een invoeropmaaker neemt volledige verantwoordelijkheid voor het lezen van gegevens uit de aanvraagbody. Als u dit proces wilt aanpassen, configureert u de API's die worden gebruikt door de input formatter. In deze sectie wordt beschreven hoe u de op System.Text.Json-gebaseerde invoerindeling aanpast om een aangepast type met de naam ObjectId te kunnen interpreteren.
Overweeg het volgende model, dat een aangepaste ObjectId eigenschap bevat:
public class InstructorObjectId
{
[Required]
public ObjectId ObjectId { get; set; } = null!;
}
Als u het modelbindingsproces wilt aanpassen wanneer u dit gebruikt System.Text.Json, maakt u een klasse die is afgeleid van 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);
}
Als u een aangepast conversieprogramma wilt gebruiken, past u het JsonConverterAttribute kenmerk toe op het type. In het volgende voorbeeld wordt het ObjectId-type geconfigureerd met ObjectIdConverter als zijn aangepaste converter:
[JsonConverter(typeof(ObjectIdConverter))]
public record ObjectId(int Id);
Zie Aangepaste conversieprogramma's schrijven voor meer informatie.
Opgegeven typen uitsluiten van modelbinding
Het gedrag van modelbindingen en validatiesystemen wordt aangestuurd door ModelMetadata. U kunt dit aanpassen ModelMetadata door een gegevensprovider toe te voegen aan MvcOptions.ModelMetadataDetailsProviders. Ingebouwde gegevensproviders zijn beschikbaar voor het uitschakelen van modelbinding of validatie voor opgegeven typen.
Als u modelbinding wilt uitschakelen voor alle modellen van een opgegeven type, voegt u een ExcludeBindingMetadataProvider in Program.cs. Als u bijvoorbeeld modelbinding wilt uitschakelen voor alle modellen van het type System.Version:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
Als u validatie wilt uitschakelen voor eigenschappen van een opgegeven type, voegt u een SuppressChildValidationMetadataProvider in Program.cs. Als u bijvoorbeeld validatie wilt uitschakelen voor eigenschappen van het type System.Guid:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
Aangepaste modelbindingen
U kunt het binden van modellen uitbreiden door een eigen modelbinder te schrijven en dan het kenmerk te gebruiken om deze [ModelBinder] te selecteren voor een gegeven doelwit. Meer informatie over aangepaste modelbinding.
Handmatige modelbinding
Modelbinding kan handmatig worden aangeroepen met behulp van de TryUpdateModelAsync methode. De methode is gedefinieerd op zowel ControllerBase als PageModel klassen. Met overbelasting van methoden kunt u het te gebruiken voorvoegsel en de waardeprovider opgeven. De methode retourneert false als modelbinding mislukt. Hier is een voorbeeld:
if (await TryUpdateModelAsync(
newInstructor,
"Instructor",
x => x.Name, x => x.HireDate!))
{
_instructorStore.Add(newInstructor);
return RedirectToPage("./Index");
}
return Page();
TryUpdateModelAsync maakt gebruik van waardeproviders om gegevens op te halen uit de hoofdtekst van het formulier, queryreeks en routegegevens.
TryUpdateModelAsync is doorgaans:
- Wordt gebruikt met Razor Pagina's en MVC-apps waarbij controllers en weergaven worden gebruikt om over-posting te voorkomen.
- Niet gebruikt met een web-API, tenzij het wordt benut vanuit formuliergegevens, queryreeksen en routegegevens. Web-API-eindpunten die JSON gebruiken, gebruiken input formatters om de hoofdtekst van de aanvraag in een object te deserialiseren.
Zie TryUpdateModelAsync voor meer informatie.
Kenmerk [FromServices]
De naam van dit kenmerk volgt het patroon van modelbindingskenmerken die een gegevensbron opgeven. Maar het gaat niet om bindingsgegevens van een waardeprovider. Er wordt een instance van een type opgehaald uit de container voor afhankelijkheidsinjectie. Het doel is om een alternatief voor constructorinjectie te bieden wanneer u een service alleen nodig hebt als een bepaalde methode wordt aangeroepen.
Als een exemplaar van het type niet is geregistreerd in de container voor afhankelijkheidsinjectie, genereert de app een uitzondering bij het verbinden van de parameter. Als u de parameter optioneel wilt maken, gebruikt u een van de volgende methoden:
- Maak de parameter nullable.
- Stel een standaardwaarde in voor de parameter.
Voor null-parameters moet u ervoor zorgen dat de parameter niet null is voordat u deze opent.
Aanvullende bronnen
In dit artikel wordt uitgelegd wat modelbinding is, hoe het werkt en hoe het gedrag ervan kan worden aangepast.
Voorbeeldcode bekijken of downloaden (hoe u kunt downloaden).
Wat is modelbinding?
Controllers en Razor pagina's werken met gegevens die afkomstig zijn van HTTP-aanvragen. Routegegevens kunnen bijvoorbeeld een recordsleutel bevatten en geplaatste formuliervelden kunnen waarden bieden voor de eigenschappen van het model. Het schrijven van code om elk van deze waarden op te halen en deze te converteren van tekenreeksen naar .NET-typen zou tijdrovend en foutgevoelig zijn. Modelbinding automatiseert dit proces. Het modelbindingssysteem:
- Hiermee worden gegevens opgehaald uit verschillende bronnen, zoals routegegevens, formuliervelden en queryreeksen.
- Stelt de gegevens beschikbaar aan controllers en Razor pagina's in methodeparameters en openbare eigenschappen.
- Converteert tekenreeksgegevens naar .NET-typen.
- Hiermee worden eigenschappen van complexe typen bijgewerkt.
Example
Stel dat u de volgende actiemethode hebt:
[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)
En de app ontvangt een aanvraag met deze URL:
http://contoso.com/api/pets/2?DogsOnly=true
Modelbinding doorloopt de volgende stappen nadat het routeringssysteem de actiemethode heeft geselecteerd:
- Hiermee zoekt u de eerste parameter van
GetById, een geheel getal met de naamid. - Bekijkt de beschikbare bronnen in de HTTP-aanvraag en vindt
id= '2' in routegegevens. - Converteert de tekenreeks '2' naar geheel getal 2.
- Hiermee zoekt u de volgende parameter van
GetById, een Booleaanse parameter genaamddogsOnly. - Bekijkt de bronnen en zoekt 'DogsOnly=true' in de querytekenreeks. Naamkoppeling is niet hoofdlettergevoelig.
- Converteert de tekenreeks "true" naar de booleaanse waarde
true.
Het framework roept vervolgens de GetById methode aan, waarbij 2 wordt doorgegeven voor de id parameter en true voor de dogsOnly parameter.
In het voorgaande voorbeeld zijn de modelbindingsdoelen methodeparameters die eenvoudige typen zijn. Doelen kunnen ook de eigenschappen van een complex type zijn. Nadat elke eigenschap is gebonden, vindt modelvalidatie plaats voor die eigenschap. De record van welke gegevens zijn gebonden aan het model, en eventuele bindings- of validatiefouten, worden opgeslagen in ControllerBase.ModelState of PageModel.ModelState. Om erachter te komen of dit proces is geslaagd, controleert de app de vlag ModelState.IsValid .
Targets
Modelbinding probeert waarden te vinden voor de volgende soorten doelen:
- Parameters van de actiemethode controller waarnaar een aanvraag wordt doorgestuurd.
- Parameters van de Razor handlermethode Pages waarnaar een aanvraag wordt doorgestuurd.
- Openbare eigenschappen van een controller of
PageModelklasse, indien opgegeven door kenmerken.
Kenmerk [BindProperty]
Kan worden toegepast op een openbare eigenschap van een controller of PageModel klasse om modelbinding te veroorzaken voor de doeleigenschap:
public class EditModel : InstructorsPageModel
{
[BindProperty]
public Instructor Instructor { get; set; }
[BindProperties] kenmerk
Beschikbaar in ASP.NET Core 2.1 of hoger. Kan worden toegepast op een controller of de PageModel klasse om aan te geven dat modelbinding zich moet richten op alle openbare eigenschappen van de klasse.
[BindProperties(SupportsGet = true)]
public class CreateModel : InstructorsPageModel
{
public Instructor Instructor { get; set; }
Modelbinding voor HTTP GET-aanvragen
Eigenschappen zijn standaard niet gebonden voor HTTP GET-aanvragen. Normaal gesproken is alles wat u nodig hebt voor een GET-aanvraag een record-id-parameter. De record-id wordt gebruikt om het item in de database op te zoeken. Daarom is het niet nodig om een eigenschap te binden die een exemplaar van het model bevat. In scenario's waarin u wilt dat eigenschappen zijn gebonden aan gegevens uit GET-aanvragen, stelt u de SupportsGet eigenschap truein op:
[BindProperty(Name = "ai_user", SupportsGet = true)]
public string ApplicationInsightsCookie { get; set; }
Sources
Modelbinding haalt standaard gegevens op in de vorm van sleutel-waardeparen uit de volgende bronnen in een HTTP-aanvraag:
- Formuliervelden
- De aanvraagbody (voor controllers die het kenmerk [ApiController] hebben.)
- Routegegevens
- Parameters voor zoekreeks
- Geüploade bestanden
Voor elke doelparameter of eigenschap worden de bronnen gescand in de volgorde die wordt aangegeven in de voorgaande lijst. Er zijn enkele uitzonderingen:
- Routegegevens en queryreekswaarden worden alleen gebruikt voor eenvoudige typen.
- Geüploade bestanden zijn alleen gebonden aan doeltypen die implementeren
IFormFileofIEnumerable<IFormFile>.
Als de standaardbron niet juist is, gebruikt u een van de volgende kenmerken om de bron op te geven:
-
[FromQuery]- Haalt waarden op uit de querytekenreeks. -
[FromRoute]- Haalt waarden op uit routegegevens. -
[FromForm]- Haalt waarden op uit geplaatste formuliervelden. -
[FromBody]- Haalt waarden op uit de hoofdtekst van de aanvraag. -
[FromHeader]- Haalt waarden op uit HTTP-headers.
Deze kenmerken:
Worden afzonderlijk toegevoegd aan modeleigenschappen (niet aan de modelklasse), zoals in het volgende voorbeeld:
public class Instructor { public int ID { get; set; } [FromQuery(Name = "Note")] public string NoteFromQueryString { get; set; }Accepteer eventueel een modelnaamwaarde in de constructor. Deze optie wordt opgegeven als de eigenschapsnaam niet overeenkomt met de waarde in de aanvraag. De waarde in de aanvraag kan bijvoorbeeld een header zijn met een afbreekstreepje in de naam, zoals in het volgende voorbeeld:
public void OnGet([FromHeader(Name = "Accept-Language")] string language)
Kenmerk [FromBody]
Pas het [FromBody] kenmerk toe op een parameter om de eigenschappen ervan te vullen vanuit de hoofdtekst van een HTTP-aanvraag. De ASP.NET Core-runtime delegeert de verantwoordelijkheid voor het lezen van de body naar een invoerformatter. Invoerindelingen worden verderop in dit artikel uitgelegd.
Wanneer [FromBody] deze wordt toegepast op een complexe typeparameter, worden alle bindingsbronkenmerken die op de eigenschappen ervan worden toegepast, genegeerd. Met de volgende Create actie wordt bijvoorbeeld aangegeven dat de parameter van pet uit de body wordt ingevuld:
public ActionResult<Pet> Create([FromBody] Pet pet)
De Pet klasse geeft aan dat de Breed eigenschap wordt ingevuld vanuit een queryreeksparameter:
public class Pet
{
public string Name { get; set; }
[FromQuery] // Attribute is ignored.
public string Breed { get; set; }
}
In het voorgaande voorbeeld:
- Het
[FromQuery]kenmerk wordt genegeerd. - De
Breedeigenschap wordt niet ingevuld vanuit een queryreeksparameter.
Invoerindelingen lezen alleen de hoofdtekst en begrijpen geen bindingsbronkenmerken. Als een geschikte waarde wordt gevonden in de hoofdtekst, wordt die waarde gebruikt om de Breed eigenschap te vullen.
Pas [FromBody] niet toe op meer dan één parameter per actiemethode. Zodra de verzoekstroom is gelezen door een invoerformatter, is deze niet meer beschikbaar om opnieuw te worden gelezen voor het binden van andere [FromBody] parameters.
Aanvullende bronnen
Brongegevens worden door waardeproviders verstrekt aan het modelbindingssysteem. U kunt aangepaste waardeproviders schrijven en registreren die gegevens ophalen voor modelbinding uit andere bronnen. U wilt bijvoorbeeld gegevens uit cookies of sessiestatus. Ga als volgt te werk om gegevens op te halen uit een nieuwe bron:
- Maak een klasse die
IValueProviderimplementeert. - Maak een klasse die
IValueProviderFactoryimplementeert. - Registreer de fabrieksklasse in
Startup.ConfigureServices.
De voorbeeld-app bevat een waardeprovider en een fabrieksvoorbeeld waarmee waarden uit cookies worden opgehaald. Hier ziet u de registratiecode in 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();
De weergegeven code plaatst de aangepaste waardeprovider na alle ingebouwde waardeproviders. Om het de eerste in de lijst te maken, gebruik Insert(0, new CookieValueProviderFactory()) in plaats van Add.
Geen bron voor een modeleigenschap
Standaard wordt er geen modelstatusfout gemaakt als er geen waarde wordt gevonden voor een modeleigenschap. De eigenschap is ingesteld op null of een standaardwaarde:
- Nullable simple types zijn ingesteld op
null. - Niet-nullwaardetypen zijn ingesteld op
default(T). Een parameterint idis bijvoorbeeld ingesteld op 0. - Voor complexe typen maakt modelbinding een exemplaar met behulp van de standaardconstructor, zonder eigenschappen in te stellen.
- Matrices zijn ingesteld op
Array.Empty<T>(), behalve datbyte[]matrices zijn ingesteld opnull.
Als de modelstatus ongeldig moet worden gemaakt wanneer er niets wordt gevonden in formuliervelden voor een modeleigenschap, gebruikt u het [BindRequired] kenmerk.
Houd er rekening mee dat dit [BindRequired] gedrag van toepassing is op modelbinding van geposte formuliergegevens, niet op JSON- of XML-gegevens in een aanvraagbody. Hoofdtekstgegevens van de aanvraag worden verwerkt door invoerformatters.
Typeconversiefouten
Als een bron wordt gevonden maar niet kan worden geconverteerd naar het doeltype, wordt de modelstatus gemarkeerd als ongeldig. De doelparameter of -eigenschap is ingesteld op null of een standaardwaarde, zoals vermeld in de vorige sectie.
In een API-controller met het [ApiController] kenmerk resulteert een ongeldige modelstatus in een automatisch HTTP 400-antwoord.
Op een Razor pagina wordt de pagina opnieuw weergegeven met een foutbericht:
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
_instructorsInMemoryStore.Add(Instructor);
return RedirectToPage("./Index");
}
Validatie aan de clientzijde onderschept de meeste ongeldige gegevens die anders naar een Razor paginaformulier zouden worden verzonden. Deze validatie maakt het lastig om de voorgaande gemarkeerde code te activeren. De voorbeeld-app bevat een knop Verzenden met ongeldige datum waarmee ongeldige gegevens in het veld Hire Date worden geplaatst en het formulier wordt verzonden. Deze knop laat zien hoe de code voor het opnieuw weergeven van de pagina werkt wanneer er fouten optreden bij het converteren van gegevens.
Wanneer de pagina opnieuw wordt weergegeven met de voorgaande code, wordt de ongeldige invoer niet weergegeven in het formulierveld. Dit komt doordat de modeleigenschap is ingesteld op null of een standaardwaarde. De ongeldige invoer wordt weergegeven in een foutbericht. Maar als u de slechte gegevens in het formulierveld opnieuw wilt weergeven, kunt u overwegen om de modeleigenschap een tekenreeks te maken en de gegevensconversie handmatig uit te voeren.
Dezelfde strategie wordt aanbevolen als u niet wilt dat typeconversiefouten resulteren in modelstatusfouten. In dat geval moet u de modeleigenschap een tekenreeks maken.
Eenvoudige typen
De eenvoudige typen waarmee de modelbindinger brontekenreeksen kan converteren, zijn onder andere:
- Boolean
- Byte, SByte
- Char
- DateTime
- DateTimeOffset
- Decimal
- Double
- Enum
- Guid
- Int16, Int32, Int64
- Single
- TimeSpan
- UInt16, UInt32, UInt64
- Uri
- Version
Complexe typen
Een complex type moet een openbare standaardconstructor en openbare schrijfbare eigenschappen hebben om te binden. Wanneer modelbinding plaatsvindt, wordt de klasse geïnstantieerd met behulp van de openbare standaardconstructor.
Voor elke eigenschap van het complexe type doorzoekt modelbinding de bronnen naar het naamspatroon prefix.property_name. Als er niets wordt gevonden, wordt gezocht naar property_name zonder het voorvoegsel.
Voor binding met een parameter is het voorvoegsel de parameternaam. Voor binding met een PageModel openbare eigenschap is het voorvoegsel de naam van de openbare eigenschap. Sommige kenmerken hebben een Prefix eigenschap waarmee u het standaardgebruik van de parameter of eigenschapsnaam kunt overschrijven.
Stel dat het complexe type de volgende Instructor klasse is:
public class Instructor
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
Voorvoegsel = parameternaam
Als het model moet worden gebonden aan een parameter met de naam instructorToUpdate:
public IActionResult OnPost(int? id, Instructor instructorToUpdate)
Modelbinding begint door de bronnen voor de sleutel instructorToUpdate.IDte bekijken. Als dat niet wordt gevonden, wordt gezocht naar ID zonder voorvoegsel.
Voorvoegsel = eigenschapsnaam
Als het model dat moet worden gebonden, een eigenschap is met de naam Instructor van de controller of PageModel klasse:
[BindProperty]
public Instructor Instructor { get; set; }
Modelbinding begint door de bronnen voor de sleutel Instructor.IDte bekijken. Als dat niet wordt gevonden, wordt gezocht naar ID zonder voorvoegsel.
Aangepast voorvoegsel
Als het model dat moet worden gebonden een parameter met de naam instructorToUpdate is en een kenmerk Bind het voorvoegsel Instructor opgeeft.
public IActionResult OnPost(
int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)
Modelbinding begint door de bronnen voor de sleutel Instructor.IDte bekijken. Als dat niet wordt gevonden, wordt gezocht naar ID zonder voorvoegsel.
Kenmerken voor complexe typedoelen
Er zijn verschillende ingebouwde kenmerken beschikbaar voor het beheren van modelbindingen van complexe typen:
[Bind][BindRequired][BindNever]
Warning
Deze kenmerken zijn van invloed op modelbinding wanneer geplaatste formuliergegevens de bron van waarden zijn. Ze hebben geen invloed op invoerindelingen, die JSON- en XML-aanvraagteksten verwerken. Invoerindelingen worden verderop in dit artikel uitgelegd.
Kenmerk [Bind]
Kan worden toegepast op een klasse- of methodeparameter. Hiermee geeft u op welke eigenschappen van een model moeten worden opgenomen in modelbinding.
[Bind] heeft geen invloed op invoerindelingen.
In het volgende voorbeeld zijn alleen de opgegeven eigenschappen van het Instructor model afhankelijk wanneer een handler of actiemethode wordt aangeroepen:
[Bind("LastName,FirstMidName,HireDate")]
public class Instructor
In het volgende voorbeeld zijn alleen de opgegeven eigenschappen van het Instructor model afhankelijk wanneer de OnPost methode wordt aangeroepen:
[HttpPost]
public IActionResult OnPost([Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
Het [Bind] kenmerk kan worden gebruikt om te beschermen tegen overposting in create-scenario's. Het werkt niet goed in bewerkingsscenario's omdat uitgesloten eigenschappen zijn ingesteld op null of een standaardwaarde in plaats van ongewijzigd te laten. Voor bescherming tegen overposting worden weergavemodellen aanbevolen in plaats van het [Bind] kenmerk. Zie Beveiligingsnotitie over overposting voor meer informatie.
[ModelBinder] kenmerk
ModelBinderAttribute kan worden toegepast op typen, eigenschappen of parameters. Hiermee kunt u het type modelbinding opgeven dat wordt gebruikt om het specifieke exemplaar of type te binden. Voorbeeld:
[HttpPost]
public IActionResult OnPost([ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)
Het [ModelBinder] kenmerk kan ook worden gebruikt om de naam van een eigenschap of parameter te wijzigen wanneer het model afhankelijk is:
public class Instructor
{
[ModelBinder(Name = "instructor_id")]
public string Id { get; set; }
public string Name { get; set; }
}
Kenmerk [BindRequired]
Kan alleen worden toegepast op modeleigenschappen, niet op methodeparameters. Zorgt ervoor dat modelbinding een fout in de modelstatus toevoegt indien de binding niet kan plaatsvinden voor een eigenschap van een model. Hier is een voorbeeld:
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; }
Zie ook de discussie over het [Required] kenmerk in modelvalidatie.
Kenmerk [BindNever]
Kan alleen worden toegepast op modeleigenschappen, niet op methodeparameters. Hiermee voorkomt u dat modelbinding de eigenschap van een model instelt. Hier is een voorbeeld:
public class InstructorWithDictionary
{
[BindNever]
public int ID { get; set; }
Collections
Voor doelen die verzamelingen eenvoudige typen zijn, zoekt modelbinding naar overeenkomsten voor parameter_name of property_name. Als er geen overeenkomst wordt gevonden, wordt gezocht naar een van de ondersteunde indelingen zonder het voorvoegsel. Voorbeeld:
Stel dat de parameter die moet worden gebonden een matrix is met de naam
selectedCourses:public IActionResult OnPost(int? id, int[] selectedCourses)Formulier- of querytekenreeksgegevens kunnen een van de volgende indelingen hebben:
selectedCourses=1050&selectedCourses=2000selectedCourses[0]=1050&selectedCourses[1]=2000[0]=1050&[1]=2000selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b[a]=1050&[b]=2000&index=a&index=bVermijd het binden van een parameter of eigenschap met de naam
indexofIndexals deze grenzen aan een verzamelingswaarde. Modelbinding probeert omindexals de index te gebruiken voor de verzameling, wat kan leiden tot onjuiste binding. Denk bijvoorbeeld aan de volgende actie:public IActionResult Post(string index, List<Product> products)In de voorgaande code wordt de
indexqueryreeksparameter gekoppeld aan deindexmethodeparameter en wordt ook gebruikt om de productverzameling te binden. Als u de naam van deindexparameter wijzigt of een modelbindingskenmerk gebruikt om binding te configureren, voorkomt u dit probleem:public IActionResult Post(string productIndex, List<Product> products)De volgende indeling wordt alleen ondersteund in formuliergegevens:
selectedCourses[]=1050&selectedCourses[]=2000Voor alle voorgaande voorbeeldindelingen geeft modelbinding een matrix van twee items door aan de
selectedCoursesparameter:- selectedCourses[0]=1050
- selectedCourses[1]=2000
Gegevensindelingen die gebruikmaken van subscriptnummers (... [0] ... [1] ...) moet ervoor zorgen dat ze opeenvolgend beginnen bij nul. Als er hiaten zijn in subscriptnummering, worden alle items na de tussenruimte genegeerd. Als de subscripts bijvoorbeeld 0 en 2 zijn in plaats van 0 en 1, wordt het tweede item genegeerd.
Dictionaries
Voor Dictionary doelen zoekt modelbinding naar overeenkomsten voor parameter_name of property_name. Als er geen overeenkomst wordt gevonden, wordt gezocht naar een van de ondersteunde indelingen zonder het voorvoegsel. Voorbeeld:
Stel dat de doelparameter een
Dictionary<int, string>naamselectedCoursesheeft:public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)De geplaatste formulier- of querytekenreeksgegevens kunnen eruitzien als een van de volgende voorbeelden:
selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics[1050]=Chemistry&selectedCourses[2000]=EconomicsselectedCourses[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=EconomicsVoor alle voorgaande voorbeeldindelingen geeft modelbinding een woordenlijst van twee items door aan de
selectedCoursesparameter:- selectedCourses["1050"]="Chemistry"
- selectedCourses["2000"]="Economics"
Constructor-binding en recordtypen
Voor modelbinding is vereist dat complexe typen een parameterloze constructor hebben. Zowel System.Text.Json als Newtonsoft.Json invoerindelingen ondersteunen deserialisatie van klassen die geen parameterloze constructor hebben.
C# 9 introduceert recordtypen, die een uitstekende manier zijn om gegevens via het netwerk beknopt weer te geven. ASP.NET Core voegt ondersteuning toe voor modelbinding en het valideren van recordtypen met één constructor:
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>
Bij het valideren van recordtypen zoekt de runtime specifiek naar bindings- en validatiemetagegevens op parameters in plaats van op eigenschappen.
Het framework maakt het mogelijk om recordtypen te binden en te valideren:
public record Person([Required] string Name, [Range(0, 100)] int Age);
Voordat het voorgaande werkt, moet het type:
- Wees een recordtype.
- Zorg ervoor dat er precies één publieke constructor is.
- Parameters bevatten die een eigenschap met dezelfde naam en hetzelfde type hebben. De namen mogen niet per geval verschillen.
POCOs zonder parameterloze constructors
POCOs die geen parameterloze constructors hebben, kunnen niet worden gebonden.
De volgende code resulteert in een uitzondering die aangeeft dat het type een parameterloze constructor moet hebben:
public class Person(string Name)
public record Person([Required] string Name, [Range(0, 100)] int Age)
{
public Person(string Name) : this (Name, 0);
}
Recordtypen met handmatig geschreven constructors
Recordtypen met handmatig geschreven constructors die eruitzien als primaire constructors werken
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; }
}
Recordtypen, validatie en bindingmetagegevens
Voor recordtypen wordt validatie en bindingsmetagegevens op parameters gebruikt. Metagegevens over eigenschappen worden genegeerd
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; }
}
Validatie en metagegevens
Validatie maakt gebruik van metagegevens voor de parameter, maar gebruikt de eigenschap om de waarde te lezen. In het normale geval met primaire constructors zouden de twee identiek zijn. Er zijn echter manieren om het te verslaan:
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 werkt geen parameters bij voor een recordtype
public record Person(string Name)
{
public int Age { get; set; }
}
var person = new Person("initial-name");
TryUpdateModel(person, ...);
In dit geval probeert MVC niet opnieuw te binden Name . Het is echter toegestaan om Age bij te werken.
Globalisatiegedrag van modelbindingsroutegegevens en queryreeksen
De routewaardeprovider en querywaardeprovider van ASP.NET Core:
- Waarden behandelen als invariante cultuur.
- Verwacht dat URL's cultuur-invariant zijn.
Waarden die afkomstig zijn van formuliergegevens, ondergaan daarentegen een cultuurgevoelige conversie. Dit is standaard zo dat URL's kunnen worden gedeeld tussen landinstellingen.
De ASP.NET Core route value provider en query string value provider moeten een cultuurafhankelijke conversie ondergaan.
- Overnemen van IValueProviderFactory
- Kopieer de code uit QueryStringValueProviderFactory of RouteValueValueProviderFactory
- Vervang de cultuurwaarde die is doorgegeven aan de constructor van de waardeprovider door CultureInfo.CurrentCulture
- Vervang de standaardwaardeproviderfabriek in de MVC-opties door uw nieuwe.
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;
}
}
Speciale gegevenstypen
Er zijn enkele speciale gegevenstypen die modelbinding kunnen verwerken.
IFormFile en IFormFileCollection
Een geüpload bestand dat is opgenomen in de HTTP-aanvraag. Ook ondersteund is IEnumerable<IFormFile> voor meerdere bestanden.
CancellationToken
Acties kunnen desgewenst een CancellationToken als parameter binden. Dit koppelt RequestAborted dat signalen afgeeft wanneer de achterliggende verbinding van het HTTP-verzoek wordt beëindigd. Acties kunnen deze parameter gebruiken om langlopende asynchrone bewerkingen te annuleren die worden uitgevoerd als onderdeel van de controlleracties.
FormCollection
Wordt gebruikt om alle waarden op te halen uit geposte formuliergegevens.
Invoerindelingen
Gegevens in de aanvraagbody kunnen in JSON, XML of een andere indeling zijn. Voor het parseren van deze gegevens maakt modelbinding gebruik van een invoerindeling die is geconfigureerd voor het afhandelen van een bepaald inhoudstype. Standaard bevat ASP.NET Core invoerindelingen op basis van JSON voor het verwerken van JSON-gegevens. U kunt andere formatters toevoegen voor andere inhoudstypen.
ASP.NET Core selecteert invoerindelingen op basis van het kenmerk Verbruiken . Als er geen kenmerk aanwezig is, wordt de header Content-Type gebruikt.
Om de ingebouwde XML-invoerindelingen te gebruiken:
Installeer het
Microsoft.AspNetCore.Mvc.Formatters.XmlNuGet-pakket.In
Startup.ConfigureServices, bel AddXmlSerializerFormatters of 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();Pas het
Consumeskenmerk toe op controllerklassen of actiemethoden die XML in de hoofdtekst van de aanvraag moeten verwachten.[HttpPost] [Consumes("application/xml")] public ActionResult<Pet> Create(Pet pet)Zie Inleiding tot XML-serialisatie voor meer informatie.
Modelbinding aanpassen met inputformatters
Een invoeropmaaker neemt volledige verantwoordelijkheid voor het lezen van gegevens uit de aanvraagbody. Als u dit proces wilt aanpassen, configureert u de API's die worden gebruikt door de input formatter. In deze sectie wordt beschreven hoe u de op System.Text.Json-gebaseerde invoerindeling aanpast om een aangepast type met de naam ObjectId te kunnen interpreteren.
Overweeg het volgende model, dat een aangepaste ObjectId eigenschap bevat met de naam Id:
public class ModelWithObjectId
{
public ObjectId Id { get; set; }
}
Als u het modelbindingsproces wilt aanpassen wanneer u dit gebruikt System.Text.Json, maakt u een klasse die is afgeleid van 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);
}
}
Als u een aangepast conversieprogramma wilt gebruiken, past u het JsonConverterAttribute kenmerk toe op het type. In het volgende voorbeeld wordt het ObjectId-type geconfigureerd met ObjectIdConverter als zijn aangepaste converter:
[JsonConverter(typeof(ObjectIdConverter))]
public struct ObjectId
{
public ObjectId(int id) =>
Id = id;
public int Id { get; }
}
Zie Aangepaste conversieprogramma's schrijven voor meer informatie.
Opgegeven typen uitsluiten van modelbinding
Het gedrag van modelbindingen en validatiesystemen wordt aangestuurd door ModelMetadata. U kunt dit aanpassen ModelMetadata door een gegevensprovider toe te voegen aan MvcOptions.ModelMetadataDetailsProviders. Ingebouwde gegevensproviders zijn beschikbaar voor het uitschakelen van modelbinding of validatie voor opgegeven typen.
Als u modelbinding wilt uitschakelen voor alle modellen van een opgegeven type, voegt u een ExcludeBindingMetadataProvider in Startup.ConfigureServices. Als u bijvoorbeeld modelbinding wilt uitschakelen voor alle modellen van het type 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();
Als u validatie wilt uitschakelen voor eigenschappen van een opgegeven type, voegt u een SuppressChildValidationMetadataProvider in Startup.ConfigureServices. Als u bijvoorbeeld validatie wilt uitschakelen voor eigenschappen van het type 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();
Aangepaste modelbindingen
U kunt het binden van modellen uitbreiden door een eigen modelbinder te schrijven en dan het kenmerk te gebruiken om deze [ModelBinder] te selecteren voor een gegeven doelwit. Meer informatie over aangepaste modelbinding.
Handmatige modelbinding
Modelbinding kan handmatig worden aangeroepen met behulp van de TryUpdateModelAsync methode. De methode is gedefinieerd op zowel ControllerBase als PageModel klassen. Met overbelasting van methoden kunt u het te gebruiken voorvoegsel en de waardeprovider opgeven. De methode retourneert false als modelbinding mislukt. Hier is een voorbeeld:
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 maakt gebruik van waardeproviders om gegevens op te halen uit de hoofdtekst van het formulier, queryreeks en routegegevens.
TryUpdateModelAsync is doorgaans:
- Wordt gebruikt met Razor Pagina's en MVC-apps waarbij controllers en weergaven worden gebruikt om over-posting te voorkomen.
- Niet gebruikt met een web-API, tenzij het wordt benut vanuit formuliergegevens, queryreeksen en routegegevens. Web-API-eindpunten die JSON gebruiken, gebruiken input formatters om de hoofdtekst van de aanvraag in een object te deserialiseren.
Zie TryUpdateModelAsync voor meer informatie.
Kenmerk [FromServices]
De naam van dit kenmerk volgt het patroon van modelbindingskenmerken die een gegevensbron opgeven. Maar het gaat niet om bindingsgegevens van een waardeprovider. Er wordt een instance van een type opgehaald uit de container voor afhankelijkheidsinjectie. Het doel is om een alternatief voor constructorinjectie te bieden wanneer u een service alleen nodig hebt als een bepaalde methode wordt aangeroepen.