ASP.NET Core Razor Pages实现表单无刷新提交添加数据
问题根因
你当前代码中前两个添加临时数据的按钮均设置为type="submit",点击后会触发表单默认的整页POST回发,这是页面刷新的直接原因。另外你当前代码中WebSitelist、SocialMedialist如果是PageModel的普通实例属性,每次HTTP请求都会重新初始化PageModel,列表数据会在回发后丢失,需要同步修复。
推荐实现方案(改动最小,适配现有Razor Pages逻辑)
基于AJAX实现无刷新添加临时数据,用Session存储跨请求的临时列表,不需要推翻现有业务逻辑。
第一步:修改后端PageModel代码
首先修正临时列表的存储逻辑,把两个添加临时项的处理方法改为返回JSON格式结果,补充批量提交的入库逻辑:
using System.Text.Json; using Microsoft.AspNetCore.Mvc; // 临时列表从Session读取,避免每次请求重置 public List<WebSite> WebSitelist { get { var listJson = HttpContext.Session.GetString("TempWebList"); return string.IsNullOrEmpty(listJson) ? new List<WebSite>() : JsonSerializer.Deserialize<List<WebSite>>(listJson); } } public List<SocialMedia> SocialMedialist { get { var listJson = HttpContext.Session.GetString("TempSocialList"); return string.IsNullOrEmpty(listJson) ? new List<SocialMedia>() : JsonSerializer.Deserialize<List<SocialMedia>>(listJson); } } // 添加网站信息的处理方法 public JsonResult OnPostAddWebsiteInfo() { var tempList = WebSitelist; tempList.Add(new WebSite { WebSiteName = NewWebSiteInfo.WebsiteName, websiteUrl = NewWebSiteInfo.websiteUrl }); // 最新列表存回Session HttpContext.Session.SetString("TempWebList", JsonSerializer.Serialize(tempList)); return new JsonResult(new { success = true, data = tempList }); } // 添加社交媒体信息的处理方法 public JsonResult OnPostAddSocialMediaInfo() { var tempList = SocialMedialist; tempList.Add(new SocialMedia { SocialMediaName = NewSocialMediaInfo.SocialMediaName, SocialMediaAccount = NewSocialMediaInfo.SocialMediaAccount }); HttpContext.Session.SetString("TempSocialList", JsonSerializer.Serialize(tempList)); return new JsonResult(new { success = true, data = tempList }); } // 批量提交所有信息入库的处理方法 public IActionResult OnPostAddAllInfo() { var allWebData = WebSitelist; var allSocialData = SocialMedialist; // 此处写你的数据库批量插入逻辑 // 入库完成后清空Session临时数据 HttpContext.Session.Remove("TempWebList"); HttpContext.Session.Remove("TempSocialList"); return RedirectToPage("/OperationSuccess"); // 替换为你的业务跳转页 }
*如果你的项目还没启用Session,需要在Program.cs中补充配置:
// 在注册Razor Pages服务的代码后添加 builder.Services.AddSession(); // 在app.UseRouting() 之后、app.MapRazorPages() 之前添加 app.UseSession();
第二步:修改前端页面代码
把前两个添加临时项的按钮改为普通按钮(阻止默认表单提交),给列表容器加唯一ID方便动态渲染,补充AJAX交互逻辑:
<!-- 网站信息添加按钮改为type="button" --> <button type="button" id="addWebBtn" class="btn btn-primary">Add Website info</button> <!-- 网站列表容器加ID --> <div class="mb-3" id="webListWrap"> @if (Model.WebSitelist.Count > 0) { <div class="col-12 border p-3"> <table class="table table-striped table-bordered"> <thead style="background-color:lightgray"> <tr> <th>WebsiteName</th> <th>websiteURL</th> </tr> </thead> <tbody> @foreach (WebSite item in Model.WebSitelist) { <tr> <td>@item.WebSiteName</td> <td>@item.websiteUrl</td> </tr> } </tbody> </table> </div> } </div> <!-- 社交媒体信息添加按钮改为type="button" --> <button type="button" id="addSocialBtn" class="btn btn-primary">Add Social Media info</button> <!-- 社交媒体列表容器加ID --> <div class="mb-3" id="socialListWrap"> @if (Model.SocialMedialist.Count > 0) { <div class="col-12 border p-3"> <table class="table table-striped table-bordered"> <thead style="background-color:lightgray"> <tr> <th>SocialMediaName</th> <th>SocialMediaAccount</th> </tr> </thead> <tbody> @foreach (SocialMedia item in Model.SocialMedialist) { <tr> <td>@item.SocialMediaName</td> <td>@item.SocialMediaAccount</td> </tr> } </tbody> </table> </div> } </div> <!-- 最后提交按钮保持submit类型,指定对应handler --> <div class="col-4 offset-2"> <button type="submit" asp-page-handler="AddAllInfo" class="btn btn-primary form-control">Add all info</button> </div>
在页面底部的脚本区块添加JS交互逻辑,注意请求要携带Razor Pages的防伪令牌,否则会报400错误:
@section Scripts { <script> // 获取页面防伪令牌 const getToken = () => document.querySelector('input[name="__RequestVerificationToken"]').value; // 渲染网站列表 const renderWebList = (list) => { const wrap = document.getElementById('webListWrap'); if(!list.length) { wrap.innerHTML = ''; return; } let html = `<div class="col-12 border p-3"><table class="table table-striped table-bordered"> <thead style="background-color:lightgray"><tr><th>WebsiteName</th><th>websiteURL</th></tr></thead><tbody>`; list.forEach(item => { html += `<tr><td>${item.webSiteName}</td><td>${item.websiteUrl}</td></tr>`; }) html += `</tbody></table></div>`; wrap.innerHTML = html; } // 渲染社交媒体列表 const renderSocialList = (list) => { const wrap = document.getElementById('socialListWrap'); if(!list.length) { wrap.innerHTML = ''; return; } let html = `<div class="col-12 border p-3"><table class="table table-striped table-bordered"> <thead style="background-color:lightgray"><tr><th>SocialMediaName</th><th>SocialMediaAccount</th></tr></thead><tbody>`; list.forEach(item => { html += `<tr><td>${item.socialMediaName}</td><td>${item.socialMediaAccount}</td></tr>`; }) html += `</tbody></table></div>`; wrap.innerHTML = html; } // 绑定添加网站按钮事件 document.getElementById('addWebBtn').addEventListener('click', async () => { const nameInput = document.querySelector('input[name="NewWebSiteInfo.websiteName"]'); const urlInput = document.querySelector('input[name="NewWebSiteInfo.websiteUrl"]'); if(!nameInput.value.trim() || !urlInput.value.trim()) { alert('请先填写完整网站信息'); return; } const res = await fetch('?handler=AddWebsiteInfo', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'RequestVerificationToken': getToken() }, body: new URLSearchParams({ 'NewWebSiteInfo.websiteName': nameInput.value.trim(), 'NewWebSiteInfo.websiteUrl': urlInput.value.trim() }) }) const result = await res.json(); if(result.success) { renderWebList(result.data); nameInput.value = ''; urlInput.value = ''; } }) // 绑定添加社交媒体按钮事件 document.getElementById('addSocialBtn').addEventListener('click', async () => { const nameInput = document.querySelector('input[name="NewSocialMediaInfo.SocialMediaName"]'); const accountInput = document.querySelector('input[name="NewSocialMediaInfo.SocialMediaAccount"]'); if(!nameInput.value.trim() || !accountInput.value.trim()) { alert('请先填写完整社交媒体信息'); return; } const res = await fetch('?handler=AddSocialMediaInfo', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'RequestVerificationToken': getToken() }, body: new URLSearchParams({ 'NewSocialMediaInfo.SocialMediaName': nameInput.value.trim(), 'NewSocialMediaInfo.SocialMediaAccount': accountInput.value.trim() }) }) const result = await res.json(); if(result.success) { renderSocialList(result.data); nameInput.value = ''; accountInput.value = ''; } }) </script> }
可选替代方案
如果不想使用Session存储临时数据,也可以选择纯前端实现:
- 前两个按钮同样改为
type="button",用JS数组在前端内存中存储临时录入的数据,动态渲染列表 - 点击最后提交按钮时,把前端数组的数据序列化为隐藏表单字段,和其他表单数据一起提交到后端
- 该方案优点是不需要额外后端配置、响应速度更快,缺点是用户手动刷新页面时,已录入的临时数据会全部丢失
原有代码需要修正的问题:
- 第三个提交按钮原来没有指定
asp-page-handler,无法触发批量入库的处理逻辑- 存在拼写错误:按钮文字
socil Media应为social Media,实体类属性名大小写不统一(websiteURL/websiteUrl混用)容易导致模型绑定失败
内容的提问来源于stack exchange,提问作者badraih alshehri
相关产品推荐
相关产品推荐

