Powiązanie modelu w ASP.NET Core
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.
Ostrzeżenie
Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz .NET i .NET Core Support Policy (Zasady obsługi platformy .NET Core). Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.
Ważne
Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.
W tym artykule wyjaśniono, czym jest powiązanie modelu, jak działa i jak dostosować jego zachowanie.
Co to jest powiązanie modelu
Kontrolery i Razor strony współpracują z danymi pochodzącymi z żądań HTTP. Na przykład dane trasy mogą zawierać klucz rekordu, a opublikowane pola formularza mogą zawierać wartości właściwości modelu. Pisanie kodu w celu pobrania każdej z tych wartości i przekonwertowanie ich z ciągów na typy platformy .NET byłoby żmudne i podatne na błędy. Powiązanie modelu automatyzuje ten proces. System powiązania modelu:
- Pobiera dane z różnych źródeł, takich jak dane trasy, pola formularza i ciągi zapytań.
- Udostępnia dane kontrolerom i Razor stronom w parametrach metody i właściwościach publicznych.
- Konwertuje dane ciągu na typy platformy .NET.
- Aktualizuje właściwości typów złożonych.
Przykład
Załóżmy, że masz następującą metodę akcji:
[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)
Aplikacja otrzymuje żądanie z następującym adresem URL:
https://contoso.com/api/pets/2?DogsOnly=true
Powiązanie modelu wykonuje następujące kroki po wybraniu przez system routingu metody akcji:
- Znajduje pierwszy parametr ,
GetById
liczba całkowita o nazwieid
. - Przegląda dostępne źródła w żądaniu HTTP i znajduje
id
= "2" w danych trasy. - Konwertuje ciąg "2" na liczbę całkowitą 2.
- Znajduje następny parametr ,
GetById
wartość logiczną o nazwiedogsOnly
. - Przegląda źródła i znajduje ciąg zapytania "DogsOnly=true". Dopasowywanie nazw nie jest uwzględniane wielkości liter.
- Konwertuje ciąg "true" na wartość logiczną
true
.
Następnie struktura wywołuje metodę GetById
, przekazując wartość 2 dla parametru id
i true
parametru dogsOnly
.
W poprzednim przykładzie docelowe powiązania modelu to parametry metody, które są prostymi typami. Obiekty docelowe mogą być również właściwościami typu złożonego. Po pomyślnym powiązaniu każdej właściwości walidacja modelu jest wykonywana dla tej właściwości. Rekord danych powiązanych z modelem oraz wszelkich błędów powiązań lub walidacji jest przechowywany w pliku ControllerBase.ModelState lub PageModel.ModelState. Aby dowiedzieć się, czy ten proces zakończył się pomyślnie, aplikacja sprawdza flagę ModelState.IsValid .
Elementy docelowe
Powiązanie modelu próbuje znaleźć wartości dla następujących rodzajów obiektów docelowych:
- Parametry metody akcji kontrolera, do którego jest kierowane żądanie.
- Razor Parametry metody obsługi stron, do których jest kierowane żądanie.
- Publiczne właściwości kontrolera lub
PageModel
klasy, jeśli są określone przez atrybuty.
[BindProperty] , atrybut
Można zastosować do właściwości publicznej kontrolera lub PageModel
klasy, aby spowodować powiązanie modelu z tą właściwością docelową:
public class EditModel : PageModel
{
[BindProperty]
public Instructor? Instructor { get; set; }
// ...
}
[BindProperties] , atrybut
Można zastosować do kontrolera lub PageModel
klasy, aby poinformować powiązanie modelu w celu kierowania wszystkich właściwości publicznych klasy:
[BindProperties]
public class CreateModel : PageModel
{
public Instructor? Instructor { get; set; }
// ...
}
Powiązanie modelu dla żądań HTTP GET
Domyślnie właściwości nie są powiązane z żądaniami HTTP GET. Zazwyczaj wszystko, czego potrzebujesz do żądania GET, to parametr identyfikatora rekordu. Identyfikator rekordu służy do wyszukiwania elementu w bazie danych. W związku z tym nie ma potrzeby wiązania właściwości, która zawiera wystąpienie modelu. W scenariuszach, w których chcesz, aby właściwości powiązane z danymi z żądań GET ustawiły SupportsGet
właściwość na true
:
[BindProperty(Name = "ai_user", SupportsGet = true)]
public string? ApplicationInsightsCookie { get; set; }
Tworzenie prostych i złożonych typów powiązania modelu
Powiązanie modelu używa określonych definicji dla typów, na których działa. Prosty typ jest konwertowany z jednego ciągu przy użyciu TypeConverter metody lub TryParse
metody. Typ złożony jest konwertowany z wielu wartości wejściowych. Struktura określa różnicę na podstawie istnienia obiektu TypeConverter
lub TryParse
. Zalecamy utworzenie konwertera typów lub użycie TryParse
metody do string
SomeType
konwersji, która nie wymaga zasobów zewnętrznych ani wielu danych wejściowych.
Źródła
Domyślnie powiązanie modelu pobiera dane w postaci par klucz-wartość z następujących źródeł w żądaniu HTTP:
- Pola formularza
- Treść żądania (dla kontrolerów, które mają atrybut [ApiController].
- Dane trasy
- Parametry ciągu zapytania
- Przekazane pliki
Dla każdego parametru docelowego lub właściwości źródła są skanowane w kolejności wskazanej na powyższej liście. Istnieje kilka wyjątków:
- Dane trasy i wartości ciągu zapytania są używane tylko dla prostych typów.
- Przekazane pliki są powiązane tylko z typami docelowymi, które implementują
IFormFile
program lubIEnumerable<IFormFile>
.
Jeśli domyślne źródło nie jest poprawne, użyj jednego z następujących atrybutów, aby określić źródło:
[FromQuery]
— Pobiera wartości z ciągu zapytania.[FromRoute]
- Pobiera wartości z danych trasy.[FromForm]
- Pobiera wartości z opublikowanych pól formularza.[FromBody]
— Pobiera wartości z treści żądania.[FromHeader]
- Pobiera wartości z nagłówków HTTP.
Te atrybuty:
Są dodawane do właściwości modelu indywidualnie, a nie do klasy modelu, jak w poniższym przykładzie:
public class Instructor { public int Id { get; set; } [FromQuery(Name = "Note")] public string? NoteFromQueryString { get; set; } // ... }
Opcjonalnie zaakceptuj wartość nazwy modelu w konstruktorze. Ta opcja jest dostępna w przypadku, gdy nazwa właściwości nie jest zgodna z wartością w żądaniu. Na przykład wartość w żądaniu może być nagłówkiem z łącznikiem w jego nazwie, jak w poniższym przykładzie:
public void OnGet([FromHeader(Name = "Accept-Language")] string language)
[FromBody] , atrybut
Zastosuj atrybut do parametru, [FromBody]
aby wypełnić jego właściwości z treści żądania HTTP. Środowisko uruchomieniowe ASP.NET Core deleguje odpowiedzialność za odczytywanie treści do elementu formatującego dane wejściowe. Formatery danych wejściowych zostały wyjaśnione w dalszej części tego artykułu.
Po [FromBody]
zastosowaniu do parametru typu złożonego wszystkie atrybuty źródła powiązania zastosowane do jego właściwości są ignorowane. Na przykład następująca Create
akcja określa, że jego pet
parametr jest wypełniany z treści:
public ActionResult<Pet> Create([FromBody] Pet pet)
Klasa Pet
określa, że jego Breed
właściwość jest wypełniana z parametru ciągu zapytania:
public class Pet
{
public string Name { get; set; } = null!;
[FromQuery] // Attribute is ignored.
public string Breed { get; set; } = null!;
}
W powyższym przykładzie:
- Atrybut
[FromQuery]
jest ignorowany. - Właściwość
Breed
nie jest wypełniana z parametru ciągu zapytania.
Formatery wejściowe odczytują tylko treść i nie rozumieją atrybutów źródła powiązania. Jeśli w treści znajduje się odpowiednia wartość, ta wartość jest używana do wypełnienia Breed
właściwości.
Nie należy stosować [FromBody]
do więcej niż jednego parametru dla metody akcji. Gdy strumień żądania zostanie odczytany przez program formatujący dane wejściowe, nie będzie już dostępny do ponownego odczytania w celu powiązania innych [FromBody]
parametrów.
Dodatkowe źródła
Dane źródłowe są dostarczane do systemu powiązania modelu przez dostawców wartości. Można zapisywać i rejestrować niestandardowych dostawców wartości, którzy pobierają dane na potrzeby powiązania modelu z innych źródeł. Możesz na przykład chcieć dane z plików cookie lub stanu sesji. Aby pobrać dane z nowego źródła:
- Utwórz klasę, która implementuje
IValueProvider
. - Utwórz klasę, która implementuje
IValueProviderFactory
. - Zarejestruj klasę fabryki w pliku
Program.cs
.
Przykład zawiera dostawcę wartości i przykład fabryki, który pobiera wartości z plików cookie. Zarejestruj fabryki dostawców wartości niestandardowych w programie Program.cs
:
builder.Services.AddControllers(options =>
{
options.ValueProviderFactories.Add(new CookieValueProviderFactory());
});
Powyższy kod umieszcza niestandardowego dostawcę wartości po wszystkich wbudowanych dostawcach wartości. Aby ustawić ją jako pierwszą na liście, wywołaj metodę Insert(0, new CookieValueProviderFactory())
zamiast Add
.
Brak źródła dla właściwości modelu
Domyślnie błąd stanu modelu nie jest tworzony, jeśli dla właściwości modelu nie zostanie znaleziona żadna wartość. Właściwość jest ustawiona na wartość null lub wartość domyślną:
- Proste typy dopuszczane do wartości null są ustawione na
null
wartość . - Typy wartości innych niż null są ustawione na
default(T)
wartość . Na przykład parametrint id
ma wartość 0. - W przypadku złożonych typów powiązanie modelu tworzy wystąpienie przy użyciu konstruktora domyślnego bez ustawiania właściwości.
- Tablice są ustawione na
Array.Empty<T>()
, z tą różnicą, żebyte[]
tablice są ustawione nanull
.
Jeśli stan modelu powinien zostać unieważniony, jeśli nic nie zostanie znalezione w polach formularza dla właściwości modelu, użyj atrybutu [BindRequired]
.
Należy pamiętać, że to [BindRequired]
zachowanie dotyczy powiązania modelu z opublikowanych danych formularza, a nie danych JSON lub XML w treści żądania. Dane treści żądania są obsługiwane przez osoby formatujące dane wejściowe.
Błędy konwersji typów
Jeśli źródło zostanie znalezione, ale nie można go przekonwertować na typ docelowy, stan modelu jest oznaczony jako nieprawidłowy. Docelowy parametr lub właściwość jest ustawiona na wartość null lub wartość domyślną, jak wspomniano w poprzedniej sekcji.
W kontrolerze interfejsu [ApiController]
API, który ma atrybut, nieprawidłowy stan modelu powoduje automatyczną odpowiedź HTTP 400.
Razor Na stronie redisplay strony z komunikatem o błędzie:
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
// ...
return RedirectToPage("./Index");
}
Gdy strona jest redisplayed przez poprzedni kod, nieprawidłowe dane wejściowe nie są wyświetlane w polu formularza. Jest to spowodowane tym, że właściwość modelu została ustawiona na wartość null lub wartość domyślną. Nieprawidłowe dane wejściowe są wyświetlane w komunikacie o błędzie. Jeśli chcesz ponownie odtworzyć nieprawidłowe dane w polu formularza, rozważ utworzenie właściwości modelu jako ciągu i ręczne przeprowadzenie konwersji danych.
Ta sama strategia jest zalecana, jeśli nie chcesz, aby błędy konwersji typów powodowały błędy stanu modelu. W takim przypadku utwórz właściwość modelu jako ciąg.
Typy proste
Aby uzyskać wyjaśnienie prostych i złożonych typów, zobacz Powiązania modelu proste i złożone typy.
Proste typy, które binder modelu mogą konwertować ciągi źródłowe na następujące:
- Wartość logiczna
- Bajty, SByte
- Char
- DateOnly
- Data/godzina
- DateTimeOffset
- Dziesiętne
- Podwójne
- Wyliczenie
- Guid
- Int16, Int32, Int64
- Pojedynczy
- TimeOnly
- Przedział czasu
- UInt16, UInt32, UInt64
- Identyfikator URI
- Wersja
Wiązanie z IParsable<T>.TryParse
Interfejs IParsable<TSelf>.TryParse
API obsługuje wartości parametrów akcji kontrolera powiązania:
public static bool TryParse (string? s, IFormatProvider? provider, out TSelf result);
DateRange
Następująca klasa implementuje IParsable<TSelf>
obsługę powiązania zakresu dat:
public class DateRange : IParsable<DateRange>
{
public DateOnly? From { get; init; }
public DateOnly? To { get; init; }
public static DateRange Parse(string value, IFormatProvider? provider)
{
if (!TryParse(value, provider, out var result))
{
throw new ArgumentException("Could not parse supplied value.", nameof(value));
}
return result;
}
public static bool TryParse(string? value,
IFormatProvider? provider, out DateRange dateRange)
{
var segments = value?.Split(',', StringSplitOptions.RemoveEmptyEntries
| StringSplitOptions.TrimEntries);
if (segments?.Length == 2
&& DateOnly.TryParse(segments[0], provider, out var fromDate)
&& DateOnly.TryParse(segments[1], provider, out var toDate))
{
dateRange = new DateRange { From = fromDate, To = toDate };
return true;
}
dateRange = new DateRange { From = default, To = default };
return false;
}
}
Powyższy kod ma następujące działanie:
- Konwertuje ciąg reprezentujący dwie daty na
DateRange
obiekt - Powiązanie modelu używa
IParsable<TSelf>.TryParse
metody , aby powiązaćDateRange
element .
Następująca akcja kontrolera używa DateRange
klasy do powiązania zakresu dat:
// GET /WeatherForecast/ByRange?range=7/24/2022,07/26/2022
public IActionResult ByRange([FromQuery] DateRange range)
{
if (!ModelState.IsValid)
return View("Error", ModelState.Values.SelectMany(v => v.Errors));
var weatherForecasts = Enumerable
.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.Where(wf => DateOnly.FromDateTime(wf.Date) >= range.From
&& DateOnly.FromDateTime(wf.Date) <= range.To)
.Select(wf => new WeatherForecastViewModel
{
Date = wf.Date.ToString("d"),
TemperatureC = wf.TemperatureC,
TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556),
Summary = wf.Summary
});
return View("Index", weatherForecasts);
}
Następująca Locale
klasa implementuje IParsable<TSelf>
obsługę powiązania z elementem 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;
}
}
}
Następująca akcja kontrolera używa Locale
klasy do powiązania CultureInfo
ciągu:
// 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);
}
Następująca akcja kontrolera używa DateRange
klas i Locale
do powiązania zakresu dat za pomocą polecenia 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);
}
Przykładowa aplikacja interfejsu API w usłudze GitHub przedstawia poprzedni przykład dla kontrolera interfejsu API.
Wiązanie z TryParse
Interfejs TryParse
API obsługuje wartości parametrów akcji kontrolera powiązania:
public static bool TryParse(string value, T out result);
public static bool TryParse(string value, IFormatProvider provider, T out result);
IParsable<T>.TryParse
jest zalecanym podejściem do powiązania parametrów, ponieważ w przeciwieństwie do TryParse
metody , nie zależy od odbicia.
DateRangeTP
Następująca klasa implementuje TryParse
element :
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;
}
}
Następująca akcja kontrolera używa DateRangeTP
klasy do powiązania zakresu dat:
// GET /WeatherForecast/ByRangeTP?range=7/24/2022,07/26/2022
public IActionResult ByRangeTP([FromQuery] DateRangeTP range)
{
if (!ModelState.IsValid)
return View("Error", ModelState.Values.SelectMany(v => v.Errors));
var weatherForecasts = Enumerable
.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.Where(wf => DateOnly.FromDateTime(wf.Date) >= range.From
&& DateOnly.FromDateTime(wf.Date) <= range.To)
.Select(wf => new WeatherForecastViewModel
{
Date = wf.Date.ToString("d"),
TemperatureC = wf.TemperatureC,
TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556),
Summary = wf.Summary
});
return View("Index", weatherForecasts);
}
Typy złożone
Typ złożony musi mieć publiczny konstruktor domyślny i publiczne właściwości zapisywalne do powiązania. Po utworzeniu powiązania modelu klasę tworzy się przy użyciu publicznego konstruktora domyślnego.
Dla każdej właściwości typu złożonego powiązanie modelu analizuje źródła wzorca nazw prefix.property_name. Jeśli nic nie zostanie znalezione, szuka tylko property_name bez prefiksu. Decyzja o użyciu prefiksu nie jest podjęta na właściwość. Na przykład z zapytaniem zawierającym ?Instructor.Id=100&Name=foo
metodę , powiązana z metodą OnGet(Instructor instructor)
, wynikowy obiekt typu Instructor
zawiera:
Id
ustaw wartość100
.Name
ustaw wartośćnull
. Powiązanie modelu oczekuje,Instructor.Name
ponieważInstructor.Id
zostało użyte w poprzednim parametrze zapytania.
W przypadku powiązania z parametrem prefiks jest nazwą parametru. W przypadku powiązania z właściwością publiczną PageModel
prefiks jest nazwą właściwości publicznej. Niektóre atrybuty mają Prefix
właściwość, która umożliwia zastąpienie domyślnego użycia parametru lub nazwy właściwości.
Załóżmy na przykład, że typ złożony to następująca Instructor
klasa:
public class Instructor
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
Prefiks = nazwa parametru
Jeśli model, który ma być powiązany, jest parametrem o nazwie instructorToUpdate
:
public IActionResult OnPost(int? id, Instructor instructorToUpdate)
Powiązanie modelu rozpoczyna się od przejrzenia źródeł klucza instructorToUpdate.ID
. Jeśli to nie zostanie znalezione, szuka ID
bez prefiksu.
Prefiks = nazwa właściwości
Jeśli model, który ma być powiązany, jest właściwością o nazwie Instructor
kontrolera lub PageModel
klasy:
[BindProperty]
public Instructor Instructor { get; set; }
Powiązanie modelu rozpoczyna się od przejrzenia źródeł klucza Instructor.ID
. Jeśli to nie zostanie znalezione, szuka ID
bez prefiksu.
Prefiks niestandardowy
Jeśli model, który ma być powiązany, jest parametrem o nazwie instructorToUpdate
, a Bind
atrybut określa Instructor
jako prefiks:
public IActionResult OnPost(
int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)
Powiązanie modelu rozpoczyna się od przejrzenia źródeł klucza Instructor.ID
. Jeśli to nie zostanie znalezione, szuka ID
bez prefiksu.
Atrybuty dla obiektów docelowych typu złożonego
Dostępnych jest kilka wbudowanych atrybutów do kontrolowania powiązania modelu typów złożonych:
Ostrzeżenie
Te atrybuty wpływają na powiązanie modelu, gdy opublikowane dane formularza są źródłem wartości. Nie mają one wpływu na formatery danych wejściowych, które przetwarzają treść żądań JSON i XML. Formatery danych wejściowych zostały wyjaśnione w dalszej części tego artykułu.
[Bind] , atrybut
Można zastosować do klasy lub parametru metody. Określa, które właściwości modelu powinny być uwzględnione w powiązaniu modelu. [Bind]
nie ma wpływu na formatery wejściowe.
W poniższym przykładzie tylko określone właściwości Instructor
modelu są powiązane, gdy wywoływana jest dowolna metoda obsługi lub akcji:
[Bind("LastName,FirstMidName,HireDate")]
public class Instructor
W poniższym przykładzie po wywołaniu OnPost
metody są powiązane tylko określone właściwości Instructor
modelu:
[HttpPost]
public IActionResult OnPost(
[Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
Atrybut [Bind]
może służyć do ochrony przed przesłanianiem w scenariuszach tworzenia . Nie działa dobrze w scenariuszach edycji, ponieważ wykluczone właściwości są ustawione na wartość null lub wartość domyślną, a nie pozostawione bez zmian. W przypadku ochrony przed przesłanianiem zalecane są modele wyświetlania, a nie atrybut.[Bind]
Aby uzyskać więcej informacji, zobacz Uwaga dotycząca zabezpieczeń dotycząca zastępowania.
[ModelBinder] , atrybut
ModelBinderAttribute można stosować do typów, właściwości lub parametrów. Umożliwia określenie typu powiązania modelu używanego do powiązania określonego wystąpienia lub typu. Na przykład:
[HttpPost]
public IActionResult OnPost(
[ModelBinder<MyInstructorModelBinder>] Instructor instructor)
Atrybut [ModelBinder]
może również służyć do zmiany nazwy właściwości lub parametru, gdy jest powiązany model:
public class Instructor
{
[ModelBinder(Name = "instructor_id")]
public string Id { get; set; }
// ...
}
[BindRequired] , atrybut
Powoduje, że powiązanie modelu powoduje dodanie błędu stanu modelu, jeśli powiązanie nie może wystąpić dla właściwości modelu. Oto przykład:
public class InstructorBindRequired
{
// ...
[BindRequired]
public DateTime HireDate { get; set; }
}
Zobacz również omówienie atrybutu [Required]
w temacie Walidacja modelu.
[BindNever] , atrybut
Można zastosować do właściwości lub typu. Uniemożliwia ustawienie właściwości modelu przez powiązanie modelu. Po zastosowaniu do typu system powiązań modelu wyklucza wszystkie właściwości zdefiniowane przez typ. Oto przykład:
public class InstructorBindNever
{
[BindNever]
public int Id { get; set; }
// ...
}
Kolekcje
W przypadku obiektów docelowych, które są kolekcjami prostych typów, powiązanie modelu wyszukuje dopasowania do parameter_name lub property_name. Jeśli nie zostanie znalezione dopasowanie, szuka jednego z obsługiwanych formatów bez prefiksu. Na przykład:
Załóżmy, że parametr, który ma być powiązany, to tablica o nazwie
selectedCourses
:public IActionResult OnPost(int? id, int[] selectedCourses)
Dane formularza lub ciągu zapytania mogą być w jednym z następujących formatów:
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
Unikaj powiązania parametru lub właściwości o nazwie
index
lubIndex
, jeśli sąsiaduje z wartością kolekcji. Powiązanie modelu próbuje użyćindex
jako indeksu kolekcji, co może spowodować nieprawidłowe powiązanie. Rozważmy na przykład następującą akcję:public IActionResult Post(string index, List<Product> products)
W poprzednim kodzie
index
parametr ciągu zapytania jest powiązany z parametremindex
metody, a także jest używany do powiązania kolekcji produktów. Zmiana nazwy parametruindex
lub użycie atrybutu powiązania modelu w celu skonfigurowania powiązania pozwala uniknąć tego problemu:public IActionResult Post(string productIndex, List<Product> products)
Następujący format jest obsługiwany tylko w danych formularza:
selectedCourses[]=1050&selectedCourses[]=2000
We wszystkich poprzednich przykładowych formatach powiązanie modelu przekazuje tablicę dwóch elementów do parametru
selectedCourses
:- selectedCourses[0]=1050
- selectedCourses[1]=2000
Formaty danych używające liczb w indeksie dolnym (... [0] ... [1] ...) musi upewnić się, że są one numerowane sekwencyjnie rozpoczynające się od zera. Jeśli występują luki w numerowaniu indeksu dolnego, wszystkie elementy po przerwie są ignorowane. Jeśli na przykład indeksy dolny to 0 i 2 zamiast 0 i 1, drugi element jest ignorowany.
Słowniki
W przypadku Dictionary
obiektów docelowych powiązanie modelu wyszukuje dopasowania do parameter_name lub property_name. Jeśli nie zostanie znalezione dopasowanie, szuka jednego z obsługiwanych formatów bez prefiksu. Na przykład:
Załóżmy, że parametr docelowy
Dictionary<int, string>
ma nazwęselectedCourses
:public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
Dane opublikowanego formularza lub ciągu zapytania mogą wyglądać podobnie do jednego z następujących przykładów:
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
We wszystkich poprzednich przykładowych formatach powiązanie modelu przekazuje słownik dwóch elementów do parametru
selectedCourses
:- selectedCourses["1050"]="Chemistry"
- selectedCourses["2000"]="Economics"
Powiązanie konstruktora i typy rekordów
Powiązanie modelu wymaga, aby złożone typy miały konstruktor bez parametrów. Zarówno System.Text.Json
metody formatowania danych wejściowych, jak i Newtonsoft.Json
obsługują deserializację klas, które nie mają konstruktora bez parametrów.
Typy rekordów to doskonały sposób, aby zwięźle reprezentować dane za pośrednictwem sieci. ASP.NET Core obsługuje wiązanie modelu i weryfikowanie typów rekordów za pomocą jednego konstruktora:
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>
Podczas sprawdzania poprawności typów rekordów środowisko uruchomieniowe wyszukuje metadane powiązania i walidacji w szczególności na parametrach, a nie we właściwościach.
Platforma umożliwia wiązanie i weryfikowanie typów rekordów:
public record Person([Required] string Name, [Range(0, 100)] int Age);
Aby poprzedni element działał, typ musi:
- Być typem rekordu.
- Mieć dokładnie jeden publiczny konstruktor.
- Zawierają parametry, które mają właściwość o tej samej nazwie i typie. Nazwy nie mogą się różnić literami.
Obiekty POCO bez konstruktorów bez parametrów
Obiekty POC, które nie mają konstruktorów bez parametrów, nie mogą być powiązane.
Poniższy kod powoduje wyjątek z informacją, że typ musi mieć konstruktor bez parametrów:
public class Person(string Name)
public record Person([Required] string Name, [Range(0, 100)] int Age)
{
public Person(string Name) : this (Name, 0);
}
Typy rekordów z ręcznie utworzonymi konstruktorami
Typy rekordów z ręcznie utworzonymi konstruktorami, które wyglądają jak konstruktory podstawowe działają
public record Person
{
public Person([Required] string Name, [Range(0, 100)] int Age)
=> (this.Name, this.Age) = (Name, Age);
public string Name { get; set; }
public int Age { get; set; }
}
Typy rekordów, walidacja i metadane powiązania
W przypadku typów rekordów używane są metadane walidacji i powiązania parametrów. Wszystkie metadane właściwości są ignorowane
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; }
}
Walidacja i metadane
Walidacja używa metadanych w parametrze, ale używa właściwości do odczytania wartości. W zwykłych przypadkach z konstruktorami podstawowymi te dwa byłyby identyczne. Istnieją jednak sposoby, aby go pokonać:
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; }
}
Funkcja TryUpdateModel nie aktualizuje parametrów typu rekordu
public record Person(string Name)
{
public int Age { get; set; }
}
var person = new Person("initial-name");
TryUpdateModel(person, ...);
W takim przypadku usługa MVC nie podejmie ponownej próby powiązania Name
. Age
Można jednak zaktualizować
Zachowanie globalizacji danych trasy powiązania modelu i ciągów zapytań
Dostawca wartości trasy ASP.NET Core i dostawca wartości ciągu zapytania:
- Traktuj wartości jako niezmienną kulturę.
- Spodziewaj się, że adresy URL są niezmienne dla kultury.
Natomiast wartości pochodzące z danych formularza są poddawane konwersji wrażliwej na kulturę. Jest to zaprojektowane tak, aby adresy URL można udostępniać między ustawieniami regionalnymi.
Aby dostawca wartości trasy ASP.NET Core i dostawca wartości ciągu zapytania przeszedł konwersję wrażliwą na kulturę:
- Dziedzicz z IValueProviderFactory
- Skopiuj kod z elementu QueryStringValueProviderFactory lub RouteValueValueProviderFactory
- Zastąp wartość kultury przekazaną do konstruktora dostawcy wartości atrybutem CultureInfo.CurrentCulture
- Zastąp domyślną fabrykę dostawcy wartości w opcjach MVC nowym:
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();
});
Specjalne typy danych
Istnieją pewne specjalne typy danych, które mogą obsłużyć powiązanie modelu.
IFormFile i IFormFileCollection
Przekazany plik uwzględniony w żądaniu HTTP. Obsługiwane jest IEnumerable<IFormFile>
również w przypadku wielu plików.
CancellationToken
Akcje mogą opcjonalnie powiązać element CancellationToken
jako parametr. RequestAborted Wiąże się to z sygnałem, gdy połączenie bazowe żądania HTTP zostanie przerwane. Akcje mogą używać tego parametru do anulowania długotrwałych operacji asynchronicznych wykonywanych w ramach akcji kontrolera.
FormCollection
Służy do pobierania wszystkich wartości z opublikowanych danych formularza.
Formatery danych wejściowych
Dane w treści żądania mogą być w formacie JSON, XML lub innym formacie. Aby przeanalizować te dane, powiązanie modelu używa formatującego danych wejściowych skonfigurowanego do obsługi określonego typu zawartości. Domyślnie ASP.NET Core zawiera formatery wejściowe oparte na formacie JSON do obsługi danych JSON. Możesz dodać inne formatery dla innych typów zawartości.
ASP.NET Core wybiera formatery wejściowe na podstawie atrybutu Consumes . Jeśli atrybut nie jest obecny, używa nagłówka Content-Type.
Aby użyć wbudowanych formatów wejściowych XML:
W
Program.cs
pliku wywołaj metodę AddXmlSerializerFormatters lub AddXmlDataContractSerializerFormatters.builder.Services.AddControllers() .AddXmlSerializerFormatters();
Consumes
Zastosuj atrybut do klas kontrolerów lub metod akcji, które powinny oczekiwać XML w treści żądania.[HttpPost] [Consumes("application/xml")] public ActionResult<Pet> Create(Pet pet)
Aby uzyskać więcej informacji, zobacz Wprowadzenie serializacji XML.
Dostosowywanie powiązania modelu za pomocą formaterów wejściowych
Moduł formatujący dane wejściowe ponosi pełną odpowiedzialność za odczytywanie danych z treści żądania. Aby dostosować ten proces, skonfiguruj interfejsy API używane przez program formatujący dane wejściowe. W tej sekcji opisano sposób dostosowywania opartego na formatatorze System.Text.Json
danych wejściowych w celu zrozumienia niestandardowego typu o nazwie ObjectId
.
Rozważmy następujący model, który zawiera właściwość niestandardową ObjectId
:
public class InstructorObjectId
{
[Required]
public ObjectId ObjectId { get; set; } = null!;
}
Aby dostosować proces powiązania modelu podczas używania metody System.Text.Json
, utwórz klasę pochodzącą z JsonConverter<T>klasy :
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);
}
Aby użyć konwertera niestandardowego, zastosuj JsonConverterAttribute atrybut do typu. W poniższym przykładzie ObjectId
typ jest skonfigurowany ObjectIdConverter
jako konwerter niestandardowy:
[JsonConverter(typeof(ObjectIdConverter))]
public record ObjectId(int Id);
Aby uzyskać więcej informacji, zobacz Jak pisać konwertery niestandardowe.
Wykluczanie określonych typów z powiązania modelu
Zachowanie systemów powiązań i walidacji modelu jest sterowane przez ModelMetadataprogram . Możesz dostosować ModelMetadata
, dodając dostawcę szczegółów do elementu MvcOptions.ModelMetadataDetailsProviders. Wbudowani dostawcy szczegółów są dostępni do wyłączania powiązania modelu lub walidacji dla określonych typów.
Aby wyłączyć powiązanie modelu dla wszystkich modeli określonego typu, dodaj element w elemExcludeBindingMetadataProvider.Program.cs
Aby na przykład wyłączyć powiązanie modelu dla wszystkich modeli typu System.Version
:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
Aby wyłączyć walidację właściwości określonego typu, dodaj element SuppressChildValidationMetadataProvider w Program.cs
pliku . Aby na przykład wyłączyć walidację właściwości typu System.Guid
:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
Niestandardowe powiązania modelu
Powiązanie modelu można rozszerzyć, pisząc powiązanie modelu niestandardowego i używając atrybutu [ModelBinder]
, aby wybrać go dla danego obiektu docelowego. Dowiedz się więcej o powiązaniu modelu niestandardowego.
Ręczne powiązanie modelu
Powiązanie modelu można wywołać ręcznie przy użyciu TryUpdateModelAsync metody . Metoda jest definiowana w klasach ControllerBase
i PageModel
. Przeciążenia metody umożliwiają określenie prefiksu i dostawcy wartości do użycia. Metoda zwraca false
wartość , jeśli powiązanie modelu nie powiedzie się. Oto przykład:
if (await TryUpdateModelAsync(
newInstructor,
"Instructor",
x => x.Name, x => x.HireDate!))
{
_instructorStore.Add(newInstructor);
return RedirectToPage("./Index");
}
return Page();
TryUpdateModelAsync używa dostawców wartości do pobierania danych z treści formularza, ciągu zapytania i kierowania danych. TryUpdateModelAsync
jest zwykle:
- Używane z aplikacjami Razor Pages i MVC przy użyciu kontrolerów i widoków, aby zapobiec nadmiernemu delegowaniu.
- Nie jest używany z internetowym interfejsem API, chyba że są używane z danych formularza, ciągów zapytań i danych trasy. Punkty końcowe internetowego interfejsu API, które używają formatu JSON, używają formatatorów wejściowych do deserializacji treści żądania do obiektu.
Aby uzyskać więcej informacji, zobacz TryUpdateModelAsync.
[FromServices] , atrybut
Nazwa tego atrybutu jest zgodna ze wzorcem atrybutów powiązania modelu, które określają źródło danych. Nie chodzi jednak o powiązanie danych od dostawcy wartości. Pobiera wystąpienie typu z kontenera wstrzykiwania zależności. Jego celem jest zapewnienie alternatywy dla iniekcji konstruktora, jeśli potrzebujesz usługi tylko wtedy, gdy wywoływana jest określona metoda.
Jeśli wystąpienie typu nie jest zarejestrowane w kontenerze wstrzykiwania zależności, aplikacja zgłasza wyjątek podczas próby powiązania parametru. Aby ustawić parametr jako opcjonalny, użyj jednego z następujących metod:
- Ustaw parametr na wartość null.
- Ustaw wartość domyślną parametru.
W przypadku parametrów dopuszczających wartość null upewnij się, że parametr nie null
jest używany przed uzyskaniem do niego dostępu.
Dodatkowe zasoby
W tym artykule wyjaśniono, czym jest powiązanie modelu, jak działa i jak dostosować jego zachowanie.
Co to jest powiązanie modelu
Kontrolery i Razor strony współpracują z danymi pochodzącymi z żądań HTTP. Na przykład dane trasy mogą zawierać klucz rekordu, a opublikowane pola formularza mogą zawierać wartości właściwości modelu. Pisanie kodu w celu pobrania każdej z tych wartości i przekonwertowanie ich z ciągów na typy platformy .NET byłoby żmudne i podatne na błędy. Powiązanie modelu automatyzuje ten proces. System powiązania modelu:
- Pobiera dane z różnych źródeł, takich jak dane trasy, pola formularza i ciągi zapytań.
- Udostępnia dane kontrolerom i Razor stronom w parametrach metody i właściwościach publicznych.
- Konwertuje dane ciągu na typy platformy .NET.
- Aktualizuje właściwości typów złożonych.
Przykład
Załóżmy, że masz następującą metodę akcji:
[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)
Aplikacja otrzymuje żądanie z następującym adresem URL:
https://contoso.com/api/pets/2?DogsOnly=true
Powiązanie modelu wykonuje następujące kroki po wybraniu przez system routingu metody akcji:
- Znajduje pierwszy parametr ,
GetById
liczba całkowita o nazwieid
. - Przegląda dostępne źródła w żądaniu HTTP i znajduje
id
= "2" w danych trasy. - Konwertuje ciąg "2" na liczbę całkowitą 2.
- Znajduje następny parametr ,
GetById
wartość logiczną o nazwiedogsOnly
. - Przegląda źródła i znajduje ciąg zapytania "DogsOnly=true". Dopasowywanie nazw nie jest uwzględniane wielkości liter.
- Konwertuje ciąg "true" na wartość logiczną
true
.
Następnie struktura wywołuje metodę GetById
, przekazując wartość 2 dla parametru id
i true
parametru dogsOnly
.
W poprzednim przykładzie docelowe powiązania modelu to parametry metody, które są prostymi typami. Obiekty docelowe mogą być również właściwościami typu złożonego. Po pomyślnym powiązaniu każdej właściwości walidacja modelu jest wykonywana dla tej właściwości. Rekord danych powiązanych z modelem oraz wszelkich błędów powiązań lub walidacji jest przechowywany w pliku ControllerBase.ModelState lub PageModel.ModelState. Aby dowiedzieć się, czy ten proces zakończył się pomyślnie, aplikacja sprawdza flagę ModelState.IsValid .
Elementy docelowe
Powiązanie modelu próbuje znaleźć wartości dla następujących rodzajów obiektów docelowych:
- Parametry metody akcji kontrolera, do którego jest kierowane żądanie.
- Razor Parametry metody obsługi stron, do których jest kierowane żądanie.
- Publiczne właściwości kontrolera lub
PageModel
klasy, jeśli są określone przez atrybuty.
[BindProperty] , atrybut
Można zastosować do właściwości publicznej kontrolera lub PageModel
klasy, aby spowodować powiązanie modelu z tą właściwością docelową:
public class EditModel : PageModel
{
[BindProperty]
public Instructor? Instructor { get; set; }
// ...
}
[BindProperties] , atrybut
Można zastosować do kontrolera lub PageModel
klasy, aby poinformować powiązanie modelu w celu kierowania wszystkich właściwości publicznych klasy:
[BindProperties]
public class CreateModel : PageModel
{
public Instructor? Instructor { get; set; }
// ...
}
Powiązanie modelu dla żądań HTTP GET
Domyślnie właściwości nie są powiązane z żądaniami HTTP GET. Zazwyczaj wszystko, czego potrzebujesz do żądania GET, to parametr identyfikatora rekordu. Identyfikator rekordu służy do wyszukiwania elementu w bazie danych. W związku z tym nie ma potrzeby wiązania właściwości, która zawiera wystąpienie modelu. W scenariuszach, w których chcesz, aby właściwości powiązane z danymi z żądań GET ustawiły SupportsGet
właściwość na true
:
[BindProperty(Name = "ai_user", SupportsGet = true)]
public string? ApplicationInsightsCookie { get; set; }
Tworzenie prostych i złożonych typów powiązania modelu
Powiązanie modelu używa określonych definicji dla typów, na których działa. Prosty typ jest konwertowany z jednego ciągu przy użyciu TypeConverter metody lub TryParse
metody. Typ złożony jest konwertowany z wielu wartości wejściowych. Struktura określa różnicę na podstawie istnienia obiektu TypeConverter
lub TryParse
. Zalecamy utworzenie konwertera typów lub użycie TryParse
metody do string
SomeType
konwersji, która nie wymaga zasobów zewnętrznych ani wielu danych wejściowych.
Źródła
Domyślnie powiązanie modelu pobiera dane w postaci par klucz-wartość z następujących źródeł w żądaniu HTTP:
- Pola formularza
- Treść żądania (dla kontrolerów, które mają atrybut [ApiController].
- Dane trasy
- Parametry ciągu zapytania
- Przekazane pliki
Dla każdego parametru docelowego lub właściwości źródła są skanowane w kolejności wskazanej na powyższej liście. Istnieje kilka wyjątków:
- Dane trasy i wartości ciągu zapytania są używane tylko dla prostych typów.
- Przekazane pliki są powiązane tylko z typami docelowymi, które implementują
IFormFile
program lubIEnumerable<IFormFile>
.
Jeśli domyślne źródło nie jest poprawne, użyj jednego z następujących atrybutów, aby określić źródło:
[FromQuery]
— Pobiera wartości z ciągu zapytania.[FromRoute]
- Pobiera wartości z danych trasy.[FromForm]
- Pobiera wartości z opublikowanych pól formularza.[FromBody]
— Pobiera wartości z treści żądania.[FromHeader]
- Pobiera wartości z nagłówków HTTP.
Te atrybuty:
Są dodawane do właściwości modelu indywidualnie, a nie do klasy modelu, jak w poniższym przykładzie:
public class Instructor { public int Id { get; set; } [FromQuery(Name = "Note")] public string? NoteFromQueryString { get; set; } // ... }
Opcjonalnie zaakceptuj wartość nazwy modelu w konstruktorze. Ta opcja jest dostępna w przypadku, gdy nazwa właściwości nie jest zgodna z wartością w żądaniu. Na przykład wartość w żądaniu może być nagłówkiem z łącznikiem w jego nazwie, jak w poniższym przykładzie:
public void OnGet([FromHeader(Name = "Accept-Language")] string language)
[FromBody] , atrybut
Zastosuj atrybut do parametru, [FromBody]
aby wypełnić jego właściwości z treści żądania HTTP. Środowisko uruchomieniowe ASP.NET Core deleguje odpowiedzialność za odczytywanie treści do elementu formatującego dane wejściowe. Formatery danych wejściowych zostały wyjaśnione w dalszej części tego artykułu.
Po [FromBody]
zastosowaniu do parametru typu złożonego wszystkie atrybuty źródła powiązania zastosowane do jego właściwości są ignorowane. Na przykład następująca Create
akcja określa, że jego pet
parametr jest wypełniany z treści:
public ActionResult<Pet> Create([FromBody] Pet pet)
Klasa Pet
określa, że jego Breed
właściwość jest wypełniana z parametru ciągu zapytania:
public class Pet
{
public string Name { get; set; } = null!;
[FromQuery] // Attribute is ignored.
public string Breed { get; set; } = null!;
}
W powyższym przykładzie:
- Atrybut
[FromQuery]
jest ignorowany. - Właściwość
Breed
nie jest wypełniana z parametru ciągu zapytania.
Formatery wejściowe odczytują tylko treść i nie rozumieją atrybutów źródła powiązania. Jeśli w treści znajduje się odpowiednia wartość, ta wartość jest używana do wypełnienia Breed
właściwości.
Nie należy stosować [FromBody]
do więcej niż jednego parametru dla metody akcji. Gdy strumień żądania zostanie odczytany przez program formatujący dane wejściowe, nie będzie już dostępny do ponownego odczytania w celu powiązania innych [FromBody]
parametrów.
Dodatkowe źródła
Dane źródłowe są dostarczane do systemu powiązania modelu przez dostawców wartości. Można zapisywać i rejestrować niestandardowych dostawców wartości, którzy pobierają dane na potrzeby powiązania modelu z innych źródeł. Możesz na przykład chcieć dane z plików cookie lub stanu sesji. Aby pobrać dane z nowego źródła:
- Utwórz klasę, która implementuje
IValueProvider
. - Utwórz klasę, która implementuje
IValueProviderFactory
. - Zarejestruj klasę fabryki w pliku
Program.cs
.
Przykład zawiera dostawcę wartości i przykład fabryki, który pobiera wartości z plików cookie. Zarejestruj fabryki dostawców wartości niestandardowych w programie Program.cs
:
builder.Services.AddControllers(options =>
{
options.ValueProviderFactories.Add(new CookieValueProviderFactory());
});
Powyższy kod umieszcza niestandardowego dostawcę wartości po wszystkich wbudowanych dostawcach wartości. Aby ustawić ją jako pierwszą na liście, wywołaj metodę Insert(0, new CookieValueProviderFactory())
zamiast Add
.
Brak źródła dla właściwości modelu
Domyślnie błąd stanu modelu nie jest tworzony, jeśli dla właściwości modelu nie zostanie znaleziona żadna wartość. Właściwość jest ustawiona na wartość null lub wartość domyślną:
- Proste typy dopuszczane do wartości null są ustawione na
null
wartość . - Typy wartości innych niż null są ustawione na
default(T)
wartość . Na przykład parametrint id
ma wartość 0. - W przypadku złożonych typów powiązanie modelu tworzy wystąpienie przy użyciu konstruktora domyślnego bez ustawiania właściwości.
- Tablice są ustawione na
Array.Empty<T>()
, z tą różnicą, żebyte[]
tablice są ustawione nanull
.
Jeśli stan modelu powinien zostać unieważniony, jeśli nic nie zostanie znalezione w polach formularza dla właściwości modelu, użyj atrybutu [BindRequired]
.
Należy pamiętać, że to [BindRequired]
zachowanie dotyczy powiązania modelu z opublikowanych danych formularza, a nie danych JSON lub XML w treści żądania. Dane treści żądania są obsługiwane przez osoby formatujące dane wejściowe.
Błędy konwersji typów
Jeśli źródło zostanie znalezione, ale nie można go przekonwertować na typ docelowy, stan modelu jest oznaczony jako nieprawidłowy. Docelowy parametr lub właściwość jest ustawiona na wartość null lub wartość domyślną, jak wspomniano w poprzedniej sekcji.
W kontrolerze interfejsu [ApiController]
API, który ma atrybut, nieprawidłowy stan modelu powoduje automatyczną odpowiedź HTTP 400.
Razor Na stronie redisplay strony z komunikatem o błędzie:
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
// ...
return RedirectToPage("./Index");
}
Gdy strona jest redisplayed przez poprzedni kod, nieprawidłowe dane wejściowe nie są wyświetlane w polu formularza. Jest to spowodowane tym, że właściwość modelu została ustawiona na wartość null lub wartość domyślną. Nieprawidłowe dane wejściowe są wyświetlane w komunikacie o błędzie. Jeśli chcesz ponownie odtworzyć nieprawidłowe dane w polu formularza, rozważ utworzenie właściwości modelu jako ciągu i ręczne przeprowadzenie konwersji danych.
Ta sama strategia jest zalecana, jeśli nie chcesz, aby błędy konwersji typów powodowały błędy stanu modelu. W takim przypadku utwórz właściwość modelu jako ciąg.
Typy proste
Aby uzyskać wyjaśnienie prostych i złożonych typów, zobacz Powiązania modelu proste i złożone typy.
Proste typy, które binder modelu mogą konwertować ciągi źródłowe na następujące:
- Wartość logiczna
- Bajty, SByte
- Char
- DateOnly
- Data/godzina
- DateTimeOffset
- Dziesiętne
- Podwójne
- Wyliczenie
- Guid
- Int16, Int32, Int64
- Pojedynczy
- TimeOnly
- Przedział czasu
- UInt16, UInt32, UInt64
- Identyfikator URI
- Wersja
Wiązanie z IParsable<T>.TryParse
Interfejs IParsable<TSelf>.TryParse
API obsługuje wartości parametrów akcji kontrolera powiązania:
public static bool TryParse (string? s, IFormatProvider? provider, out TSelf result);
DateRange
Następująca klasa implementuje IParsable<TSelf>
obsługę powiązania zakresu dat:
public class DateRange : IParsable<DateRange>
{
public DateOnly? From { get; init; }
public DateOnly? To { get; init; }
public static DateRange Parse(string value, IFormatProvider? provider)
{
if (!TryParse(value, provider, out var result))
{
throw new ArgumentException("Could not parse supplied value.", nameof(value));
}
return result;
}
public static bool TryParse(string? value,
IFormatProvider? provider, out DateRange dateRange)
{
var segments = value?.Split(',', StringSplitOptions.RemoveEmptyEntries
| StringSplitOptions.TrimEntries);
if (segments?.Length == 2
&& DateOnly.TryParse(segments[0], provider, out var fromDate)
&& DateOnly.TryParse(segments[1], provider, out var toDate))
{
dateRange = new DateRange { From = fromDate, To = toDate };
return true;
}
dateRange = new DateRange { From = default, To = default };
return false;
}
}
Powyższy kod ma następujące działanie:
- Konwertuje ciąg reprezentujący dwie daty na
DateRange
obiekt - Powiązanie modelu używa
IParsable<TSelf>.TryParse
metody , aby powiązaćDateRange
element .
Następująca akcja kontrolera używa DateRange
klasy do powiązania zakresu dat:
// GET /WeatherForecast/ByRange?range=7/24/2022,07/26/2022
public IActionResult ByRange([FromQuery] DateRange range)
{
if (!ModelState.IsValid)
return View("Error", ModelState.Values.SelectMany(v => v.Errors));
var weatherForecasts = Enumerable
.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.Where(wf => DateOnly.FromDateTime(wf.Date) >= range.From
&& DateOnly.FromDateTime(wf.Date) <= range.To)
.Select(wf => new WeatherForecastViewModel
{
Date = wf.Date.ToString("d"),
TemperatureC = wf.TemperatureC,
TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556),
Summary = wf.Summary
});
return View("Index", weatherForecasts);
}
Następująca Locale
klasa implementuje IParsable<TSelf>
obsługę powiązania z elementem 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;
}
}
}
Następująca akcja kontrolera używa Locale
klasy do powiązania CultureInfo
ciągu:
// 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);
}
Następująca akcja kontrolera używa DateRange
klas i Locale
do powiązania zakresu dat za pomocą polecenia 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);
}
Przykładowa aplikacja interfejsu API w usłudze GitHub przedstawia poprzedni przykład dla kontrolera interfejsu API.
Wiązanie z TryParse
Interfejs TryParse
API obsługuje wartości parametrów akcji kontrolera powiązania:
public static bool TryParse(string value, T out result);
public static bool TryParse(string value, IFormatProvider provider, T out result);
IParsable<T>.TryParse
jest zalecanym podejściem do powiązania parametrów, ponieważ w przeciwieństwie do TryParse
metody , nie zależy od odbicia.
DateRangeTP
Następująca klasa implementuje TryParse
element :
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;
}
}
Następująca akcja kontrolera używa DateRangeTP
klasy do powiązania zakresu dat:
// GET /WeatherForecast/ByRangeTP?range=7/24/2022,07/26/2022
public IActionResult ByRangeTP([FromQuery] DateRangeTP range)
{
if (!ModelState.IsValid)
return View("Error", ModelState.Values.SelectMany(v => v.Errors));
var weatherForecasts = Enumerable
.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.Where(wf => DateOnly.FromDateTime(wf.Date) >= range.From
&& DateOnly.FromDateTime(wf.Date) <= range.To)
.Select(wf => new WeatherForecastViewModel
{
Date = wf.Date.ToString("d"),
TemperatureC = wf.TemperatureC,
TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556),
Summary = wf.Summary
});
return View("Index", weatherForecasts);
}
Typy złożone
Typ złożony musi mieć publiczny konstruktor domyślny i publiczne właściwości zapisywalne do powiązania. Po utworzeniu powiązania modelu klasę tworzy się przy użyciu publicznego konstruktora domyślnego.
Dla każdej właściwości typu złożonego powiązanie modelu analizuje źródła wzorca nazw prefix.property_name. Jeśli nic nie zostanie znalezione, szuka tylko property_name bez prefiksu. Decyzja o użyciu prefiksu nie jest podjęta na właściwość. Na przykład z zapytaniem zawierającym ?Instructor.Id=100&Name=foo
metodę , powiązana z metodą OnGet(Instructor instructor)
, wynikowy obiekt typu Instructor
zawiera:
Id
ustaw wartość100
.Name
ustaw wartośćnull
. Powiązanie modelu oczekuje,Instructor.Name
ponieważInstructor.Id
zostało użyte w poprzednim parametrze zapytania.
W przypadku powiązania z parametrem prefiks jest nazwą parametru. W przypadku powiązania z właściwością publiczną PageModel
prefiks jest nazwą właściwości publicznej. Niektóre atrybuty mają Prefix
właściwość, która umożliwia zastąpienie domyślnego użycia parametru lub nazwy właściwości.
Załóżmy na przykład, że typ złożony to następująca Instructor
klasa:
public class Instructor
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
Prefiks = nazwa parametru
Jeśli model, który ma być powiązany, jest parametrem o nazwie instructorToUpdate
:
public IActionResult OnPost(int? id, Instructor instructorToUpdate)
Powiązanie modelu rozpoczyna się od przejrzenia źródeł klucza instructorToUpdate.ID
. Jeśli to nie zostanie znalezione, szuka ID
bez prefiksu.
Prefiks = nazwa właściwości
Jeśli model, który ma być powiązany, jest właściwością o nazwie Instructor
kontrolera lub PageModel
klasy:
[BindProperty]
public Instructor Instructor { get; set; }
Powiązanie modelu rozpoczyna się od przejrzenia źródeł klucza Instructor.ID
. Jeśli to nie zostanie znalezione, szuka ID
bez prefiksu.
Prefiks niestandardowy
Jeśli model, który ma być powiązany, jest parametrem o nazwie instructorToUpdate
, a Bind
atrybut określa Instructor
jako prefiks:
public IActionResult OnPost(
int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)
Powiązanie modelu rozpoczyna się od przejrzenia źródeł klucza Instructor.ID
. Jeśli to nie zostanie znalezione, szuka ID
bez prefiksu.
Atrybuty dla obiektów docelowych typu złożonego
Dostępnych jest kilka wbudowanych atrybutów do kontrolowania powiązania modelu typów złożonych:
Ostrzeżenie
Te atrybuty wpływają na powiązanie modelu, gdy opublikowane dane formularza są źródłem wartości. Nie mają one wpływu na formatery danych wejściowych, które przetwarzają treść żądań JSON i XML. Formatery danych wejściowych zostały wyjaśnione w dalszej części tego artykułu.
[Bind] , atrybut
Można zastosować do klasy lub parametru metody. Określa, które właściwości modelu powinny być uwzględnione w powiązaniu modelu. [Bind]
nie ma wpływu na formatery wejściowe.
W poniższym przykładzie tylko określone właściwości Instructor
modelu są powiązane, gdy wywoływana jest dowolna metoda obsługi lub akcji:
[Bind("LastName,FirstMidName,HireDate")]
public class Instructor
W poniższym przykładzie po wywołaniu OnPost
metody są powiązane tylko określone właściwości Instructor
modelu:
[HttpPost]
public IActionResult OnPost(
[Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
Atrybut [Bind]
może służyć do ochrony przed przesłanianiem w scenariuszach tworzenia . Nie działa dobrze w scenariuszach edycji, ponieważ wykluczone właściwości są ustawione na wartość null lub wartość domyślną, a nie pozostawione bez zmian. W przypadku ochrony przed przesłanianiem zalecane są modele wyświetlania, a nie atrybut.[Bind]
Aby uzyskać więcej informacji, zobacz Uwaga dotycząca zabezpieczeń dotycząca zastępowania.
[ModelBinder] , atrybut
ModelBinderAttribute można stosować do typów, właściwości lub parametrów. Umożliwia określenie typu powiązania modelu używanego do powiązania określonego wystąpienia lub typu. Na przykład:
[HttpPost]
public IActionResult OnPost(
[ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)
Atrybut [ModelBinder]
może również służyć do zmiany nazwy właściwości lub parametru, gdy jest powiązany model:
public class Instructor
{
[ModelBinder(Name = "instructor_id")]
public string Id { get; set; }
// ...
}
[BindRequired] , atrybut
Powoduje, że powiązanie modelu powoduje dodanie błędu stanu modelu, jeśli powiązanie nie może wystąpić dla właściwości modelu. Oto przykład:
public class InstructorBindRequired
{
// ...
[BindRequired]
public DateTime HireDate { get; set; }
}
Zobacz również omówienie atrybutu [Required]
w temacie Walidacja modelu.
[BindNever] , atrybut
Można zastosować do właściwości lub typu. Uniemożliwia ustawienie właściwości modelu przez powiązanie modelu. Po zastosowaniu do typu system powiązań modelu wyklucza wszystkie właściwości zdefiniowane przez typ. Oto przykład:
public class InstructorBindNever
{
[BindNever]
public int Id { get; set; }
// ...
}
Kolekcje
W przypadku obiektów docelowych, które są kolekcjami prostych typów, powiązanie modelu wyszukuje dopasowania do parameter_name lub property_name. Jeśli nie zostanie znalezione dopasowanie, szuka jednego z obsługiwanych formatów bez prefiksu. Na przykład:
Załóżmy, że parametr, który ma być powiązany, to tablica o nazwie
selectedCourses
:public IActionResult OnPost(int? id, int[] selectedCourses)
Dane formularza lub ciągu zapytania mogą być w jednym z następujących formatów:
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
Unikaj powiązania parametru lub właściwości o nazwie
index
lubIndex
, jeśli sąsiaduje z wartością kolekcji. Powiązanie modelu próbuje użyćindex
jako indeksu kolekcji, co może spowodować nieprawidłowe powiązanie. Rozważmy na przykład następującą akcję:public IActionResult Post(string index, List<Product> products)
W poprzednim kodzie
index
parametr ciągu zapytania jest powiązany z parametremindex
metody, a także jest używany do powiązania kolekcji produktów. Zmiana nazwy parametruindex
lub użycie atrybutu powiązania modelu w celu skonfigurowania powiązania pozwala uniknąć tego problemu:public IActionResult Post(string productIndex, List<Product> products)
Następujący format jest obsługiwany tylko w danych formularza:
selectedCourses[]=1050&selectedCourses[]=2000
We wszystkich poprzednich przykładowych formatach powiązanie modelu przekazuje tablicę dwóch elementów do parametru
selectedCourses
:- selectedCourses[0]=1050
- selectedCourses[1]=2000
Formaty danych używające liczb w indeksie dolnym (... [0] ... [1] ...) musi upewnić się, że są one numerowane sekwencyjnie rozpoczynające się od zera. Jeśli występują luki w numerowaniu indeksu dolnego, wszystkie elementy po przerwie są ignorowane. Jeśli na przykład indeksy dolny to 0 i 2 zamiast 0 i 1, drugi element jest ignorowany.
Słowniki
W przypadku Dictionary
obiektów docelowych powiązanie modelu wyszukuje dopasowania do parameter_name lub property_name. Jeśli nie zostanie znalezione dopasowanie, szuka jednego z obsługiwanych formatów bez prefiksu. Na przykład:
Załóżmy, że parametr docelowy
Dictionary<int, string>
ma nazwęselectedCourses
:public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
Dane opublikowanego formularza lub ciągu zapytania mogą wyglądać podobnie do jednego z następujących przykładów:
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
We wszystkich poprzednich przykładowych formatach powiązanie modelu przekazuje słownik dwóch elementów do parametru
selectedCourses
:- selectedCourses["1050"]="Chemistry"
- selectedCourses["2000"]="Economics"
Powiązanie konstruktora i typy rekordów
Powiązanie modelu wymaga, aby złożone typy miały konstruktor bez parametrów. Zarówno System.Text.Json
metody formatowania danych wejściowych, jak i Newtonsoft.Json
obsługują deserializację klas, które nie mają konstruktora bez parametrów.
Typy rekordów to doskonały sposób, aby zwięźle reprezentować dane za pośrednictwem sieci. ASP.NET Core obsługuje wiązanie modelu i weryfikowanie typów rekordów za pomocą jednego konstruktora:
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>
Podczas sprawdzania poprawności typów rekordów środowisko uruchomieniowe wyszukuje metadane powiązania i walidacji w szczególności na parametrach, a nie we właściwościach.
Platforma umożliwia wiązanie i weryfikowanie typów rekordów:
public record Person([Required] string Name, [Range(0, 100)] int Age);
Aby poprzedni element działał, typ musi:
- Być typem rekordu.
- Mieć dokładnie jeden publiczny konstruktor.
- Zawierają parametry, które mają właściwość o tej samej nazwie i typie. Nazwy nie mogą się różnić literami.
Obiekty POCO bez konstruktorów bez parametrów
Obiekty POC, które nie mają konstruktorów bez parametrów, nie mogą być powiązane.
Poniższy kod powoduje wyjątek z informacją, że typ musi mieć konstruktor bez parametrów:
public class Person(string Name)
public record Person([Required] string Name, [Range(0, 100)] int Age)
{
public Person(string Name) : this (Name, 0);
}
Typy rekordów z ręcznie utworzonymi konstruktorami
Typy rekordów z ręcznie utworzonymi konstruktorami, które wyglądają jak konstruktory podstawowe działają
public record Person
{
public Person([Required] string Name, [Range(0, 100)] int Age)
=> (this.Name, this.Age) = (Name, Age);
public string Name { get; set; }
public int Age { get; set; }
}
Typy rekordów, walidacja i metadane powiązania
W przypadku typów rekordów używane są metadane walidacji i powiązania parametrów. Wszystkie metadane właściwości są ignorowane
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; }
}
Walidacja i metadane
Walidacja używa metadanych w parametrze, ale używa właściwości do odczytania wartości. W zwykłych przypadkach z konstruktorami podstawowymi te dwa byłyby identyczne. Istnieją jednak sposoby, aby go pokonać:
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; }
}
Funkcja TryUpdateModel nie aktualizuje parametrów typu rekordu
public record Person(string Name)
{
public int Age { get; set; }
}
var person = new Person("initial-name");
TryUpdateModel(person, ...);
W takim przypadku usługa MVC nie podejmie ponownej próby powiązania Name
. Age
Można jednak zaktualizować
Zachowanie globalizacji danych trasy powiązania modelu i ciągów zapytań
Dostawca wartości trasy ASP.NET Core i dostawca wartości ciągu zapytania:
- Traktuj wartości jako niezmienną kulturę.
- Spodziewaj się, że adresy URL są niezmienne dla kultury.
Natomiast wartości pochodzące z danych formularza są poddawane konwersji wrażliwej na kulturę. Jest to zaprojektowane tak, aby adresy URL można udostępniać między ustawieniami regionalnymi.
Aby dostawca wartości trasy ASP.NET Core i dostawca wartości ciągu zapytania przeszedł konwersję wrażliwą na kulturę:
- Dziedzicz z IValueProviderFactory
- Skopiuj kod z elementu QueryStringValueProviderFactory lub RouteValueValueProviderFactory
- Zastąp wartość kultury przekazaną do konstruktora dostawcy wartości atrybutem CultureInfo.CurrentCulture
- Zastąp domyślną fabrykę dostawcy wartości w opcjach MVC nowym:
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();
});
Specjalne typy danych
Istnieją pewne specjalne typy danych, które mogą obsłużyć powiązanie modelu.
IFormFile i IFormFileCollection
Przekazany plik uwzględniony w żądaniu HTTP. Obsługiwane jest IEnumerable<IFormFile>
również w przypadku wielu plików.
CancellationToken
Akcje mogą opcjonalnie powiązać element CancellationToken
jako parametr. RequestAborted Wiąże się to z sygnałem, gdy połączenie bazowe żądania HTTP zostanie przerwane. Akcje mogą używać tego parametru do anulowania długotrwałych operacji asynchronicznych wykonywanych w ramach akcji kontrolera.
FormCollection
Służy do pobierania wszystkich wartości z opublikowanych danych formularza.
Formatery danych wejściowych
Dane w treści żądania mogą być w formacie JSON, XML lub innym formacie. Aby przeanalizować te dane, powiązanie modelu używa formatującego danych wejściowych skonfigurowanego do obsługi określonego typu zawartości. Domyślnie ASP.NET Core zawiera formatery wejściowe oparte na formacie JSON do obsługi danych JSON. Możesz dodać inne formatery dla innych typów zawartości.
ASP.NET Core wybiera formatery wejściowe na podstawie atrybutu Consumes . Jeśli atrybut nie jest obecny, używa nagłówka Content-Type.
Aby użyć wbudowanych formatów wejściowych XML:
W
Program.cs
pliku wywołaj metodę AddXmlSerializerFormatters lub AddXmlDataContractSerializerFormatters.builder.Services.AddControllers() .AddXmlSerializerFormatters();
Consumes
Zastosuj atrybut do klas kontrolerów lub metod akcji, które powinny oczekiwać XML w treści żądania.[HttpPost] [Consumes("application/xml")] public ActionResult<Pet> Create(Pet pet)
Aby uzyskać więcej informacji, zobacz Wprowadzenie serializacji XML.
Dostosowywanie powiązania modelu za pomocą formaterów wejściowych
Moduł formatujący dane wejściowe ponosi pełną odpowiedzialność za odczytywanie danych z treści żądania. Aby dostosować ten proces, skonfiguruj interfejsy API używane przez program formatujący dane wejściowe. W tej sekcji opisano sposób dostosowywania opartego na formatatorze System.Text.Json
danych wejściowych w celu zrozumienia niestandardowego typu o nazwie ObjectId
.
Rozważmy następujący model, który zawiera właściwość niestandardową ObjectId
:
public class InstructorObjectId
{
[Required]
public ObjectId ObjectId { get; set; } = null!;
}
Aby dostosować proces powiązania modelu podczas używania metody System.Text.Json
, utwórz klasę pochodzącą z JsonConverter<T>klasy :
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);
}
Aby użyć konwertera niestandardowego, zastosuj JsonConverterAttribute atrybut do typu. W poniższym przykładzie ObjectId
typ jest skonfigurowany ObjectIdConverter
jako konwerter niestandardowy:
[JsonConverter(typeof(ObjectIdConverter))]
public record ObjectId(int Id);
Aby uzyskać więcej informacji, zobacz Jak pisać konwertery niestandardowe.
Wykluczanie określonych typów z powiązania modelu
Zachowanie systemów powiązań i walidacji modelu jest sterowane przez ModelMetadataprogram . Możesz dostosować ModelMetadata
, dodając dostawcę szczegółów do elementu MvcOptions.ModelMetadataDetailsProviders. Wbudowani dostawcy szczegółów są dostępni do wyłączania powiązania modelu lub walidacji dla określonych typów.
Aby wyłączyć powiązanie modelu dla wszystkich modeli określonego typu, dodaj element w elemExcludeBindingMetadataProvider.Program.cs
Aby na przykład wyłączyć powiązanie modelu dla wszystkich modeli typu System.Version
:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
Aby wyłączyć walidację właściwości określonego typu, dodaj element SuppressChildValidationMetadataProvider w Program.cs
pliku . Aby na przykład wyłączyć walidację właściwości typu System.Guid
:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
Niestandardowe powiązania modelu
Powiązanie modelu można rozszerzyć, pisząc powiązanie modelu niestandardowego i używając atrybutu [ModelBinder]
, aby wybrać go dla danego obiektu docelowego. Dowiedz się więcej o powiązaniu modelu niestandardowego.
Ręczne powiązanie modelu
Powiązanie modelu można wywołać ręcznie przy użyciu TryUpdateModelAsync metody . Metoda jest definiowana w klasach ControllerBase
i PageModel
. Przeciążenia metody umożliwiają określenie prefiksu i dostawcy wartości do użycia. Metoda zwraca false
wartość , jeśli powiązanie modelu nie powiedzie się. Oto przykład:
if (await TryUpdateModelAsync(
newInstructor,
"Instructor",
x => x.Name, x => x.HireDate!))
{
_instructorStore.Add(newInstructor);
return RedirectToPage("./Index");
}
return Page();
TryUpdateModelAsync używa dostawców wartości do pobierania danych z treści formularza, ciągu zapytania i kierowania danych. TryUpdateModelAsync
jest zwykle:
- Używane z aplikacjami Razor Pages i MVC przy użyciu kontrolerów i widoków, aby zapobiec nadmiernemu delegowaniu.
- Nie jest używany z internetowym interfejsem API, chyba że są używane z danych formularza, ciągów zapytań i danych trasy. Punkty końcowe internetowego interfejsu API, które używają formatu JSON, używają formatatorów wejściowych do deserializacji treści żądania do obiektu.
Aby uzyskać więcej informacji, zobacz TryUpdateModelAsync.
[FromServices] , atrybut
Nazwa tego atrybutu jest zgodna ze wzorcem atrybutów powiązania modelu, które określają źródło danych. Nie chodzi jednak o powiązanie danych od dostawcy wartości. Pobiera wystąpienie typu z kontenera wstrzykiwania zależności. Jego celem jest zapewnienie alternatywy dla iniekcji konstruktora, jeśli potrzebujesz usługi tylko wtedy, gdy wywoływana jest określona metoda.
Jeśli wystąpienie typu nie jest zarejestrowane w kontenerze wstrzykiwania zależności, aplikacja zgłasza wyjątek podczas próby powiązania parametru. Aby ustawić parametr jako opcjonalny, użyj jednego z następujących metod:
- Ustaw parametr na wartość null.
- Ustaw wartość domyślną parametru.
W przypadku parametrów dopuszczających wartość null upewnij się, że parametr nie null
jest używany przed uzyskaniem do niego dostępu.
Dodatkowe zasoby
W tym artykule wyjaśniono, czym jest powiązanie modelu, jak działa i jak dostosować jego zachowanie.
Co to jest powiązanie modelu
Kontrolery i Razor strony współpracują z danymi pochodzącymi z żądań HTTP. Na przykład dane trasy mogą zawierać klucz rekordu, a opublikowane pola formularza mogą zawierać wartości właściwości modelu. Pisanie kodu w celu pobrania każdej z tych wartości i przekonwertowanie ich z ciągów na typy platformy .NET byłoby żmudne i podatne na błędy. Powiązanie modelu automatyzuje ten proces. System powiązania modelu:
- Pobiera dane z różnych źródeł, takich jak dane trasy, pola formularza i ciągi zapytań.
- Udostępnia dane kontrolerom i Razor stronom w parametrach metody i właściwościach publicznych.
- Konwertuje dane ciągu na typy platformy .NET.
- Aktualizuje właściwości typów złożonych.
Przykład
Załóżmy, że masz następującą metodę akcji:
[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)
Aplikacja otrzymuje żądanie z następującym adresem URL:
https://contoso.com/api/pets/2?DogsOnly=true
Powiązanie modelu wykonuje następujące kroki po wybraniu przez system routingu metody akcji:
- Znajduje pierwszy parametr ,
GetById
liczba całkowita o nazwieid
. - Przegląda dostępne źródła w żądaniu HTTP i znajduje
id
= "2" w danych trasy. - Konwertuje ciąg "2" na liczbę całkowitą 2.
- Znajduje następny parametr ,
GetById
wartość logiczną o nazwiedogsOnly
. - Przegląda źródła i znajduje ciąg zapytania "DogsOnly=true". Dopasowywanie nazw nie jest uwzględniane wielkości liter.
- Konwertuje ciąg "true" na wartość logiczną
true
.
Następnie struktura wywołuje metodę GetById
, przekazując wartość 2 dla parametru id
i true
parametru dogsOnly
.
W poprzednim przykładzie docelowe powiązania modelu to parametry metody, które są prostymi typami. Obiekty docelowe mogą być również właściwościami typu złożonego. Po pomyślnym powiązaniu każdej właściwości walidacja modelu jest wykonywana dla tej właściwości. Rekord danych powiązanych z modelem oraz wszelkich błędów powiązań lub walidacji jest przechowywany w pliku ControllerBase.ModelState lub PageModel.ModelState. Aby dowiedzieć się, czy ten proces zakończył się pomyślnie, aplikacja sprawdza flagę ModelState.IsValid .
Elementy docelowe
Powiązanie modelu próbuje znaleźć wartości dla następujących rodzajów obiektów docelowych:
- Parametry metody akcji kontrolera, do którego jest kierowane żądanie.
- Razor Parametry metody obsługi stron, do których jest kierowane żądanie.
- Publiczne właściwości kontrolera lub
PageModel
klasy, jeśli są określone przez atrybuty.
[BindProperty] , atrybut
Można zastosować do właściwości publicznej kontrolera lub PageModel
klasy, aby spowodować powiązanie modelu z tą właściwością docelową:
public class EditModel : PageModel
{
[BindProperty]
public Instructor? Instructor { get; set; }
// ...
}
[BindProperties] , atrybut
Można zastosować do kontrolera lub PageModel
klasy, aby poinformować powiązanie modelu w celu kierowania wszystkich właściwości publicznych klasy:
[BindProperties]
public class CreateModel : PageModel
{
public Instructor? Instructor { get; set; }
// ...
}
Powiązanie modelu dla żądań HTTP GET
Domyślnie właściwości nie są powiązane z żądaniami HTTP GET. Zazwyczaj wszystko, czego potrzebujesz do żądania GET, to parametr identyfikatora rekordu. Identyfikator rekordu służy do wyszukiwania elementu w bazie danych. W związku z tym nie ma potrzeby wiązania właściwości, która zawiera wystąpienie modelu. W scenariuszach, w których chcesz, aby właściwości powiązane z danymi z żądań GET ustawiły SupportsGet
właściwość na true
:
[BindProperty(Name = "ai_user", SupportsGet = true)]
public string? ApplicationInsightsCookie { get; set; }
Źródła
Domyślnie powiązanie modelu pobiera dane w postaci par klucz-wartość z następujących źródeł w żądaniu HTTP:
- Pola formularza
- Treść żądania (dla kontrolerów, które mają atrybut [ApiController].
- Dane trasy
- Parametry ciągu zapytania
- Przekazane pliki
Dla każdego parametru docelowego lub właściwości źródła są skanowane w kolejności wskazanej na powyższej liście. Istnieje kilka wyjątków:
- Dane trasy i wartości ciągu zapytania są używane tylko dla prostych typów.
- Przekazane pliki są powiązane tylko z typami docelowymi, które implementują
IFormFile
program lubIEnumerable<IFormFile>
.
Jeśli domyślne źródło nie jest poprawne, użyj jednego z następujących atrybutów, aby określić źródło:
[FromQuery]
— Pobiera wartości z ciągu zapytania.[FromRoute]
- Pobiera wartości z danych trasy.[FromForm]
- Pobiera wartości z opublikowanych pól formularza.[FromBody]
— Pobiera wartości z treści żądania.[FromHeader]
- Pobiera wartości z nagłówków HTTP.
Te atrybuty:
Są dodawane do właściwości modelu indywidualnie, a nie do klasy modelu, jak w poniższym przykładzie:
public class Instructor { public int Id { get; set; } [FromQuery(Name = "Note")] public string? NoteFromQueryString { get; set; } // ... }
Opcjonalnie zaakceptuj wartość nazwy modelu w konstruktorze. Ta opcja jest dostępna w przypadku, gdy nazwa właściwości nie jest zgodna z wartością w żądaniu. Na przykład wartość w żądaniu może być nagłówkiem z łącznikiem w jego nazwie, jak w poniższym przykładzie:
public void OnGet([FromHeader(Name = "Accept-Language")] string language)
[FromBody] , atrybut
Zastosuj atrybut do parametru, [FromBody]
aby wypełnić jego właściwości z treści żądania HTTP. Środowisko uruchomieniowe ASP.NET Core deleguje odpowiedzialność za odczytywanie treści do elementu formatującego dane wejściowe. Formatery danych wejściowych zostały wyjaśnione w dalszej części tego artykułu.
Po [FromBody]
zastosowaniu do parametru typu złożonego wszystkie atrybuty źródła powiązania zastosowane do jego właściwości są ignorowane. Na przykład następująca Create
akcja określa, że jego pet
parametr jest wypełniany z treści:
public ActionResult<Pet> Create([FromBody] Pet pet)
Klasa Pet
określa, że jego Breed
właściwość jest wypełniana z parametru ciągu zapytania:
public class Pet
{
public string Name { get; set; } = null!;
[FromQuery] // Attribute is ignored.
public string Breed { get; set; } = null!;
}
W powyższym przykładzie:
- Atrybut
[FromQuery]
jest ignorowany. - Właściwość
Breed
nie jest wypełniana z parametru ciągu zapytania.
Formatery wejściowe odczytują tylko treść i nie rozumieją atrybutów źródła powiązania. Jeśli w treści znajduje się odpowiednia wartość, ta wartość jest używana do wypełnienia Breed
właściwości.
Nie należy stosować [FromBody]
do więcej niż jednego parametru dla metody akcji. Gdy strumień żądania zostanie odczytany przez program formatujący dane wejściowe, nie będzie już dostępny do ponownego odczytania w celu powiązania innych [FromBody]
parametrów.
Dodatkowe źródła
Dane źródłowe są dostarczane do systemu powiązania modelu przez dostawców wartości. Można zapisywać i rejestrować niestandardowych dostawców wartości, którzy pobierają dane na potrzeby powiązania modelu z innych źródeł. Możesz na przykład chcieć dane z plików cookie lub stanu sesji. Aby pobrać dane z nowego źródła:
- Utwórz klasę, która implementuje
IValueProvider
. - Utwórz klasę, która implementuje
IValueProviderFactory
. - Zarejestruj klasę fabryki w pliku
Program.cs
.
Przykład zawiera dostawcę wartości i przykład fabryki, który pobiera wartości z plików cookie. Zarejestruj fabryki dostawców wartości niestandardowych w programie Program.cs
:
builder.Services.AddControllers(options =>
{
options.ValueProviderFactories.Add(new CookieValueProviderFactory());
});
Powyższy kod umieszcza niestandardowego dostawcę wartości po wszystkich wbudowanych dostawcach wartości. Aby ustawić ją jako pierwszą na liście, wywołaj metodę Insert(0, new CookieValueProviderFactory())
zamiast Add
.
Brak źródła dla właściwości modelu
Domyślnie błąd stanu modelu nie jest tworzony, jeśli dla właściwości modelu nie zostanie znaleziona żadna wartość. Właściwość jest ustawiona na wartość null lub wartość domyślną:
- Proste typy dopuszczane do wartości null są ustawione na
null
wartość . - Typy wartości innych niż null są ustawione na
default(T)
wartość . Na przykład parametrint id
ma wartość 0. - W przypadku złożonych typów powiązanie modelu tworzy wystąpienie przy użyciu konstruktora domyślnego bez ustawiania właściwości.
- Tablice są ustawione na
Array.Empty<T>()
, z tą różnicą, żebyte[]
tablice są ustawione nanull
.
Jeśli stan modelu powinien zostać unieważniony, jeśli nic nie zostanie znalezione w polach formularza dla właściwości modelu, użyj atrybutu [BindRequired]
.
Należy pamiętać, że to [BindRequired]
zachowanie dotyczy powiązania modelu z opublikowanych danych formularza, a nie danych JSON lub XML w treści żądania. Dane treści żądania są obsługiwane przez osoby formatujące dane wejściowe.
Błędy konwersji typów
Jeśli źródło zostanie znalezione, ale nie można go przekonwertować na typ docelowy, stan modelu jest oznaczony jako nieprawidłowy. Docelowy parametr lub właściwość jest ustawiona na wartość null lub wartość domyślną, jak wspomniano w poprzedniej sekcji.
W kontrolerze interfejsu [ApiController]
API, który ma atrybut, nieprawidłowy stan modelu powoduje automatyczną odpowiedź HTTP 400.
Razor Na stronie redisplay strony z komunikatem o błędzie:
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
// ...
return RedirectToPage("./Index");
}
Gdy strona jest redisplayed przez poprzedni kod, nieprawidłowe dane wejściowe nie są wyświetlane w polu formularza. Jest to spowodowane tym, że właściwość modelu została ustawiona na wartość null lub wartość domyślną. Nieprawidłowe dane wejściowe są wyświetlane w komunikacie o błędzie. Jeśli chcesz ponownie odtworzyć nieprawidłowe dane w polu formularza, rozważ utworzenie właściwości modelu jako ciągu i ręczne przeprowadzenie konwersji danych.
Ta sama strategia jest zalecana, jeśli nie chcesz, aby błędy konwersji typów powodowały błędy stanu modelu. W takim przypadku utwórz właściwość modelu jako ciąg.
Typy proste
Proste typy, które binder modelu mogą konwertować ciągi źródłowe na następujące:
- Wartość logiczna
- Bajty, SByte
- Char
- Data/godzina
- DateTimeOffset
- Dziesiętne
- Podwójne
- Wyliczenie
- Guid
- Int16, Int32, Int64
- Pojedynczy
- Przedział czasu
- UInt16, UInt32, UInt64
- Identyfikator URI
- Wersja
Typy złożone
Typ złożony musi mieć publiczny konstruktor domyślny i publiczne właściwości zapisywalne do powiązania. Po utworzeniu powiązania modelu klasę tworzy się przy użyciu publicznego konstruktora domyślnego.
Dla każdej właściwości typu złożonego powiązanie modelu analizuje źródła wzorca nazw prefix.property_name. Jeśli nic nie zostanie znalezione, szuka tylko property_name bez prefiksu. Decyzja o użyciu prefiksu nie jest podjęta na właściwość. Na przykład z zapytaniem zawierającym ?Instructor.Id=100&Name=foo
metodę , powiązana z metodą OnGet(Instructor instructor)
, wynikowy obiekt typu Instructor
zawiera:
Id
ustaw wartość100
.Name
ustaw wartośćnull
. Powiązanie modelu oczekuje,Instructor.Name
ponieważInstructor.Id
zostało użyte w poprzednim parametrze zapytania.
W przypadku powiązania z parametrem prefiks jest nazwą parametru. W przypadku powiązania z właściwością publiczną PageModel
prefiks jest nazwą właściwości publicznej. Niektóre atrybuty mają Prefix
właściwość, która umożliwia zastąpienie domyślnego użycia parametru lub nazwy właściwości.
Załóżmy na przykład, że typ złożony to następująca Instructor
klasa:
public class Instructor
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
Prefiks = nazwa parametru
Jeśli model, który ma być powiązany, jest parametrem o nazwie instructorToUpdate
:
public IActionResult OnPost(int? id, Instructor instructorToUpdate)
Powiązanie modelu rozpoczyna się od przejrzenia źródeł klucza instructorToUpdate.ID
. Jeśli to nie zostanie znalezione, szuka ID
bez prefiksu.
Prefiks = nazwa właściwości
Jeśli model, który ma być powiązany, jest właściwością o nazwie Instructor
kontrolera lub PageModel
klasy:
[BindProperty]
public Instructor Instructor { get; set; }
Powiązanie modelu rozpoczyna się od przejrzenia źródeł klucza Instructor.ID
. Jeśli to nie zostanie znalezione, szuka ID
bez prefiksu.
Prefiks niestandardowy
Jeśli model, który ma być powiązany, jest parametrem o nazwie instructorToUpdate
, a Bind
atrybut określa Instructor
jako prefiks:
public IActionResult OnPost(
int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)
Powiązanie modelu rozpoczyna się od przejrzenia źródeł klucza Instructor.ID
. Jeśli to nie zostanie znalezione, szuka ID
bez prefiksu.
Atrybuty dla obiektów docelowych typu złożonego
Dostępnych jest kilka wbudowanych atrybutów do kontrolowania powiązania modelu typów złożonych:
Ostrzeżenie
Te atrybuty wpływają na powiązanie modelu, gdy opublikowane dane formularza są źródłem wartości. Nie mają one wpływu na formatery danych wejściowych, które przetwarzają treść żądań JSON i XML. Formatery danych wejściowych zostały wyjaśnione w dalszej części tego artykułu.
[Bind] , atrybut
Można zastosować do klasy lub parametru metody. Określa, które właściwości modelu powinny być uwzględnione w powiązaniu modelu. [Bind]
nie ma wpływu na formatery wejściowe.
W poniższym przykładzie tylko określone właściwości Instructor
modelu są powiązane, gdy wywoływana jest dowolna metoda obsługi lub akcji:
[Bind("LastName,FirstMidName,HireDate")]
public class Instructor
W poniższym przykładzie po wywołaniu OnPost
metody są powiązane tylko określone właściwości Instructor
modelu:
[HttpPost]
public IActionResult OnPost(
[Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
Atrybut [Bind]
może służyć do ochrony przed przesłanianiem w scenariuszach tworzenia . Nie działa dobrze w scenariuszach edycji, ponieważ wykluczone właściwości są ustawione na wartość null lub wartość domyślną, a nie pozostawione bez zmian. W przypadku ochrony przed przesłanianiem zalecane są modele wyświetlania, a nie atrybut.[Bind]
Aby uzyskać więcej informacji, zobacz Uwaga dotycząca zabezpieczeń dotycząca zastępowania.
[ModelBinder] , atrybut
ModelBinderAttribute można stosować do typów, właściwości lub parametrów. Umożliwia określenie typu powiązania modelu używanego do powiązania określonego wystąpienia lub typu. Na przykład:
[HttpPost]
public IActionResult OnPost(
[ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)
Atrybut [ModelBinder]
może również służyć do zmiany nazwy właściwości lub parametru, gdy jest powiązany model:
public class Instructor
{
[ModelBinder(Name = "instructor_id")]
public string Id { get; set; }
// ...
}
[BindRequired] , atrybut
Powoduje, że powiązanie modelu powoduje dodanie błędu stanu modelu, jeśli powiązanie nie może wystąpić dla właściwości modelu. Oto przykład:
public class InstructorBindRequired
{
// ...
[BindRequired]
public DateTime HireDate { get; set; }
}
Zobacz również omówienie atrybutu [Required]
w temacie Walidacja modelu.
[BindNever] , atrybut
Można zastosować do właściwości lub typu. Uniemożliwia ustawienie właściwości modelu przez powiązanie modelu. Po zastosowaniu do typu system powiązań modelu wyklucza wszystkie właściwości zdefiniowane przez typ. Oto przykład:
public class InstructorBindNever
{
[BindNever]
public int Id { get; set; }
// ...
}
Kolekcje
W przypadku obiektów docelowych, które są kolekcjami prostych typów, powiązanie modelu wyszukuje dopasowania do parameter_name lub property_name. Jeśli nie zostanie znalezione dopasowanie, szuka jednego z obsługiwanych formatów bez prefiksu. Na przykład:
Załóżmy, że parametr, który ma być powiązany, to tablica o nazwie
selectedCourses
:public IActionResult OnPost(int? id, int[] selectedCourses)
Dane formularza lub ciągu zapytania mogą być w jednym z następujących formatów:
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
Unikaj powiązania parametru lub właściwości o nazwie
index
lubIndex
, jeśli sąsiaduje z wartością kolekcji. Powiązanie modelu próbuje użyćindex
jako indeksu kolekcji, co może spowodować nieprawidłowe powiązanie. Rozważmy na przykład następującą akcję:public IActionResult Post(string index, List<Product> products)
W poprzednim kodzie
index
parametr ciągu zapytania jest powiązany z parametremindex
metody, a także jest używany do powiązania kolekcji produktów. Zmiana nazwy parametruindex
lub użycie atrybutu powiązania modelu w celu skonfigurowania powiązania pozwala uniknąć tego problemu:public IActionResult Post(string productIndex, List<Product> products)
Następujący format jest obsługiwany tylko w danych formularza:
selectedCourses[]=1050&selectedCourses[]=2000
We wszystkich poprzednich przykładowych formatach powiązanie modelu przekazuje tablicę dwóch elementów do parametru
selectedCourses
:- selectedCourses[0]=1050
- selectedCourses[1]=2000
Formaty danych używające liczb w indeksie dolnym (... [0] ... [1] ...) musi upewnić się, że są one numerowane sekwencyjnie rozpoczynające się od zera. Jeśli występują luki w numerowaniu indeksu dolnego, wszystkie elementy po przerwie są ignorowane. Jeśli na przykład indeksy dolny to 0 i 2 zamiast 0 i 1, drugi element jest ignorowany.
Słowniki
W przypadku Dictionary
obiektów docelowych powiązanie modelu wyszukuje dopasowania do parameter_name lub property_name. Jeśli nie zostanie znalezione dopasowanie, szuka jednego z obsługiwanych formatów bez prefiksu. Na przykład:
Załóżmy, że parametr docelowy
Dictionary<int, string>
ma nazwęselectedCourses
:public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
Dane opublikowanego formularza lub ciągu zapytania mogą wyglądać podobnie do jednego z następujących przykładów:
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
We wszystkich poprzednich przykładowych formatach powiązanie modelu przekazuje słownik dwóch elementów do parametru
selectedCourses
:- selectedCourses["1050"]="Chemistry"
- selectedCourses["2000"]="Economics"
Powiązanie konstruktora i typy rekordów
Powiązanie modelu wymaga, aby złożone typy miały konstruktor bez parametrów. Zarówno System.Text.Json
metody formatowania danych wejściowych, jak i Newtonsoft.Json
obsługują deserializację klas, które nie mają konstruktora bez parametrów.
Typy rekordów to doskonały sposób, aby zwięźle reprezentować dane za pośrednictwem sieci. ASP.NET Core obsługuje wiązanie modelu i weryfikowanie typów rekordów za pomocą jednego konstruktora:
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>
Podczas sprawdzania poprawności typów rekordów środowisko uruchomieniowe wyszukuje metadane powiązania i walidacji w szczególności na parametrach, a nie we właściwościach.
Platforma umożliwia wiązanie i weryfikowanie typów rekordów:
public record Person([Required] string Name, [Range(0, 100)] int Age);
Aby poprzedni element działał, typ musi:
- Być typem rekordu.
- Mieć dokładnie jeden publiczny konstruktor.
- Zawierają parametry, które mają właściwość o tej samej nazwie i typie. Nazwy nie mogą się różnić literami.
Obiekty POCO bez konstruktorów bez parametrów
Obiekty POC, które nie mają konstruktorów bez parametrów, nie mogą być powiązane.
Poniższy kod powoduje wyjątek z informacją, że typ musi mieć konstruktor bez parametrów:
public class Person(string Name)
public record Person([Required] string Name, [Range(0, 100)] int Age)
{
public Person(string Name) : this (Name, 0);
}
Typy rekordów z ręcznie utworzonymi konstruktorami
Typy rekordów z ręcznie utworzonymi konstruktorami, które wyglądają jak konstruktory podstawowe działają
public record Person
{
public Person([Required] string Name, [Range(0, 100)] int Age)
=> (this.Name, this.Age) = (Name, Age);
public string Name { get; set; }
public int Age { get; set; }
}
Typy rekordów, walidacja i metadane powiązania
W przypadku typów rekordów używane są metadane walidacji i powiązania parametrów. Wszystkie metadane właściwości są ignorowane
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; }
}
Walidacja i metadane
Walidacja używa metadanych w parametrze, ale używa właściwości do odczytania wartości. W zwykłych przypadkach z konstruktorami podstawowymi te dwa byłyby identyczne. Istnieją jednak sposoby, aby go pokonać:
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; }
}
Funkcja TryUpdateModel nie aktualizuje parametrów typu rekordu
public record Person(string Name)
{
public int Age { get; set; }
}
var person = new Person("initial-name");
TryUpdateModel(person, ...);
W takim przypadku usługa MVC nie podejmie ponownej próby powiązania Name
. Age
Można jednak zaktualizować
Zachowanie globalizacji danych trasy powiązania modelu i ciągów zapytań
Dostawca wartości trasy ASP.NET Core i dostawca wartości ciągu zapytania:
- Traktuj wartości jako niezmienną kulturę.
- Spodziewaj się, że adresy URL są niezmienne dla kultury.
Natomiast wartości pochodzące z danych formularza są poddawane konwersji wrażliwej na kulturę. Jest to zaprojektowane tak, aby adresy URL można udostępniać między ustawieniami regionalnymi.
Aby dostawca wartości trasy ASP.NET Core i dostawca wartości ciągu zapytania przeszedł konwersję wrażliwą na kulturę:
- Dziedzicz z IValueProviderFactory
- Skopiuj kod z elementu QueryStringValueProviderFactory lub RouteValueValueProviderFactory
- Zastąp wartość kultury przekazaną do konstruktora dostawcy wartości atrybutem CultureInfo.CurrentCulture
- Zastąp domyślną fabrykę dostawcy wartości w opcjach MVC nowym:
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();
});
Specjalne typy danych
Istnieją pewne specjalne typy danych, które mogą obsłużyć powiązanie modelu.
IFormFile i IFormFileCollection
Przekazany plik uwzględniony w żądaniu HTTP. Obsługiwane jest IEnumerable<IFormFile>
również w przypadku wielu plików.
CancellationToken
Akcje mogą opcjonalnie powiązać element CancellationToken
jako parametr. RequestAborted Wiąże się to z sygnałem, gdy połączenie bazowe żądania HTTP zostanie przerwane. Akcje mogą używać tego parametru do anulowania długotrwałych operacji asynchronicznych wykonywanych w ramach akcji kontrolera.
FormCollection
Służy do pobierania wszystkich wartości z opublikowanych danych formularza.
Formatery danych wejściowych
Dane w treści żądania mogą być w formacie JSON, XML lub innym formacie. Aby przeanalizować te dane, powiązanie modelu używa formatującego danych wejściowych skonfigurowanego do obsługi określonego typu zawartości. Domyślnie ASP.NET Core zawiera formatery wejściowe oparte na formacie JSON do obsługi danych JSON. Możesz dodać inne formatery dla innych typów zawartości.
ASP.NET Core wybiera formatery wejściowe na podstawie atrybutu Consumes . Jeśli atrybut nie jest obecny, używa nagłówka Content-Type.
Aby użyć wbudowanych formatów wejściowych XML:
W
Program.cs
pliku wywołaj metodę AddXmlSerializerFormatters lub AddXmlDataContractSerializerFormatters.builder.Services.AddControllers() .AddXmlSerializerFormatters();
Consumes
Zastosuj atrybut do klas kontrolerów lub metod akcji, które powinny oczekiwać XML w treści żądania.[HttpPost] [Consumes("application/xml")] public ActionResult<Pet> Create(Pet pet)
Aby uzyskać więcej informacji, zobacz Wprowadzenie serializacji XML.
Dostosowywanie powiązania modelu za pomocą formaterów wejściowych
Moduł formatujący dane wejściowe ponosi pełną odpowiedzialność za odczytywanie danych z treści żądania. Aby dostosować ten proces, skonfiguruj interfejsy API używane przez program formatujący dane wejściowe. W tej sekcji opisano sposób dostosowywania opartego na formatatorze System.Text.Json
danych wejściowych w celu zrozumienia niestandardowego typu o nazwie ObjectId
.
Rozważmy następujący model, który zawiera właściwość niestandardową ObjectId
:
public class InstructorObjectId
{
[Required]
public ObjectId ObjectId { get; set; } = null!;
}
Aby dostosować proces powiązania modelu podczas używania metody System.Text.Json
, utwórz klasę pochodzącą z JsonConverter<T>klasy :
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);
}
Aby użyć konwertera niestandardowego, zastosuj JsonConverterAttribute atrybut do typu. W poniższym przykładzie ObjectId
typ jest skonfigurowany ObjectIdConverter
jako konwerter niestandardowy:
[JsonConverter(typeof(ObjectIdConverter))]
public record ObjectId(int Id);
Aby uzyskać więcej informacji, zobacz Jak pisać konwertery niestandardowe.
Wykluczanie określonych typów z powiązania modelu
Zachowanie systemów powiązań i walidacji modelu jest sterowane przez ModelMetadataprogram . Możesz dostosować ModelMetadata
, dodając dostawcę szczegółów do elementu MvcOptions.ModelMetadataDetailsProviders. Wbudowani dostawcy szczegółów są dostępni do wyłączania powiązania modelu lub walidacji dla określonych typów.
Aby wyłączyć powiązanie modelu dla wszystkich modeli określonego typu, dodaj element w elemExcludeBindingMetadataProvider.Program.cs
Aby na przykład wyłączyć powiązanie modelu dla wszystkich modeli typu System.Version
:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
Aby wyłączyć walidację właściwości określonego typu, dodaj element SuppressChildValidationMetadataProvider w Program.cs
pliku . Aby na przykład wyłączyć walidację właściwości typu System.Guid
:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
Niestandardowe powiązania modelu
Powiązanie modelu można rozszerzyć, pisząc powiązanie modelu niestandardowego i używając atrybutu [ModelBinder]
, aby wybrać go dla danego obiektu docelowego. Dowiedz się więcej o powiązaniu modelu niestandardowego.
Ręczne powiązanie modelu
Powiązanie modelu można wywołać ręcznie przy użyciu TryUpdateModelAsync metody . Metoda jest definiowana w klasach ControllerBase
i PageModel
. Przeciążenia metody umożliwiają określenie prefiksu i dostawcy wartości do użycia. Metoda zwraca false
wartość , jeśli powiązanie modelu nie powiedzie się. Oto przykład:
if (await TryUpdateModelAsync(
newInstructor,
"Instructor",
x => x.Name, x => x.HireDate!))
{
_instructorStore.Add(newInstructor);
return RedirectToPage("./Index");
}
return Page();
TryUpdateModelAsync używa dostawców wartości do pobierania danych z treści formularza, ciągu zapytania i kierowania danych. TryUpdateModelAsync
jest zwykle:
- Używane z aplikacjami Razor Pages i MVC przy użyciu kontrolerów i widoków, aby zapobiec nadmiernemu delegowaniu.
- Nie jest używany z internetowym interfejsem API, chyba że są używane z danych formularza, ciągów zapytań i danych trasy. Punkty końcowe internetowego interfejsu API, które używają formatu JSON, używają formatatorów wejściowych do deserializacji treści żądania do obiektu.
Aby uzyskać więcej informacji, zobacz TryUpdateModelAsync.
[FromServices] , atrybut
Nazwa tego atrybutu jest zgodna ze wzorcem atrybutów powiązania modelu, które określają źródło danych. Nie chodzi jednak o powiązanie danych od dostawcy wartości. Pobiera wystąpienie typu z kontenera wstrzykiwania zależności. Jego celem jest zapewnienie alternatywy dla iniekcji konstruktora, jeśli potrzebujesz usługi tylko wtedy, gdy wywoływana jest określona metoda.
Jeśli wystąpienie typu nie jest zarejestrowane w kontenerze wstrzykiwania zależności, aplikacja zgłasza wyjątek podczas próby powiązania parametru. Aby ustawić parametr jako opcjonalny, użyj jednego z następujących metod:
- Ustaw parametr na wartość null.
- Ustaw wartość domyślną parametru.
W przypadku parametrów dopuszczających wartość null upewnij się, że parametr nie null
jest używany przed uzyskaniem do niego dostępu.
Dodatkowe zasoby
W tym artykule wyjaśniono, czym jest powiązanie modelu, jak działa i jak dostosować jego zachowanie.
Wyświetl lub pobierz przykładowy kod (jak pobrać).
Co to jest powiązanie modelu
Kontrolery i Razor strony współpracują z danymi pochodzącymi z żądań HTTP. Na przykład dane trasy mogą zawierać klucz rekordu, a opublikowane pola formularza mogą zawierać wartości właściwości modelu. Pisanie kodu w celu pobrania każdej z tych wartości i przekonwertowanie ich z ciągów na typy platformy .NET byłoby żmudne i podatne na błędy. Powiązanie modelu automatyzuje ten proces. System powiązania modelu:
- Pobiera dane z różnych źródeł, takich jak dane trasy, pola formularza i ciągi zapytań.
- Udostępnia dane kontrolerom i Razor stronom w parametrach metody i właściwościach publicznych.
- Konwertuje dane ciągu na typy platformy .NET.
- Aktualizuje właściwości typów złożonych.
Przykład
Załóżmy, że masz następującą metodę akcji:
[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)
Aplikacja otrzymuje żądanie z następującym adresem URL:
http://contoso.com/api/pets/2?DogsOnly=true
Powiązanie modelu wykonuje następujące kroki po wybraniu przez system routingu metody akcji:
- Znajduje pierwszy parametr ,
GetById
liczba całkowita o nazwieid
. - Przegląda dostępne źródła w żądaniu HTTP i znajduje
id
= "2" w danych trasy. - Konwertuje ciąg "2" na liczbę całkowitą 2.
- Znajduje następny parametr ,
GetById
wartość logiczną o nazwiedogsOnly
. - Przegląda źródła i znajduje ciąg zapytania "DogsOnly=true". Dopasowywanie nazw nie jest uwzględniane wielkości liter.
- Konwertuje ciąg "true" na wartość logiczną
true
.
Następnie struktura wywołuje metodę GetById
, przekazując wartość 2 dla parametru id
i true
parametru dogsOnly
.
W poprzednim przykładzie docelowe powiązania modelu to parametry metody, które są prostymi typami. Obiekty docelowe mogą być również właściwościami typu złożonego. Po pomyślnym powiązaniu każdej właściwości walidacja modelu jest wykonywana dla tej właściwości. Rekord danych powiązanych z modelem oraz wszelkich błędów powiązań lub walidacji jest przechowywany w pliku ControllerBase.ModelState lub PageModel.ModelState. Aby dowiedzieć się, czy ten proces zakończył się pomyślnie, aplikacja sprawdza flagę ModelState.IsValid .
Elementy docelowe
Powiązanie modelu próbuje znaleźć wartości dla następujących rodzajów obiektów docelowych:
- Parametry metody akcji kontrolera, do którego jest kierowane żądanie.
- Razor Parametry metody obsługi stron, do których jest kierowane żądanie.
- Publiczne właściwości kontrolera lub
PageModel
klasy, jeśli są określone przez atrybuty.
[BindProperty] , atrybut
Można zastosować do właściwości publicznej kontrolera lub PageModel
klasy, aby spowodować powiązanie modelu z tą właściwością docelową:
public class EditModel : InstructorsPageModel
{
[BindProperty]
public Instructor Instructor { get; set; }
[BindProperties] , atrybut
Dostępne w systemie ASP.NET Core 2.1 lub nowszym. Można zastosować do kontrolera lub PageModel
klasy, aby poinformować powiązanie modelu w celu kierowania wszystkich właściwości publicznych klasy:
[BindProperties(SupportsGet = true)]
public class CreateModel : InstructorsPageModel
{
public Instructor Instructor { get; set; }
Powiązanie modelu dla żądań HTTP GET
Domyślnie właściwości nie są powiązane z żądaniami HTTP GET. Zazwyczaj wszystko, czego potrzebujesz do żądania GET, to parametr identyfikatora rekordu. Identyfikator rekordu służy do wyszukiwania elementu w bazie danych. W związku z tym nie ma potrzeby wiązania właściwości, która zawiera wystąpienie modelu. W scenariuszach, w których chcesz, aby właściwości powiązane z danymi z żądań GET ustawiły SupportsGet
właściwość na true
:
[BindProperty(Name = "ai_user", SupportsGet = true)]
public string ApplicationInsightsCookie { get; set; }
Źródła
Domyślnie powiązanie modelu pobiera dane w postaci par klucz-wartość z następujących źródeł w żądaniu HTTP:
- Pola formularza
- Treść żądania (dla kontrolerów, które mają atrybut [ApiController].
- Dane trasy
- Parametry ciągu zapytania
- Przekazane pliki
Dla każdego parametru docelowego lub właściwości źródła są skanowane w kolejności wskazanej na powyższej liście. Istnieje kilka wyjątków:
- Dane trasy i wartości ciągu zapytania są używane tylko dla prostych typów.
- Przekazane pliki są powiązane tylko z typami docelowymi, które implementują
IFormFile
program lubIEnumerable<IFormFile>
.
Jeśli domyślne źródło nie jest poprawne, użyj jednego z następujących atrybutów, aby określić źródło:
[FromQuery]
— Pobiera wartości z ciągu zapytania.[FromRoute]
- Pobiera wartości z danych trasy.[FromForm]
- Pobiera wartości z opublikowanych pól formularza.[FromBody]
— Pobiera wartości z treści żądania.[FromHeader]
- Pobiera wartości z nagłówków HTTP.
Te atrybuty:
Są dodawane do właściwości modelu indywidualnie (nie do klasy modelu), jak w poniższym przykładzie:
public class Instructor { public int ID { get; set; } [FromQuery(Name = "Note")] public string NoteFromQueryString { get; set; }
Opcjonalnie zaakceptuj wartość nazwy modelu w konstruktorze. Ta opcja jest dostępna w przypadku, gdy nazwa właściwości nie jest zgodna z wartością w żądaniu. Na przykład wartość w żądaniu może być nagłówkiem z łącznikiem w jego nazwie, jak w poniższym przykładzie:
public void OnGet([FromHeader(Name = "Accept-Language")] string language)
[FromBody] , atrybut
Zastosuj atrybut do parametru, [FromBody]
aby wypełnić jego właściwości z treści żądania HTTP. Środowisko uruchomieniowe ASP.NET Core deleguje odpowiedzialność za odczytywanie treści do elementu formatującego dane wejściowe. Formatery danych wejściowych zostały wyjaśnione w dalszej części tego artykułu.
Po [FromBody]
zastosowaniu do parametru typu złożonego wszystkie atrybuty źródła powiązania zastosowane do jego właściwości są ignorowane. Na przykład następująca Create
akcja określa, że jego pet
parametr jest wypełniany z treści:
public ActionResult<Pet> Create([FromBody] Pet pet)
Klasa Pet
określa, że jego Breed
właściwość jest wypełniana z parametru ciągu zapytania:
public class Pet
{
public string Name { get; set; }
[FromQuery] // Attribute is ignored.
public string Breed { get; set; }
}
W powyższym przykładzie:
- Atrybut
[FromQuery]
jest ignorowany. - Właściwość
Breed
nie jest wypełniana z parametru ciągu zapytania.
Formatery wejściowe odczytują tylko treść i nie rozumieją atrybutów źródła powiązania. Jeśli w treści znajduje się odpowiednia wartość, ta wartość jest używana do wypełnienia Breed
właściwości.
Nie należy stosować [FromBody]
do więcej niż jednego parametru dla metody akcji. Gdy strumień żądania zostanie odczytany przez program formatujący dane wejściowe, nie będzie już dostępny do ponownego odczytania w celu powiązania innych [FromBody]
parametrów.
Dodatkowe źródła
Dane źródłowe są dostarczane do systemu powiązania modelu przez dostawców wartości. Można zapisywać i rejestrować niestandardowych dostawców wartości, którzy pobierają dane na potrzeby powiązania modelu z innych źródeł. Możesz na przykład chcieć dane z plików cookie lub stanu sesji. Aby pobrać dane z nowego źródła:
- Utwórz klasę, która implementuje
IValueProvider
. - Utwórz klasę, która implementuje
IValueProviderFactory
. - Zarejestruj klasę fabryki w pliku
Startup.ConfigureServices
.
Przykładowa aplikacja zawiera dostawcę wartości i przykład fabryki, który pobiera wartości z plików cookie. Oto kod rejestracji w pliku 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();
Pokazany kod umieszcza niestandardowego dostawcę wartości po wszystkich wbudowanych dostawcach wartości. Aby ustawić ją jako pierwszą na liście, wywołaj metodę Insert(0, new CookieValueProviderFactory())
zamiast Add
.
Brak źródła dla właściwości modelu
Domyślnie błąd stanu modelu nie jest tworzony, jeśli dla właściwości modelu nie zostanie znaleziona żadna wartość. Właściwość jest ustawiona na wartość null lub wartość domyślną:
- Proste typy dopuszczane do wartości null są ustawione na
null
wartość . - Typy wartości innych niż null są ustawione na
default(T)
wartość . Na przykład parametrint id
ma wartość 0. - W przypadku złożonych typów powiązanie modelu tworzy wystąpienie przy użyciu konstruktora domyślnego bez ustawiania właściwości.
- Tablice są ustawione na
Array.Empty<T>()
, z tą różnicą, żebyte[]
tablice są ustawione nanull
.
Jeśli stan modelu powinien zostać unieważniony, jeśli nic nie zostanie znalezione w polach formularza dla właściwości modelu, użyj atrybutu [BindRequired]
.
Należy pamiętać, że to [BindRequired]
zachowanie dotyczy powiązania modelu z opublikowanych danych formularza, a nie danych JSON lub XML w treści żądania. Dane treści żądania są obsługiwane przez osoby formatujące dane wejściowe.
Błędy konwersji typów
Jeśli źródło zostanie znalezione, ale nie można go przekonwertować na typ docelowy, stan modelu jest oznaczony jako nieprawidłowy. Docelowy parametr lub właściwość jest ustawiona na wartość null lub wartość domyślną, jak wspomniano w poprzedniej sekcji.
W kontrolerze interfejsu [ApiController]
API, który ma atrybut, nieprawidłowy stan modelu powoduje automatyczną odpowiedź HTTP 400.
Razor Na stronie redisplay strony z komunikatem o błędzie:
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
_instructorsInMemoryStore.Add(Instructor);
return RedirectToPage("./Index");
}
Walidacja po stronie klienta przechwytuje większość nieprawidłowych danych, które w przeciwnym razie zostaną przesłane do Razor formularza strony. Ta walidacja utrudnia wyzwolenie poprzedniego wyróżnionego kodu. Przykładowa aplikacja zawiera przycisk Prześlij z nieprawidłową datą , który umieszcza nieprawidłowe dane w polu Data zatrudnienia i przesyła formularz. Ten przycisk pokazuje, jak kod ponownego tworzenia strony działa po wystąpieniu błędów konwersji danych.
Gdy strona jest redisplayed przez poprzedni kod, nieprawidłowe dane wejściowe nie są wyświetlane w polu formularza. Jest to spowodowane tym, że właściwość modelu została ustawiona na wartość null lub wartość domyślną. Nieprawidłowe dane wejściowe są wyświetlane w komunikacie o błędzie. Jeśli jednak chcesz ponownie odtworzyć nieprawidłowe dane w polu formularza, rozważ ręczne utworzenie właściwości modelu jako ciągu i ręczne przeprowadzenie konwersji danych.
Ta sama strategia jest zalecana, jeśli nie chcesz, aby błędy konwersji typów powodowały błędy stanu modelu. W takim przypadku utwórz właściwość modelu jako ciąg.
Typy proste
Proste typy, które binder modelu mogą konwertować ciągi źródłowe na następujące:
- Wartość logiczna
- Bajty, SByte
- Char
- Data/godzina
- DateTimeOffset
- Dziesiętne
- Podwójne
- Wyliczenie
- Guid
- Int16, Int32, Int64
- Pojedynczy
- Przedział czasu
- UInt16, UInt32, UInt64
- Identyfikator URI
- Wersja
Typy złożone
Typ złożony musi mieć publiczny konstruktor domyślny i publiczne właściwości zapisywalne do powiązania. Po utworzeniu powiązania modelu klasę tworzy się przy użyciu publicznego konstruktora domyślnego.
Dla każdej właściwości typu złożonego powiązanie modelu analizuje źródła wzorca nazw prefix.property_name. Jeśli nic nie zostanie znalezione, szuka tylko property_name bez prefiksu.
W przypadku powiązania z parametrem prefiks jest nazwą parametru. W przypadku powiązania z właściwością publiczną PageModel
prefiks jest nazwą właściwości publicznej. Niektóre atrybuty mają Prefix
właściwość, która umożliwia zastąpienie domyślnego użycia parametru lub nazwy właściwości.
Załóżmy na przykład, że typ złożony to następująca Instructor
klasa:
public class Instructor
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
Prefiks = nazwa parametru
Jeśli model, który ma być powiązany, jest parametrem o nazwie instructorToUpdate
:
public IActionResult OnPost(int? id, Instructor instructorToUpdate)
Powiązanie modelu rozpoczyna się od przejrzenia źródeł klucza instructorToUpdate.ID
. Jeśli to nie zostanie znalezione, szuka ID
bez prefiksu.
Prefiks = nazwa właściwości
Jeśli model, który ma być powiązany, jest właściwością o nazwie Instructor
kontrolera lub PageModel
klasy:
[BindProperty]
public Instructor Instructor { get; set; }
Powiązanie modelu rozpoczyna się od przejrzenia źródeł klucza Instructor.ID
. Jeśli to nie zostanie znalezione, szuka ID
bez prefiksu.
Prefiks niestandardowy
Jeśli model, który ma być powiązany, jest parametrem o nazwie instructorToUpdate
, a Bind
atrybut określa Instructor
jako prefiks:
public IActionResult OnPost(
int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)
Powiązanie modelu rozpoczyna się od przejrzenia źródeł klucza Instructor.ID
. Jeśli to nie zostanie znalezione, szuka ID
bez prefiksu.
Atrybuty dla obiektów docelowych typu złożonego
Dostępnych jest kilka wbudowanych atrybutów do kontrolowania powiązania modelu typów złożonych:
[Bind]
[BindRequired]
[BindNever]
Ostrzeżenie
Te atrybuty wpływają na powiązanie modelu, gdy opublikowane dane formularza są źródłem wartości. Nie mają one wpływu na formatery danych wejściowych, które przetwarzają treść żądań JSON i XML. Formatery danych wejściowych zostały wyjaśnione w dalszej części tego artykułu.
[Bind] , atrybut
Można zastosować do klasy lub parametru metody. Określa, które właściwości modelu powinny być uwzględnione w powiązaniu modelu. [Bind]
nie ma wpływu na formatery wejściowe.
W poniższym przykładzie tylko określone właściwości Instructor
modelu są powiązane, gdy wywoływana jest dowolna metoda obsługi lub akcji:
[Bind("LastName,FirstMidName,HireDate")]
public class Instructor
W poniższym przykładzie po wywołaniu OnPost
metody są powiązane tylko określone właściwości Instructor
modelu:
[HttpPost]
public IActionResult OnPost([Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
Atrybut [Bind]
może służyć do ochrony przed przesłanianiem w scenariuszach tworzenia . Nie działa dobrze w scenariuszach edycji, ponieważ wykluczone właściwości są ustawione na wartość null lub wartość domyślną, a nie pozostawione bez zmian. W przypadku ochrony przed przesłanianiem zalecane są modele wyświetlania, a nie atrybut.[Bind]
Aby uzyskać więcej informacji, zobacz Uwaga dotycząca zabezpieczeń dotycząca zastępowania.
[ModelBinder] , atrybut
ModelBinderAttribute można stosować do typów, właściwości lub parametrów. Umożliwia określenie typu powiązania modelu używanego do powiązania określonego wystąpienia lub typu. Na przykład:
[HttpPost]
public IActionResult OnPost([ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)
Atrybut [ModelBinder]
może również służyć do zmiany nazwy właściwości lub parametru, gdy jest powiązany model:
public class Instructor
{
[ModelBinder(Name = "instructor_id")]
public string Id { get; set; }
public string Name { get; set; }
}
[BindRequired] , atrybut
Można stosować tylko do właściwości modelu, a nie do parametrów metody. Powoduje, że powiązanie modelu powoduje dodanie błędu stanu modelu, jeśli powiązanie nie może wystąpić dla właściwości modelu. Oto przykład:
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; }
Zobacz również omówienie atrybutu [Required]
w temacie Walidacja modelu.
[BindNever] , atrybut
Można stosować tylko do właściwości modelu, a nie do parametrów metody. Uniemożliwia ustawienie właściwości modelu przez powiązanie modelu. Oto przykład:
public class InstructorWithDictionary
{
[BindNever]
public int ID { get; set; }
Kolekcje
W przypadku obiektów docelowych, które są kolekcjami prostych typów, powiązanie modelu wyszukuje dopasowania do parameter_name lub property_name. Jeśli nie zostanie znalezione dopasowanie, szuka jednego z obsługiwanych formatów bez prefiksu. Na przykład:
Załóżmy, że parametr, który ma być powiązany, to tablica o nazwie
selectedCourses
:public IActionResult OnPost(int? id, int[] selectedCourses)
Dane formularza lub ciągu zapytania mogą być w jednym z następujących formatów:
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
Unikaj powiązania parametru lub właściwości o nazwie
index
lubIndex
, jeśli sąsiaduje z wartością kolekcji. Powiązanie modelu próbuje użyćindex
jako indeksu kolekcji, co może spowodować nieprawidłowe powiązanie. Rozważmy na przykład następującą akcję:public IActionResult Post(string index, List<Product> products)
W poprzednim kodzie
index
parametr ciągu zapytania jest powiązany z parametremindex
metody, a także jest używany do powiązania kolekcji produktów. Zmiana nazwy parametruindex
lub użycie atrybutu powiązania modelu w celu skonfigurowania powiązania pozwala uniknąć tego problemu:public IActionResult Post(string productIndex, List<Product> products)
Następujący format jest obsługiwany tylko w danych formularza:
selectedCourses[]=1050&selectedCourses[]=2000
We wszystkich poprzednich przykładowych formatach powiązanie modelu przekazuje tablicę dwóch elementów do parametru
selectedCourses
:- selectedCourses[0]=1050
- selectedCourses[1]=2000
Formaty danych używające liczb w indeksie dolnym (... [0] ... [1] ...) musi upewnić się, że są one numerowane sekwencyjnie rozpoczynające się od zera. Jeśli występują luki w numerowaniu indeksu dolnego, wszystkie elementy po przerwie są ignorowane. Jeśli na przykład indeksy dolny to 0 i 2 zamiast 0 i 1, drugi element jest ignorowany.
Słowniki
W przypadku Dictionary
obiektów docelowych powiązanie modelu wyszukuje dopasowania do parameter_name lub property_name. Jeśli nie zostanie znalezione dopasowanie, szuka jednego z obsługiwanych formatów bez prefiksu. Na przykład:
Załóżmy, że parametr docelowy
Dictionary<int, string>
ma nazwęselectedCourses
:public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
Dane opublikowanego formularza lub ciągu zapytania mogą wyglądać podobnie do jednego z następujących przykładów:
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
We wszystkich poprzednich przykładowych formatach powiązanie modelu przekazuje słownik dwóch elementów do parametru
selectedCourses
:- selectedCourses["1050"]="Chemistry"
- selectedCourses["2000"]="Economics"
Powiązanie konstruktora i typy rekordów
Powiązanie modelu wymaga, aby złożone typy miały konstruktor bez parametrów. Zarówno System.Text.Json
metody formatowania danych wejściowych, jak i Newtonsoft.Json
obsługują deserializację klas, które nie mają konstruktora bez parametrów.
W języku C# 9 wprowadzono typy rekordów, które są doskonałym sposobem zwięźle reprezentowania danych za pośrednictwem sieci. ASP.NET Core dodaje obsługę wiązania modelu i sprawdzania poprawności typów rekordów za pomocą jednego konstruktora:
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>
Podczas sprawdzania poprawności typów rekordów środowisko uruchomieniowe wyszukuje metadane powiązania i walidacji w szczególności na parametrach, a nie we właściwościach.
Platforma umożliwia wiązanie i weryfikowanie typów rekordów:
public record Person([Required] string Name, [Range(0, 100)] int Age);
Aby poprzedni element działał, typ musi:
- Być typem rekordu.
- Mieć dokładnie jeden publiczny konstruktor.
- Zawierają parametry, które mają właściwość o tej samej nazwie i typie. Nazwy nie mogą się różnić literami.
Obiekty POCO bez konstruktorów bez parametrów
Obiekty POC, które nie mają konstruktorów bez parametrów, nie mogą być powiązane.
Poniższy kod powoduje wyjątek z informacją, że typ musi mieć konstruktor bez parametrów:
public class Person(string Name)
public record Person([Required] string Name, [Range(0, 100)] int Age)
{
public Person(string Name) : this (Name, 0);
}
Typy rekordów z ręcznie utworzonymi konstruktorami
Typy rekordów z ręcznie utworzonymi konstruktorami, które wyglądają jak konstruktory podstawowe działają
public record Person
{
public Person([Required] string Name, [Range(0, 100)] int Age) => (this.Name, this.Age) = (Name, Age);
public string Name { get; set; }
public int Age { get; set; }
}
Typy rekordów, walidacja i metadane powiązania
W przypadku typów rekordów używane są metadane walidacji i powiązania parametrów. Wszystkie metadane właściwości są ignorowane
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; }
}
Walidacja i metadane
Walidacja używa metadanych w parametrze, ale używa właściwości do odczytania wartości. W zwykłych przypadkach z konstruktorami podstawowymi te dwa byłyby identyczne. Istnieją jednak sposoby, aby go pokonać:
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);`
}
Funkcja TryUpdateModel nie aktualizuje parametrów typu rekordu
public record Person(string Name)
{
public int Age { get; set; }
}
var person = new Person("initial-name");
TryUpdateModel(person, ...);
W takim przypadku usługa MVC nie podejmie ponownej próby powiązania Name
. Age
Można jednak zaktualizować
Zachowanie globalizacji danych trasy powiązania modelu i ciągów zapytań
Dostawca wartości trasy ASP.NET Core i dostawca wartości ciągu zapytania:
- Traktuj wartości jako niezmienną kulturę.
- Spodziewaj się, że adresy URL są niezmienne dla kultury.
Natomiast wartości pochodzące z danych formularza są poddawane konwersji wrażliwej na kulturę. Jest to zaprojektowane tak, aby adresy URL można udostępniać między ustawieniami regionalnymi.
Aby dostawca wartości trasy ASP.NET Core i dostawca wartości ciągu zapytania przeszedł konwersję wrażliwą na kulturę:
- Dziedzicz z IValueProviderFactory
- Skopiuj kod z elementu QueryStringValueProviderFactory lub RouteValueValueProviderFactory
- Zastąp wartość kultury przekazaną do konstruktora dostawcy wartości atrybutem CultureInfo.CurrentCulture
- Zastąp domyślną fabrykę dostawcy wartości w opcjach MVC nowym:
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;
}
}
Specjalne typy danych
Istnieją pewne specjalne typy danych, które mogą obsłużyć powiązanie modelu.
IFormFile i IFormFileCollection
Przekazany plik uwzględniony w żądaniu HTTP. Obsługiwane jest IEnumerable<IFormFile>
również w przypadku wielu plików.
CancellationToken
Akcje mogą opcjonalnie powiązać element CancellationToken
jako parametr. RequestAborted Wiąże się to z sygnałem, gdy połączenie bazowe żądania HTTP zostanie przerwane. Akcje mogą używać tego parametru do anulowania długotrwałych operacji asynchronicznych wykonywanych w ramach akcji kontrolera.
FormCollection
Służy do pobierania wszystkich wartości z opublikowanych danych formularza.
Formatery danych wejściowych
Dane w treści żądania mogą być w formacie JSON, XML lub innym formacie. Aby przeanalizować te dane, powiązanie modelu używa formatującego danych wejściowych skonfigurowanego do obsługi określonego typu zawartości. Domyślnie ASP.NET Core zawiera formatery wejściowe oparte na formacie JSON do obsługi danych JSON. Możesz dodać inne formatery dla innych typów zawartości.
ASP.NET Core wybiera formatery wejściowe na podstawie atrybutu Consumes . Jeśli atrybut nie jest obecny, używa nagłówka Content-Type.
Aby użyć wbudowanych formatów wejściowych XML:
Microsoft.AspNetCore.Mvc.Formatters.Xml
Zainstaluj pakiet NuGet.W
Startup.ConfigureServices
pliku wywołaj metodę AddXmlSerializerFormatters lub AddXmlDataContractSerializerFormatters.services.AddRazorPages() .AddMvcOptions(options => { options.ValueProviderFactories.Add(new CookieValueProviderFactory()); options.ModelMetadataDetailsProviders.Add( new ExcludeBindingMetadataProvider(typeof(System.Version))); options.ModelMetadataDetailsProviders.Add( new SuppressChildValidationMetadataProvider(typeof(System.Guid))); }) .AddXmlSerializerFormatters();
Consumes
Zastosuj atrybut do klas kontrolerów lub metod akcji, które powinny oczekiwać XML w treści żądania.[HttpPost] [Consumes("application/xml")] public ActionResult<Pet> Create(Pet pet)
Aby uzyskać więcej informacji, zobacz Wprowadzenie serializacji XML.
Dostosowywanie powiązania modelu za pomocą formaterów wejściowych
Moduł formatujący dane wejściowe ponosi pełną odpowiedzialność za odczytywanie danych z treści żądania. Aby dostosować ten proces, skonfiguruj interfejsy API używane przez program formatujący dane wejściowe. W tej sekcji opisano sposób dostosowywania opartego na formatatorze System.Text.Json
danych wejściowych w celu zrozumienia niestandardowego typu o nazwie ObjectId
.
Rozważmy następujący model, który zawiera właściwość niestandardową ObjectId
o nazwie Id
:
public class ModelWithObjectId
{
public ObjectId Id { get; set; }
}
Aby dostosować proces powiązania modelu podczas używania metody System.Text.Json
, utwórz klasę pochodzącą z JsonConverter<T>klasy :
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);
}
}
Aby użyć konwertera niestandardowego, zastosuj JsonConverterAttribute atrybut do typu. W poniższym przykładzie ObjectId
typ jest skonfigurowany ObjectIdConverter
jako konwerter niestandardowy:
[JsonConverter(typeof(ObjectIdConverter))]
public struct ObjectId
{
public ObjectId(int id) =>
Id = id;
public int Id { get; }
}
Aby uzyskać więcej informacji, zobacz Jak pisać konwertery niestandardowe.
Wykluczanie określonych typów z powiązania modelu
Zachowanie systemów powiązań i walidacji modelu jest sterowane przez ModelMetadataprogram . Możesz dostosować ModelMetadata
, dodając dostawcę szczegółów do elementu MvcOptions.ModelMetadataDetailsProviders. Wbudowani dostawcy szczegółów są dostępni do wyłączania powiązania modelu lub walidacji dla określonych typów.
Aby wyłączyć powiązanie modelu dla wszystkich modeli określonego typu, dodaj element w elemExcludeBindingMetadataProvider.Startup.ConfigureServices
Aby na przykład wyłączyć powiązanie modelu dla wszystkich modeli typu System.Version
:
services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ValueProviderFactories.Add(new CookieValueProviderFactory());
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(System.Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(System.Guid)));
})
.AddXmlSerializerFormatters();
Aby wyłączyć walidację właściwości określonego typu, dodaj element SuppressChildValidationMetadataProvider w Startup.ConfigureServices
pliku . Aby na przykład wyłączyć walidację właściwości typu System.Guid
:
services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ValueProviderFactories.Add(new CookieValueProviderFactory());
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(System.Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(System.Guid)));
})
.AddXmlSerializerFormatters();
Niestandardowe powiązania modelu
Powiązanie modelu można rozszerzyć, pisząc powiązanie modelu niestandardowego i używając atrybutu [ModelBinder]
, aby wybrać go dla danego obiektu docelowego. Dowiedz się więcej o powiązaniu modelu niestandardowego.
Ręczne powiązanie modelu
Powiązanie modelu można wywołać ręcznie przy użyciu TryUpdateModelAsync metody . Metoda jest definiowana w klasach ControllerBase
i PageModel
. Przeciążenia metody umożliwiają określenie prefiksu i dostawcy wartości do użycia. Metoda zwraca false
wartość , jeśli powiązanie modelu nie powiedzie się. Oto przykład:
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 używa dostawców wartości do pobierania danych z treści formularza, ciągu zapytania i kierowania danych. TryUpdateModelAsync
jest zwykle:
- Używane z aplikacjami Razor Pages i MVC przy użyciu kontrolerów i widoków, aby zapobiec nadmiernemu delegowaniu.
- Nie jest używany z internetowym interfejsem API, chyba że są używane z danych formularza, ciągów zapytań i danych trasy. Punkty końcowe internetowego interfejsu API, które używają formatu JSON, używają formatatorów wejściowych do deserializacji treści żądania do obiektu.
Aby uzyskać więcej informacji, zobacz TryUpdateModelAsync.
[FromServices] , atrybut
Nazwa tego atrybutu jest zgodna ze wzorcem atrybutów powiązania modelu, które określają źródło danych. Nie chodzi jednak o powiązanie danych od dostawcy wartości. Pobiera wystąpienie typu z kontenera wstrzykiwania zależności. Jego celem jest zapewnienie alternatywy dla iniekcji konstruktora, jeśli potrzebujesz usługi tylko wtedy, gdy wywoływana jest określona metoda.