Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Note
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z aktualną wersją, zobacz artykuł w wersji .NET 10.
Warning
Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz zasady pomocy technicznej platformy .NET i platformy .NET Core. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu dla .NET 9.
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 wią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 tekstowe na typy .NET.
- Aktualizuje właściwości typów złożonych.
Example
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, czyli liczbę całkowitą 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, typ logiczny o nazwiedogsOnly. - Przegląda źródła i znajduje ciąg zapytania "DogsOnly=true". Dopasowywanie nazw nie uwzględnia 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 .
Targets
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
PageModelklasy, 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 powiązanie modelu kierowało się na wszystkie właściwości publiczne 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 przypisywania właściwości, która zawiera instancję modelu. W scenariuszach, w których chcesz, aby właściwości były powiązane z danymi z żądań GET, ustaw właściwość SupportsGet na true.
[BindProperty(Name = "ai_user", SupportsGet = true)]
public string? ApplicationInsightsCookie { get; set; }
Powiązanie modelu z prostymi i złożonymi typami
Powiązanie modelu używa określonych definicji dla typów, na których działa. Prosty typ jest konwertowany z jednego ciągu za pomocą metody TypeConverter lub TryParse. 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 do konwersji z string na SomeType, która nie wymaga zasobów zewnętrznych ani wielu danych wejściowych.
Sources
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 dotyczące trasy
- Parametry ciągu zapytania
- Przesłane 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ą
IFormFilelubIEnumerable<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 [FromBody] do parametru, 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.
Kiedy [FromBody] jest zastosowane do parametru typu złożonego, wszelkie 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ść
Breednie 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 właściwości Breed.
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
nullwartość . - Typy wartości niemające wartości null są ustawione na
default(T). Na przykład parametrint idma 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 z danych JSON lub XML w treści żądania. Dane treści żądania obsługiwane są przez formatery danych wejściowych.
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 API z atrybutem [ApiController], nieprawidłowy stan modelu skutkuje automatyczną odpowiedzią HTTP 400.
Na stronie Razor ponownie wyświetl stronę z komunikatem o błędzie:
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
// ...
return RedirectToPage("./Index");
}
Gdy strona jest ponownie wyświetlana przez poprzedni kod, nieprawidłowe dane wejściowe nie są widoczne 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 Proste i złożone typy powiązań modelu.
Proste typy, na które wiązanie modelu może konwertować ciągi źródłowe, to następujące:
- Boolean
- Bajty, SByte
- Char
- DateOnly
- DateTime
- DateTimeOffset
- Decimal
- Double
- Enum
- Guid
- Int16, Int32, Int64
- Single
- TimeOnly
- TimeSpan
- UInt16, UInt32, UInt64
- Uri
- Version
Połącz z IParsable<T>.TryParse
Interfejs API IParsable<TSelf>.TryParse obsługuje powiązywanie wartości parametrów akcji kontrolera.
public static bool TryParse (string? s, IFormatProvider? provider, out TSelf result);
Następująca klasa DateRange implementuje IParsable<TSelf>, aby obsłużyć powiązanie 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:
- Konwertuje ciąg reprezentujący dwie daty na
DateRangeobiekt - Model binder używa metody
IParsable<TSelf>.TryParse, aby powiązaćDateRange.
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 klasa Locale implementuje IParsable<TSelf> aby wspierać powiązanie z 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 klasy Locale do powiązania ciągu CultureInfo.
// 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 klas DateRange i Locale do powiązania zakresu dat z 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 na GitHubie pokazuje powyższy przykład dla kontrolera interfejsu API.
Połącz z TryParse
Interfejs API TryParse obsługuje powiązywanie wartości parametrów akcji kontrolera.
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 TryParsemetody , nie zależy od odbicia.
Następująca klasa implementuje DateRangeTP:
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, które można powiązać. 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 nazwprefix.property_name. Jeśli nic nie zostanie znalezione, szuka tylko property_name bez prefiksu. Decyzja o użyciu prefiksu nie jest podejmowana dla każdej właściwości. Na przykład z zapytaniem zawierającym ?Instructor.Id=100&Name=foo, powiązanym z metodą OnGet(Instructor instructor), wynikowy obiekt typu Instructor zawiera:
-
Idustaw wartość100. -
Nameustaw wartośćnull. Powiązanie modelu oczekuje,Instructor.NameponieważInstructor.Idzostało użyte w poprzednim parametrze zapytania.
Note
Linki dokumentacyjne do źródła referencyjnego .NET zwykle ładują domyślną gałąź repozytorium, która odzwierciedla obecne prace rozwojowe nad nadchodzącą wersją .NET. Aby wybrać tag dla określonej wersji, użyj listy rozwijanej Przełącz gałęzie lub tagi. Aby uzyskać więcej informacji, zobacz Jak wybrać tag wersji kodu źródłowego ASP.NET Core (dotnet/AspNetCore.Docs #26205).
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:
Warning
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 tylko określone właściwości modelu Instructor są powiązane, gdy wywołana zostanie metoda OnPost.
[HttpPost]
public IActionResult OnPost(
[Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
Atrybut [Bind] może być użyty do ochrony przed nadpisywaniem 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. Aby zabezpieczyć się przed nadpisywaniem, zalecane są modele wyświetlania zamiast atrybutu [Bind]. Aby uzyskać więcej informacji, zobacz Informacja dotycząca zabezpieczeń dotycząca nadpisywania.
[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. 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 podczas wiązania modelu.
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; }
// ...
}
Collections
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. 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=2000selectedCourses[0]=1050&selectedCourses[1]=2000[0]=1050&[1]=2000selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b[a]=1050&[b]=2000&index=a&index=bUnikaj powiązania parametru lub właściwości o nazwie
indexlubIndex, jeśli sąsiaduje z wartością kolekcji. Powiązanie modelu próbuje użyćindexjako 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
indexparametr ciągu zapytania jest powiązany z parametremindexmetody, a także jest używany do powiązania kolekcji produktów. Zmiana nazwy parametruindexlub 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[]=2000We wszystkich poprzednich przykładowych formatach wią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] ...) muszą upewnić się, że są one numerowane sekwencyjnie, zaczynając 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.
Dictionaries
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. 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]=EconomicsselectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry& selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics[0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=EconomicsWe 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 formattery danych wejściowych System.Text.Json, 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:
- Bądź typem rekordu.
- Powinien posiadać 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ć wielkością liter.
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 {
public 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 zdefiniowanymi konstruktorami
Typy rekordów z ręcznie utworzonymi konstruktorami, które wyglądają jak konstruktory podstawowe, działają poprawnie.
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 do walidacji i wią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łym przypadku z konstruktorami podstawowymi oba te elementy 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 . Można jednak zaktualizować Age
Globalizacja zachowania wiązania modelu, danych tras i ciągów zapytań
Dostawca wartości trasy w ASP.NET Core oraz dostawca wartości z 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ć w różnych lokalizacjach.
Aby dostawca wartości tras ASP.NET Core oraz dostawca wartości z ciągu zapytania mogły zostać poddane konwersji uwzględniającej kontekst kulturowy:
- Dziedziczenie 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 swoją nową:
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 może obsłużyć powiązanie modelu.
IFormFile i IFormFileCollection
Plik przesłany i dołączony do żądania HTTP. Obsługiwane jest IEnumerable<IFormFile> również w przypadku wielu plików.
CancellationToken
Akcje mogą opcjonalnie powiązać element CancellationToken jako parametr. Wiąże to RequestAborted, który sygnalizuje, kiedy połączenie bazowe żądania HTTP zostaje 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 program ASP.NET Core zawiera formatery danych wejściowych oparte na formacie JSON do obsługi danych JSON przy użyciu polecenia System.Text.Json. Możesz dodać inne formatery dla innych typów zawartości.
Domyślny formater danych wejściowych JSON można skonfigurować przy użyciu AddJsonOptions metody :
builder.Services.AddControllers().AddJsonOptions(options =>
{
// Configure property naming policy (camelCase)
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
// Add enum converter to serialize enums as strings
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
// Configure other JSON options
options.JsonSerializerOptions.WriteIndented = true;
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
});
Typowe opcje konfiguracji obejmują:
- Zasady nazewnictwa właściwości — konfigurowanie camelCase lub innych konwencji nazewnictwa
- Konwertery wyliczenia — obsługa serializacji wyliczenia jako ciągów
- Konwertery niestandardowe — dodawanie logiki serializacji specyficznej dla typu
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.cspliku wywołaj metodę AddXmlSerializerFormatters lub AddXmlDataContractSerializerFormatters.builder.Services.AddControllers() .AddXmlSerializerFormatters();ConsumesZastosuj 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.Jsondanych 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.
Wyklucz określone typy z powiązania modelu
Zachowanie systemów wiązania modelu i walidacji jest sterowane przez ModelMetadata. 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 typu określonego, dodaj SuppressChildValidationMetadataProvider w Program.cs. 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
Możesz rozszerzyć bindowanie modelu, pisząc niestandardowy wiązacz modelu i używając atrybutu kodu [ModelBinder], aby wybrać go dla danego obiektu docelowego. Dowiedz się więcej o niestandardowym wiązaniu modelu.
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, których należy użyć. Metoda zwraca false, 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 Razor Pages i aplikacjami MVC wykorzystującymi kontrolery i widoki, aby zapobiec nadmiernemu przesyłaniu danych.
- Nie jest używany z internetowym interfejsem API, chyba że konsumowany 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 wstrzykiwania przez konstruktor na wypadek, gdy potrzebujesz usługi tylko wtedy, gdy wywoływana jest określona metoda.
Jeśli instancja danego typu nie jest zarejestrowana w kontenerze wstrzykiwania zależności, aplikacja zgłasza wyjątek przy próbie 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 ma wartości null przed uzyskaniem do niego dostępu.
Deserializacja JSON+PipeReader w ramach MVC
Począwszy od platformy .NET 10, następujące obszary funkcjonalne ASP.NET Core używają przeciążeń JsonSerializer.DeserializeAsync opartych na PipeReader zamiast Stream:
- Minimalne interfejsy API (powiązanie parametrów, treść żądania odczytu)
- MVC (formatery danych wejściowych, model)
- Metody rozszerzeń HttpRequestJsonExtensions do odczytywania ciała żądania w formacie JSON.
W przypadku większości aplikacji przejście z użycia Stream do PipeReader zapewnia lepszą wydajność bez konieczności wprowadzania zmian w kodzie aplikacji. Jeśli jednak aplikacja ma konwerter niestandardowy, konwerter może nie obsługiwać Utf8JsonReader.HasValueSequence poprawnie. Jeśli tak nie jest, wynikiem mogą być błędy, takie jak ArgumentOutOfRangeException lub brakujące dane podczas deserializacji. Masz następujące opcje, aby Twój konwerter działał bez błędów związanych z PipeReader.
Opcja 1. Tymczasowe obejście
Szybkie obejście to powrót do korzystania z Stream bez obsługi PipeReader. Aby zaimplementować tę opcję, ustaw przełącznik "Microsoft.AspNetCore.UseStreamBasedJsonParsing" AppContext na wartość "true". Zalecamy wykonanie tej czynności tylko jako tymczasowe rozwiązanie, a następnie zaktualizowanie konwertera, aby obsługiwał HasValueSequence tak szybko, jak to możliwe. Przełącznik może zostać usunięty na platformie .NET 11. Jedynym celem było zapewnienie deweloperom czasu na zaktualizowanie konwerterów.
Opcja 2. Szybka poprawka implementacji JsonConverter
W przypadku tej poprawki należy przydzielić tablicę z obiektu ReadOnlySequence. W tym przykładzie pokazano, jak wygląda kod:
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
// previous code
}
Opcja 3. Bardziej skomplikowana, ale lepsza wydajność poprawki
Ta poprawka polega na skonfigurowaniu oddzielnej ścieżki kodu dla obsługi ReadOnlySequence.
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.HasValueSequence)
{
reader.ValueSequence;
// ReadOnlySequence optimized path
}
else
{
reader.ValueSpan;
// ReadOnlySpan optimized path
}
}
Aby uzyskać więcej informacji, zobacz
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 wią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 tekstowe na typy .NET.
- Aktualizuje właściwości typów złożonych.
Example
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, czyli liczbę całkowitą 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, typ logiczny o nazwiedogsOnly. - Przegląda źródła i znajduje ciąg zapytania "DogsOnly=true". Dopasowywanie nazw nie uwzględnia 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 .
Targets
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
PageModelklasy, 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 powiązanie modelu kierowało się na wszystkie właściwości publiczne 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 przypisywania właściwości, która zawiera instancję modelu. W scenariuszach, w których chcesz, aby właściwości były powiązane z danymi z żądań GET, ustaw właściwość SupportsGet na true.
[BindProperty(Name = "ai_user", SupportsGet = true)]
public string? ApplicationInsightsCookie { get; set; }
Powiązanie modelu z prostymi i złożonymi typami
Powiązanie modelu używa określonych definicji dla typów, na których działa. Prosty typ jest konwertowany z jednego ciągu za pomocą metody TypeConverter lub TryParse. 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 do konwersji z string na SomeType, która nie wymaga zasobów zewnętrznych ani wielu danych wejściowych.
Sources
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 dotyczące trasy
- Parametry ciągu zapytania
- Przesłane 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ą
IFormFilelubIEnumerable<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 [FromBody] do parametru, 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.
Kiedy [FromBody] jest zastosowane do parametru typu złożonego, wszelkie 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ść
Breednie 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 właściwości Breed.
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
nullwartość . - Typy wartości niemające wartości null są ustawione na
default(T). Na przykład parametrint idma 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 obsługiwane są przez formatery danych wejściowych.
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 API z atrybutem [ApiController], nieprawidłowy stan modelu skutkuje automatyczną odpowiedzią HTTP 400.
Na stronie Razor ponownie wyświetl stronę z komunikatem o błędzie:
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
// ...
return RedirectToPage("./Index");
}
Gdy strona jest ponownie wyświetlana przez poprzedni kod, nieprawidłowe dane wejściowe nie są widoczne 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 Proste i złożone typy powiązań modelu.
Proste typy, na które wiązanie modelu może konwertować ciągi źródłowe, to następujące:
- Boolean
- Bajty, SByte
- Char
- DateOnly
- DateTime
- DateTimeOffset
- Decimal
- Double
- Enum
- Guid
- Int16, Int32, Int64
- Single
- TimeOnly
- TimeSpan
- UInt16, UInt32, UInt64
- Uri
- Version
Połącz z IParsable<T>.TryParse
Interfejs API IParsable<TSelf>.TryParse obsługuje powiązywanie wartości parametrów akcji kontrolera.
public static bool TryParse (string? s, IFormatProvider? provider, out TSelf result);
Następująca klasa DateRange implementuje IParsable<TSelf>, aby obsłużyć powiązanie 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:
- Konwertuje ciąg reprezentujący dwie daty na
DateRangeobiekt - Model binder używa metody
IParsable<TSelf>.TryParse, aby powiązaćDateRange.
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 klasa Locale implementuje IParsable<TSelf> aby wspierać powiązanie z 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 klasy Locale do powiązania ciągu CultureInfo.
// 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 klas DateRange i Locale do powiązania zakresu dat z 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 na GitHubie pokazuje powyższy przykład dla kontrolera interfejsu API.
Połącz z TryParse
Interfejs API TryParse obsługuje powiązywanie wartości parametrów akcji kontrolera.
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 TryParsemetody , nie zależy od odbicia.
Następująca klasa implementuje DateRangeTP:
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, które można powiązać. 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 nazwprefix.property_name. Jeśli nic nie zostanie znalezione, szuka tylko property_name bez prefiksu. Decyzja o użyciu prefiksu nie jest podejmowana dla każdej właściwości. Na przykład z zapytaniem zawierającym ?Instructor.Id=100&Name=foo, powiązanym z metodą OnGet(Instructor instructor), wynikowy obiekt typu Instructor zawiera:
-
Idustaw wartość100. -
Nameustaw wartośćnull. Powiązanie modelu oczekuje,Instructor.NameponieważInstructor.Idzostało użyte w poprzednim parametrze zapytania.
Note
Linki dokumentacyjne do źródła referencyjnego .NET zwykle ładują domyślną gałąź repozytorium, która odzwierciedla obecne prace rozwojowe nad nadchodzącą wersją .NET. Aby wybrać tag dla określonej wersji, użyj listy rozwijanej Przełącz gałęzie lub tagi. Aby uzyskać więcej informacji, zobacz Jak wybrać tag wersji kodu źródłowego ASP.NET Core (dotnet/AspNetCore.Docs #26205).
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:
Warning
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 tylko określone właściwości modelu Instructor są powiązane, gdy wywołana zostanie metoda OnPost.
[HttpPost]
public IActionResult OnPost(
[Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
Atrybut [Bind] może być użyty do ochrony przed nadpisywaniem 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. Aby zabezpieczyć się przed nadpisywaniem, zalecane są modele wyświetlania zamiast atrybutu [Bind]. Aby uzyskać więcej informacji, zobacz Informacja dotycząca zabezpieczeń dotycząca nadpisywania.
[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. 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 podczas wiązania modelu.
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; }
// ...
}
Collections
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. 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=2000selectedCourses[0]=1050&selectedCourses[1]=2000[0]=1050&[1]=2000selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b[a]=1050&[b]=2000&index=a&index=bUnikaj powiązania parametru lub właściwości o nazwie
indexlubIndex, jeśli sąsiaduje z wartością kolekcji. Powiązanie modelu próbuje użyćindexjako 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
indexparametr ciągu zapytania jest powiązany z parametremindexmetody, a także jest używany do powiązania kolekcji produktów. Zmiana nazwy parametruindexlub 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[]=2000We wszystkich poprzednich przykładowych formatach wią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] ...) muszą upewnić się, że są one numerowane sekwencyjnie, zaczynając 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.
Dictionaries
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. 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]=EconomicsselectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry& selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics[0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=EconomicsWe 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 formattery danych wejściowych System.Text.Json, 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:
- Bądź typem rekordu.
- Powinien posiadać 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ć wielkością liter.
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 zdefiniowanymi konstruktorami
Typy rekordów z ręcznie utworzonymi konstruktorami, które wyglądają jak konstruktory podstawowe, działają poprawnie.
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 do walidacji i wią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łym przypadku z konstruktorami podstawowymi oba te elementy 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 . Można jednak zaktualizować Age
Globalizacja zachowania wiązania modelu, danych tras i ciągów zapytań
Dostawca wartości trasy w ASP.NET Core oraz dostawca wartości z 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ć w różnych lokalizacjach.
Aby dostawca wartości tras ASP.NET Core oraz dostawca wartości z ciągu zapytania mogły zostać poddane konwersji uwzględniającej kontekst kulturowy:
- Dziedziczenie 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 swoją nową:
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 może obsłużyć powiązanie modelu.
IFormFile i IFormFileCollection
Plik przesłany i dołączony do żądania HTTP. Obsługiwane jest IEnumerable<IFormFile> również w przypadku wielu plików.
CancellationToken
Akcje mogą opcjonalnie powiązać element CancellationToken jako parametr. Wiąże to RequestAborted, który sygnalizuje, kiedy połączenie bazowe żądania HTTP zostaje 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.cspliku wywołaj metodę AddXmlSerializerFormatters lub AddXmlDataContractSerializerFormatters.builder.Services.AddControllers() .AddXmlSerializerFormatters();ConsumesZastosuj 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.Jsondanych 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.
Wyklucz określone typy z powiązania modelu
Zachowanie systemów wiązania modelu i walidacji jest sterowane przez ModelMetadata. 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 typu określonego, dodaj SuppressChildValidationMetadataProvider w Program.cs. 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
Możesz rozszerzyć bindowanie modelu, pisząc niestandardowy wiązacz modelu i używając atrybutu kodu [ModelBinder], aby wybrać go dla danego obiektu docelowego. Dowiedz się więcej o niestandardowym wiązaniu modelu.
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, których należy użyć. Metoda zwraca false, 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 Razor Pages i aplikacjami MVC wykorzystującymi kontrolery i widoki, aby zapobiec nadmiernemu przesyłaniu danych.
- Nie jest używany z internetowym interfejsem API, chyba że konsumowany 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 wstrzykiwania przez konstruktor na wypadek, gdy potrzebujesz usługi tylko wtedy, gdy wywoływana jest określona metoda.
Jeśli instancja danego typu nie jest zarejestrowana w kontenerze wstrzykiwania zależności, aplikacja zgłasza wyjątek przy próbie 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 ma wartości null 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 wią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 tekstowe na typy .NET.
- Aktualizuje właściwości typów złożonych.
Example
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, czyli liczbę całkowitą 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, typ logiczny o nazwiedogsOnly. - Przegląda źródła i znajduje ciąg zapytania "DogsOnly=true". Dopasowywanie nazw nie uwzględnia 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 .
Targets
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
PageModelklasy, 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 powiązanie modelu kierowało się na wszystkie właściwości publiczne 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 przypisywania właściwości, która zawiera instancję modelu. W scenariuszach, w których chcesz, aby właściwości były powiązane z danymi z żądań GET, ustaw właściwość SupportsGet na true.
[BindProperty(Name = "ai_user", SupportsGet = true)]
public string? ApplicationInsightsCookie { get; set; }
Sources
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 dotyczące trasy
- Parametry ciągu zapytania
- Przesłane 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ą
IFormFilelubIEnumerable<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 [FromBody] do parametru, 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.
Kiedy [FromBody] jest zastosowane do parametru typu złożonego, wszelkie 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ść
Breednie 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 właściwości Breed.
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 danych dopuszczające wartości null są ustawione na
null. - Typy wartości niemające wartości null są ustawione na
default(T). Na przykład parametrint idma 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 obsługiwane są przez formatery danych wejściowych.
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 API z atrybutem [ApiController], nieprawidłowy stan modelu skutkuje automatyczną odpowiedzią HTTP 400.
Na stronie Razor ponownie wyświetl stronę z komunikatem o błędzie:
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
// ...
return RedirectToPage("./Index");
}
Gdy strona jest ponownie wyświetlana przez poprzedni kod, nieprawidłowe dane wejściowe nie są widoczne 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, na które wiązanie modelu może konwertować ciągi źródłowe, to następujące:
- Boolean
- Bajty, SByte
- Char
- DateTime
- DateTimeOffset
- Decimal
- Double
- Enum
- Guid
- Int16, Int32, Int64
- Single
- TimeSpan
- UInt16, UInt32, UInt64
- Uri
- Version
Typy złożone
Typ złożony musi mieć publiczny konstruktor domyślny i publiczne właściwości zapisywalne, które można powiązać. 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 nazwprefix.property_name. Jeśli nic nie zostanie znalezione, szuka tylko property_name bez prefiksu. Decyzja o użyciu prefiksu nie jest podejmowana dla każdej właściwości. Na przykład z zapytaniem zawierającym ?Instructor.Id=100&Name=foo, powiązanym z metodą OnGet(Instructor instructor), wynikowy obiekt typu Instructor zawiera:
-
Idustaw wartość100. -
Nameustaw wartośćnull. Powiązanie modelu oczekuje,Instructor.NameponieważInstructor.Idzostało użyte w poprzednim parametrze zapytania.
Note
Linki dokumentacyjne do źródła referencyjnego .NET zwykle ładują domyślną gałąź repozytorium, która odzwierciedla obecne prace rozwojowe nad nadchodzącą wersją .NET. Aby wybrać tag dla określonej wersji, użyj listy rozwijanej Przełącz gałęzie lub tagi. Aby uzyskać więcej informacji, zobacz Jak wybrać tag wersji kodu źródłowego ASP.NET Core (dotnet/AspNetCore.Docs #26205).
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:
Warning
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 tylko określone właściwości modelu Instructor są powiązane, gdy wywołana zostanie metoda OnPost.
[HttpPost]
public IActionResult OnPost(
[Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
Atrybut [Bind] może być użyty do ochrony przed nadpisywaniem 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. Aby zabezpieczyć się przed nadpisywaniem, zalecane są modele wyświetlania zamiast atrybutu [Bind]. Aby uzyskać więcej informacji, zobacz Informacja dotycząca zabezpieczeń dotycząca nadpisywania.
[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. 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 podczas wiązania modelu.
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; }
// ...
}
Collections
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. 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=2000selectedCourses[0]=1050&selectedCourses[1]=2000[0]=1050&[1]=2000selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b[a]=1050&[b]=2000&index=a&index=bUnikaj powiązania parametru lub właściwości o nazwie
indexlubIndex, jeśli sąsiaduje z wartością kolekcji. Powiązanie modelu próbuje użyćindexjako 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
indexparametr ciągu zapytania jest powiązany z parametremindexmetody, a także jest używany do powiązania kolekcji produktów. Zmiana nazwy parametruindexlub 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[]=2000We wszystkich poprzednich przykładowych formatach wią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] ...) muszą upewnić się, że są one numerowane sekwencyjnie, zaczynając 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.
Dictionaries
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. 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]=EconomicsselectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry& selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics[0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=EconomicsWe 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 formattery danych wejściowych System.Text.Json, 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:
- Bądź typem rekordu.
- Powinien posiadać 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ć wielkością liter.
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 zdefiniowanymi konstruktorami
Typy rekordów z ręcznie utworzonymi konstruktorami, które wyglądają jak konstruktory podstawowe, działają poprawnie.
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 do walidacji i wią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łym przypadku z konstruktorami podstawowymi oba te elementy 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 . Można jednak zaktualizować Age
Globalizacja zachowania wiązania modelu, danych tras i ciągów zapytań
Dostawca wartości trasy w ASP.NET Core oraz dostawca wartości z 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ć w różnych lokalizacjach.
Aby dostawca wartości tras ASP.NET Core oraz dostawca wartości z ciągu zapytania mogły zostać poddane konwersji uwzględniającej kontekst kulturowy:
- Dziedziczenie 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 swoją nową:
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 może obsłużyć powiązanie modelu.
IFormFile i IFormFileCollection
Plik przesłany i dołączony do żądania HTTP. Obsługiwane jest IEnumerable<IFormFile> również w przypadku wielu plików.
CancellationToken
Akcje mogą opcjonalnie powiązać element CancellationToken jako parametr. Wiąże to RequestAborted, który sygnalizuje, kiedy połączenie bazowe żądania HTTP zostaje 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.cspliku wywołaj metodę AddXmlSerializerFormatters lub AddXmlDataContractSerializerFormatters.builder.Services.AddControllers() .AddXmlSerializerFormatters();ConsumesZastosuj 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.Jsondanych 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.
Wyklucz określone typy z powiązania modelu
Zachowanie systemów wiązania modelu i walidacji jest sterowane przez ModelMetadata. 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 typu określonego, dodaj SuppressChildValidationMetadataProvider w Program.cs. 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
Możesz rozszerzyć bindowanie modelu, pisząc niestandardowy wiązacz modelu i używając atrybutu kodu [ModelBinder], aby wybrać go dla danego obiektu docelowego. Dowiedz się więcej o niestandardowym wiązaniu modelu.
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, których należy użyć. Metoda zwraca false, 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 Razor Pages i aplikacjami MVC wykorzystującymi kontrolery i widoki, aby zapobiec nadmiernemu przesyłaniu danych.
- Nie jest używany z internetowym interfejsem API, chyba że konsumowany 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 wstrzykiwania przez konstruktor na wypadek, gdy potrzebujesz usługi tylko wtedy, gdy wywoływana jest określona metoda.
Jeśli instancja danego typu nie jest zarejestrowana w kontenerze wstrzykiwania zależności, aplikacja zgłasza wyjątek przy próbie 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 ma wartości null 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 wią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 tekstowe na typy .NET.
- Aktualizuje właściwości typów złożonych.
Example
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, czyli liczbę całkowitą 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, typ logiczny o nazwiedogsOnly. - Przegląda źródła i znajduje ciąg zapytania "DogsOnly=true". Dopasowywanie nazw nie uwzględnia 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 .
Targets
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
PageModelklasy, 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 wersji ASP.NET Core 2.1 lub nowszej. Można zastosować do kontrolera lub PageModel klasy, aby powiązanie modelu kierowało się na wszystkie właściwości publiczne 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 przypisywania właściwości, która zawiera instancję modelu. W scenariuszach, w których chcesz, aby właściwości były powiązane z danymi z żądań GET, ustaw właściwość SupportsGet na true.
[BindProperty(Name = "ai_user", SupportsGet = true)]
public string ApplicationInsightsCookie { get; set; }
Sources
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 dotyczące trasy
- Parametry ciągu zapytania
- Przesłane 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ą
IFormFilelubIEnumerable<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 [FromBody] do parametru, 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.
Kiedy [FromBody] jest zastosowane do parametru typu złożonego, wszelkie 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ść
Breednie 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 właściwości Breed.
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 przykład dostawcy wartości i 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();
Kod przedstawiony umieszcza niestandardowego dostawcę wartości za wszystkimi wbudowanymi dostawcami 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 danych dopuszczające wartości null są ustawione na
null. - Typy wartości niemające wartości null są ustawione na
default(T). Na przykład parametrint idma 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 obsługiwane są przez formatery danych wejściowych.
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 API z atrybutem [ApiController], nieprawidłowy stan modelu skutkuje automatyczną odpowiedzią HTTP 400.
Na stronie Razor ponownie wyświetl stronę 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 formularza Razor Pages. 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 ponownie wyświetlona przez kod z poprzedniego kroku, 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, na które wiązanie modelu może konwertować ciągi źródłowe, to następujące:
- Boolean
- Bajty, SByte
- Char
- DateTime
- DateTimeOffset
- Decimal
- Double
- Enum
- Guid
- Int16, Int32, Int64
- Single
- TimeSpan
- UInt16, UInt32, UInt64
- Uri
- Version
Typy złożone
Typ złożony musi mieć publiczny konstruktor domyślny i publiczne właściwości zapisywalne, które można powiązać. 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]
Warning
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 tylko określone właściwości modelu Instructor są powiązane, gdy wywołana zostanie metoda OnPost.
[HttpPost]
public IActionResult OnPost([Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
Atrybut [Bind] może być użyty do ochrony przed nadpisywaniem 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. Aby zabezpieczyć się przed nadpisywaniem, zalecane są modele wyświetlania zamiast atrybutu [Bind]. Aby uzyskać więcej informacji, zobacz Informacja dotycząca zabezpieczeń dotycząca nadpisywania.
[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. 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 podczas wiązania modelu.
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; }
Collections
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. 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=2000selectedCourses[0]=1050&selectedCourses[1]=2000[0]=1050&[1]=2000selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b[a]=1050&[b]=2000&index=a&index=bUnikaj powiązania parametru lub właściwości o nazwie
indexlubIndex, jeśli sąsiaduje z wartością kolekcji. Powiązanie modelu próbuje użyćindexjako 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
indexparametr ciągu zapytania jest powiązany z parametremindexmetody, a także jest używany do powiązania kolekcji produktów. Zmiana nazwy parametruindexlub 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[]=2000We wszystkich poprzednich przykładowych formatach wią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] ...) muszą upewnić się, że są one numerowane sekwencyjnie, zaczynając 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.
Dictionaries
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. 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]=EconomicsselectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry& selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics[0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=EconomicsWe 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 formattery danych wejściowych System.Text.Json, 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:
- Bądź typem rekordu.
- Powinien posiadać 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ć wielkością liter.
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 zdefiniowanymi konstruktorami
Typy rekordów z ręcznie utworzonymi konstruktorami, które wyglądają jak konstruktory podstawowe, działają poprawnie.
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 do walidacji i wią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łym przypadku z konstruktorami podstawowymi oba te elementy 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 . Można jednak zaktualizować Age
Globalizacja zachowania wiązania modelu, danych tras i ciągów zapytań
Dostawca wartości trasy w ASP.NET Core oraz dostawca wartości z 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ć w różnych lokalizacjach.
Aby dostawca wartości tras ASP.NET Core oraz dostawca wartości z ciągu zapytania mogły zostać poddane konwersji uwzględniającej kontekst kulturowy:
- Dziedziczenie 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 swoją nową:
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 może obsłużyć powiązanie modelu.
IFormFile i IFormFileCollection
Plik przesłany i dołączony do żądania HTTP. Obsługiwane jest IEnumerable<IFormFile> również w przypadku wielu plików.
CancellationToken
Akcje mogą opcjonalnie powiązać element CancellationToken jako parametr. Wiąże to RequestAborted, który sygnalizuje, kiedy połączenie bazowe żądania HTTP zostaje 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.XmlZainstaluj pakiet NuGet.W
Startup.ConfigureServicespliku 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();ConsumesZastosuj 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.Jsondanych 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.
Wyklucz określone typy z powiązania modelu
Zachowanie systemów wiązania modelu i walidacji jest sterowane przez ModelMetadata. 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 typu określonego, dodaj SuppressChildValidationMetadataProvider w Startup.ConfigureServices. 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
Możesz rozszerzyć bindowanie modelu, pisząc niestandardowy wiązacz modelu i używając atrybutu kodu [ModelBinder], aby wybrać go dla danego obiektu docelowego. Dowiedz się więcej o niestandardowym wiązaniu modelu.
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, których należy użyć. Metoda zwraca false, 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 Razor Pages i aplikacjami MVC wykorzystującymi kontrolery i widoki, aby zapobiec nadmiernemu przesyłaniu danych.
- Nie jest używany z internetowym interfejsem API, chyba że konsumowany 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 wstrzykiwania przez konstruktor na wypadek, gdy potrzebujesz usługi tylko wtedy, gdy wywoływana jest określona metoda.