第二次POST请求后类属性ServiceIds为空的问题排查
问题原因
ASP.NET Razor Pages是无状态的,每个HTTP请求都会创建全新的页面类实例。第一次AJAX请求中赋值的私有ServiceIds属性,会在请求结束后被销毁;第二次模态框表单提交的POST请求会初始化新的页面实例,因此ServiceIds自然为空。
解决方案
以下是几种可行的解决思路,按实现复杂度排序:
方案1:在模态框表单中添加隐藏字段传递ServiceIds
前端修改
点击编辑按钮的AJAX请求成功后,将选中的ServiceIds以隐藏字段形式插入模态框表单,提交时一并传递到后端:
$(btnEdit).on('click', function () { var checkedList = $('[type=checkbox]:checked'); var ids = [] for (var item of checkedList) { var serviceId = $(item).closest('tr').find(".serviceId").text(); ids.push(serviceId); } $.ajax({ type: "POST", data: JSON.stringify(ids), headers: { "RequestVerificationToken": $('input:hidden[name="__RequestVerificationToken"]').val() }, contentType: "application/json", success: function () { // 清空表单中已有的ServiceIds隐藏字段 $('#editDialog form').find('input[name="ServiceIds"]').remove(); // 为每个ServiceId添加隐藏字段 ids.forEach(id => { $('#editDialog form').append(`<input type="hidden" name="ServiceIds" value="${id}" />`); }); // 打开模态框 $('#editDialog').modal('show'); } }); })
同时给模态框中的输入框添加name属性(否则后端Request.Form无法正确取值):
<div class="modal-body"> <label for="driName">DRI Name: </label> <input id="driName" name="driName" type="text" /> <label for="driEmail">DRI Email: </label> <input id="driEmail" name="driEmail" type="text" /> <hr /> <label for="delegateName">Delegate Name: </label> <input id="delegateName" name="delegateName" type="text" /> <label for="delgateEmail">Delegate Email: </label> <input id="delegateEmail" name="delegateEmail" type="text" /> </div>
后端修改
将ServiceIds改为可绑定的公共属性(或添加[BindProperty]特性),表单提交时会自动绑定:
[BindProperty] public List<string>? ServiceIds { get; set; } public async Task<IActionResult> OnPostSaveAsync() { var dri = Request.Form["driName"].ToString(); var driEmail = Request.Form["driEmail"].ToString(); var delegateName = Request.Form["delegateName"].ToString(); var delegateEmail = Request.Form["delegateEmail"].ToString(); if (ContactRepository != null && ServiceIds != null) { NewContacts = await ContactRepository.GetContactsAsync(ServiceIds).ConfigureAwait(false) ?? new List<Contact> { }; foreach (var contact in NewContacts!) { contact.DRIName = dri ?? string.Empty; contact.DRIEmail = driEmail ?? string.Empty; contact.DelegateName = delegateName ?? string.Empty; contact.DelegateEmail = delegateEmail ?? string.Empty; } await ContactRepository.UpdateContactsAsync(NewContacts).ConfigureAwait(false); } return new RedirectToPageResult("Index"); }
方案2:使用Session存储ServiceIds
后端配置
先确保项目启用Session,在Program.cs中添加:
builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(5); // 设置会话超时时间 options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; }); // 在app.UseRouting()之后添加 app.UseSession();
第一次AJAX请求的后端处理
假设你有接收AJAX的OnPostSetServiceIdsAsync方法,将ServiceIds存入Session:
public async Task<IActionResult> OnPostSetServiceIdsAsync([FromBody] List<string> serviceIds) { HttpContext.Session.SetString("ServiceIds", JsonSerializer.Serialize(serviceIds)); return new JsonResult(new { success = true }); }
OnPostSaveAsync中读取Session
public async Task<IActionResult> OnPostSaveAsync() { var dri = Request.Form["driName"].ToString(); var driEmail = Request.Form["driEmail"].ToString(); var delegateName = Request.Form["delegateName"].ToString(); var delegateEmail = Request.Form["delegateEmail"].ToString(); var serviceIdsJson = HttpContext.Session.GetString("ServiceIds"); var ServiceIds = serviceIdsJson != null ? JsonSerializer.Deserialize<List<string>>(serviceIdsJson) : null; if (ContactRepository != null && ServiceIds != null) { NewContacts = await ContactRepository.GetContactsAsync(ServiceIds).ConfigureAwait(false) ?? new List<Contact> { }; foreach (var contact in NewContacts!) { contact.DRIName = dri ?? string.Empty; contact.DRIEmail = driEmail ?? string.Empty; contact.DelegateName = delegateName ?? string.Empty; contact.DelegateEmail = delegateEmail ?? string.Empty; } await ContactRepository.UpdateContactsAsync(NewContacts).ConfigureAwait(false); // 用完后清除Session中的数据 HttpContext.Session.Remove("ServiceIds"); } return new RedirectToPageResult("Index"); }
方案3:合并两次请求(取消AJAX,前端暂存ServiceIds)
去掉第一次AJAX请求,点击编辑按钮时直接将ServiceIds存在前端变量,提交模态框表单时通过AJAX一并发送:
let selectedServiceIds = []; $(btnEdit).on('click', function () { var checkedList = $('[type=checkbox]:checked'); selectedServiceIds = []; for (var item of checkedList) { var serviceId = $(item).closest('tr').find(".serviceId").text(); selectedServiceIds.push(serviceId); } // 直接打开模态框 $('#editDialog').modal('show'); }); // 监听模态框表单提交,改用AJAX发送 $('#editDialog form').on('submit', function(e) { e.preventDefault(); var formData = { driName: $('#driName').val(), driEmail: $('#driEmail').val(), delegateName: $('#delegateName').val(), delegateEmail: $('#delegateEmail').val(), ServiceIds: selectedServiceIds }; $.ajax({ type: "POST", url: $(this).attr('action'), data: JSON.stringify(formData), headers: { "RequestVerificationToken": $('input:hidden[name="__RequestVerificationToken"]').val() }, contentType: "application/json", success: function () { $('#editDialog').modal('hide'); // 刷新页面 window.location.reload(); } }); });
后端修改OnPostSaveAsync接收JSON数据:
public async Task<IActionResult> OnPostSaveAsync([FromBody] ContactUpdateModel model) { if (ContactRepository != null && model.ServiceIds != null) { NewContacts = await ContactRepository.GetContactsAsync(model.ServiceIds).ConfigureAwait(false) ?? new List<Contact> { }; foreach (var contact in NewContacts!) { contact.DRIName = model.DriName ?? string.Empty; contact.DRIEmail = model.DriEmail ?? string.Empty; contact.DelegateName = model.DelegateName ?? string.Empty; contact.DelegateEmail = model.DelegateEmail ?? string.Empty; } await ContactRepository.UpdateContactsAsync(NewContacts).ConfigureAwait(false); } return new JsonResult(new { success = true }); } // 定义接收模型 public class ContactUpdateModel { public string? DriName { get; set; } public string? DriEmail { get; set; } public string? DelegateName { get; set; } public string? DelegateEmail { get; set; } public List<string>? ServiceIds { get; set; } }
内容的提问来源于stack exchange,提问作者USMC6072
相关产品推荐
相关产品推荐

