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 the preceding example, the model binding targets are method parameters that are simple types. Doelen kunnen ook de eigenschappen van een complex type zijn. After each property is successfully bound, model validation occurs for that property. The record of what data is bound to the model, and any binding or validation errors, is stored in ControllerBase.ModelState or PageModel.ModelState. To find out if this process was successful, the app checks the ModelState.IsValid flag.
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
PageModel
klasse, indien opgegeven door kenmerken.
[BindProperty] attribute
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] attribute
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 true
in 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. A simple type is converted from a single string using TypeConverter or a TryParse
method. A complex type is converted from multiple input values. 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:
- Form fields
- De aanvraagbody (voor controllers die het kenmerk [ApiController] hebben.)
- Route data
- Parameters voor zoekreeks
- Uploaded files
Voor elke doelparameter of eigenschap worden de bronnen gescand in de volgorde die wordt aangegeven in de voorgaande lijst. Er zijn enkele uitzonderingen:
- Route data and query string values are used only for simple types.
- Geüploade bestanden zijn alleen gebonden aan doeltypen die implementeren
IFormFile
ofIEnumerable<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.
These attributes:
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)
[FromBody] attribute
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
Breed
eigenschap 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.
Additional sources
Source data is provided to the model binding system by value providers. 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
IValueProvider
implementeert. - Maak een klasse die
IValueProviderFactory
implementeert. - Registreer de fabrieksklasse in
Program.cs
.
The sample includes a value provider and factory example that gets values from 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 are set to
null
. - Niet-nullwaardetypen zijn ingesteld op
default(T)
. Een parameterint id
is 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. Request body data is handled by input formatters.
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.
Simple types
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
DateRange
object - De modelbinder gebruikt de
IParsable<TSelf>.TryParse
methode om deDateRange
te 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);
}
Complex types
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. If nothing is found, it looks for just property_name without the prefix. 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
:
-
Id
ingesteld op100
. -
Name
ingesteld opnull
. Model binding verwachtInstructor.Name
omdatInstructor.Id
werd gebruikt in de voorgaande queryparameter.
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.ID
te 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.ID
te bekijken. Als dat niet wordt gevonden, wordt gezocht naar ID
zonder voorvoegsel.
Custom prefix
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.ID
te 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. They do not affect input formatters, which process posted JSON and XML request bodies. Invoerindelingen worden verderop in dit artikel uitgelegd.
[Bind] attribute
Kan worden toegepast op een klasse- of methodeparameter. Hiermee geeft u op welke eigenschappen van een model moeten worden opgenomen in modelbinding.
[Bind]
does not affect input formatters.
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)
The [Bind]
attribute can be used to protect against overposting in create scenarios. 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] attribute
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. For example:
[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; }
// ...
}
[BindRequired] attribute
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; }
}
See also the discussion of the [Required]
attribute in Model validation.
[BindNever] attribute
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
For targets that are collections of simple types, model binding looks for matches to parameter_name or property_name. Als er geen overeenkomst wordt gevonden, wordt gezocht naar een van de ondersteunde indelingen zonder het voorvoegsel. For example:
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=2000
selectedCourses[0]=1050&selectedCourses[1]=2000
[0]=1050&[1]=2000
selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
[a]=1050&[b]=2000&index=a&index=b
Vermijd het binden van een parameter of eigenschap met de naam
index
ofIndex
als deze grenzen aan een verzamelingswaarde. Modelbinding probeert omindex
als 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
index
queryreeksparameter gekoppeld aan deindex
methodeparameter en wordt ook gebruikt om de productverzameling te binden. Als u de naam van deindex
parameter 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[]=2000
Voor alle voorgaande voorbeeldindelingen geeft modelbinding een matrix van twee items door aan de
selectedCourses
parameter:- 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
For Dictionary
targets, model binding looks for matches to parameter_name or property_name. Als er geen overeenkomst wordt gevonden, wordt gezocht naar een van de ondersteunde indelingen zonder het voorvoegsel. For example:
Stel dat de doelparameter een
Dictionary<int, string>
naamselectedCourses
heeft: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]=Economics
selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry& selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
[0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
Voor alle voorgaande voorbeeldindelingen geeft modelbinding een woordenlijst van twee items door aan de
selectedCourses
parameter:- 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
- Copy the code from QueryStringValueProviderFactory or RouteValueValueProviderFactory
- Replace the culture value passed to the value provider constructor with 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.
Input formatters
Gegevens in de aanvraagbody kunnen in JSON, XML of een andere indeling zijn. To parse this data, model binding uses an input formatter that is configured to handle a particular content type. 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 - Handle enum serialization as strings
- Custom converters - Add type-specific serialization logic
ASP.NET Core selects input formatters based on the Consumes attribute. If no attribute is present, it uses the Content-Type header.
Om de ingebouwde XML-invoerindelingen te gebruiken:
In
Program.cs
, bel AddXmlSerializerFormatters of AddXmlDataContractSerializerFormatters.builder.Services.AddControllers() .AddXmlSerializerFormatters();
Pas het
Consumes
kenmerk 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. You can customize ModelMetadata
by adding a details provider to 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 endpoints that consume JSON use Input formatters to deserialize the request body into an object.
For more information, see TryUpdateModelAsync.
[FromServices] attribute
De naam van dit kenmerk volgt het patroon van modelbindingskenmerken die een gegevensbron opgeven. Maar het gaat niet om bindingsgegevens van een waardeprovider. It gets an instance of a type from the dependency injection container. 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.
Additional resources
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 the preceding example, the model binding targets are method parameters that are simple types. Doelen kunnen ook de eigenschappen van een complex type zijn. After each property is successfully bound, model validation occurs for that property. The record of what data is bound to the model, and any binding or validation errors, is stored in ControllerBase.ModelState or PageModel.ModelState. To find out if this process was successful, the app checks the ModelState.IsValid flag.
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
PageModel
klasse, indien opgegeven door kenmerken.
[BindProperty] attribute
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] attribute
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 true
in 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. A simple type is converted from a single string using TypeConverter or a TryParse
method. A complex type is converted from multiple input values. 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:
- Form fields
- De aanvraagbody (voor controllers die het kenmerk [ApiController] hebben.)
- Route data
- Parameters voor zoekreeks
- Uploaded files
Voor elke doelparameter of eigenschap worden de bronnen gescand in de volgorde die wordt aangegeven in de voorgaande lijst. Er zijn enkele uitzonderingen:
- Route data and query string values are used only for simple types.
- Geüploade bestanden zijn alleen gebonden aan doeltypen die implementeren
IFormFile
ofIEnumerable<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.
These attributes:
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)
[FromBody] attribute
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
Breed
eigenschap 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.
Additional sources
Source data is provided to the model binding system by value providers. 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
IValueProvider
implementeert. - Maak een klasse die
IValueProviderFactory
implementeert. - Registreer de fabrieksklasse in
Program.cs
.
The sample includes a value provider and factory example that gets values from 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 are set to
null
. - Niet-nullwaardetypen zijn ingesteld op
default(T)
. Een parameterint id
is 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. Request body data is handled by input formatters.
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.
Simple types
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
DateRange
object - De modelbinder gebruikt de
IParsable<TSelf>.TryParse
methode om deDateRange
te 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);
}
Complex types
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. If nothing is found, it looks for just property_name without the prefix. 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
:
-
Id
ingesteld op100
. -
Name
ingesteld opnull
. Model binding verwachtInstructor.Name
omdatInstructor.Id
werd gebruikt in de voorgaande queryparameter.
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.ID
te 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.ID
te bekijken. Als dat niet wordt gevonden, wordt gezocht naar ID
zonder voorvoegsel.
Custom prefix
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.ID
te 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. They do not affect input formatters, which process posted JSON and XML request bodies. Invoerindelingen worden verderop in dit artikel uitgelegd.
[Bind] attribute
Kan worden toegepast op een klasse- of methodeparameter. Hiermee geeft u op welke eigenschappen van een model moeten worden opgenomen in modelbinding.
[Bind]
does not affect input formatters.
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)
The [Bind]
attribute can be used to protect against overposting in create scenarios. 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] attribute
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. For example:
[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; }
// ...
}
[BindRequired] attribute
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; }
}
See also the discussion of the [Required]
attribute in Model validation.
[BindNever] attribute
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
For targets that are collections of simple types, model binding looks for matches to parameter_name or property_name. Als er geen overeenkomst wordt gevonden, wordt gezocht naar een van de ondersteunde indelingen zonder het voorvoegsel. For example:
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=2000
selectedCourses[0]=1050&selectedCourses[1]=2000
[0]=1050&[1]=2000
selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
[a]=1050&[b]=2000&index=a&index=b
Vermijd het binden van een parameter of eigenschap met de naam
index
ofIndex
als deze grenzen aan een verzamelingswaarde. Modelbinding probeert omindex
als 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
index
queryreeksparameter gekoppeld aan deindex
methodeparameter en wordt ook gebruikt om de productverzameling te binden. Als u de naam van deindex
parameter 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[]=2000
Voor alle voorgaande voorbeeldindelingen geeft modelbinding een matrix van twee items door aan de
selectedCourses
parameter:- 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
For Dictionary
targets, model binding looks for matches to parameter_name or property_name. Als er geen overeenkomst wordt gevonden, wordt gezocht naar een van de ondersteunde indelingen zonder het voorvoegsel. For example:
Stel dat de doelparameter een
Dictionary<int, string>
naamselectedCourses
heeft: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]=Economics
selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry& selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
[0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
Voor alle voorgaande voorbeeldindelingen geeft modelbinding een woordenlijst van twee items door aan de
selectedCourses
parameter:- 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
- Copy the code from QueryStringValueProviderFactory or RouteValueValueProviderFactory
- Replace the culture value passed to the value provider constructor with 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.
Input formatters
Gegevens in de aanvraagbody kunnen in JSON, XML of een andere indeling zijn. To parse this data, model binding uses an input formatter that is configured to handle a particular content type. 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 selects input formatters based on the Consumes attribute. If no attribute is present, it uses the Content-Type header.
Om de ingebouwde XML-invoerindelingen te gebruiken:
In
Program.cs
, bel AddXmlSerializerFormatters of AddXmlDataContractSerializerFormatters.builder.Services.AddControllers() .AddXmlSerializerFormatters();
Pas het
Consumes
kenmerk 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. You can customize ModelMetadata
by adding a details provider to 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 endpoints that consume JSON use Input formatters to deserialize the request body into an object.
For more information, see TryUpdateModelAsync.
[FromServices] attribute
De naam van dit kenmerk volgt het patroon van modelbindingskenmerken die een gegevensbron opgeven. Maar het gaat niet om bindingsgegevens van een waardeprovider. It gets an instance of a type from the dependency injection container. 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.
Additional resources
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. After each property is successfully bound, model validation occurs for that property. The record of what data is bound to the model, and any binding or validation errors, is stored in ControllerBase.ModelState or PageModel.ModelState. To find out if this process was successful, the app checks the ModelState.IsValid flag.
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
PageModel
klasse, indien opgegeven door kenmerken.
[BindProperty] attribute
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] attribute
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 true
in 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:
- Form fields
- De aanvraagbody (voor controllers die het kenmerk [ApiController] hebben.)
- Route data
- Parameters voor zoekreeks
- Uploaded files
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
IFormFile
ofIEnumerable<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.
These attributes:
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)
[FromBody] attribute
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
Breed
eigenschap 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.
Additional sources
Source data is provided to the model binding system by value providers. 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
IValueProvider
implementeert. - Maak een klasse die
IValueProviderFactory
implementeert. - Registreer de fabrieksklasse in
Program.cs
.
The sample includes a value provider and factory example that gets values from 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 id
is 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. Request body data is handled by input formatters.
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.
Simple types
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
Complex types
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. If nothing is found, it looks for just property_name without the prefix. 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
:
-
Id
ingesteld op100
. -
Name
ingesteld opnull
. Model binding verwachtInstructor.Name
omdatInstructor.Id
werd gebruikt in de voorgaande queryparameter.
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.ID
te 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.ID
te bekijken. Als dat niet wordt gevonden, wordt gezocht naar ID
zonder voorvoegsel.
Custom prefix
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.ID
te 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. They do not affect input formatters, which process posted JSON and XML request bodies. Invoerindelingen worden verderop in dit artikel uitgelegd.
[Bind] attribute
Kan worden toegepast op een klasse- of methodeparameter. Hiermee geeft u op welke eigenschappen van een model moeten worden opgenomen in modelbinding.
[Bind]
does not affect input formatters.
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)
The [Bind]
attribute can be used to protect against overposting in create scenarios. 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] attribute
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. For example:
[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; }
// ...
}
[BindRequired] attribute
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; }
}
See also the discussion of the [Required]
attribute in Model validation.
[BindNever] attribute
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
For targets that are collections of simple types, model binding looks for matches to parameter_name or property_name. Als er geen overeenkomst wordt gevonden, wordt gezocht naar een van de ondersteunde indelingen zonder het voorvoegsel. For example:
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=2000
selectedCourses[0]=1050&selectedCourses[1]=2000
[0]=1050&[1]=2000
selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
[a]=1050&[b]=2000&index=a&index=b
Vermijd het binden van een parameter of eigenschap met de naam
index
ofIndex
als deze grenzen aan een verzamelingswaarde. Modelbinding probeert omindex
als 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
index
queryreeksparameter gekoppeld aan deindex
methodeparameter en wordt ook gebruikt om de productverzameling te binden. Als u de naam van deindex
parameter 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[]=2000
Voor alle voorgaande voorbeeldindelingen geeft modelbinding een matrix van twee items door aan de
selectedCourses
parameter:- 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
For Dictionary
targets, model binding looks for matches to parameter_name or property_name. Als er geen overeenkomst wordt gevonden, wordt gezocht naar een van de ondersteunde indelingen zonder het voorvoegsel. For example:
Stel dat de doelparameter een
Dictionary<int, string>
naamselectedCourses
heeft: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]=Economics
selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry& selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
[0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
Voor alle voorgaande voorbeeldindelingen geeft modelbinding een woordenlijst van twee items door aan de
selectedCourses
parameter:- 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
- Copy the code from QueryStringValueProviderFactory or RouteValueValueProviderFactory
- Replace the culture value passed to the value provider constructor with 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.
Input formatters
Gegevens in de aanvraagbody kunnen in JSON, XML of een andere indeling zijn. To parse this data, model binding uses an input formatter that is configured to handle a particular content type. 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 selects input formatters based on the Consumes attribute. If no attribute is present, it uses the Content-Type header.
Om de ingebouwde XML-invoerindelingen te gebruiken:
In
Program.cs
, bel AddXmlSerializerFormatters of AddXmlDataContractSerializerFormatters.builder.Services.AddControllers() .AddXmlSerializerFormatters();
Pas het
Consumes
kenmerk 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. You can customize ModelMetadata
by adding a details provider to 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 endpoints that consume JSON use Input formatters to deserialize the request body into an object.
For more information, see TryUpdateModelAsync.
[FromServices] attribute
De naam van dit kenmerk volgt het patroon van modelbindingskenmerken die een gegevensbron opgeven. Maar het gaat niet om bindingsgegevens van een waardeprovider. It gets an instance of a type from the dependency injection container. 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.
Additional resources
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. After each property is successfully bound, model validation occurs for that property. The record of what data is bound to the model, and any binding or validation errors, is stored in ControllerBase.ModelState or PageModel.ModelState. To find out if this process was successful, the app checks the ModelState.IsValid flag.
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
PageModel
klasse, indien opgegeven door kenmerken.
[BindProperty] attribute
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] attribute
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 true
in 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:
- Form fields
- De aanvraagbody (voor controllers die het kenmerk [ApiController] hebben.)
- Route data
- Parameters voor zoekreeks
- Uploaded files
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
IFormFile
ofIEnumerable<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.
These attributes:
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)
[FromBody] attribute
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
Breed
eigenschap 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.
Additional sources
Source data is provided to the model binding system by value providers. 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
IValueProvider
implementeert. - Maak een klasse die
IValueProviderFactory
implementeert. - Registreer de fabrieksklasse in
Startup.ConfigureServices
.
The sample app includes a value provider and factory example that gets values from cookies. 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 id
is 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. Request body data is handled by input formatters.
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.
Simple types
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
Complex types
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.
For each property of the complex type, model binding looks through the sources for the name pattern prefix.property_name. If nothing is found, it looks for just property_name without the prefix.
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.ID
te 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.ID
te bekijken. Als dat niet wordt gevonden, wordt gezocht naar ID
zonder voorvoegsel.
Custom prefix
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.ID
te 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. They do not affect input formatters, which process posted JSON and XML request bodies. Invoerindelingen worden verderop in dit artikel uitgelegd.
[Bind] attribute
Kan worden toegepast op een klasse- of methodeparameter. Hiermee geeft u op welke eigenschappen van een model moeten worden opgenomen in modelbinding.
[Bind]
does not affect input formatters.
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)
The [Bind]
attribute can be used to protect against overposting in create scenarios. 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] attribute
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. For example:
[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; }
}
[BindRequired] attribute
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; }
See also the discussion of the [Required]
attribute in Model validation.
[BindNever] attribute
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
For targets that are collections of simple types, model binding looks for matches to parameter_name or property_name. Als er geen overeenkomst wordt gevonden, wordt gezocht naar een van de ondersteunde indelingen zonder het voorvoegsel. For example:
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=2000
selectedCourses[0]=1050&selectedCourses[1]=2000
[0]=1050&[1]=2000
selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
[a]=1050&[b]=2000&index=a&index=b
Vermijd het binden van een parameter of eigenschap met de naam
index
ofIndex
als deze grenzen aan een verzamelingswaarde. Modelbinding probeert omindex
als 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
index
queryreeksparameter gekoppeld aan deindex
methodeparameter en wordt ook gebruikt om de productverzameling te binden. Als u de naam van deindex
parameter 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[]=2000
Voor alle voorgaande voorbeeldindelingen geeft modelbinding een matrix van twee items door aan de
selectedCourses
parameter:- 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
For Dictionary
targets, model binding looks for matches to parameter_name or property_name. Als er geen overeenkomst wordt gevonden, wordt gezocht naar een van de ondersteunde indelingen zonder het voorvoegsel. For example:
Stel dat de doelparameter een
Dictionary<int, string>
naamselectedCourses
heeft: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]=Economics
selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry& selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
[0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
Voor alle voorgaande voorbeeldindelingen geeft modelbinding een woordenlijst van twee items door aan de
selectedCourses
parameter:- 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
- Copy the code from QueryStringValueProviderFactory or RouteValueValueProviderFactory
- Replace the culture value passed to the value provider constructor with 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.
Input formatters
Gegevens in de aanvraagbody kunnen in JSON, XML of een andere indeling zijn. To parse this data, model binding uses an input formatter that is configured to handle a particular content type. 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 selects input formatters based on the Consumes attribute. If no attribute is present, it uses the Content-Type header.
Om de ingebouwde XML-invoerindelingen te gebruiken:
Installeer het
Microsoft.AspNetCore.Mvc.Formatters.Xml
NuGet-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
Consumes
kenmerk 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. You can customize ModelMetadata
by adding a details provider to 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 endpoints that consume JSON use Input formatters to deserialize the request body into an object.
For more information, see TryUpdateModelAsync.
[FromServices] attribute
De naam van dit kenmerk volgt het patroon van modelbindingskenmerken die een gegevensbron opgeven. Maar het gaat niet om bindingsgegevens van een waardeprovider. It gets an instance of a type from the dependency injection container. Het doel is om een alternatief voor constructorinjectie te bieden wanneer u een service alleen nodig hebt als een bepaalde methode wordt aangeroepen.