翻譯自 Model Binding in ASP.NET Core | Microsoft Docs
什麼是模型綁定?
控制器和 Razor 頁面處理來自 HTTP 請求的數據。例如
public class Pet { public string Name { get; set; } = null!; [FromQuery] // Attribute is ignored. public string Breed { get; set; } = null!; }
,路由數據可能提供了一條記錄的鍵值,提交的表單字段裏可能提供了模型的屬性值。編寫代碼獲取每一個值,並把它們從字符串轉換爲 .NET 類型是繁瑣和容易出錯的。模型綁定會自動處理這些過程。模型綁定系統:
- 從不同的源獲取數據,例如路由數據,表單字段和查詢字符串
- 爲控制器和 Razor 頁面的方法參數和公共屬性提供數據
- 轉換字符串數據爲 .NET 類型數據
- 更新複雜類型的屬性
示例
假設你有以下的操作方法:
[HttpGet("{id}")] public ActionResult<Pet> GetById(int id, bool dogsOnly)
應用程序接收到了使用以下這樣一個 URL 的請求:
https://contoso.com/api/pets/2?DogsOnly=true
模型綁定在路由系統選擇了操作方法之後會經過以下步驟:
- 查找 GetById 方法的第一個參數,名稱爲 id 的整數
- 查詢 HTTP 請求中可用的源,在路由數據中查找到 id = "2"
- 轉換字符串 "2" 爲整數 2
- 查找 GetById 的下一個參數,名稱爲 dogsOnly 的布爾類型的值
- 查詢在查詢字符串源中並查找到 "DogsOnly=true"。名稱匹配不是大小寫敏感的
- 轉換字符串 "true" 爲布爾 true
然後框架會調用 GetById 方法,爲參數 id 賦值 2,爲參數 dogsOnly 賦值 true。
上面這個例子中,模型綁定的目標是簡單類型的方法參數。目標也可以是複雜類型的屬性。在每一個屬性綁定成功後,會爲每個屬性進行模型驗證(model validation)。哪些數據被綁定到了模型,以及所有綁定或者驗證出現的錯誤都被存儲在 ControllerBase.ModelState 或者 PageModel.ModelState。爲了檢查這個過程是否成功,應用程序會檢查 ModelState.IsValid 標記。
目標
模型綁定試圖爲以下幾類目標查找值:
- 一個請求路由到的,控制器操作方法的參數
- 一個請求路由到的,Razors 頁面處理方法的參數
- 通過屬性指定的控制器或者 PageModel 類的公開屬性
[BindProperty] 屬性
可以被應用到一個控制器或者 PageModel 類的一個公開屬性上,以把模型綁定在這個屬性上起作用:
public class EditModel : PageModel { [BindProperty] public Instructor? Instructor { get; set; } }
[BindProperties] attribute
可以被應用到控制器或者 PageModel 類來告訴模型綁定應用到類的所有公開屬性上:
[BindProperties] public class CreateModel : PageModel { public Instructor? Instructor { get; set; } // ... }
HTTP GET 請求的模型綁定
默認情況下,屬性不會綁定到 HTTP GET 請求上。通常,一個 GET 請求的所有需求是一條記錄的 ID 參數。記錄的 ID 用來在數據庫中查詢項目。因此,沒有必要綁定持有一個模型實例的屬性。在你想要綁定來自 HTTP GET 青丘的數據的情況下,設置 SupportsGet 屬性爲 true:
[BindProperty(Name = "ai_user"), SupportsGet = true] public string? ApplicationInsigthsCooks { get; set; }
源
默認情況下,模型綁定以鍵值對的形式從一個 HTTP 請求中下列源中獲取數據:
- 表單字段
- 請求體(對於有 [ApiController] 屬性的控制器)
- 路由數據
- 查詢字符串參數
- 上傳的文件
對於每一個目標參數或者屬性,將會按照上面列表顯示的順序掃描所有源。有以下幾種異常:
- 路由數據和查詢字符串的值僅僅能爲簡單類型使用
- 上傳的文件僅僅能夠綁定到實現了 IFormFile 或者 IEnumerable<IFormFile> 的目標類型
如果默認的源不正確,使用下列的其中的屬性來指定源:
- [FromQuery] - 從查詢字符串獲取值
[FromRoute]
- 從路由數據獲取值[FromForm]
- 從提交的表單獲取值- [FromBody] - 從請求體中獲取值
- [FromHeader] - 從 HTTP 頭部獲取值
這些屬性:
- 單獨被添加到模型屬性上,而不是模型類上,就像下面的示例:
public class Instructor { public int Id { get; set; } [FromQuery(Name = "Note")] public string? NoteFromQueryString { get; set; } // ... }
- 在構造方法中接受一個可選模型名稱值。這個選項在屬性名稱不匹配請求中的值的時候被提供。舉個例子,請求中的值可能是一個名稱帶有橫槓的頭部,就像下面這個例子:
public void OnGet([FromHeader(Name = "Accept-Language")] string language)
[FromBody] 屬性
將 [FromBody] 屬性應用到參數上,以從 HTTP 請求體中獲取數據來填充它的屬性。ASP.NET Core 運行時通過代理負責讀取請求體到一個輸入格式化中。輸入格式化在後面的文章中解釋(later in this article)。
當 [FromBody] 被應用到一個複雜類型的參數時,任何應用到它的屬性的綁定源屬性都被會被忽略。例如,下面的 Create 方法指定了它的 pet 參數從請求體中填充:
public ActionResult<Pet> Create([FromBody] Pet pet)
Pet 類指定了它的 Breed 屬性從一個查詢字符串中填充值:
public class Pet { public string Name { get; set; } = null!; [FromQuery] // Attribute is ignored. public string Breed { get; set; } = null!; }
上面的示例中:
- [FromQuery] 屬性被忽略
- Breed 屬性不會從查詢字符串的參數中填充屬性值
輸入格式化器只會讀取請求體,並不會去理會綁定源屬性。如果一個合適的值在請求體中被發現,它的值將會用來填充 Breed 屬性的值。
對於一個操作方法,不要把 [FromBody] 應用與多個參數。一旦通過輸入格式化器讀取過請求流,將不在能爲應用了 [FromBody] 參數綁定。
其它源
源數據通過值提供器爲模型綁定系統提供數據。你可以編寫和註冊自定義的值提供器,用來從其它數據源爲模型綁定獲取數據。例如,你可能想從 cookies 或者 會話狀態獲取數據。從一個新的源獲取數據:
- 創建一個實現 IValueProvider 的類
- 創建一個實現 IValueProviderFactory 的類
- 在 Program.cs 中註冊工廠類
例子包含了一個 value provider 和 factory, 展示了從 cookies 中獲取值。在 Program.cs 中註冊了自定義的值提供器工廠方法:
builder.Services.AddControllers(options => { options.ValueProviderFactories.Add(new CookieValueProviderFactory()); });
上面的代碼把自定義的值提供器放在了所有內置值提供器的之後。要實現它處於列表的最前面,調用 Insert(0, new CookieValueProviderFactory()) 替換 Add 即可。
模型屬性沒有源
默認情況下,一個模型的狀態錯誤不會創建,如果模型屬性的值沒有被找到。屬性值被設置爲 null 或者一個默認的值:
- 可空的簡單類型被設置爲 null
- 可空的值類型被設置爲 default(T)。例如,一個 int 類型的參數 id 被設置爲 0
- 對於複雜類型,模型綁定使用默認的構造方法創建一個實例,不會設置實例屬性的值
- 數組被設置爲 Array.Empty<T>(),除了 byte[] 數組會被設置爲 null
對於一個模型屬性,在沒有任何值在表單字段中被找到,如果要單獨設置模型狀態,可以使用 [BindRequired]
屬性。
注意,只有從提交的表單中獲取數據時,[BindRequired] 屬性的行爲才被應用到模型綁定,而從請求體中的 JSON 或者 XML 數據則不會。請求體的數據通過 input formatters 處理。
類型轉換錯誤
如果一個源被發現,但是不能夠被轉換爲目標類型,模型狀態就被標記爲無效。如前一節所述,目標參數或者屬性被設置爲 null 或者默認值。
在一個擁有 [ApiController] 屬性的 API 控制器中,無效的模型狀態將會產生一個自動的 HTTP 400 響應。
在一個 Razor 頁面中,會重新展示帶有一個錯誤信息的頁面:
public IActionResult OnPost() { if (!ModelState.IsValid) { return Page(); } // ... return RedirectToPage("./Index"); }
當頁面通過前面的代碼重新顯示時,無效的輸入沒有在表單字段中顯示。這是由於模型的屬性已經被設置爲 null 或者默認值。無效的輸入不會出現在一個錯誤信息中。如果你想要在表單字段中重新顯示錯誤的數據,可以考慮將模型屬性設置爲字符串,並手動進行數據轉換。
如果你不想類型轉換錯誤導致模型狀態錯誤,建議使用相同的策略。在這種情況下,設置模型屬性爲字符串。
簡單類型
模型綁定器可以將源字符串轉換爲以下簡單類型:
- Boolean
- Byte, SByte
- Char
- DateTime
- DateTimeOffset
- Decimal
- Double
- Enum
- Guid
- Int16, Int32, Int64
- Single
- TimeSpan
- UInt16, UInt32, UInt64
- Uri
- Version
複雜類型
一個複雜類型必須有一個公開默認的構造方法和公開的可寫的屬性去綁定。當模型綁定發生時,將會使用公開默認的構造方法實例化類。
對於複雜類型的每一個屬性,模型綁定將在源中查找名稱模式(model binding looks through the sources for the name pattern) prefix.property_name。如果什麼都沒有找到,它只查找沒有前綴的 property_name。使用查詢的決定不是根據每個屬性做出的。例如,使用包含 ?Instructor.Id=100&Name=foo 的查詢字符串,綁定到方法 OnGet(Instructor instructor),產生的類型 Instructor 的對象包含:
- Id 被設置爲 100
- Name 被設置爲 null。模型綁定期望 Instructor.Name,因爲 Instructor.Id 在前面的查詢參數中被使用
對於綁定到一個參數,前綴是參數的名稱。對於綁定到一個 PageModel 公開屬性,前綴就是公開屬性的名稱。有些屬性有一個 Prefix 屬性,該屬性允許你覆蓋參數或者屬性名稱的默認用法。
例如,假設一個複雜類型是以下的 Instructor 類:
public class Instructor { public int ID { get; set; } public string LastName { get; set; } public string FirstName { get; set; } }
Prefix = parameter name
如果要綁定的模型是一個名稱爲 instructorToUpdate 的參數:
public IActionResult OnPost(int? id, Instructor instructorToUpdate)
模型綁定開始在源中爲鍵 instructorToUpdate.ID 查找值。如果沒有找到,它將爲沒有前綴的鍵 ID 查找。
Prefix = property name
如果要綁定的模型是控制器或者 PageModel 類的一個名稱爲 Instructor 的屬性:
[BindProperty] public Instructor Instructor { get; set; }
模型綁定開始在源中爲鍵 Instructor.ID 查找值。如果沒有找到,它將爲沒有前綴的鍵 ID 查找。
自定義前綴
如果要綁定的模型是一個名稱爲 instructorToUpdate 的參數,並且綁定屬性指定 Instructor 作爲前綴:
public IActionResult OnPost( int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)
模型綁定開始在源中爲鍵 Instructor.ID 查找值。如果沒有找到,它將查找沒有前綴的 ID。
複雜類型目標屬性
幾個內置的屬性可以用來爲控制器綁定複雜類型:
警告
當提交的表單數據作爲值的來源時,這些屬性將會影響模型綁定。當處理通過 JSON 和 XML 請求體提交的數據時,這些屬性不會產生影響。輸入格式化器會在之後的文章中講到。
[Bind] 屬性
可以應用到一個類或者一個方法參數。用來指定模型的那些屬性應該被包含在模型綁定中。[Bind] 不會影響輸入格式化器。
在下面的例子中,當任何處理或者操作方法被調用時,只有被指定的 Instructor 模型的屬性會被綁定:
[Bind("LastName,FirstMidName,HireDate")] public class Instructor
在下面的例子中,當 OnPost 方法被調用時,只有被指定的 Instructor 模型的屬性會被綁定:
[HttpPost] public IActionResult OnPost( [Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
[Bind] 屬性可用於在創建場景中防止重複發佈。它在編輯場景中工作的不是很好,因爲被排除的屬性被設置爲 null 或者一個默認值,而不是保持不變。對於防止重複發佈,建議使用視圖模型而不是 [Bind] 屬性。更多信息,查看 Security note about overposting。
[ModelBinder] 屬性
ModelBinderAttribute 可以被應用到類型,屬性或者參數。它允許指定用於綁定特定實例或類型的模型綁定器的類型。例如:
[HttpPost] public IActionResult OnPost( [ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)
[ModelBinder] 屬性在它是模型綁定時,也可以用於改變屬性或者參數的名稱:
public class Instructor { [ModelBinder(Name = "instructor_id")] public string Id { get; set; } // ... }
[BindRequired] 屬性
僅僅可以應用到模型屬性,不能應用到方法參數。如果一個模型的屬性綁定不成功,將會產生一個添加模型狀態的錯誤。例如:
public class InstructorBindRequired { // ... [BindRequired] public DateTime HireDate { get; set; } }
在 Model validation 中查看有關 [Required] 屬性的討論。
[BindNever] 屬性
可以應用到屬性或類型。阻止模型綁定設置模型的屬性值。當應用到一個類型時,模型綁定系統將排除類型定義的所有屬性。例如:
public class InstructorBindNever { [BindNever] public int Id { get; set; } // ... }
集合
對於目標是簡單類型的情況,模型綁定查找匹配 parameter_name 或 property_name。如果沒有匹配,它將查找一種支持的沒有前綴的格式。例如:
- 假設要綁定的參數是一個名稱爲 selectedCourses 的數組
public IActionResult OnPost(int? id, int[] selectedCourses)
- 表單或查詢字符串數據可以是以下一種格式:
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
避免綁定一個名稱爲 index 或 Index 的參數或屬性,如果該參數或屬性與集合值相鄰。模型綁定試圖使用 index 作爲集合的索引,這會導致不正確的綁定。例如,考慮一下方法:
public IActionResult Post(string index, List<Product> products)
public IActionResult Post(string productIndex, List<Product> products)
selectedCourses[]=1050&selectedCourses[]=2000
- 對於前面所有的示例格式,模型綁定傳遞了包含兩個項目的數組給 selectedCaourses 參數:
selectedCourses[0]=1050
selectedCourses[1]=2000
使用下標數字的數據格式必須保證它們是從0開始的數字序列。如果下標編號有空格,則忽略空格後的所有項目。例如,如果下面使用 0 和 2 ,而不是 0 和 1,則第二個項目會被忽略。
字典
對於字典目標,模型綁定查找匹配 parameter_name 或 property_name 的目標。如果沒有發現目標,它將查找一種受支持的沒有前綴的格式。例如:
- 假設目標參數是一個名稱爲 selectedCourses 的字典 Dictionary<int, string>:
public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
- 提交的表單或查詢字符串數據可能看起來是下列示例中的一種:
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
對於上面所有示例的格式,模型綁定傳遞了一個包含兩個項目的字典到參數 selectedCourses:
selectedCourses["1050"]="Chemistry"
selectedCourses["2000"]="Economics"
構造方法綁定和記錄類型
複雜類型的模型綁定要求有一個無參的構造方法。基於 System.Text.Json 和 Newtsoft.Json 的輸入格式化器支持對沒有無參構造方法的類的反序列化。
記錄類型是一種簡要的代表在網絡上的數據的好方式。ASP.NET Core 支持模型綁定和使用單個構造方法驗證記錄類型:
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 Name: <input asp-for="Name" /> <br /> Age: <input asp-for="Age" />
在驗證記錄類型時,運行時會在參數上搜索綁定的和驗證元數據,而不是在屬性上。
框架允許綁定和驗證記錄數據:
public record Person([Required] string Name, [Range(0, 100)] int Age);
要想前面的代碼能夠工作,類型必須:
- 是一個記錄類型
- 只有一個公共構造方法
- 包含具有相同名稱和類型屬性的參數。名稱必須不能因爲大小寫而不同
沒有無參構造方法的 POCOs
沒有無參構造方法的 POCOs 不能被綁定。
下面的代碼會導致異常,因爲類型必須有一個無參構造方法:
public class Person(string Name) public record Person([Required] string Name, [Range(0, 100)] int Age) { public Person(string Name) : this (Name, 0); }
手動創建構造方法的記錄類型
使用手動構造方法的記錄類型就像主構造方法一樣工作:
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; } }
記錄類型,驗證和綁定元數據
對於記錄類型,參數上驗證和綁定的元數據被使用。任何屬性上的元數據都被忽略。
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; } }
驗證和元數據
驗證使用參數上的元數據,但是使用屬性讀取值。使用主構造方法的一般情況下,這兩者是相同的。然而,在一些情況下並不是這樣的:
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 並不會更新記錄類型的參數
public record Person(string Name) { public int Age { get; set; } } var person = new Person("initial-name"); TryUpdateModel(person, ...);
在這個例子中,MVC 不會再次綁定 Name。然而,Age 是允許被更新的。
模型綁定路由數據和查詢字符串的國際化行爲
- 認爲值是和區域沒有關係的
- 期望 URLs 是和區域沒有關係的
相反,來自表單數據的值要經過文化敏感的轉換。這是爲了讓 URL 可以跨地區而設計的。
爲了使 ASP.NET Core 路由值提供器和查詢字符串值提供器能夠實現文化敏感的轉換:
- 繼承自 IValueProviderFactory
- 從 QueryStringValueProviderFactory 或者 RouteValueValueProviderFactory 複製代碼
- 替換使用 CultureInfo.CurrentCulture 傳遞給值提供器構造方法的 culture value
- 使用新的值提供器工廠方法替換 MVC 選項中的默認值
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(); });
特殊數據類型
有一些模型綁定可以處理的特殊數據類型
IFormFile 和 IFormFileCollection
HTTP 請求中的上傳的文件。同時也支持多個文件的類型是 IEnumerable<IFormFile>。
CancellationToken
操作可以選擇性的綁定一個 CancellationToken 作爲一個參數。這綁定的是一個當 HTTP 請求下的連接斷開時的一個信號 RequestAborted。操作可以使用這個參數來取消作爲控制器操作一部分的長時間異步運行的操作。
FormCollection
用來獲取提交的表單數據中的所有值。
輸入格式器
請求體中的數據可能是 JSON,XML,或者其他格式。爲了解析這些數據,模型綁定會用 input formatter 來配置處理一個特定的內容類型。默認的,ASP.NET Core 包含基於 JSON 的輸入格式器來處理 JSON 數據。你可以添加其他格式器來處理其它內容類型。
ASP.NET Core 基於 Consumes 屬性來選擇輸入格式化器。如果沒有指定屬性,它將使用 Content-Type header。
使用內置的 XML 輸入格式化器:
- 在 Program.cs 中,調用 AddXmlSerializerFormatters 或者 AddXmlDataContractSerializerFormatters.
builder.Services.AddControllers() .AddXmlSerializerFormatters();
- 應用 Consumes 屬性到期望請求體中是 XML 的控制器類或者操作方法
[HttpPost] [Consumes("application/xml")] public ActionResult<Pet> Create(Pet pet)
更多信息,查看 Introducing XML Serialization
使用輸入格式化器自定義模型綁定
輸入格式化器負責從請求體中讀取數據。爲了自定義這個過程,需要配置輸入格式化器使用的 APIs。這部分描述瞭如何自定義一個基於 System.Text.Json 的輸入格式化器,用來理解一個名稱爲 ObjectId 的自定義類型。
考慮下面的模型,包含了一個名稱爲 Id ,類型爲自定義類型 ObjectId 的屬性:
public class InstructorObjectId { [Required] public ObjectId ObjectId { get; set; } = null!; }
在使用 System.Text.Json 時自定義模型綁定的過程,創建一個繼承自 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); }
要使用一個自定義的轉換,應用 JsonConverterAttribute 到類型。在下面的例子中,類型 ObjectId 類型使用了 ObjectIdConverter 作爲自定義的轉換器:
[JsonConverter(typeof(ObjectIdConverter))] public record ObjectId(int Id);
更多信息,查看 How to write custom converters。
從模型綁定中排除指定的類型
模型綁定和驗證系統的行爲由 ModelMetadata 驅動。你可以通過往 MvcOptions.ModelMetadataDetailsProviders 中添加一個詳細的提供器來自定義 ModelMetadata。內置的提供器可以用來爲指定類型禁用模型綁定或者驗證。
要想禁用一個指定類型所有模型的模型綁定,在 Program.cs 中添加一個 ExcludeBindingMetadataProvider。例如,爲了禁用類型 System.Version 的模型綁定:
builder.Services.AddRazorPages() .AddMvcOptions(options => { options.ModelMetadataDetailsProviders.Add( new ExcludeBindingMetadataProvider(typeof(Version))); options.ModelMetadataDetailsProviders.Add( new SuppressChildValidationMetadataProvider(typeof(Guid))); });
要想禁用指定類型的屬性的驗證,在 Program.cs 中添加一個 SuppressChildValidationMetadataProvider。例如,禁用 System.Guid 類型的屬性驗證:
builder.Services.AddRazorPages() .AddMvcOptions(options => { options.ModelMetadataDetailsProviders.Add( new ExcludeBindingMetadataProvider(typeof(Version))); options.ModelMetadataDetailsProviders.Add( new SuppressChildValidationMetadataProvider(typeof(Guid))); });
自定義模型綁定器
你可以通過編寫一個自定義的模型綁定器,然後使用 [ModelBinder] 屬性爲一個給定的目標選擇它來擴展模型綁定。更多信息,查看 custom model binding。
手動模型綁定
模型綁定可以通過使用 TryUpdateModelAsync 方法手動的調用。這個方法在 ControllerBase 和 PageModel 類中都有定義。方法重載允許你指定前綴和值提供器來使用。如果模型綁定失敗,方法返回 false。這裏有一個例子:
if (await TryUpdateModelAsync( newInstructor, "Instructor", x => x.Name, x => x.HireDate!)) { _instructorStore.Add(newInstructor); return RedirectToPage("./Index"); } return Page();
TryUpdateModelAsync 使用了值提供器從請求體,查詢字符串和路由數據中獲取數據。TryUpdateModelAsync 通常的:
- 在使用控制器和視圖的 Razor Pages 及 MVC 應用程序中來阻止重複提交
- 不與 web API 一起使用,除非從表單數據,查詢字符串和路由數據中獲取數據。Web API 中使用 JSON 的重點使用 Input formatters 來解析請求體到一個對象中
更多信息,查看 TryUpdateModelAsync。
[FromServices] 屬性
該屬性的名稱遵循指定數據源的模型綁定的屬性。但是並不是關於來自值提供器的綁定的數據。它從依賴注入(dependency injection)容器中獲取一個類型的實例。它的目的是隻在爲一個特定的方法被調用你需要一個服務時爲構造注入提供一個替代方法。
如果一個實例的類型沒有在依賴注入容器中註冊,應用程序將會在試圖綁定參數的時候拋出一個異常。要使參數爲可選的,可以使用下列的方法:
- 使參數爲可空
- 爲參數設置一個默認值
對於可空參數,保證在訪問它之前不爲 null。