.NET 7 Razor Page POST请求属性绑定全为null求助
.NET 7 Razor Page POST请求绑定属性全为null的解决方案
问题描述
基于.NET 7开发的Razor Page,提交POST请求时所有绑定属性均为null,已引入Tag Helper,尝试创建独立类、调试等方法后仍未解决。相关代码如下:
页面代码
@page @model AppointmentModel @{ ViewBag.Title = "Rendez-Vous"; ViewBag.pTitle = "Rendez-Vous"; ViewBag.pageTitle = "Accueil"; Layout = "~/Views/Shared/_Layout.cshtml"; } ... <form id="formSubmit" method="post"> <div class="row"> <div class="col-md-6"> <div class="mb-3"> <label for="formrow-firstname-input" class="form-label">Prénom</label> <input asp-for="Input.FirstName" type="text" class="form-control" id="formrow-firstname-input" placeholder="Entrez votre prénom" autocomplete="firstname" disabled> </div> </div> <div class="col-md-6"> <div class="mb-3"> <label for="formrow-lastname-input" class="form-label">Nom de Famille</label> <input asp-for="Input.LastName" class="form-control" autocomplete="lastname" aria-required="true" placeholder="Entrez votre nom de Famille" disabled /> </div> </div> </div> <div class="row"> <div class="col-md-6"> <div class="mb-3"> <label for="formrow-specialite" class="form-label">Cliniques</label> <select id="formrow-specialite" asp-for="Input.Clinic" asp-items="Model.Locations" class="form-select"></select> </div> </div> </div> <div class="row"> <div class="col-md-6"> <div class="mb-3"> <label>Date désirée du rendez-vous</label> <div class="input-group" id="datepicker2"> <input type="text" class="form-control" placeholder="dd M, yyyy" data-date-format="dd M, yyyy" data-date-container='#datepicker2' data-provide="datepicker" data-date-autoclose="true"> <span class="input-group-text"><i class="mdi mdi-calendar"></i></span> </div><!-- input-group --> </div> </div> <div class="col-md-6"> <div class="mb-3"> <label for="formrow-periode" class="form-label">Période de la journée</label> <select id="formrow-periode" class="form-select"> <option>Matin</option> <option>Après-Midi</option> </select> </div> </div> </div> <div class="row"> <div class="col-md-6"> <div class="mb-3"> <label for="formrow-email-input" class="form-label">Courriel</label> <input asp-for="Input.Email" type="email" class="form-control" id="formrow-email-input" placeholder="Courriel" disabled> </div> </div> </div> <div class="row"> <div class="col-md-6"> <div class="mb-3"> <label for="formrow-email-input" class="form-label">Raison de votre visite</label> <textarea class="form-control" id="formrow-email-input" placeholder="Riason de votre visite"></textarea> </div> </div> </div> <div> <button type="submit" id="btnSubmit" class="btn btn-primary w-md">Soumettre</button> ... </div> }
后台类代码
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.AspNetCore.Identity.UI.Services; using Microsoft.AspNetCore.Mvc; #nullable disable using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.Rendering; using System.ComponentModel.DataAnnotations; namespace CBQAdmin.Pages { [Authorize] public class AppointmentModel : PageModel { private readonly CBQAdminIContext _context; private readonly UserManager<CBQIdentityUser> _userManager; private readonly ILogger<AppointmentModel> _logger; [BindProperty] public InputModel Input { get; set; } public SelectList Locations { get; set; } public AppointmentModel( UserManager<CBQIdentityUser> userManager, ILogger<AppointmentModel> logger, CBQAdminIContext context) { _userManager = userManager; _logger = logger; _context = context; } public class InputModel { [DataType(DataType.Text)] public string FirstName { get; set; } [DataType(DataType.Text)] public string LastName { get; set; } [DataType(DataType.Text)] public string Email { get; set; } [DataType(DataType.Text)] public string Clinic { get; set; } [DataType(DataType.Text)] public string PolicyNumber { get; set; } [DataType(DataType.Text)] public string Reason { get; set; } } public async Task OnGetAsync() { Locations = new SelectList(_context.Clinics.OrderBy(n => n.Name).ToList(), "Name", "Name"); var user = await _userManager.GetUserAsync(User); Input = new InputModel(); if (user != null) { Input.Email = await _userManager.GetEmailAsync(user); if (!string.IsNullOrEmpty(user.FirstName)) { Input.FirstName = user.FirstName; } if (!string.IsNullOrEmpty(user.LastName)) { Input.LastName = user.LastName; } if (!string.IsNullOrEmpty(user.PolicyNumber)) { Input.PolicyNumber = user.PolicyNumber; } } } public async Task<IActionResult> OnPostAsync() { var user = await _userManager.GetUserAsync(User); InputModel input = new InputModel(); await TryUpdateModelAsync<InputModel>( input, "input", i => i.FirstName, i => i.LastName, i => i.PolicyNumber, i => i.Clinic, i => i.Reason); if (ModelState.IsValid) { if (user != null) { EmailService emailService = new EmailService(); await emailService.SendAppointmentRequest(Input); } } return Page(); } } }
问题原因
- 禁用元素不提交值:
FirstName、LastName、Email输入框带有disabled属性,浏览器不会提交禁用元素的数值,导致这些属性无法绑定。 - 部分表单元素未绑定:日期输入框、时间段下拉框、
Reason文本框未使用asp-for标记绑定属性,无法将用户输入传递到后台。 - 后台绑定逻辑错误:
OnPostAsync中手动创建新的InputModel实例并调用TryUpdateModelAsync,但未使用已标记[BindProperty]的Input属性;同时TryUpdateModelAsync的前缀参数为小写"input",与Razor Pages绑定的大小写要求不匹配(应使用大写"Input")。
修复方案
1. 解决禁用元素的提交问题
将disabled替换为readonly(用户无法编辑但值会被提交),或保留disabled并添加隐藏字段同步值:
<!-- 方案1:替换为readonly --> <input asp-for="Input.FirstName" type="text" class="form-control" id="formrow-firstname-input" placeholder="Entrez votre prénom" autocomplete="firstname" readonly> <!-- 方案2:保留disabled,添加隐藏字段 --> <input asp-for="Input.FirstName" type="hidden"> <input asp-for="Input.FirstName" type="text" class="form-control" id="formrow-firstname-input" placeholder="Entrez votre prénom" autocomplete="firstname" disabled>
2. 为所有表单元素添加绑定标记
给未绑定的元素添加asp-for,并在InputModel中补充对应属性:
<!-- 日期输入框 --> <input asp-for="Input.AppointmentDate" type="text" class="form-control" placeholder="dd M, yyyy" data-date-format="dd M, yyyy" data-date-container='#datepicker2' data-provide="datepicker" data-date-autoclose="true"> <!-- 时间段下拉框 --> <select id="formrow-periode" asp-for="Input.Period" class="form-select"> <option>Matin</option> <option>Après-Midi</option> </select> <!-- Reason文本框 --> <textarea asp-for="Input.Reason" class="form-control" id="formrow-reason-input" placeholder="Riason de votre visite"></textarea>
补充InputModel属性:
public class InputModel { // 原有属性... public string AppointmentDate { get; set; } public string Period { get; set; } }
3. 修正后台绑定逻辑
删除手动创建InputModel和TryUpdateModelAsync的代码,直接使用已标记[BindProperty]的Input属性:
public async Task<IActionResult> OnPostAsync() { var user = await _userManager.GetUserAsync(User); if (ModelState.IsValid) { if (user != null) { EmailService emailService = new EmailService(); await emailService.SendAppointmentRequest(Input); } } // 重新加载下拉框数据,避免页面刷新后无选项 Locations = new SelectList(_context.Clinics.OrderBy(n => n.Name).ToList(), "Name", "Name"); return Page(); }
4. 验证表单字段名称
确保表单元素的name属性与后台InputModel的属性名一致,asp-for会自动生成正确的name(如Input.FirstName对应name="Input.FirstName"),无需手动修改。
内容的提问来源于stack exchange,提问作者David Abitbol
相关产品推荐
相关产品推荐

