Pengikatan Model di ASP.NET Core
Catatan
Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.
Peringatan
Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan .NET dan .NET Core. Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.
Penting
Informasi ini berkaitan dengan produk pra-rilis yang mungkin dimodifikasi secara substansial sebelum dirilis secara komersial. Microsoft tidak memberikan jaminan, tersirat maupun tersurat, sehubungan dengan informasi yang diberikan di sini.
Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.
Artikel ini menjelaskan apa itu pengikatan model, cara kerjanya, dan cara menyesuaikan perilakunya.
Apa itu Pengikatan Model
Pengontrol dan Razor halaman bekerja dengan data yang berasal dari permintaan HTTP. Misalnya, data rute dapat menyediakan kunci rekaman, dan bidang formulir yang diposting dapat memberikan nilai untuk properti model. Menulis kode untuk mengambil masing-masing nilai ini dan mengonversinya dari string ke jenis .NET akan melelahkan dan rawan kesalahan. Pengikatan model mengotomatiskan proses ini. Sistem pengikatan model:
- Mengambil data dari berbagai sumber seperti data rute, bidang formulir, dan string kueri.
- Menyediakan data ke pengontrol dan Razor halaman dalam parameter metode dan properti publik.
- Mengonversi data string ke jenis .NET.
- Memperbarui properti jenis kompleks.
Contoh
Misalkan Anda memiliki metode tindakan berikut:
[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)
Dan aplikasi menerima permintaan dengan URL ini:
https://contoso.com/api/pets/2?DogsOnly=true
Pengikatan model melalui langkah-langkah berikut setelah sistem perutean memilih metode tindakan:
- Menemukan parameter pertama dari , bilangan
GetById
bulat bernamaid
. - Lihat sumber yang tersedia dalam permintaan HTTP dan temukan
id
= "2" dalam data rute. - Mengonversi string "2" menjadi bilangan bulat 2.
- Menemukan parameter berikutnya dari
GetById
, boolean bernamadogsOnly
. - Telusuri sumber dan temukan "DogsOnly=true" dalam string kueri. Pencocokan nama tidak peka huruf besar/kecil.
- Mengonversi string "true" menjadi boolean
true
.
Kerangka kerja kemudian memanggil GetById
metode , meneruskan 2 untuk id
parameter , dan true
untuk dogsOnly
parameter .
Dalam contoh sebelumnya, target pengikatan model adalah parameter metode yang merupakan jenis sederhana . Target mungkin juga merupakan properti dari jenis kompleks. Setelah setiap properti berhasil diikat, validasi model terjadi untuk properti tersebut. Catatan data apa yang terikat ke model, dan kesalahan pengikatan atau validasi apa pun, disimpan di ControllerBase.ModelState atau PageModel.ModelState. Untuk mengetahui apakah proses ini berhasil, aplikasi memeriksa bendera ModelState.IsValid .
Target
Pengikatan model mencoba menemukan nilai untuk jenis target berikut:
- Parameter metode tindakan pengontrol tempat permintaan dirutekan.
- Razor Parameter metode handler Pages tempat permintaan dirutekan.
- Properti publik pengontrol atau
PageModel
kelas, jika ditentukan oleh atribut.
Atribut [BindProperty]
Dapat diterapkan ke properti publik pengontrol atau PageModel
kelas untuk menyebabkan pengikatan model ke menargetkan properti tersebut:
public class EditModel : PageModel
{
[BindProperty]
public Instructor? Instructor { get; set; }
// ...
}
Atribut [BindProperties]
Dapat diterapkan ke pengontrol atau PageModel
kelas untuk memberi tahu pengikatan model untuk menargetkan semua properti publik kelas:
[BindProperties]
public class CreateModel : PageModel
{
public Instructor? Instructor { get; set; }
// ...
}
Pengikatan model untuk permintaan HTTP GET
Secara default, properti tidak terikat untuk permintaan HTTP GET. Biasanya, yang Anda butuhkan untuk permintaan GET adalah parameter ID rekaman. ID rekaman digunakan untuk mencari item dalam database. Oleh karena itu, tidak perlu mengikat properti yang menyimpan instans model. Dalam skenario di mana Anda ingin properti terikat ke data dari permintaan GET, atur properti ke SupportsGet
true
:
[BindProperty(Name = "ai_user", SupportsGet = true)]
public string? ApplicationInsightsCookie { get; set; }
Pengikatan model jenis sederhana dan kompleks
Pengikatan model menggunakan definisi tertentu untuk jenis yang dioperasikannya. Jenis sederhana dikonversi dari satu string menggunakan TypeConverter atau TryParse
metode . Jenis kompleks dikonversi dari beberapa nilai input. Kerangka kerja menentukan perbedaan berdasarkan keberadaan TypeConverter
atau TryParse
. Sebaiknya buat pengonversi jenis atau gunakan TryParse
untuk string
konversi ke SomeType
yang tidak memerlukan sumber daya eksternal atau beberapa input.
Sumber
Secara default, pengikatan model mendapatkan data dalam bentuk pasangan kunci-nilai dari sumber berikut dalam permintaan HTTP:
- Bidang formulir
- Isi permintaan (Untuk pengontrol yang memiliki atribut [ApiController].)
- Merutekan data
- Parameter untai kueri
- File yang diunggah
Untuk setiap parameter atau properti target, sumber dipindai dalam urutan yang ditunjukkan dalam daftar sebelumnya. Ada beberapa pengecualian:
- Data rute dan nilai string kueri hanya digunakan untuk jenis sederhana .
- File yang diunggah hanya terikat ke jenis target yang mengimplementasikan
IFormFile
atauIEnumerable<IFormFile>
.
Jika sumber default tidak benar, gunakan salah satu atribut berikut untuk menentukan sumber:
[FromQuery]
- Mendapatkan nilai dari string kueri.[FromRoute]
- Mendapatkan nilai dari data rute.[FromForm]
- Mendapatkan nilai dari bidang formulir yang diposting.[FromBody]
- Mendapatkan nilai dari isi permintaan.[FromHeader]
- Mendapatkan nilai dari header HTTP.
Atribut ini:
Ditambahkan ke properti model satu per satu dan bukan ke kelas model, seperti dalam contoh berikut:
public class Instructor { public int Id { get; set; } [FromQuery(Name = "Note")] public string? NoteFromQueryString { get; set; } // ... }
Secara opsional menerima nilai nama model di konstruktor. Opsi ini disediakan jika nama properti tidak cocok dengan nilai dalam permintaan. Misalnya, nilai dalam permintaan mungkin berupa header dengan tanda hubung atas namanya, seperti dalam contoh berikut:
public void OnGet([FromHeader(Name = "Accept-Language")] string language)
Atribut [FromBody]
Terapkan [FromBody]
atribut ke parameter untuk mengisi propertinya dari isi permintaan HTTP. Runtime ASP.NET Core mendelegasikan tanggung jawab membaca isi ke formatter input. Pemformat input dijelaskan nanti dalam artikel ini.
Ketika [FromBody]
diterapkan ke parameter jenis kompleks, atribut sumber pengikatan apa pun yang diterapkan ke propertinya diabaikan. Misalnya, tindakan berikut Create
menentukan bahwa parameternya pet
diisi dari isi:
public ActionResult<Pet> Create([FromBody] Pet pet)
Kelas Pet
menentukan bahwa propertinya Breed
diisi dari parameter string kueri:
public class Pet
{
public string Name { get; set; } = null!;
[FromQuery] // Attribute is ignored.
public string Breed { get; set; } = null!;
}
Dalam contoh sebelumnya:
- Atribut
[FromQuery]
diabaikan. - Properti
Breed
tidak diisi dari parameter string kueri.
Pemformat input hanya membaca isi dan tidak memahami atribut sumber pengikatan. Jika nilai yang sesuai ditemukan dalam isi, nilai tersebut digunakan untuk mengisi Breed
properti.
Jangan berlaku [FromBody]
untuk lebih dari satu parameter per metode tindakan. Setelah aliran permintaan dibaca oleh pemformat input, aliran permintaan tidak lagi tersedia untuk dibaca lagi untuk mengikat parameter lain [FromBody]
.
Sumber tambahan
Data sumber disediakan untuk sistem pengikatan model oleh penyedia nilai. Anda dapat menulis dan mendaftarkan penyedia nilai kustom yang mendapatkan data untuk pengikatan model dari sumber lain. Misalnya, Anda mungkin menginginkan data dari cookie atau status sesi. Untuk mendapatkan data dari sumber baru:
- Buat kelas yang mengimplementasikan
IValueProvider
. - Buat kelas yang mengimplementasikan
IValueProviderFactory
. - Daftarkan kelas pabrik di
Program.cs
.
Sampel mencakup penyedia nilai dan contoh pabrik yang mendapatkan nilai dari cookie. Daftarkan pabrik penyedia nilai kustom di Program.cs
:
builder.Services.AddControllers(options =>
{
options.ValueProviderFactories.Add(new CookieValueProviderFactory());
});
Kode sebelumnya menempatkan penyedia nilai kustom setelah semua penyedia nilai bawaan. Untuk menjadikannya yang pertama dalam daftar, panggil Insert(0, new CookieValueProviderFactory())
alih-alih Add
.
Tidak ada sumber untuk properti model
Secara default, kesalahan status model tidak dibuat jika tidak ada nilai yang ditemukan untuk properti model. Properti diatur ke null atau nilai default:
- Jenis sederhana yang dapat diubah ke null diatur ke
null
. - Jenis nilai yang tidak dapat diubah ke null diatur ke
default(T)
. Misalnya, parameterint id
diatur ke 0. - Untuk Jenis kompleks, pengikatan model membuat instans dengan menggunakan konstruktor default, tanpa mengatur properti.
- Array diatur ke
Array.Empty<T>()
, kecuali bahwabyte[]
array diatur kenull
.
Jika status model harus divalidasi ketika tidak ada yang ditemukan di bidang formulir untuk properti model, gunakan [BindRequired]
atribut .
Perhatikan bahwa perilaku ini [BindRequired]
berlaku untuk pengikatan model dari data formulir yang diposting, bukan ke data JSON atau XML dalam isi permintaan. Data isi permintaan ditangani oleh pemformat input.
Kesalahan konversi jenis
Jika sumber ditemukan tetapi tidak dapat dikonversi menjadi jenis target, status model ditandai sebagai tidak valid. Parameter target atau properti diatur ke null atau nilai default, seperti yang disebutkan di bagian sebelumnya.
Dalam pengontrol API yang memiliki [ApiController]
atribut , status model yang tidak valid menghasilkan respons HTTP 400 otomatis.
Razor Di halaman, putar ulang halaman dengan pesan kesalahan:
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
// ...
return RedirectToPage("./Index");
}
Ketika halaman diputar ulang oleh kode sebelumnya, input yang tidak valid tidak ditampilkan di bidang formulir. Ini karena properti model telah diatur ke null atau nilai default. Input yang tidak valid memang muncul dalam pesan kesalahan. Jika Anda ingin memutar ulang data buruk di bidang formulir, pertimbangkan untuk menjadikan properti model sebagai string dan melakukan konversi data secara manual.
Strategi yang sama direkomendasikan jika Anda tidak ingin kesalahan konversi jenis mengakibatkan kesalahan status model. Dalam hal ini, jadikan properti model sebagai string.
Jenis sederhana
Lihat Model yang mengikat jenis sederhana dan kompleks untuk penjelasan tentang jenis sederhana dan kompleks.
Jenis sederhana pengikat model dapat mengonversi string sumber menjadi termasuk yang berikut:
- Boolean
- Byte, SByte
- Char
- DateOnly
- DateTime
- DateTimeOffset
- Desimal
- Laju
- Enum
- Guid
- Int16, Int32, Int64
- Satu
- TimeOnly
- Rentang Waktu
- UInt16, UInt32, UInt64
- Uri
- Versi
Ikat dengan IParsable<T>.TryParse
IParsable<TSelf>.TryParse
API mendukung nilai parameter tindakan pengikatan pengontrol:
public static bool TryParse (string? s, IFormatProvider? provider, out TSelf result);
Kelas berikut DateRange
mengimplementasikan IParsable<TSelf>
untuk mendukung pengikatan rentang tanggal:
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;
}
}
Kode sebelumnya:
- Mengonversi string yang mewakili dua tanggal menjadi
DateRange
objek - Pengikat
IParsable<TSelf>.TryParse
model menggunakan metode untuk mengikatDateRange
.
Tindakan pengontrol berikut menggunakan DateRange
kelas untuk mengikat rentang tanggal:
// 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);
}
Kelas berikut Locale
mengimplementasikan IParsable<TSelf>
untuk mendukung pengikatan ke 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;
}
}
}
Tindakan pengontrol berikut menggunakan Locale
kelas untuk mengikat CultureInfo
string:
// 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);
}
Tindakan pengontrol berikut menggunakan DateRange
kelas dan Locale
untuk mengikat rentang tanggal dengan 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);
}
Aplikasi sampel API di GitHub menunjukkan sampel sebelumnya untuk pengontrol API.
Ikat dengan TryParse
TryParse
API mendukung nilai parameter tindakan pengikatan pengontrol:
public static bool TryParse(string value, T out result);
public static bool TryParse(string value, IFormatProvider provider, T out result);
IParsable<T>.TryParse
adalah pendekatan yang direkomendasikan untuk pengikatan parameter karena tidak seperti TryParse
, itu tidak bergantung pada refleksi.
Kelas berikut DateRangeTP
mengimplementasikan TryParse
:
public class DateRangeTP
{
public DateOnly? From { get; }
public DateOnly? To { get; }
public DateRangeTP(string from, string to)
{
if (string.IsNullOrEmpty(from))
throw new ArgumentNullException(nameof(from));
if (string.IsNullOrEmpty(to))
throw new ArgumentNullException(nameof(to));
From = DateOnly.Parse(from);
To = DateOnly.Parse(to);
}
public static bool TryParse(string? value, out DateRangeTP? result)
{
var range = value?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (range?.Length != 2)
{
result = default;
return false;
}
result = new DateRangeTP(range[0], range[1]);
return true;
}
}
Tindakan pengontrol berikut menggunakan DateRangeTP
kelas untuk mengikat rentang tanggal:
// 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);
}
Jenis kompleks
Jenis kompleks harus memiliki konstruktor default publik dan properti bisa-tulis publik untuk diikat. Saat pengikatan model terjadi, kelas dibuat menggunakan konstruktor default publik.
Untuk setiap properti dari jenis kompleks, pengikatan model melihat melalui sumber untuk pola nama prefix.property_name. Jika tidak ada yang ditemukan, ia mencari hanya property_name tanpa awalan. Keputusan untuk menggunakan awalan tidak dibuat per properti. Misalnya, dengan kueri yang berisi ?Instructor.Id=100&Name=foo
, terikat ke metode OnGet(Instructor instructor)
, objek jenis Instructor
yang dihasilkan berisi:
Id
atur ke100
.Name
atur kenull
. Pengikatan model mengharapkanInstructor.Name
karenaInstructor.Id
digunakan dalam parameter kueri sebelumnya.
Untuk mengikat parameter, awalannya adalah nama parameter. Untuk mengikat ke PageModel
properti publik, awalannya adalah nama properti publik. Beberapa atribut memiliki Prefix
properti yang memungkinkan Anda mengambil alih penggunaan default parameter atau nama properti.
Misalnya, misalkan jenis kompleks adalah kelas berikut Instructor
:
public class Instructor
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
Awalan = nama parameter
Jika model yang akan diikat adalah parameter bernama instructorToUpdate
:
public IActionResult OnPost(int? id, Instructor instructorToUpdate)
Pengikatan model dimulai dengan melihat melalui sumber untuk kunci instructorToUpdate.ID
. Jika itu tidak ditemukan, itu mencari ID
tanpa awalan.
Awalan = nama properti
Jika model yang akan diikat adalah properti bernama Instructor
pengontrol atau PageModel
kelas:
[BindProperty]
public Instructor Instructor { get; set; }
Pengikatan model dimulai dengan melihat melalui sumber untuk kunci Instructor.ID
. Jika itu tidak ditemukan, itu mencari ID
tanpa awalan.
Awalan kustom
Jika model yang akan diikat adalah parameter bernama instructorToUpdate
dan Bind
atribut menentukan Instructor
sebagai awalan:
public IActionResult OnPost(
int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)
Pengikatan model dimulai dengan melihat melalui sumber untuk kunci Instructor.ID
. Jika itu tidak ditemukan, itu mencari ID
tanpa awalan.
Atribut untuk target jenis kompleks
Beberapa atribut bawaan tersedia untuk mengontrol pengikatan model dari jenis kompleks:
Peringatan
Atribut ini memengaruhi pengikatan model saat data formulir yang diposting adalah sumber nilai. Ini tidak memengaruhi pemformat input, yang memproses isi permintaan JSON dan XML yang diposting. Pemformat input dijelaskan nanti dalam artikel ini.
Atribut [Ikat]
Dapat diterapkan ke kelas atau parameter metode. Menentukan properti model mana yang harus disertakan dalam pengikatan model. [Bind]
tidak memengaruhi pemformat input.
Dalam contoh berikut, hanya properti Instructor
model yang ditentukan yang terikat ketika penangan atau metode tindakan apa pun dipanggil:
[Bind("LastName,FirstMidName,HireDate")]
public class Instructor
Dalam contoh berikut, hanya properti Instructor
model yang ditentukan yang terikat saat OnPost
metode dipanggil:
[HttpPost]
public IActionResult OnPost(
[Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
Atribut [Bind]
dapat digunakan untuk melindungi dari overposting dalam membuat skenario. Ini tidak berfungsi dengan baik dalam skenario edit karena properti yang dikecualikan diatur ke null atau nilai default alih-alih dibiarkan tidak berubah. Untuk perlindungan terhadap overposting, lihat model direkomendasikan daripada [Bind]
atribut . Untuk informasi selengkapnya, lihat Catatan keamanan tentang overposting.
Atribut [ModelBinder]
ModelBinderAttribute dapat diterapkan ke jenis, properti, atau parameter. Ini memungkinkan menentukan jenis pengikat model yang digunakan untuk mengikat instans atau jenis tertentu. Contohnya:
[HttpPost]
public IActionResult OnPost(
[ModelBinder<MyInstructorModelBinder>] Instructor instructor)
Atribut [ModelBinder]
juga dapat digunakan untuk mengubah nama properti atau parameter saat sedang terikat model:
public class Instructor
{
[ModelBinder(Name = "instructor_id")]
public string Id { get; set; }
// ...
}
Atribut [BindRequired]
Menyebabkan pengikatan model untuk menambahkan kesalahan status model jika pengikatan tidak dapat terjadi untuk properti model. Berikut contohnya:
public class InstructorBindRequired
{
// ...
[BindRequired]
public DateTime HireDate { get; set; }
}
Lihat juga diskusi [Required]
atribut dalam validasi Model.
Atribut [BindNever]
Dapat diterapkan ke properti atau jenis. Mencegah pengikatan model dari pengaturan properti model. Saat diterapkan ke jenis, sistem pengikatan model mengecualikan semua properti yang ditentukan jenis. Berikut contohnya:
public class InstructorBindNever
{
[BindNever]
public int Id { get; set; }
// ...
}
Koleksi
Untuk target yang merupakan kumpulan jenis sederhana, pengikatan model mencari kecocokan untuk parameter_name atau property_name. Jika tidak ada kecocokan yang ditemukan, ia mencari salah satu format yang didukung tanpa awalan. Contohnya:
Misalkan parameter yang akan diikat adalah array bernama
selectedCourses
:public IActionResult OnPost(int? id, int[] selectedCourses)
Data string formulir atau kueri bisa dalam salah satu format berikut:
selectedCourses=1050&selectedCourses=2000
selectedCourses[0]=1050&selectedCourses[1]=2000
[0]=1050&[1]=2000
selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
[a]=1050&[b]=2000&index=a&index=b
Hindari mengikat parameter atau properti bernama
index
atauIndex
jika berdekatan dengan nilai koleksi. Pengikatan model mencoba digunakanindex
sebagai indeks untuk koleksi yang mungkin mengakibatkan pengikatan yang salah. Misalnya, pertimbangkan tindakan berikut:public IActionResult Post(string index, List<Product> products)
Dalam kode sebelumnya,
index
parameter string kueri mengikat parameterindex
metode dan juga digunakan untuk mengikat koleksi produk. Mengganti namaindex
parameter atau menggunakan atribut pengikatan model untuk mengonfigurasi pengikatan menghindari masalah ini:public IActionResult Post(string productIndex, List<Product> products)
Format berikut ini hanya didukung dalam data formulir:
selectedCourses[]=1050&selectedCourses[]=2000
Untuk semua format contoh sebelumnya, pengikatan model meneruskan array dua item ke
selectedCourses
parameter :- selectedCourses[0]=1050
- selectedCourses[1]=2000
Format data yang menggunakan nomor subskrip (... [0] ... [1] ...) harus memastikan bahwa mereka diberi nomor secara berurutan mulai dari nol. Jika ada celah dalam penomoran subskrip, semua item setelah celah diabaikan. Misalnya, jika subskrip adalah 0 dan 2, bukan 0 dan 1, item kedua diabaikan.
Kamus
Untuk Dictionary
target, pengikatan model mencari kecocokan untuk parameter_name atau property_name. Jika tidak ada kecocokan yang ditemukan, ia mencari salah satu format yang didukung tanpa awalan. Contohnya:
Misalkan parameter target bernama
Dictionary<int, string>
selectedCourses
:public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
Formulir yang diposting atau data string kueri bisa terlihat seperti salah satu contoh berikut:
selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics
[1050]=Chemistry&selectedCourses[2000]=Economics
selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry& selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
[0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
Untuk semua format contoh sebelumnya, pengikatan model meneruskan kamus dua item ke
selectedCourses
parameter :- selectedCourses["1050"]="Chemistry"
- selectedCourses["2000"]="Economics"
Pengikatan konstruktor dan jenis rekaman
Pengikatan model mengharuskan jenis kompleks memiliki konstruktor tanpa parameter. Pemformat System.Text.Json
input berbasis dan Newtonsoft.Json
mendukung deserialisasi kelas yang tidak memiliki konstruktor tanpa parameter.
Jenis rekaman adalah cara yang bagus untuk mewakili data melalui jaringan. ASP.NET Core mendukung pengikatan model dan memvalidasi jenis rekaman dengan satu konstruktor:
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>
Saat memvalidasi jenis rekaman, runtime mencari metadata pengikatan dan validasi khusus pada parameter daripada pada properti.
Kerangka kerja memungkinkan pengikatan ke dan memvalidasi jenis rekaman:
public record Person([Required] string Name, [Range(0, 100)] int Age);
Agar sebelumnya berfungsi, jenisnya harus:
- Jadilah jenis catatan.
- Memiliki tepat satu konstruktor publik.
- Berisi parameter yang memiliki properti dengan nama dan jenis yang sama. Nama tidak boleh berbeda menurut kasus.
POCO tanpa konstruktor tanpa parameter
POCO yang tidak memiliki konstruktor tanpa parameter tidak dapat terikat.
Kode berikut menghasilkan pengecualian yang mengatakan bahwa jenis harus memiliki konstruktor tanpa parameter:
public class Person(string Name)
public record Person([Required] string Name, [Range(0, 100)] int Age)
{
public Person(string Name) : this (Name, 0);
}
Jenis rekaman dengan konstruktor yang ditulis secara manual
Jenis rekaman dengan konstruktor yang ditulis secara manual yang terlihat seperti pekerjaan konstruktor utama
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; }
}
Jenis rekaman, validasi, dan metadata pengikatan
Untuk jenis rekaman, metadata validasi dan pengikatan pada parameter digunakan. Metadata apa pun pada properti diabaikan
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; }
}
Validasi dan metadata
Validasi menggunakan metadata pada parameter tetapi menggunakan properti untuk membaca nilai. Dalam kasus biasa dengan konstruktor utama, keduanya akan identik. Namun, ada cara untuk mengalahkannya:
public record Person([Required] string Name)
{
private readonly string _name;
// The following property is never null.
// However this object could have been constructed as "new Person(null)".
public string Name { get; init => _name = value ?? string.Empty; }
}
TryUpdateModel tidak memperbarui parameter pada jenis catatan
public record Person(string Name)
{
public int Age { get; set; }
}
var person = new Person("initial-name");
TryUpdateModel(person, ...);
Dalam hal ini, MVC tidak akan mencoba mengikat Name
lagi. Namun, Age
diizinkan untuk diperbarui
Perilaku globalisasi data rute pengikatan model dan string kueri
Penyedia nilai rute inti ASP.NET dan penyedia nilai string kueri:
- Perlakukan nilai sebagai budaya invarian.
- Harapkan bahwa URL invarian budaya.
Sebaliknya, nilai yang berasal dari data formulir mengalami konversi sensitif terhadap budaya. Ini dirancang agar URL dapat dibagikan di seluruh lokal.
Untuk membuat penyedia nilai rute inti ASP.NET dan penyedia nilai string kueri menjalani konversi yang sensitif terhadap budaya:
- Mewarisi dari IValueProviderFactory
- Salin kode dari QueryStringValueProviderFactory atau RouteValueValueProviderFactory
- Ganti nilai budaya yang diteruskan ke konstruktor penyedia nilai dengan CultureInfo.CurrentCulture
- Ganti pabrik penyedia nilai default di opsi MVC dengan yang baru Anda:
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();
});
Tipe data khusus
Ada beberapa jenis data khusus yang dapat ditangani oleh pengikatan model.
IFormFile dan IFormFileCollection
File yang diunggah disertakan dalam permintaan HTTP. Juga didukung adalah IEnumerable<IFormFile>
untuk beberapa file.
TokenPembatalan
Tindakan dapat secara opsional mengikat CancellationToken
sebagai parameter. Ini mengikat yang memberi sinyal ketika koneksi yang mendasar RequestAborted permintaan HTTP dibatalkan. Tindakan dapat menggunakan parameter ini untuk membatalkan operasi asinkron jangka panjang yang dijalankan sebagai bagian dari tindakan pengontrol.
FormCollection
Digunakan untuk mengambil semua nilai dari data formulir yang diposting.
Pemformat input
Data dalam isi permintaan dapat berada di JSON, XML, atau beberapa format lainnya. Untuk mengurai data ini, pengikatan model menggunakan pemformat input yang dikonfigurasi untuk menangani jenis konten tertentu. Secara default, ASP.NET Core menyertakan pemformat input berbasis JSON untuk menangani data JSON. Anda bisa menambahkan formatter lain untuk tipe isi lainnya.
ASP.NET Core memilih pemformat input berdasarkan atribut Konsumsi . Jika tidak ada atribut, atribut menggunakan header Content-Type.
Untuk menggunakan pemformat input XML bawaan:
Di
Program.cs
, panggil AddXmlSerializerFormatters atau AddXmlDataContractSerializerFormatters.builder.Services.AddControllers() .AddXmlSerializerFormatters();
Terapkan
Consumes
atribut ke kelas pengontrol atau metode tindakan yang seharusnya mengharapkan XML dalam isi permintaan.[HttpPost] [Consumes("application/xml")] public ActionResult<Pet> Create(Pet pet)
Untuk informasi selengkapnya, lihat Memperkenalkan Serialisasi XML.
Menyesuaikan pengikatan model dengan pemformat input
Pemformat input bertanggung jawab penuh untuk membaca data dari isi permintaan. Untuk menyesuaikan proses ini, konfigurasikan API yang digunakan oleh pemformat input. Bagian ini menjelaskan cara mengkustomisasi System.Text.Json
pemformat input berbasis untuk memahami jenis kustom bernama ObjectId
.
Pertimbangkan model berikut, yang berisi properti kustom ObjectId
:
public class InstructorObjectId
{
[Required]
public ObjectId ObjectId { get; set; } = null!;
}
Untuk menyesuaikan proses pengikatan model saat menggunakan System.Text.Json
, buat kelas yang berasal dari JsonConverter<T>:
internal class ObjectIdConverter : JsonConverter<ObjectId>
{
public override ObjectId Read(
ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> new(JsonSerializer.Deserialize<int>(ref reader, options));
public override void Write(
Utf8JsonWriter writer, ObjectId value, JsonSerializerOptions options)
=> writer.WriteNumberValue(value.Id);
}
Untuk menggunakan pengonversi kustom, terapkan JsonConverterAttribute atribut ke jenis . Dalam contoh berikut, jenis dikonfigurasi ObjectId
dengan ObjectIdConverter
sebagai pengonversi kustomnya:
[JsonConverter(typeof(ObjectIdConverter))]
public record ObjectId(int Id);
Untuk informasi selengkapnya, lihat Cara menulis pengonversi kustom.
Mengecualikan jenis yang ditentukan dari pengikatan model
Perilaku sistem pengikatan dan validasi model didorong oleh ModelMetadata. Anda dapat menyesuaikan ModelMetadata
dengan menambahkan penyedia detail ke MvcOptions.ModelMetadataDetailsProviders. Penyedia detail bawaan tersedia untuk menonaktifkan pengikatan atau validasi model untuk jenis tertentu.
Untuk menonaktifkan pengikatan model pada semua model dari jenis tertentu, tambahkan ExcludeBindingMetadataProvider di Program.cs
. Misalnya, untuk menonaktifkan pengikatan model pada semua model jenis System.Version
:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
Untuk menonaktifkan validasi pada properti dari jenis tertentu, tambahkan SuppressChildValidationMetadataProvider di Program.cs
. Misalnya, untuk menonaktifkan validasi pada properti jenis System.Guid
:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
Pengikat model kustom
Anda dapat memperluas pengikatan model dengan menulis pengikat model kustom dan menggunakan [ModelBinder]
atribut untuk memilihnya untuk target tertentu. Pelajari selengkapnya tentang pengikatan model kustom.
Pengikatan model manual
Pengikatan model dapat dipanggil secara manual dengan menggunakan TryUpdateModelAsync metode . Metode ini didefinisikan pada kedua ControllerBase
kelas dan PageModel
. Kelebihan metode memungkinkan Anda menentukan awalan dan penyedia nilai yang akan digunakan. Metode mengembalikan false
jika pengikatan model gagal. Berikut contohnya:
if (await TryUpdateModelAsync(
newInstructor,
"Instructor",
x => x.Name, x => x.HireDate!))
{
_instructorStore.Add(newInstructor);
return RedirectToPage("./Index");
}
return Page();
TryUpdateModelAsync menggunakan penyedia nilai untuk mendapatkan data dari isi formulir, string kueri, dan data rute. TryUpdateModelAsync
biasanya:
- Digunakan dengan Razor aplikasi Pages dan MVC menggunakan pengontrol dan tampilan untuk mencegah pengeposan berlebihan.
- Tidak digunakan dengan API web kecuali dikonsumsi dari data formulir, string kueri, dan data rute. Titik akhir API Web yang menggunakan JSON menggunakan Pemformat input untuk mendeserialisasi isi permintaan ke dalam objek.
Untuk informasi selengkapnya, lihat TryUpdateModelAsync.
Atribut [FromServices]
Nama atribut ini mengikuti pola atribut pengikatan model yang menentukan sumber data. Tetapi ini bukan tentang mengikat data dari penyedia nilai. Ini mendapatkan instans jenis dari kontainer injeksi dependensi. Tujuannya adalah untuk memberikan alternatif untuk injeksi konstruktor ketika Anda membutuhkan layanan hanya jika metode tertentu dipanggil.
Jika instans jenis tidak terdaftar dalam kontainer injeksi dependensi, aplikasi akan memberikan pengecualian saat mencoba mengikat parameter. Untuk membuat parameter opsional, gunakan salah satu pendekatan berikut:
- Buat parameter nullable.
- Tetapkan nilai default untuk parameter .
Untuk parameter nullable, pastikan parameter tidak null
sebelum mengaksesnya.
Sumber Daya Tambahan:
Artikel ini menjelaskan apa itu pengikatan model, cara kerjanya, dan cara menyesuaikan perilakunya.
Apa itu Pengikatan Model
Pengontrol dan Razor halaman bekerja dengan data yang berasal dari permintaan HTTP. Misalnya, data rute dapat menyediakan kunci rekaman, dan bidang formulir yang diposting dapat memberikan nilai untuk properti model. Menulis kode untuk mengambil masing-masing nilai ini dan mengonversinya dari string ke jenis .NET akan melelahkan dan rawan kesalahan. Pengikatan model mengotomatiskan proses ini. Sistem pengikatan model:
- Mengambil data dari berbagai sumber seperti data rute, bidang formulir, dan string kueri.
- Menyediakan data ke pengontrol dan Razor halaman dalam parameter metode dan properti publik.
- Mengonversi data string ke jenis .NET.
- Memperbarui properti jenis kompleks.
Contoh
Misalkan Anda memiliki metode tindakan berikut:
[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)
Dan aplikasi menerima permintaan dengan URL ini:
https://contoso.com/api/pets/2?DogsOnly=true
Pengikatan model melalui langkah-langkah berikut setelah sistem perutean memilih metode tindakan:
- Menemukan parameter pertama dari , bilangan
GetById
bulat bernamaid
. - Lihat sumber yang tersedia dalam permintaan HTTP dan temukan
id
= "2" dalam data rute. - Mengonversi string "2" menjadi bilangan bulat 2.
- Menemukan parameter berikutnya dari
GetById
, boolean bernamadogsOnly
. - Telusuri sumber dan temukan "DogsOnly=true" dalam string kueri. Pencocokan nama tidak peka huruf besar/kecil.
- Mengonversi string "true" menjadi boolean
true
.
Kerangka kerja kemudian memanggil GetById
metode , meneruskan 2 untuk id
parameter , dan true
untuk dogsOnly
parameter .
Dalam contoh sebelumnya, target pengikatan model adalah parameter metode yang merupakan jenis sederhana . Target mungkin juga merupakan properti dari jenis kompleks. Setelah setiap properti berhasil diikat, validasi model terjadi untuk properti tersebut. Catatan data apa yang terikat ke model, dan kesalahan pengikatan atau validasi apa pun, disimpan di ControllerBase.ModelState atau PageModel.ModelState. Untuk mengetahui apakah proses ini berhasil, aplikasi memeriksa bendera ModelState.IsValid .
Target
Pengikatan model mencoba menemukan nilai untuk jenis target berikut:
- Parameter metode tindakan pengontrol tempat permintaan dirutekan.
- Razor Parameter metode handler Pages tempat permintaan dirutekan.
- Properti publik pengontrol atau
PageModel
kelas, jika ditentukan oleh atribut.
Atribut [BindProperty]
Dapat diterapkan ke properti publik pengontrol atau PageModel
kelas untuk menyebabkan pengikatan model ke menargetkan properti tersebut:
public class EditModel : PageModel
{
[BindProperty]
public Instructor? Instructor { get; set; }
// ...
}
Atribut [BindProperties]
Dapat diterapkan ke pengontrol atau PageModel
kelas untuk memberi tahu pengikatan model untuk menargetkan semua properti publik kelas:
[BindProperties]
public class CreateModel : PageModel
{
public Instructor? Instructor { get; set; }
// ...
}
Pengikatan model untuk permintaan HTTP GET
Secara default, properti tidak terikat untuk permintaan HTTP GET. Biasanya, yang Anda butuhkan untuk permintaan GET adalah parameter ID rekaman. ID rekaman digunakan untuk mencari item dalam database. Oleh karena itu, tidak perlu mengikat properti yang menyimpan instans model. Dalam skenario di mana Anda ingin properti terikat ke data dari permintaan GET, atur properti ke SupportsGet
true
:
[BindProperty(Name = "ai_user", SupportsGet = true)]
public string? ApplicationInsightsCookie { get; set; }
Pengikatan model jenis sederhana dan kompleks
Pengikatan model menggunakan definisi tertentu untuk jenis yang dioperasikannya. Jenis sederhana dikonversi dari satu string menggunakan TypeConverter atau TryParse
metode . Jenis kompleks dikonversi dari beberapa nilai input. Kerangka kerja menentukan perbedaan berdasarkan keberadaan TypeConverter
atau TryParse
. Sebaiknya buat pengonversi jenis atau gunakan TryParse
untuk string
konversi ke SomeType
yang tidak memerlukan sumber daya eksternal atau beberapa input.
Sumber
Secara default, pengikatan model mendapatkan data dalam bentuk pasangan kunci-nilai dari sumber berikut dalam permintaan HTTP:
- Bidang formulir
- Isi permintaan (Untuk pengontrol yang memiliki atribut [ApiController].)
- Merutekan data
- Parameter untai kueri
- File yang diunggah
Untuk setiap parameter atau properti target, sumber dipindai dalam urutan yang ditunjukkan dalam daftar sebelumnya. Ada beberapa pengecualian:
- Data rute dan nilai string kueri hanya digunakan untuk jenis sederhana .
- File yang diunggah hanya terikat ke jenis target yang mengimplementasikan
IFormFile
atauIEnumerable<IFormFile>
.
Jika sumber default tidak benar, gunakan salah satu atribut berikut untuk menentukan sumber:
[FromQuery]
- Mendapatkan nilai dari string kueri.[FromRoute]
- Mendapatkan nilai dari data rute.[FromForm]
- Mendapatkan nilai dari bidang formulir yang diposting.[FromBody]
- Mendapatkan nilai dari isi permintaan.[FromHeader]
- Mendapatkan nilai dari header HTTP.
Atribut ini:
Ditambahkan ke properti model satu per satu dan bukan ke kelas model, seperti dalam contoh berikut:
public class Instructor { public int Id { get; set; } [FromQuery(Name = "Note")] public string? NoteFromQueryString { get; set; } // ... }
Secara opsional menerima nilai nama model di konstruktor. Opsi ini disediakan jika nama properti tidak cocok dengan nilai dalam permintaan. Misalnya, nilai dalam permintaan mungkin berupa header dengan tanda hubung atas namanya, seperti dalam contoh berikut:
public void OnGet([FromHeader(Name = "Accept-Language")] string language)
Atribut [FromBody]
Terapkan [FromBody]
atribut ke parameter untuk mengisi propertinya dari isi permintaan HTTP. Runtime ASP.NET Core mendelegasikan tanggung jawab membaca isi ke formatter input. Pemformat input dijelaskan nanti dalam artikel ini.
Ketika [FromBody]
diterapkan ke parameter jenis kompleks, atribut sumber pengikatan apa pun yang diterapkan ke propertinya diabaikan. Misalnya, tindakan berikut Create
menentukan bahwa parameternya pet
diisi dari isi:
public ActionResult<Pet> Create([FromBody] Pet pet)
Kelas Pet
menentukan bahwa propertinya Breed
diisi dari parameter string kueri:
public class Pet
{
public string Name { get; set; } = null!;
[FromQuery] // Attribute is ignored.
public string Breed { get; set; } = null!;
}
Dalam contoh sebelumnya:
- Atribut
[FromQuery]
diabaikan. - Properti
Breed
tidak diisi dari parameter string kueri.
Pemformat input hanya membaca isi dan tidak memahami atribut sumber pengikatan. Jika nilai yang sesuai ditemukan dalam isi, nilai tersebut digunakan untuk mengisi Breed
properti.
Jangan berlaku [FromBody]
untuk lebih dari satu parameter per metode tindakan. Setelah aliran permintaan dibaca oleh pemformat input, aliran permintaan tidak lagi tersedia untuk dibaca lagi untuk mengikat parameter lain [FromBody]
.
Sumber tambahan
Data sumber disediakan untuk sistem pengikatan model oleh penyedia nilai. Anda dapat menulis dan mendaftarkan penyedia nilai kustom yang mendapatkan data untuk pengikatan model dari sumber lain. Misalnya, Anda mungkin menginginkan data dari cookie atau status sesi. Untuk mendapatkan data dari sumber baru:
- Buat kelas yang mengimplementasikan
IValueProvider
. - Buat kelas yang mengimplementasikan
IValueProviderFactory
. - Daftarkan kelas pabrik di
Program.cs
.
Sampel mencakup penyedia nilai dan contoh pabrik yang mendapatkan nilai dari cookie. Daftarkan pabrik penyedia nilai kustom di Program.cs
:
builder.Services.AddControllers(options =>
{
options.ValueProviderFactories.Add(new CookieValueProviderFactory());
});
Kode sebelumnya menempatkan penyedia nilai kustom setelah semua penyedia nilai bawaan. Untuk menjadikannya yang pertama dalam daftar, panggil Insert(0, new CookieValueProviderFactory())
alih-alih Add
.
Tidak ada sumber untuk properti model
Secara default, kesalahan status model tidak dibuat jika tidak ada nilai yang ditemukan untuk properti model. Properti diatur ke null atau nilai default:
- Jenis sederhana yang dapat diubah ke null diatur ke
null
. - Jenis nilai yang tidak dapat diubah ke null diatur ke
default(T)
. Misalnya, parameterint id
diatur ke 0. - Untuk Jenis kompleks, pengikatan model membuat instans dengan menggunakan konstruktor default, tanpa mengatur properti.
- Array diatur ke
Array.Empty<T>()
, kecuali bahwabyte[]
array diatur kenull
.
Jika status model harus divalidasi ketika tidak ada yang ditemukan di bidang formulir untuk properti model, gunakan [BindRequired]
atribut .
Perhatikan bahwa perilaku ini [BindRequired]
berlaku untuk pengikatan model dari data formulir yang diposting, bukan ke data JSON atau XML dalam isi permintaan. Data isi permintaan ditangani oleh pemformat input.
Kesalahan konversi jenis
Jika sumber ditemukan tetapi tidak dapat dikonversi menjadi jenis target, status model ditandai sebagai tidak valid. Parameter target atau properti diatur ke null atau nilai default, seperti yang disebutkan di bagian sebelumnya.
Dalam pengontrol API yang memiliki [ApiController]
atribut , status model yang tidak valid menghasilkan respons HTTP 400 otomatis.
Razor Di halaman, putar ulang halaman dengan pesan kesalahan:
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
// ...
return RedirectToPage("./Index");
}
Ketika halaman diputar ulang oleh kode sebelumnya, input yang tidak valid tidak ditampilkan di bidang formulir. Ini karena properti model telah diatur ke null atau nilai default. Input yang tidak valid memang muncul dalam pesan kesalahan. Jika Anda ingin memutar ulang data buruk di bidang formulir, pertimbangkan untuk menjadikan properti model sebagai string dan melakukan konversi data secara manual.
Strategi yang sama direkomendasikan jika Anda tidak ingin kesalahan konversi jenis mengakibatkan kesalahan status model. Dalam hal ini, jadikan properti model sebagai string.
Jenis sederhana
Lihat Model yang mengikat jenis sederhana dan kompleks untuk penjelasan tentang jenis sederhana dan kompleks.
Jenis sederhana pengikat model dapat mengonversi string sumber menjadi termasuk yang berikut:
- Boolean
- Byte, SByte
- Char
- DateOnly
- DateTime
- DateTimeOffset
- Desimal
- Laju
- Enum
- Guid
- Int16, Int32, Int64
- Satu
- TimeOnly
- Rentang Waktu
- UInt16, UInt32, UInt64
- Uri
- Versi
Ikat dengan IParsable<T>.TryParse
IParsable<TSelf>.TryParse
API mendukung nilai parameter tindakan pengikatan pengontrol:
public static bool TryParse (string? s, IFormatProvider? provider, out TSelf result);
Kelas berikut DateRange
mengimplementasikan IParsable<TSelf>
untuk mendukung pengikatan rentang tanggal:
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;
}
}
Kode sebelumnya:
- Mengonversi string yang mewakili dua tanggal menjadi
DateRange
objek - Pengikat
IParsable<TSelf>.TryParse
model menggunakan metode untuk mengikatDateRange
.
Tindakan pengontrol berikut menggunakan DateRange
kelas untuk mengikat rentang tanggal:
// 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);
}
Kelas berikut Locale
mengimplementasikan IParsable<TSelf>
untuk mendukung pengikatan ke 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;
}
}
}
Tindakan pengontrol berikut menggunakan Locale
kelas untuk mengikat CultureInfo
string:
// 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);
}
Tindakan pengontrol berikut menggunakan DateRange
kelas dan Locale
untuk mengikat rentang tanggal dengan 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);
}
Aplikasi sampel API di GitHub menunjukkan sampel sebelumnya untuk pengontrol API.
Ikat dengan TryParse
TryParse
API mendukung nilai parameter tindakan pengikatan pengontrol:
public static bool TryParse(string value, T out result);
public static bool TryParse(string value, IFormatProvider provider, T out result);
IParsable<T>.TryParse
adalah pendekatan yang direkomendasikan untuk pengikatan parameter karena tidak seperti TryParse
, itu tidak bergantung pada refleksi.
Kelas berikut DateRangeTP
mengimplementasikan TryParse
:
public class DateRangeTP
{
public DateOnly? From { get; }
public DateOnly? To { get; }
public DateRangeTP(string from, string to)
{
if (string.IsNullOrEmpty(from))
throw new ArgumentNullException(nameof(from));
if (string.IsNullOrEmpty(to))
throw new ArgumentNullException(nameof(to));
From = DateOnly.Parse(from);
To = DateOnly.Parse(to);
}
public static bool TryParse(string? value, out DateRangeTP? result)
{
var range = value?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (range?.Length != 2)
{
result = default;
return false;
}
result = new DateRangeTP(range[0], range[1]);
return true;
}
}
Tindakan pengontrol berikut menggunakan DateRangeTP
kelas untuk mengikat rentang tanggal:
// 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);
}
Jenis kompleks
Jenis kompleks harus memiliki konstruktor default publik dan properti bisa-tulis publik untuk diikat. Saat pengikatan model terjadi, kelas dibuat menggunakan konstruktor default publik.
Untuk setiap properti dari jenis kompleks, pengikatan model melihat melalui sumber untuk pola nama prefix.property_name. Jika tidak ada yang ditemukan, ia mencari hanya property_name tanpa awalan. Keputusan untuk menggunakan awalan tidak dibuat per properti. Misalnya, dengan kueri yang berisi ?Instructor.Id=100&Name=foo
, terikat ke metode OnGet(Instructor instructor)
, objek jenis Instructor
yang dihasilkan berisi:
Id
atur ke100
.Name
atur kenull
. Pengikatan model mengharapkanInstructor.Name
karenaInstructor.Id
digunakan dalam parameter kueri sebelumnya.
Untuk mengikat parameter, awalannya adalah nama parameter. Untuk mengikat ke PageModel
properti publik, awalannya adalah nama properti publik. Beberapa atribut memiliki Prefix
properti yang memungkinkan Anda mengambil alih penggunaan default parameter atau nama properti.
Misalnya, misalkan jenis kompleks adalah kelas berikut Instructor
:
public class Instructor
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
Awalan = nama parameter
Jika model yang akan diikat adalah parameter bernama instructorToUpdate
:
public IActionResult OnPost(int? id, Instructor instructorToUpdate)
Pengikatan model dimulai dengan melihat melalui sumber untuk kunci instructorToUpdate.ID
. Jika itu tidak ditemukan, itu mencari ID
tanpa awalan.
Awalan = nama properti
Jika model yang akan diikat adalah properti bernama Instructor
pengontrol atau PageModel
kelas:
[BindProperty]
public Instructor Instructor { get; set; }
Pengikatan model dimulai dengan melihat melalui sumber untuk kunci Instructor.ID
. Jika itu tidak ditemukan, itu mencari ID
tanpa awalan.
Awalan kustom
Jika model yang akan diikat adalah parameter bernama instructorToUpdate
dan Bind
atribut menentukan Instructor
sebagai awalan:
public IActionResult OnPost(
int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)
Pengikatan model dimulai dengan melihat melalui sumber untuk kunci Instructor.ID
. Jika itu tidak ditemukan, itu mencari ID
tanpa awalan.
Atribut untuk target jenis kompleks
Beberapa atribut bawaan tersedia untuk mengontrol pengikatan model dari jenis kompleks:
Peringatan
Atribut ini memengaruhi pengikatan model saat data formulir yang diposting adalah sumber nilai. Ini tidak memengaruhi pemformat input, yang memproses isi permintaan JSON dan XML yang diposting. Pemformat input dijelaskan nanti dalam artikel ini.
Atribut [Ikat]
Dapat diterapkan ke kelas atau parameter metode. Menentukan properti model mana yang harus disertakan dalam pengikatan model. [Bind]
tidak memengaruhi pemformat input.
Dalam contoh berikut, hanya properti Instructor
model yang ditentukan yang terikat ketika penangan atau metode tindakan apa pun dipanggil:
[Bind("LastName,FirstMidName,HireDate")]
public class Instructor
Dalam contoh berikut, hanya properti Instructor
model yang ditentukan yang terikat saat OnPost
metode dipanggil:
[HttpPost]
public IActionResult OnPost(
[Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
Atribut [Bind]
dapat digunakan untuk melindungi dari overposting dalam membuat skenario. Ini tidak berfungsi dengan baik dalam skenario edit karena properti yang dikecualikan diatur ke null atau nilai default alih-alih dibiarkan tidak berubah. Untuk perlindungan terhadap overposting, lihat model direkomendasikan daripada [Bind]
atribut . Untuk informasi selengkapnya, lihat Catatan keamanan tentang overposting.
Atribut [ModelBinder]
ModelBinderAttribute dapat diterapkan ke jenis, properti, atau parameter. Ini memungkinkan menentukan jenis pengikat model yang digunakan untuk mengikat instans atau jenis tertentu. Contohnya:
[HttpPost]
public IActionResult OnPost(
[ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)
Atribut [ModelBinder]
juga dapat digunakan untuk mengubah nama properti atau parameter saat sedang terikat model:
public class Instructor
{
[ModelBinder(Name = "instructor_id")]
public string Id { get; set; }
// ...
}
Atribut [BindRequired]
Menyebabkan pengikatan model untuk menambahkan kesalahan status model jika pengikatan tidak dapat terjadi untuk properti model. Berikut contohnya:
public class InstructorBindRequired
{
// ...
[BindRequired]
public DateTime HireDate { get; set; }
}
Lihat juga diskusi [Required]
atribut dalam validasi Model.
Atribut [BindNever]
Dapat diterapkan ke properti atau jenis. Mencegah pengikatan model dari pengaturan properti model. Saat diterapkan ke jenis, sistem pengikatan model mengecualikan semua properti yang ditentukan jenis. Berikut contohnya:
public class InstructorBindNever
{
[BindNever]
public int Id { get; set; }
// ...
}
Koleksi
Untuk target yang merupakan kumpulan jenis sederhana, pengikatan model mencari kecocokan untuk parameter_name atau property_name. Jika tidak ada kecocokan yang ditemukan, ia mencari salah satu format yang didukung tanpa awalan. Contohnya:
Misalkan parameter yang akan diikat adalah array bernama
selectedCourses
:public IActionResult OnPost(int? id, int[] selectedCourses)
Data string formulir atau kueri bisa dalam salah satu format berikut:
selectedCourses=1050&selectedCourses=2000
selectedCourses[0]=1050&selectedCourses[1]=2000
[0]=1050&[1]=2000
selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
[a]=1050&[b]=2000&index=a&index=b
Hindari mengikat parameter atau properti bernama
index
atauIndex
jika berdekatan dengan nilai koleksi. Pengikatan model mencoba digunakanindex
sebagai indeks untuk koleksi yang mungkin mengakibatkan pengikatan yang salah. Misalnya, pertimbangkan tindakan berikut:public IActionResult Post(string index, List<Product> products)
Dalam kode sebelumnya,
index
parameter string kueri mengikat parameterindex
metode dan juga digunakan untuk mengikat koleksi produk. Mengganti namaindex
parameter atau menggunakan atribut pengikatan model untuk mengonfigurasi pengikatan menghindari masalah ini:public IActionResult Post(string productIndex, List<Product> products)
Format berikut ini hanya didukung dalam data formulir:
selectedCourses[]=1050&selectedCourses[]=2000
Untuk semua format contoh sebelumnya, pengikatan model meneruskan array dua item ke
selectedCourses
parameter :- selectedCourses[0]=1050
- selectedCourses[1]=2000
Format data yang menggunakan nomor subskrip (... [0] ... [1] ...) harus memastikan bahwa mereka diberi nomor secara berurutan mulai dari nol. Jika ada celah dalam penomoran subskrip, semua item setelah celah diabaikan. Misalnya, jika subskrip adalah 0 dan 2, bukan 0 dan 1, item kedua diabaikan.
Kamus
Untuk Dictionary
target, pengikatan model mencari kecocokan untuk parameter_name atau property_name. Jika tidak ada kecocokan yang ditemukan, ia mencari salah satu format yang didukung tanpa awalan. Contohnya:
Misalkan parameter target bernama
Dictionary<int, string>
selectedCourses
:public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
Formulir yang diposting atau data string kueri bisa terlihat seperti salah satu contoh berikut:
selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics
[1050]=Chemistry&selectedCourses[2000]=Economics
selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry& selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
[0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
Untuk semua format contoh sebelumnya, pengikatan model meneruskan kamus dua item ke
selectedCourses
parameter :- selectedCourses["1050"]="Chemistry"
- selectedCourses["2000"]="Economics"
Pengikatan konstruktor dan jenis rekaman
Pengikatan model mengharuskan jenis kompleks memiliki konstruktor tanpa parameter. Pemformat System.Text.Json
input berbasis dan Newtonsoft.Json
mendukung deserialisasi kelas yang tidak memiliki konstruktor tanpa parameter.
Jenis rekaman adalah cara yang bagus untuk mewakili data melalui jaringan. ASP.NET Core mendukung pengikatan model dan memvalidasi jenis rekaman dengan satu konstruktor:
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>
Saat memvalidasi jenis rekaman, runtime mencari metadata pengikatan dan validasi khusus pada parameter daripada pada properti.
Kerangka kerja memungkinkan pengikatan ke dan memvalidasi jenis rekaman:
public record Person([Required] string Name, [Range(0, 100)] int Age);
Agar sebelumnya berfungsi, jenisnya harus:
- Jadilah jenis catatan.
- Memiliki tepat satu konstruktor publik.
- Berisi parameter yang memiliki properti dengan nama dan jenis yang sama. Nama tidak boleh berbeda menurut kasus.
POCO tanpa konstruktor tanpa parameter
POCO yang tidak memiliki konstruktor tanpa parameter tidak dapat terikat.
Kode berikut menghasilkan pengecualian yang mengatakan bahwa jenis harus memiliki konstruktor tanpa parameter:
public class Person(string Name)
public record Person([Required] string Name, [Range(0, 100)] int Age)
{
public Person(string Name) : this (Name, 0);
}
Jenis rekaman dengan konstruktor yang ditulis secara manual
Jenis rekaman dengan konstruktor yang ditulis secara manual yang terlihat seperti pekerjaan konstruktor utama
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; }
}
Jenis rekaman, validasi, dan metadata pengikatan
Untuk jenis rekaman, metadata validasi dan pengikatan pada parameter digunakan. Metadata apa pun pada properti diabaikan
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; }
}
Validasi dan metadata
Validasi menggunakan metadata pada parameter tetapi menggunakan properti untuk membaca nilai. Dalam kasus biasa dengan konstruktor utama, keduanya akan identik. Namun, ada cara untuk mengalahkannya:
public record Person([Required] string Name)
{
private readonly string _name;
// The following property is never null.
// However this object could have been constructed as "new Person(null)".
public string Name { get; init => _name = value ?? string.Empty; }
}
TryUpdateModel tidak memperbarui parameter pada jenis catatan
public record Person(string Name)
{
public int Age { get; set; }
}
var person = new Person("initial-name");
TryUpdateModel(person, ...);
Dalam hal ini, MVC tidak akan mencoba mengikat Name
lagi. Namun, Age
diizinkan untuk diperbarui
Perilaku globalisasi data rute pengikatan model dan string kueri
Penyedia nilai rute inti ASP.NET dan penyedia nilai string kueri:
- Perlakukan nilai sebagai budaya invarian.
- Harapkan bahwa URL invarian budaya.
Sebaliknya, nilai yang berasal dari data formulir mengalami konversi sensitif terhadap budaya. Ini dirancang agar URL dapat dibagikan di seluruh lokal.
Untuk membuat penyedia nilai rute inti ASP.NET dan penyedia nilai string kueri menjalani konversi yang sensitif terhadap budaya:
- Mewarisi dari IValueProviderFactory
- Salin kode dari QueryStringValueProviderFactory atau RouteValueValueProviderFactory
- Ganti nilai budaya yang diteruskan ke konstruktor penyedia nilai dengan CultureInfo.CurrentCulture
- Ganti pabrik penyedia nilai default di opsi MVC dengan yang baru Anda:
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();
});
Tipe data khusus
Ada beberapa jenis data khusus yang dapat ditangani oleh pengikatan model.
IFormFile dan IFormFileCollection
File yang diunggah disertakan dalam permintaan HTTP. Juga didukung adalah IEnumerable<IFormFile>
untuk beberapa file.
TokenPembatalan
Tindakan dapat secara opsional mengikat CancellationToken
sebagai parameter. Ini mengikat yang memberi sinyal ketika koneksi yang mendasar RequestAborted permintaan HTTP dibatalkan. Tindakan dapat menggunakan parameter ini untuk membatalkan operasi asinkron jangka panjang yang dijalankan sebagai bagian dari tindakan pengontrol.
FormCollection
Digunakan untuk mengambil semua nilai dari data formulir yang diposting.
Pemformat input
Data dalam isi permintaan dapat berada di JSON, XML, atau beberapa format lainnya. Untuk mengurai data ini, pengikatan model menggunakan pemformat input yang dikonfigurasi untuk menangani jenis konten tertentu. Secara default, ASP.NET Core menyertakan pemformat input berbasis JSON untuk menangani data JSON. Anda bisa menambahkan formatter lain untuk tipe isi lainnya.
ASP.NET Core memilih pemformat input berdasarkan atribut Konsumsi . Jika tidak ada atribut, atribut menggunakan header Content-Type.
Untuk menggunakan pemformat input XML bawaan:
Di
Program.cs
, panggil AddXmlSerializerFormatters atau AddXmlDataContractSerializerFormatters.builder.Services.AddControllers() .AddXmlSerializerFormatters();
Terapkan
Consumes
atribut ke kelas pengontrol atau metode tindakan yang seharusnya mengharapkan XML dalam isi permintaan.[HttpPost] [Consumes("application/xml")] public ActionResult<Pet> Create(Pet pet)
Untuk informasi selengkapnya, lihat Memperkenalkan Serialisasi XML.
Menyesuaikan pengikatan model dengan pemformat input
Pemformat input bertanggung jawab penuh untuk membaca data dari isi permintaan. Untuk menyesuaikan proses ini, konfigurasikan API yang digunakan oleh pemformat input. Bagian ini menjelaskan cara mengkustomisasi System.Text.Json
pemformat input berbasis untuk memahami jenis kustom bernama ObjectId
.
Pertimbangkan model berikut, yang berisi properti kustom ObjectId
:
public class InstructorObjectId
{
[Required]
public ObjectId ObjectId { get; set; } = null!;
}
Untuk menyesuaikan proses pengikatan model saat menggunakan System.Text.Json
, buat kelas yang berasal dari JsonConverter<T>:
internal class ObjectIdConverter : JsonConverter<ObjectId>
{
public override ObjectId Read(
ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> new(JsonSerializer.Deserialize<int>(ref reader, options));
public override void Write(
Utf8JsonWriter writer, ObjectId value, JsonSerializerOptions options)
=> writer.WriteNumberValue(value.Id);
}
Untuk menggunakan pengonversi kustom, terapkan JsonConverterAttribute atribut ke jenis . Dalam contoh berikut, jenis dikonfigurasi ObjectId
dengan ObjectIdConverter
sebagai pengonversi kustomnya:
[JsonConverter(typeof(ObjectIdConverter))]
public record ObjectId(int Id);
Untuk informasi selengkapnya, lihat Cara menulis pengonversi kustom.
Mengecualikan jenis yang ditentukan dari pengikatan model
Perilaku sistem pengikatan dan validasi model didorong oleh ModelMetadata. Anda dapat menyesuaikan ModelMetadata
dengan menambahkan penyedia detail ke MvcOptions.ModelMetadataDetailsProviders. Penyedia detail bawaan tersedia untuk menonaktifkan pengikatan atau validasi model untuk jenis tertentu.
Untuk menonaktifkan pengikatan model pada semua model dari jenis tertentu, tambahkan ExcludeBindingMetadataProvider di Program.cs
. Misalnya, untuk menonaktifkan pengikatan model pada semua model jenis System.Version
:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
Untuk menonaktifkan validasi pada properti dari jenis tertentu, tambahkan SuppressChildValidationMetadataProvider di Program.cs
. Misalnya, untuk menonaktifkan validasi pada properti jenis System.Guid
:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
Pengikat model kustom
Anda dapat memperluas pengikatan model dengan menulis pengikat model kustom dan menggunakan [ModelBinder]
atribut untuk memilihnya untuk target tertentu. Pelajari selengkapnya tentang pengikatan model kustom.
Pengikatan model manual
Pengikatan model dapat dipanggil secara manual dengan menggunakan TryUpdateModelAsync metode . Metode ini didefinisikan pada kedua ControllerBase
kelas dan PageModel
. Kelebihan metode memungkinkan Anda menentukan awalan dan penyedia nilai yang akan digunakan. Metode mengembalikan false
jika pengikatan model gagal. Berikut contohnya:
if (await TryUpdateModelAsync(
newInstructor,
"Instructor",
x => x.Name, x => x.HireDate!))
{
_instructorStore.Add(newInstructor);
return RedirectToPage("./Index");
}
return Page();
TryUpdateModelAsync menggunakan penyedia nilai untuk mendapatkan data dari isi formulir, string kueri, dan data rute. TryUpdateModelAsync
biasanya:
- Digunakan dengan Razor aplikasi Pages dan MVC menggunakan pengontrol dan tampilan untuk mencegah pengeposan berlebihan.
- Tidak digunakan dengan API web kecuali dikonsumsi dari data formulir, string kueri, dan data rute. Titik akhir API Web yang menggunakan JSON menggunakan Pemformat input untuk mendeserialisasi isi permintaan ke dalam objek.
Untuk informasi selengkapnya, lihat TryUpdateModelAsync.
Atribut [FromServices]
Nama atribut ini mengikuti pola atribut pengikatan model yang menentukan sumber data. Tetapi ini bukan tentang mengikat data dari penyedia nilai. Ini mendapatkan instans jenis dari kontainer injeksi dependensi. Tujuannya adalah untuk memberikan alternatif untuk injeksi konstruktor ketika Anda membutuhkan layanan hanya jika metode tertentu dipanggil.
Jika instans jenis tidak terdaftar dalam kontainer injeksi dependensi, aplikasi akan memberikan pengecualian saat mencoba mengikat parameter. Untuk membuat parameter opsional, gunakan salah satu pendekatan berikut:
- Buat parameter nullable.
- Tetapkan nilai default untuk parameter .
Untuk parameter nullable, pastikan parameter tidak null
sebelum mengaksesnya.
Sumber Daya Tambahan:
Artikel ini menjelaskan apa itu pengikatan model, cara kerjanya, dan cara menyesuaikan perilakunya.
Apa itu Pengikatan Model
Pengontrol dan Razor halaman bekerja dengan data yang berasal dari permintaan HTTP. Misalnya, data rute dapat menyediakan kunci rekaman, dan bidang formulir yang diposting dapat memberikan nilai untuk properti model. Menulis kode untuk mengambil masing-masing nilai ini dan mengonversinya dari string ke jenis .NET akan melelahkan dan rawan kesalahan. Pengikatan model mengotomatiskan proses ini. Sistem pengikatan model:
- Mengambil data dari berbagai sumber seperti data rute, bidang formulir, dan string kueri.
- Menyediakan data ke pengontrol dan Razor halaman dalam parameter metode dan properti publik.
- Mengonversi data string ke jenis .NET.
- Memperbarui properti jenis kompleks.
Contoh
Misalkan Anda memiliki metode tindakan berikut:
[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)
Dan aplikasi menerima permintaan dengan URL ini:
https://contoso.com/api/pets/2?DogsOnly=true
Pengikatan model melalui langkah-langkah berikut setelah sistem perutean memilih metode tindakan:
- Menemukan parameter pertama dari , bilangan
GetById
bulat bernamaid
. - Lihat sumber yang tersedia dalam permintaan HTTP dan temukan
id
= "2" dalam data rute. - Mengonversi string "2" menjadi bilangan bulat 2.
- Menemukan parameter berikutnya dari
GetById
, boolean bernamadogsOnly
. - Telusuri sumber dan temukan "DogsOnly=true" dalam string kueri. Pencocokan nama tidak peka huruf besar/kecil.
- Mengonversi string "true" menjadi boolean
true
.
Kerangka kerja kemudian memanggil GetById
metode , meneruskan 2 untuk id
parameter , dan true
untuk dogsOnly
parameter .
Dalam contoh sebelumnya, target pengikatan model adalah parameter metode yang merupakan jenis sederhana. Target mungkin juga merupakan properti dari jenis kompleks. Setelah setiap properti berhasil diikat, validasi model terjadi untuk properti tersebut. Catatan data apa yang terikat ke model, dan kesalahan pengikatan atau validasi apa pun, disimpan di ControllerBase.ModelState atau PageModel.ModelState. Untuk mengetahui apakah proses ini berhasil, aplikasi memeriksa bendera ModelState.IsValid .
Target
Pengikatan model mencoba menemukan nilai untuk jenis target berikut:
- Parameter metode tindakan pengontrol tempat permintaan dirutekan.
- Razor Parameter metode handler Pages tempat permintaan dirutekan.
- Properti publik pengontrol atau
PageModel
kelas, jika ditentukan oleh atribut.
Atribut [BindProperty]
Dapat diterapkan ke properti publik pengontrol atau PageModel
kelas untuk menyebabkan pengikatan model ke menargetkan properti tersebut:
public class EditModel : PageModel
{
[BindProperty]
public Instructor? Instructor { get; set; }
// ...
}
Atribut [BindProperties]
Dapat diterapkan ke pengontrol atau PageModel
kelas untuk memberi tahu pengikatan model untuk menargetkan semua properti publik kelas:
[BindProperties]
public class CreateModel : PageModel
{
public Instructor? Instructor { get; set; }
// ...
}
Pengikatan model untuk permintaan HTTP GET
Secara default, properti tidak terikat untuk permintaan HTTP GET. Biasanya, yang Anda butuhkan untuk permintaan GET adalah parameter ID rekaman. ID rekaman digunakan untuk mencari item dalam database. Oleh karena itu, tidak perlu mengikat properti yang menyimpan instans model. Dalam skenario di mana Anda ingin properti terikat ke data dari permintaan GET, atur properti ke SupportsGet
true
:
[BindProperty(Name = "ai_user", SupportsGet = true)]
public string? ApplicationInsightsCookie { get; set; }
Sumber
Secara default, pengikatan model mendapatkan data dalam bentuk pasangan kunci-nilai dari sumber berikut dalam permintaan HTTP:
- Bidang formulir
- Isi permintaan (Untuk pengontrol yang memiliki atribut [ApiController].)
- Merutekan data
- Parameter untai kueri
- File yang diunggah
Untuk setiap parameter atau properti target, sumber dipindai dalam urutan yang ditunjukkan dalam daftar sebelumnya. Ada beberapa pengecualian:
- Data rute dan nilai string kueri hanya digunakan untuk jenis sederhana.
- File yang diunggah hanya terikat ke jenis target yang mengimplementasikan
IFormFile
atauIEnumerable<IFormFile>
.
Jika sumber default tidak benar, gunakan salah satu atribut berikut untuk menentukan sumber:
[FromQuery]
- Mendapatkan nilai dari string kueri.[FromRoute]
- Mendapatkan nilai dari data rute.[FromForm]
- Mendapatkan nilai dari bidang formulir yang diposting.[FromBody]
- Mendapatkan nilai dari isi permintaan.[FromHeader]
- Mendapatkan nilai dari header HTTP.
Atribut ini:
Ditambahkan ke properti model satu per satu dan bukan ke kelas model, seperti dalam contoh berikut:
public class Instructor { public int Id { get; set; } [FromQuery(Name = "Note")] public string? NoteFromQueryString { get; set; } // ... }
Secara opsional menerima nilai nama model di konstruktor. Opsi ini disediakan jika nama properti tidak cocok dengan nilai dalam permintaan. Misalnya, nilai dalam permintaan mungkin berupa header dengan tanda hubung atas namanya, seperti dalam contoh berikut:
public void OnGet([FromHeader(Name = "Accept-Language")] string language)
Atribut [FromBody]
Terapkan [FromBody]
atribut ke parameter untuk mengisi propertinya dari isi permintaan HTTP. Runtime ASP.NET Core mendelegasikan tanggung jawab membaca isi ke formatter input. Pemformat input dijelaskan nanti dalam artikel ini.
Ketika [FromBody]
diterapkan ke parameter jenis kompleks, atribut sumber pengikatan apa pun yang diterapkan ke propertinya diabaikan. Misalnya, tindakan berikut Create
menentukan bahwa parameternya pet
diisi dari isi:
public ActionResult<Pet> Create([FromBody] Pet pet)
Kelas Pet
menentukan bahwa propertinya Breed
diisi dari parameter string kueri:
public class Pet
{
public string Name { get; set; } = null!;
[FromQuery] // Attribute is ignored.
public string Breed { get; set; } = null!;
}
Dalam contoh sebelumnya:
- Atribut
[FromQuery]
diabaikan. - Properti
Breed
tidak diisi dari parameter string kueri.
Pemformat input hanya membaca isi dan tidak memahami atribut sumber pengikatan. Jika nilai yang sesuai ditemukan dalam isi, nilai tersebut digunakan untuk mengisi Breed
properti.
Jangan berlaku [FromBody]
untuk lebih dari satu parameter per metode tindakan. Setelah aliran permintaan dibaca oleh pemformat input, aliran permintaan tidak lagi tersedia untuk dibaca lagi untuk mengikat parameter lain [FromBody]
.
Sumber tambahan
Data sumber disediakan untuk sistem pengikatan model oleh penyedia nilai. Anda dapat menulis dan mendaftarkan penyedia nilai kustom yang mendapatkan data untuk pengikatan model dari sumber lain. Misalnya, Anda mungkin menginginkan data dari cookie atau status sesi. Untuk mendapatkan data dari sumber baru:
- Buat kelas yang mengimplementasikan
IValueProvider
. - Buat kelas yang mengimplementasikan
IValueProviderFactory
. - Daftarkan kelas pabrik di
Program.cs
.
Sampel mencakup penyedia nilai dan contoh pabrik yang mendapatkan nilai dari cookie. Daftarkan pabrik penyedia nilai kustom di Program.cs
:
builder.Services.AddControllers(options =>
{
options.ValueProviderFactories.Add(new CookieValueProviderFactory());
});
Kode sebelumnya menempatkan penyedia nilai kustom setelah semua penyedia nilai bawaan. Untuk menjadikannya yang pertama dalam daftar, panggil Insert(0, new CookieValueProviderFactory())
alih-alih Add
.
Tidak ada sumber untuk properti model
Secara default, kesalahan status model tidak dibuat jika tidak ada nilai yang ditemukan untuk properti model. Properti diatur ke null atau nilai default:
- Jenis sederhana yang dapat diubah ke null diatur ke
null
. - Jenis nilai yang tidak dapat diubah ke null diatur ke
default(T)
. Misalnya, parameterint id
diatur ke 0. - Untuk Jenis kompleks, pengikatan model membuat instans dengan menggunakan konstruktor default, tanpa mengatur properti.
- Array diatur ke
Array.Empty<T>()
, kecuali bahwabyte[]
array diatur kenull
.
Jika status model harus divalidasi ketika tidak ada yang ditemukan di bidang formulir untuk properti model, gunakan [BindRequired]
atribut .
Perhatikan bahwa perilaku ini [BindRequired]
berlaku untuk pengikatan model dari data formulir yang diposting, bukan ke data JSON atau XML dalam isi permintaan. Data isi permintaan ditangani oleh pemformat input.
Kesalahan konversi jenis
Jika sumber ditemukan tetapi tidak dapat dikonversi menjadi jenis target, status model ditandai sebagai tidak valid. Parameter target atau properti diatur ke null atau nilai default, seperti yang disebutkan di bagian sebelumnya.
Dalam pengontrol API yang memiliki [ApiController]
atribut , status model yang tidak valid menghasilkan respons HTTP 400 otomatis.
Razor Di halaman, putar ulang halaman dengan pesan kesalahan:
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
// ...
return RedirectToPage("./Index");
}
Ketika halaman diputar ulang oleh kode sebelumnya, input yang tidak valid tidak ditampilkan di bidang formulir. Ini karena properti model telah diatur ke null atau nilai default. Input yang tidak valid memang muncul dalam pesan kesalahan. Jika Anda ingin memutar ulang data buruk di bidang formulir, pertimbangkan untuk menjadikan properti model sebagai string dan melakukan konversi data secara manual.
Strategi yang sama direkomendasikan jika Anda tidak ingin kesalahan konversi jenis mengakibatkan kesalahan status model. Dalam hal ini, jadikan properti model sebagai string.
Jenis sederhana
Jenis sederhana pengikat model dapat mengonversi string sumber menjadi termasuk yang berikut:
- Boolean
- Byte, SByte
- Char
- DateTime
- DateTimeOffset
- Desimal
- Laju
- Enum
- Guid
- Int16, Int32, Int64
- Satu
- Rentang Waktu
- UInt16, UInt32, UInt64
- Uri
- Versi
Jenis kompleks
Jenis kompleks harus memiliki konstruktor default publik dan properti bisa-tulis publik untuk diikat. Saat pengikatan model terjadi, kelas dibuat menggunakan konstruktor default publik.
Untuk setiap properti dari jenis kompleks, pengikatan model melihat melalui sumber untuk pola nama prefix.property_name. Jika tidak ada yang ditemukan, ia mencari hanya property_name tanpa awalan. Keputusan untuk menggunakan awalan tidak dibuat per properti. Misalnya, dengan kueri yang berisi ?Instructor.Id=100&Name=foo
, terikat ke metode OnGet(Instructor instructor)
, objek jenis Instructor
yang dihasilkan berisi:
Id
atur ke100
.Name
atur kenull
. Pengikatan model mengharapkanInstructor.Name
karenaInstructor.Id
digunakan dalam parameter kueri sebelumnya.
Untuk mengikat parameter, awalannya adalah nama parameter. Untuk mengikat ke PageModel
properti publik, awalannya adalah nama properti publik. Beberapa atribut memiliki Prefix
properti yang memungkinkan Anda mengambil alih penggunaan default parameter atau nama properti.
Misalnya, misalkan jenis kompleks adalah kelas berikut Instructor
:
public class Instructor
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
Awalan = nama parameter
Jika model yang akan diikat adalah parameter bernama instructorToUpdate
:
public IActionResult OnPost(int? id, Instructor instructorToUpdate)
Pengikatan model dimulai dengan melihat melalui sumber untuk kunci instructorToUpdate.ID
. Jika itu tidak ditemukan, itu mencari ID
tanpa awalan.
Awalan = nama properti
Jika model yang akan diikat adalah properti bernama Instructor
pengontrol atau PageModel
kelas:
[BindProperty]
public Instructor Instructor { get; set; }
Pengikatan model dimulai dengan melihat melalui sumber untuk kunci Instructor.ID
. Jika itu tidak ditemukan, itu mencari ID
tanpa awalan.
Awalan kustom
Jika model yang akan diikat adalah parameter bernama instructorToUpdate
dan Bind
atribut menentukan Instructor
sebagai awalan:
public IActionResult OnPost(
int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)
Pengikatan model dimulai dengan melihat melalui sumber untuk kunci Instructor.ID
. Jika itu tidak ditemukan, itu mencari ID
tanpa awalan.
Atribut untuk target jenis kompleks
Beberapa atribut bawaan tersedia untuk mengontrol pengikatan model dari jenis kompleks:
Peringatan
Atribut ini memengaruhi pengikatan model saat data formulir yang diposting adalah sumber nilai. Ini tidak memengaruhi pemformat input, yang memproses isi permintaan JSON dan XML yang diposting. Pemformat input dijelaskan nanti dalam artikel ini.
Atribut [Ikat]
Dapat diterapkan ke kelas atau parameter metode. Menentukan properti model mana yang harus disertakan dalam pengikatan model. [Bind]
tidak memengaruhi pemformat input.
Dalam contoh berikut, hanya properti Instructor
model yang ditentukan yang terikat ketika penangan atau metode tindakan apa pun dipanggil:
[Bind("LastName,FirstMidName,HireDate")]
public class Instructor
Dalam contoh berikut, hanya properti Instructor
model yang ditentukan yang terikat saat OnPost
metode dipanggil:
[HttpPost]
public IActionResult OnPost(
[Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
Atribut [Bind]
dapat digunakan untuk melindungi dari overposting dalam membuat skenario. Ini tidak berfungsi dengan baik dalam skenario edit karena properti yang dikecualikan diatur ke null atau nilai default alih-alih dibiarkan tidak berubah. Untuk perlindungan terhadap overposting, lihat model direkomendasikan daripada [Bind]
atribut . Untuk informasi selengkapnya, lihat Catatan keamanan tentang overposting.
Atribut [ModelBinder]
ModelBinderAttribute dapat diterapkan ke jenis, properti, atau parameter. Ini memungkinkan menentukan jenis pengikat model yang digunakan untuk mengikat instans atau jenis tertentu. Contohnya:
[HttpPost]
public IActionResult OnPost(
[ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)
Atribut [ModelBinder]
juga dapat digunakan untuk mengubah nama properti atau parameter saat sedang terikat model:
public class Instructor
{
[ModelBinder(Name = "instructor_id")]
public string Id { get; set; }
// ...
}
Atribut [BindRequired]
Menyebabkan pengikatan model untuk menambahkan kesalahan status model jika pengikatan tidak dapat terjadi untuk properti model. Berikut contohnya:
public class InstructorBindRequired
{
// ...
[BindRequired]
public DateTime HireDate { get; set; }
}
Lihat juga diskusi [Required]
atribut dalam validasi Model.
Atribut [BindNever]
Dapat diterapkan ke properti atau jenis. Mencegah pengikatan model dari pengaturan properti model. Saat diterapkan ke jenis, sistem pengikatan model mengecualikan semua properti yang ditentukan jenis. Berikut contohnya:
public class InstructorBindNever
{
[BindNever]
public int Id { get; set; }
// ...
}
Koleksi
Untuk target yang merupakan kumpulan jenis sederhana, pengikatan model mencari kecocokan untuk parameter_name atau property_name. Jika tidak ada kecocokan yang ditemukan, ia mencari salah satu format yang didukung tanpa awalan. Contohnya:
Misalkan parameter yang akan diikat adalah array bernama
selectedCourses
:public IActionResult OnPost(int? id, int[] selectedCourses)
Data string formulir atau kueri bisa dalam salah satu format berikut:
selectedCourses=1050&selectedCourses=2000
selectedCourses[0]=1050&selectedCourses[1]=2000
[0]=1050&[1]=2000
selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
[a]=1050&[b]=2000&index=a&index=b
Hindari mengikat parameter atau properti bernama
index
atauIndex
jika berdekatan dengan nilai koleksi. Pengikatan model mencoba digunakanindex
sebagai indeks untuk koleksi yang mungkin mengakibatkan pengikatan yang salah. Misalnya, pertimbangkan tindakan berikut:public IActionResult Post(string index, List<Product> products)
Dalam kode sebelumnya,
index
parameter string kueri mengikat parameterindex
metode dan juga digunakan untuk mengikat koleksi produk. Mengganti namaindex
parameter atau menggunakan atribut pengikatan model untuk mengonfigurasi pengikatan menghindari masalah ini:public IActionResult Post(string productIndex, List<Product> products)
Format berikut ini hanya didukung dalam data formulir:
selectedCourses[]=1050&selectedCourses[]=2000
Untuk semua format contoh sebelumnya, pengikatan model meneruskan array dua item ke
selectedCourses
parameter :- selectedCourses[0]=1050
- selectedCourses[1]=2000
Format data yang menggunakan nomor subskrip (... [0] ... [1] ...) harus memastikan bahwa mereka diberi nomor secara berurutan mulai dari nol. Jika ada celah dalam penomoran subskrip, semua item setelah celah diabaikan. Misalnya, jika subskrip adalah 0 dan 2, bukan 0 dan 1, item kedua diabaikan.
Kamus
Untuk Dictionary
target, pengikatan model mencari kecocokan untuk parameter_name atau property_name. Jika tidak ada kecocokan yang ditemukan, ia mencari salah satu format yang didukung tanpa awalan. Contohnya:
Misalkan parameter target bernama
Dictionary<int, string>
selectedCourses
:public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
Formulir yang diposting atau data string kueri bisa terlihat seperti salah satu contoh berikut:
selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics
[1050]=Chemistry&selectedCourses[2000]=Economics
selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry& selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
[0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
Untuk semua format contoh sebelumnya, pengikatan model meneruskan kamus dua item ke
selectedCourses
parameter :- selectedCourses["1050"]="Chemistry"
- selectedCourses["2000"]="Economics"
Pengikatan konstruktor dan jenis rekaman
Pengikatan model mengharuskan jenis kompleks memiliki konstruktor tanpa parameter. Pemformat System.Text.Json
input berbasis dan Newtonsoft.Json
mendukung deserialisasi kelas yang tidak memiliki konstruktor tanpa parameter.
Jenis rekaman adalah cara yang bagus untuk mewakili data melalui jaringan. ASP.NET Core mendukung pengikatan model dan memvalidasi jenis rekaman dengan satu konstruktor:
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>
Saat memvalidasi jenis rekaman, runtime mencari metadata pengikatan dan validasi khusus pada parameter daripada pada properti.
Kerangka kerja memungkinkan pengikatan ke dan memvalidasi jenis rekaman:
public record Person([Required] string Name, [Range(0, 100)] int Age);
Agar sebelumnya berfungsi, jenisnya harus:
- Jadilah jenis catatan.
- Memiliki tepat satu konstruktor publik.
- Berisi parameter yang memiliki properti dengan nama dan jenis yang sama. Nama tidak boleh berbeda menurut kasus.
POCO tanpa konstruktor tanpa parameter
POCO yang tidak memiliki konstruktor tanpa parameter tidak dapat terikat.
Kode berikut menghasilkan pengecualian yang mengatakan bahwa jenis harus memiliki konstruktor tanpa parameter:
public class Person(string Name)
public record Person([Required] string Name, [Range(0, 100)] int Age)
{
public Person(string Name) : this (Name, 0);
}
Jenis rekaman dengan konstruktor yang ditulis secara manual
Jenis rekaman dengan konstruktor yang ditulis secara manual yang terlihat seperti pekerjaan konstruktor utama
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; }
}
Jenis rekaman, validasi, dan metadata pengikatan
Untuk jenis rekaman, metadata validasi dan pengikatan pada parameter digunakan. Metadata apa pun pada properti diabaikan
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; }
}
Validasi dan metadata
Validasi menggunakan metadata pada parameter tetapi menggunakan properti untuk membaca nilai. Dalam kasus biasa dengan konstruktor utama, keduanya akan identik. Namun, ada cara untuk mengalahkannya:
public record Person([Required] string Name)
{
private readonly string _name;
// The following property is never null.
// However this object could have been constructed as "new Person(null)".
public string Name { get; init => _name = value ?? string.Empty; }
}
TryUpdateModel tidak memperbarui parameter pada jenis catatan
public record Person(string Name)
{
public int Age { get; set; }
}
var person = new Person("initial-name");
TryUpdateModel(person, ...);
Dalam hal ini, MVC tidak akan mencoba mengikat Name
lagi. Namun, Age
diizinkan untuk diperbarui
Perilaku globalisasi data rute pengikatan model dan string kueri
Penyedia nilai rute inti ASP.NET dan penyedia nilai string kueri:
- Perlakukan nilai sebagai budaya invarian.
- Harapkan bahwa URL invarian budaya.
Sebaliknya, nilai yang berasal dari data formulir mengalami konversi sensitif terhadap budaya. Ini dirancang agar URL dapat dibagikan di seluruh lokal.
Untuk membuat penyedia nilai rute inti ASP.NET dan penyedia nilai string kueri menjalani konversi yang sensitif terhadap budaya:
- Mewarisi dari IValueProviderFactory
- Salin kode dari QueryStringValueProviderFactory atau RouteValueValueProviderFactory
- Ganti nilai budaya yang diteruskan ke konstruktor penyedia nilai dengan CultureInfo.CurrentCulture
- Ganti pabrik penyedia nilai default di opsi MVC dengan yang baru Anda:
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();
});
Tipe data khusus
Ada beberapa jenis data khusus yang dapat ditangani oleh pengikatan model.
IFormFile dan IFormFileCollection
File yang diunggah disertakan dalam permintaan HTTP. Juga didukung adalah IEnumerable<IFormFile>
untuk beberapa file.
TokenPembatalan
Tindakan dapat secara opsional mengikat CancellationToken
sebagai parameter. Ini mengikat yang memberi sinyal ketika koneksi yang mendasar RequestAborted permintaan HTTP dibatalkan. Tindakan dapat menggunakan parameter ini untuk membatalkan operasi asinkron jangka panjang yang dijalankan sebagai bagian dari tindakan pengontrol.
FormCollection
Digunakan untuk mengambil semua nilai dari data formulir yang diposting.
Pemformat input
Data dalam isi permintaan dapat berada di JSON, XML, atau beberapa format lainnya. Untuk mengurai data ini, pengikatan model menggunakan pemformat input yang dikonfigurasi untuk menangani jenis konten tertentu. Secara default, ASP.NET Core menyertakan pemformat input berbasis JSON untuk menangani data JSON. Anda bisa menambahkan formatter lain untuk tipe isi lainnya.
ASP.NET Core memilih pemformat input berdasarkan atribut Konsumsi . Jika tidak ada atribut, atribut menggunakan header Content-Type.
Untuk menggunakan pemformat input XML bawaan:
Di
Program.cs
, panggil AddXmlSerializerFormatters atau AddXmlDataContractSerializerFormatters.builder.Services.AddControllers() .AddXmlSerializerFormatters();
Terapkan
Consumes
atribut ke kelas pengontrol atau metode tindakan yang seharusnya mengharapkan XML dalam isi permintaan.[HttpPost] [Consumes("application/xml")] public ActionResult<Pet> Create(Pet pet)
Untuk informasi selengkapnya, lihat Memperkenalkan Serialisasi XML.
Menyesuaikan pengikatan model dengan pemformat input
Pemformat input bertanggung jawab penuh untuk membaca data dari isi permintaan. Untuk menyesuaikan proses ini, konfigurasikan API yang digunakan oleh pemformat input. Bagian ini menjelaskan cara mengkustomisasi System.Text.Json
pemformat input berbasis untuk memahami jenis kustom bernama ObjectId
.
Pertimbangkan model berikut, yang berisi properti kustom ObjectId
:
public class InstructorObjectId
{
[Required]
public ObjectId ObjectId { get; set; } = null!;
}
Untuk menyesuaikan proses pengikatan model saat menggunakan System.Text.Json
, buat kelas yang berasal dari JsonConverter<T>:
internal class ObjectIdConverter : JsonConverter<ObjectId>
{
public override ObjectId Read(
ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> new(JsonSerializer.Deserialize<int>(ref reader, options));
public override void Write(
Utf8JsonWriter writer, ObjectId value, JsonSerializerOptions options)
=> writer.WriteNumberValue(value.Id);
}
Untuk menggunakan pengonversi kustom, terapkan JsonConverterAttribute atribut ke jenis . Dalam contoh berikut, jenis dikonfigurasi ObjectId
dengan ObjectIdConverter
sebagai pengonversi kustomnya:
[JsonConverter(typeof(ObjectIdConverter))]
public record ObjectId(int Id);
Untuk informasi selengkapnya, lihat Cara menulis pengonversi kustom.
Mengecualikan jenis yang ditentukan dari pengikatan model
Perilaku sistem pengikatan dan validasi model didorong oleh ModelMetadata. Anda dapat menyesuaikan ModelMetadata
dengan menambahkan penyedia detail ke MvcOptions.ModelMetadataDetailsProviders. Penyedia detail bawaan tersedia untuk menonaktifkan pengikatan atau validasi model untuk jenis tertentu.
Untuk menonaktifkan pengikatan model pada semua model dari jenis tertentu, tambahkan ExcludeBindingMetadataProvider di Program.cs
. Misalnya, untuk menonaktifkan pengikatan model pada semua model jenis System.Version
:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
Untuk menonaktifkan validasi pada properti dari jenis tertentu, tambahkan SuppressChildValidationMetadataProvider di Program.cs
. Misalnya, untuk menonaktifkan validasi pada properti jenis System.Guid
:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
Pengikat model kustom
Anda dapat memperluas pengikatan model dengan menulis pengikat model kustom dan menggunakan [ModelBinder]
atribut untuk memilihnya untuk target tertentu. Pelajari selengkapnya tentang pengikatan model kustom.
Pengikatan model manual
Pengikatan model dapat dipanggil secara manual dengan menggunakan TryUpdateModelAsync metode . Metode ini didefinisikan pada kedua ControllerBase
kelas dan PageModel
. Kelebihan metode memungkinkan Anda menentukan awalan dan penyedia nilai yang akan digunakan. Metode mengembalikan false
jika pengikatan model gagal. Berikut contohnya:
if (await TryUpdateModelAsync(
newInstructor,
"Instructor",
x => x.Name, x => x.HireDate!))
{
_instructorStore.Add(newInstructor);
return RedirectToPage("./Index");
}
return Page();
TryUpdateModelAsync menggunakan penyedia nilai untuk mendapatkan data dari isi formulir, string kueri, dan data rute. TryUpdateModelAsync
biasanya:
- Digunakan dengan Razor aplikasi Pages dan MVC menggunakan pengontrol dan tampilan untuk mencegah pengeposan berlebihan.
- Tidak digunakan dengan API web kecuali dikonsumsi dari data formulir, string kueri, dan data rute. Titik akhir API Web yang menggunakan JSON menggunakan Pemformat input untuk mendeserialisasi isi permintaan ke dalam objek.
Untuk informasi selengkapnya, lihat TryUpdateModelAsync.
Atribut [FromServices]
Nama atribut ini mengikuti pola atribut pengikatan model yang menentukan sumber data. Tetapi ini bukan tentang mengikat data dari penyedia nilai. Ini mendapatkan instans jenis dari kontainer injeksi dependensi. Tujuannya adalah untuk memberikan alternatif untuk injeksi konstruktor ketika Anda membutuhkan layanan hanya jika metode tertentu dipanggil.
Jika instans jenis tidak terdaftar dalam kontainer injeksi dependensi, aplikasi akan memberikan pengecualian saat mencoba mengikat parameter. Untuk membuat parameter opsional, gunakan salah satu pendekatan berikut:
- Buat parameter nullable.
- Tetapkan nilai default untuk parameter .
Untuk parameter nullable, pastikan parameter tidak null
sebelum mengaksesnya.
Sumber Daya Tambahan:
Artikel ini menjelaskan apa itu pengikatan model, cara kerjanya, dan cara menyesuaikan perilakunya.
Lihat atau unduh sampel kode (cara mengunduh).
Apa itu Pengikatan Model
Pengontrol dan Razor halaman bekerja dengan data yang berasal dari permintaan HTTP. Misalnya, data rute dapat menyediakan kunci rekaman, dan bidang formulir yang diposting dapat memberikan nilai untuk properti model. Menulis kode untuk mengambil masing-masing nilai ini dan mengonversinya dari string ke jenis .NET akan melelahkan dan rawan kesalahan. Pengikatan model mengotomatiskan proses ini. Sistem pengikatan model:
- Mengambil data dari berbagai sumber seperti data rute, bidang formulir, dan string kueri.
- Menyediakan data ke pengontrol dan Razor halaman dalam parameter metode dan properti publik.
- Mengonversi data string ke jenis .NET.
- Memperbarui properti jenis kompleks.
Contoh
Misalkan Anda memiliki metode tindakan berikut:
[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)
Dan aplikasi menerima permintaan dengan URL ini:
http://contoso.com/api/pets/2?DogsOnly=true
Pengikatan model melalui langkah-langkah berikut setelah sistem perutean memilih metode tindakan:
- Menemukan parameter pertama dari , bilangan
GetById
bulat bernamaid
. - Lihat sumber yang tersedia dalam permintaan HTTP dan temukan
id
= "2" dalam data rute. - Mengonversi string "2" menjadi bilangan bulat 2.
- Menemukan parameter berikutnya dari
GetById
, boolean bernamadogsOnly
. - Telusuri sumber dan temukan "DogsOnly=true" dalam string kueri. Pencocokan nama tidak peka huruf besar/kecil.
- Mengonversi string "true" menjadi boolean
true
.
Kerangka kerja kemudian memanggil GetById
metode , meneruskan 2 untuk id
parameter , dan true
untuk dogsOnly
parameter .
Dalam contoh sebelumnya, target pengikatan model adalah parameter metode yang merupakan jenis sederhana. Target mungkin juga merupakan properti dari jenis kompleks. Setelah setiap properti berhasil diikat, validasi model terjadi untuk properti tersebut. Catatan data apa yang terikat ke model, dan kesalahan pengikatan atau validasi apa pun, disimpan di ControllerBase.ModelState atau PageModel.ModelState. Untuk mengetahui apakah proses ini berhasil, aplikasi memeriksa bendera ModelState.IsValid .
Target
Pengikatan model mencoba menemukan nilai untuk jenis target berikut:
- Parameter metode tindakan pengontrol tempat permintaan dirutekan.
- Razor Parameter metode handler Pages tempat permintaan dirutekan.
- Properti publik pengontrol atau
PageModel
kelas, jika ditentukan oleh atribut.
Atribut [BindProperty]
Dapat diterapkan ke properti publik pengontrol atau PageModel
kelas untuk menyebabkan pengikatan model ke menargetkan properti tersebut:
public class EditModel : InstructorsPageModel
{
[BindProperty]
public Instructor Instructor { get; set; }
Atribut [BindProperties]
Tersedia di ASP.NET Core 2.1 dan yang lebih baru. Dapat diterapkan ke pengontrol atau PageModel
kelas untuk memberi tahu pengikatan model untuk menargetkan semua properti publik kelas:
[BindProperties(SupportsGet = true)]
public class CreateModel : InstructorsPageModel
{
public Instructor Instructor { get; set; }
Pengikatan model untuk permintaan HTTP GET
Secara default, properti tidak terikat untuk permintaan HTTP GET. Biasanya, yang Anda butuhkan untuk permintaan GET adalah parameter ID rekaman. ID rekaman digunakan untuk mencari item dalam database. Oleh karena itu, tidak perlu mengikat properti yang menyimpan instans model. Dalam skenario di mana Anda ingin properti terikat ke data dari permintaan GET, atur properti ke SupportsGet
true
:
[BindProperty(Name = "ai_user", SupportsGet = true)]
public string ApplicationInsightsCookie { get; set; }
Sumber
Secara default, pengikatan model mendapatkan data dalam bentuk pasangan kunci-nilai dari sumber berikut dalam permintaan HTTP:
- Bidang formulir
- Isi permintaan (Untuk pengontrol yang memiliki atribut [ApiController].)
- Merutekan data
- Parameter untai kueri
- File yang diunggah
Untuk setiap parameter atau properti target, sumber dipindai dalam urutan yang ditunjukkan dalam daftar sebelumnya. Ada beberapa pengecualian:
- Data rute dan nilai string kueri hanya digunakan untuk jenis sederhana.
- File yang diunggah hanya terikat ke jenis target yang mengimplementasikan
IFormFile
atauIEnumerable<IFormFile>
.
Jika sumber default tidak benar, gunakan salah satu atribut berikut untuk menentukan sumber:
[FromQuery]
- Mendapatkan nilai dari string kueri.[FromRoute]
- Mendapatkan nilai dari data rute.[FromForm]
- Mendapatkan nilai dari bidang formulir yang diposting.[FromBody]
- Mendapatkan nilai dari isi permintaan.[FromHeader]
- Mendapatkan nilai dari header HTTP.
Atribut ini:
Ditambahkan ke properti model satu per satu (bukan ke kelas model), seperti dalam contoh berikut:
public class Instructor { public int ID { get; set; } [FromQuery(Name = "Note")] public string NoteFromQueryString { get; set; }
Secara opsional menerima nilai nama model di konstruktor. Opsi ini disediakan jika nama properti tidak cocok dengan nilai dalam permintaan. Misalnya, nilai dalam permintaan mungkin berupa header dengan tanda hubung atas namanya, seperti dalam contoh berikut:
public void OnGet([FromHeader(Name = "Accept-Language")] string language)
Atribut [FromBody]
Terapkan [FromBody]
atribut ke parameter untuk mengisi propertinya dari isi permintaan HTTP. Runtime ASP.NET Core mendelegasikan tanggung jawab membaca isi ke formatter input. Pemformat input dijelaskan nanti dalam artikel ini.
Ketika [FromBody]
diterapkan ke parameter jenis kompleks, atribut sumber pengikatan apa pun yang diterapkan ke propertinya diabaikan. Misalnya, tindakan berikut Create
menentukan bahwa parameternya pet
diisi dari isi:
public ActionResult<Pet> Create([FromBody] Pet pet)
Kelas Pet
menentukan bahwa propertinya Breed
diisi dari parameter string kueri:
public class Pet
{
public string Name { get; set; }
[FromQuery] // Attribute is ignored.
public string Breed { get; set; }
}
Dalam contoh sebelumnya:
- Atribut
[FromQuery]
diabaikan. - Properti
Breed
tidak diisi dari parameter string kueri.
Pemformat input hanya membaca isi dan tidak memahami atribut sumber pengikatan. Jika nilai yang sesuai ditemukan dalam isi, nilai tersebut digunakan untuk mengisi Breed
properti.
Jangan berlaku [FromBody]
untuk lebih dari satu parameter per metode tindakan. Setelah aliran permintaan dibaca oleh pemformat input, aliran permintaan tidak lagi tersedia untuk dibaca lagi untuk mengikat parameter lain [FromBody]
.
Sumber tambahan
Data sumber disediakan untuk sistem pengikatan model oleh penyedia nilai. Anda dapat menulis dan mendaftarkan penyedia nilai kustom yang mendapatkan data untuk pengikatan model dari sumber lain. Misalnya, Anda mungkin menginginkan data dari cookie atau status sesi. Untuk mendapatkan data dari sumber baru:
- Buat kelas yang mengimplementasikan
IValueProvider
. - Buat kelas yang mengimplementasikan
IValueProviderFactory
. - Daftarkan kelas pabrik di
Startup.ConfigureServices
.
Aplikasi sampel mencakup penyedia nilai dan contoh pabrik yang mendapatkan nilai dari cookie. Berikut adalah kode pendaftaran di 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();
Kode yang ditampilkan menempatkan penyedia nilai kustom setelah semua penyedia nilai bawaan. Untuk menjadikannya yang pertama dalam daftar, panggil Insert(0, new CookieValueProviderFactory())
alih-alih Add
.
Tidak ada sumber untuk properti model
Secara default, kesalahan status model tidak dibuat jika tidak ada nilai yang ditemukan untuk properti model. Properti diatur ke null atau nilai default:
- Jenis sederhana yang dapat diubah ke null diatur ke
null
. - Jenis nilai yang tidak dapat diubah ke null diatur ke
default(T)
. Misalnya, parameterint id
diatur ke 0. - Untuk Jenis kompleks, pengikatan model membuat instans dengan menggunakan konstruktor default, tanpa mengatur properti.
- Array diatur ke
Array.Empty<T>()
, kecuali bahwabyte[]
array diatur kenull
.
Jika status model harus divalidasi ketika tidak ada yang ditemukan di bidang formulir untuk properti model, gunakan [BindRequired]
atribut .
Perhatikan bahwa perilaku ini [BindRequired]
berlaku untuk pengikatan model dari data formulir yang diposting, bukan ke data JSON atau XML dalam isi permintaan. Data isi permintaan ditangani oleh pemformat input.
Kesalahan konversi jenis
Jika sumber ditemukan tetapi tidak dapat dikonversi menjadi jenis target, status model ditandai sebagai tidak valid. Parameter target atau properti diatur ke null atau nilai default, seperti yang disebutkan di bagian sebelumnya.
Dalam pengontrol API yang memiliki [ApiController]
atribut , status model yang tidak valid menghasilkan respons HTTP 400 otomatis.
Razor Di halaman, putar ulang halaman dengan pesan kesalahan:
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
_instructorsInMemoryStore.Add(Instructor);
return RedirectToPage("./Index");
}
Validasi sisi klien menangkap data paling buruk yang akan dikirimkan ke Razor formulir Pages. Validasi ini membuatnya sulit untuk memicu kode yang disorot sebelumnya. Aplikasi sampel menyertakan tombol Kirim dengan Tanggal Tidak Valid yang menempatkan data buruk di bidang Tanggal Perekrutan dan mengirimkan formulir. Tombol ini menunjukkan cara kerja kode untuk memutar ulang halaman saat terjadi kesalahan konversi data.
Ketika halaman diputar ulang oleh kode sebelumnya, input yang tidak valid tidak ditampilkan di bidang formulir. Ini karena properti model telah diatur ke null atau nilai default. Input yang tidak valid memang muncul dalam pesan kesalahan. Tetapi jika Anda ingin memutar ulang data buruk di bidang formulir, pertimbangkan untuk menjadikan properti model sebagai string dan melakukan konversi data secara manual.
Strategi yang sama direkomendasikan jika Anda tidak ingin kesalahan konversi jenis mengakibatkan kesalahan status model. Dalam hal ini, jadikan properti model sebagai string.
Jenis sederhana
Jenis sederhana pengikat model dapat mengonversi string sumber menjadi termasuk yang berikut:
- Boolean
- Byte, SByte
- Char
- DateTime
- DateTimeOffset
- Desimal
- Laju
- Enum
- Guid
- Int16, Int32, Int64
- Satu
- Rentang Waktu
- UInt16, UInt32, UInt64
- Uri
- Versi
Jenis kompleks
Jenis kompleks harus memiliki konstruktor default publik dan properti bisa-tulis publik untuk diikat. Saat pengikatan model terjadi, kelas dibuat menggunakan konstruktor default publik.
Untuk setiap properti dari jenis kompleks, pengikatan model melihat melalui sumber untuk pola nama prefix.property_name. Jika tidak ada yang ditemukan, ia mencari hanya property_name tanpa awalan.
Untuk mengikat parameter, awalannya adalah nama parameter. Untuk mengikat ke PageModel
properti publik, awalannya adalah nama properti publik. Beberapa atribut memiliki Prefix
properti yang memungkinkan Anda mengambil alih penggunaan default parameter atau nama properti.
Misalnya, misalkan jenis kompleks adalah kelas berikut Instructor
:
public class Instructor
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
Awalan = nama parameter
Jika model yang akan diikat adalah parameter bernama instructorToUpdate
:
public IActionResult OnPost(int? id, Instructor instructorToUpdate)
Pengikatan model dimulai dengan melihat melalui sumber untuk kunci instructorToUpdate.ID
. Jika itu tidak ditemukan, itu mencari ID
tanpa awalan.
Awalan = nama properti
Jika model yang akan diikat adalah properti bernama Instructor
pengontrol atau PageModel
kelas:
[BindProperty]
public Instructor Instructor { get; set; }
Pengikatan model dimulai dengan melihat melalui sumber untuk kunci Instructor.ID
. Jika itu tidak ditemukan, itu mencari ID
tanpa awalan.
Awalan kustom
Jika model yang akan diikat adalah parameter bernama instructorToUpdate
dan Bind
atribut menentukan Instructor
sebagai awalan:
public IActionResult OnPost(
int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)
Pengikatan model dimulai dengan melihat melalui sumber untuk kunci Instructor.ID
. Jika itu tidak ditemukan, itu mencari ID
tanpa awalan.
Atribut untuk target jenis kompleks
Beberapa atribut bawaan tersedia untuk mengontrol pengikatan model dari jenis kompleks:
[Bind]
[BindRequired]
[BindNever]
Peringatan
Atribut ini memengaruhi pengikatan model saat data formulir yang diposting adalah sumber nilai. Ini tidak memengaruhi pemformat input, yang memproses isi permintaan JSON dan XML yang diposting. Pemformat input dijelaskan nanti dalam artikel ini.
Atribut [Ikat]
Dapat diterapkan ke kelas atau parameter metode. Menentukan properti model mana yang harus disertakan dalam pengikatan model. [Bind]
tidak memengaruhi pemformat input.
Dalam contoh berikut, hanya properti Instructor
model yang ditentukan yang terikat ketika penangan atau metode tindakan apa pun dipanggil:
[Bind("LastName,FirstMidName,HireDate")]
public class Instructor
Dalam contoh berikut, hanya properti Instructor
model yang ditentukan yang terikat saat OnPost
metode dipanggil:
[HttpPost]
public IActionResult OnPost([Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
Atribut [Bind]
dapat digunakan untuk melindungi dari overposting dalam membuat skenario. Ini tidak berfungsi dengan baik dalam skenario edit karena properti yang dikecualikan diatur ke null atau nilai default alih-alih dibiarkan tidak berubah. Untuk perlindungan terhadap overposting, lihat model direkomendasikan daripada [Bind]
atribut . Untuk informasi selengkapnya, lihat Catatan keamanan tentang overposting.
Atribut [ModelBinder]
ModelBinderAttribute dapat diterapkan ke jenis, properti, atau parameter. Ini memungkinkan menentukan jenis pengikat model yang digunakan untuk mengikat instans atau jenis tertentu. Contohnya:
[HttpPost]
public IActionResult OnPost([ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)
Atribut [ModelBinder]
juga dapat digunakan untuk mengubah nama properti atau parameter saat sedang terikat model:
public class Instructor
{
[ModelBinder(Name = "instructor_id")]
public string Id { get; set; }
public string Name { get; set; }
}
Atribut [BindRequired]
Hanya dapat diterapkan ke properti model, bukan ke parameter metode. Menyebabkan pengikatan model untuk menambahkan kesalahan status model jika pengikatan tidak dapat terjadi untuk properti model. Berikut contohnya:
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; }
Lihat juga diskusi [Required]
atribut dalam validasi Model.
Atribut [BindNever]
Hanya dapat diterapkan ke properti model, bukan ke parameter metode. Mencegah pengikatan model dari pengaturan properti model. Berikut contohnya:
public class InstructorWithDictionary
{
[BindNever]
public int ID { get; set; }
Koleksi
Untuk target yang merupakan kumpulan jenis sederhana, pengikatan model mencari kecocokan untuk parameter_name atau property_name. Jika tidak ada kecocokan yang ditemukan, ia mencari salah satu format yang didukung tanpa awalan. Contohnya:
Misalkan parameter yang akan diikat adalah array bernama
selectedCourses
:public IActionResult OnPost(int? id, int[] selectedCourses)
Data string formulir atau kueri bisa dalam salah satu format berikut:
selectedCourses=1050&selectedCourses=2000
selectedCourses[0]=1050&selectedCourses[1]=2000
[0]=1050&[1]=2000
selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
[a]=1050&[b]=2000&index=a&index=b
Hindari mengikat parameter atau properti bernama
index
atauIndex
jika berdekatan dengan nilai koleksi. Pengikatan model mencoba digunakanindex
sebagai indeks untuk koleksi yang mungkin mengakibatkan pengikatan yang salah. Misalnya, pertimbangkan tindakan berikut:public IActionResult Post(string index, List<Product> products)
Dalam kode sebelumnya,
index
parameter string kueri mengikat parameterindex
metode dan juga digunakan untuk mengikat koleksi produk. Mengganti namaindex
parameter atau menggunakan atribut pengikatan model untuk mengonfigurasi pengikatan menghindari masalah ini:public IActionResult Post(string productIndex, List<Product> products)
Format berikut ini hanya didukung dalam data formulir:
selectedCourses[]=1050&selectedCourses[]=2000
Untuk semua format contoh sebelumnya, pengikatan model meneruskan array dua item ke
selectedCourses
parameter :- selectedCourses[0]=1050
- selectedCourses[1]=2000
Format data yang menggunakan nomor subskrip (... [0] ... [1] ...) harus memastikan bahwa mereka diberi nomor secara berurutan mulai dari nol. Jika ada celah dalam penomoran subskrip, semua item setelah celah diabaikan. Misalnya, jika subskrip adalah 0 dan 2, bukan 0 dan 1, item kedua diabaikan.
Kamus
Untuk Dictionary
target, pengikatan model mencari kecocokan untuk parameter_name atau property_name. Jika tidak ada kecocokan yang ditemukan, ia mencari salah satu format yang didukung tanpa awalan. Contohnya:
Misalkan parameter target bernama
Dictionary<int, string>
selectedCourses
:public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
Formulir yang diposting atau data string kueri bisa terlihat seperti salah satu contoh berikut:
selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics
[1050]=Chemistry&selectedCourses[2000]=Economics
selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry& selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
[0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
Untuk semua format contoh sebelumnya, pengikatan model meneruskan kamus dua item ke
selectedCourses
parameter :- selectedCourses["1050"]="Chemistry"
- selectedCourses["2000"]="Economics"
Pengikatan konstruktor dan jenis rekaman
Pengikatan model mengharuskan jenis kompleks memiliki konstruktor tanpa parameter. Pemformat System.Text.Json
input berbasis dan Newtonsoft.Json
mendukung deserialisasi kelas yang tidak memiliki konstruktor tanpa parameter.
C# 9 memperkenalkan jenis rekaman, yang merupakan cara yang bagus untuk mewakili data melalui jaringan. ASP.NET Core menambahkan dukungan untuk pengikatan model dan memvalidasi jenis rekaman dengan satu konstruktor:
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>
Saat memvalidasi jenis rekaman, runtime mencari metadata pengikatan dan validasi khusus pada parameter daripada pada properti.
Kerangka kerja memungkinkan pengikatan ke dan memvalidasi jenis rekaman:
public record Person([Required] string Name, [Range(0, 100)] int Age);
Agar sebelumnya berfungsi, jenisnya harus:
- Jadilah jenis catatan.
- Memiliki tepat satu konstruktor publik.
- Berisi parameter yang memiliki properti dengan nama dan jenis yang sama. Nama tidak boleh berbeda menurut kasus.
POCO tanpa konstruktor tanpa parameter
POCO yang tidak memiliki konstruktor tanpa parameter tidak dapat terikat.
Kode berikut menghasilkan pengecualian yang mengatakan bahwa jenis harus memiliki konstruktor tanpa parameter:
public class Person(string Name)
public record Person([Required] string Name, [Range(0, 100)] int Age)
{
public Person(string Name) : this (Name, 0);
}
Jenis rekaman dengan konstruktor yang ditulis secara manual
Jenis rekaman dengan konstruktor yang ditulis secara manual yang terlihat seperti pekerjaan konstruktor utama
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; }
}
Jenis rekaman, validasi, dan metadata pengikatan
Untuk jenis rekaman, metadata validasi dan pengikatan pada parameter digunakan. Metadata apa pun pada properti diabaikan
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; }
}
Validasi dan metadata
Validasi menggunakan metadata pada parameter tetapi menggunakan properti untuk membaca nilai. Dalam kasus biasa dengan konstruktor utama, keduanya akan identik. Namun, ada cara untuk mengalahkannya:
public record Person([Required] string Name)
{
private readonly string _name;
public Name { get; init => _name = value ?? string.Empty; } // Now this property is never null. However this object could have been constructed as `new Person(null);`
}
TryUpdateModel tidak memperbarui parameter pada jenis catatan
public record Person(string Name)
{
public int Age { get; set; }
}
var person = new Person("initial-name");
TryUpdateModel(person, ...);
Dalam hal ini, MVC tidak akan mencoba mengikat Name
lagi. Namun, Age
diizinkan untuk diperbarui
Perilaku globalisasi data rute pengikatan model dan string kueri
Penyedia nilai rute inti ASP.NET dan penyedia nilai string kueri:
- Perlakukan nilai sebagai budaya invarian.
- Harapkan bahwa URL invarian budaya.
Sebaliknya, nilai yang berasal dari data formulir mengalami konversi sensitif terhadap budaya. Ini dirancang agar URL dapat dibagikan di seluruh lokal.
Untuk membuat penyedia nilai rute inti ASP.NET dan penyedia nilai string kueri menjalani konversi yang sensitif terhadap budaya:
- Mewarisi dari IValueProviderFactory
- Salin kode dari QueryStringValueProviderFactory atau RouteValueValueProviderFactory
- Ganti nilai budaya yang diteruskan ke konstruktor penyedia nilai dengan CultureInfo.CurrentCulture
- Ganti pabrik penyedia nilai default di opsi MVC dengan yang baru Anda:
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;
}
}
Tipe data khusus
Ada beberapa jenis data khusus yang dapat ditangani oleh pengikatan model.
IFormFile dan IFormFileCollection
File yang diunggah disertakan dalam permintaan HTTP. Juga didukung adalah IEnumerable<IFormFile>
untuk beberapa file.
TokenPembatalan
Tindakan dapat secara opsional mengikat CancellationToken
sebagai parameter. Ini mengikat yang memberi sinyal ketika koneksi yang mendasar RequestAborted permintaan HTTP dibatalkan. Tindakan dapat menggunakan parameter ini untuk membatalkan operasi asinkron jangka panjang yang dijalankan sebagai bagian dari tindakan pengontrol.
FormCollection
Digunakan untuk mengambil semua nilai dari data formulir yang diposting.
Pemformat input
Data dalam isi permintaan dapat berada di JSON, XML, atau beberapa format lainnya. Untuk mengurai data ini, pengikatan model menggunakan pemformat input yang dikonfigurasi untuk menangani jenis konten tertentu. Secara default, ASP.NET Core menyertakan pemformat input berbasis JSON untuk menangani data JSON. Anda bisa menambahkan formatter lain untuk tipe isi lainnya.
ASP.NET Core memilih pemformat input berdasarkan atribut Konsumsi . Jika tidak ada atribut, atribut menggunakan header Content-Type.
Untuk menggunakan pemformat input XML bawaan:
Microsoft.AspNetCore.Mvc.Formatters.Xml
Instal paket NuGet.Di
Startup.ConfigureServices
, panggil AddXmlSerializerFormatters atau 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();
Terapkan
Consumes
atribut ke kelas pengontrol atau metode tindakan yang seharusnya mengharapkan XML dalam isi permintaan.[HttpPost] [Consumes("application/xml")] public ActionResult<Pet> Create(Pet pet)
Untuk informasi selengkapnya, lihat Memperkenalkan Serialisasi XML.
Menyesuaikan pengikatan model dengan pemformat input
Pemformat input bertanggung jawab penuh untuk membaca data dari isi permintaan. Untuk menyesuaikan proses ini, konfigurasikan API yang digunakan oleh pemformat input. Bagian ini menjelaskan cara mengkustomisasi System.Text.Json
pemformat input berbasis untuk memahami jenis kustom bernama ObjectId
.
Pertimbangkan model berikut, yang berisi properti kustom ObjectId
bernama Id
:
public class ModelWithObjectId
{
public ObjectId Id { get; set; }
}
Untuk menyesuaikan proses pengikatan model saat menggunakan System.Text.Json
, buat kelas yang berasal dari JsonConverter<T>:
internal class ObjectIdConverter : JsonConverter<ObjectId>
{
public override ObjectId Read(
ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return new ObjectId(JsonSerializer.Deserialize<int>(ref reader, options));
}
public override void Write(
Utf8JsonWriter writer, ObjectId value, JsonSerializerOptions options)
{
writer.WriteNumberValue(value.Id);
}
}
Untuk menggunakan pengonversi kustom, terapkan JsonConverterAttribute atribut ke jenis . Dalam contoh berikut, jenis dikonfigurasi ObjectId
dengan ObjectIdConverter
sebagai pengonversi kustomnya:
[JsonConverter(typeof(ObjectIdConverter))]
public struct ObjectId
{
public ObjectId(int id) =>
Id = id;
public int Id { get; }
}
Untuk informasi selengkapnya, lihat Cara menulis pengonversi kustom.
Mengecualikan jenis yang ditentukan dari pengikatan model
Perilaku sistem pengikatan dan validasi model didorong oleh ModelMetadata. Anda dapat menyesuaikan ModelMetadata
dengan menambahkan penyedia detail ke MvcOptions.ModelMetadataDetailsProviders. Penyedia detail bawaan tersedia untuk menonaktifkan pengikatan atau validasi model untuk jenis tertentu.
Untuk menonaktifkan pengikatan model pada semua model dari jenis tertentu, tambahkan ExcludeBindingMetadataProvider di Startup.ConfigureServices
. Misalnya, untuk menonaktifkan pengikatan model pada semua model jenis 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();
Untuk menonaktifkan validasi pada properti dari jenis tertentu, tambahkan SuppressChildValidationMetadataProvider di Startup.ConfigureServices
. Misalnya, untuk menonaktifkan validasi pada properti jenis 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();
Pengikat model kustom
Anda dapat memperluas pengikatan model dengan menulis pengikat model kustom dan menggunakan [ModelBinder]
atribut untuk memilihnya untuk target tertentu. Pelajari selengkapnya tentang pengikatan model kustom.
Pengikatan model manual
Pengikatan model dapat dipanggil secara manual dengan menggunakan TryUpdateModelAsync metode . Metode ini didefinisikan pada kedua ControllerBase
kelas dan PageModel
. Kelebihan metode memungkinkan Anda menentukan awalan dan penyedia nilai yang akan digunakan. Metode mengembalikan false
jika pengikatan model gagal. Berikut contohnya:
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 menggunakan penyedia nilai untuk mendapatkan data dari isi formulir, string kueri, dan data rute. TryUpdateModelAsync
biasanya:
- Digunakan dengan Razor aplikasi Pages dan MVC menggunakan pengontrol dan tampilan untuk mencegah pengeposan berlebihan.
- Tidak digunakan dengan API web kecuali dikonsumsi dari data formulir, string kueri, dan data rute. Titik akhir API Web yang menggunakan JSON menggunakan Pemformat input untuk mendeserialisasi isi permintaan ke dalam objek.
Untuk informasi selengkapnya, lihat TryUpdateModelAsync.
Atribut [FromServices]
Nama atribut ini mengikuti pola atribut pengikatan model yang menentukan sumber data. Tetapi ini bukan tentang mengikat data dari penyedia nilai. Ini mendapatkan instans jenis dari kontainer injeksi dependensi. Tujuannya adalah untuk memberikan alternatif untuk injeksi konstruktor ketika Anda membutuhkan layanan hanya jika metode tertentu dipanggil.
Sumber Daya Tambahan:
ASP.NET Core