ASP.NET通过CSHTML页面删除数据库行时的异常问题:未点击删除按钮却清空整个数据库
问题根源分析
你遇到的核心问题是页面加载时就自动执行了所有行的删除操作,完全不需要点击按钮。罪魁祸首是你在Razor视图的按钮代码块里写了这段服务器端代码:
@{ var smth = new AdminController(); smth.delete_by_id(row.Id); //HERE IS WHERE I CALL THE QUERY }
Razor里的@{ ... }属于服务器端执行代码,当用户访问页面时,服务器会先渲染整个视图:遍历数据库里的每一行数据,每遍历一行就实例化一次AdminController并调用delete_by_id(row.Id)——这就导致页面刚加载完成,所有用户数据已经被全部清空了。
另外,手动实例化Controller是不符合ASP.NET MVC设计模式的,框架应该负责Controller的创建和生命周期管理,手动实例化可能会带来上下文泄漏、依赖注入失效等问题。
正确实现方案
我们需要把删除操作改成用户点击按钮时才触发的异步请求,并且严格遵循MVC的分层原则。
1. 重构Controller的删除方法
首先把删除方法改成标准的Action,使用[HttpPost]标记(因为删除是修改操作,必须用POST而非GET,防止误触发),并且返回合适的JSON结果:
public class AdminController : BaseController { private readonly UserContext _context; // 推荐用构造函数注入UserContext(如果你的项目支持依赖注入) public AdminController(UserContext context) { _context = context; } // 如果暂时无法使用依赖注入,保留原构造函数也可以,但尽量避免手动实例化 // public AdminController() // { // _context = new UserContext(); // } [HttpPost] // 必须标记为POST,防止GET请求误触发删除 public JsonResult DeleteById(int id) { try { // 先找到要删除的用户 var user = _context.Users.FirstOrDefault(u => u.Id == id); if (user == null) { return Json(new { success = false, message = "用户不存在" }); } // 执行删除操作(如果用EntityFramework Plus的DeleteFromQuery也可以,这里用原生EF示例) _context.Users.Remove(user); _context.SaveChanges(); return Json(new { success = true }); } catch (Exception ex) { // 实际项目中建议记录日志,这里简化处理 return Json(new { success = false, message = $"删除失败: {ex.Message}" }); } } // 重构Index方法,通过ViewModel传递用户数据(符合MVC模式) [AdminMod] public ActionResult Index() { SessionStatus(); if ((string)System.Web.HttpContext.Current.Session["LoginStatus"] != "login") { return RedirectToAction("Index", "Login"); } var currentUser = System.Web.HttpContext.Current.GetMySessionObject(); var allUsers = _context.Users.ToList(); // 创建ViewModel统一传递视图所需数据 var viewModel = new AdminIndexViewModel { CurrentUser = new UserData { Username = currentUser.Username, Level = currentUser.Level }, Users = allUsers }; return View(viewModel); } }
2. 创建ViewModel(统一传递视图数据)
新建一个ViewModel类,用来打包视图需要的所有数据:
public class AdminIndexViewModel { public UserData CurrentUser { get; set; } public List<UDbTable> Users { get; set; } }
3. 重构视图(用AJAX触发删除)
移除视图中直接执行的服务器端删除代码,改用AJAX在按钮点击时发送删除请求:
@model AdminIndexViewModel @using WebApplication1.Domain.Enums @{ ViewBag.Title = "Admin"; Layout = "~/Views/Shared/_Layout.cshtml"; } <!-- 添加防CSRF攻击的Token --> @Html.AntiForgeryToken() <script> $(document).ready(function() { // 给所有删除按钮绑定点击事件 $('.delete-btn').click(function() { const userId = $(this).data('user-id'); // 先弹出确认框,防止误操作 if (!confirm("确定要删除这个用户吗?")) { return; } // 发送AJAX请求到删除接口 $.ajax({ url: '@Url.Action("DeleteById", "Admin")', type: 'POST', data: { id: userId }, // 带上CSRF Token,防止跨站请求伪造 headers: { RequestVerificationToken: $('input[name="__RequestVerificationToken"]').val() }, success: function(response) { if (response.success) { // 删除成功后,移除页面上对应的行 $(event.target).closest('tr').remove(); alert("删除成功!"); } else { alert(`删除失败:${response.message}`); } }, error: function() { alert("网络异常,删除失败!"); } }); }); }); </script> <div class="container" style="margin-top: 1%;"> <h1>AdminPage</h1> <table class="table table-bordered"> <thead> <tr> <th scope="col">#Id</th> <th scope="col">Username</th> <th scope="col">Email</th> <th scope="col">Level</th> <th scope="col"></th> </tr> </thead> <tbody> @foreach (var user in Model.Users) { <tr> <th scope="row">#@user.Id</th> <td>@user.Username</td> <td>@user.Email</td> <td> @switch(user.Level) { case 0: <text>User</text>; break; case 1: <text>Premium</text>; break; default: <text>Admin</text>; break; } </td> <td> <!-- 删除按钮:用data属性存储用户ID,绑定统一的点击事件 --> <button type="button" class="btn btn-danger delete-btn" data-user-id="@user.Id" title="Delete User"> <i class="fas fa-trash"></i> </button> </td> </tr> } </tbody> </table> </div>
关键注意点
- 服务器端代码vs客户端代码:Razor的服务器端代码会在页面渲染时执行,而客户端JS代码才是用户交互时触发的,一定要区分清楚两者的执行时机。
- HTTP方法规范:删除、修改这类写操作必须用POST(或PUT/DELETE),禁止用GET,防止浏览器预加载、爬虫等误触发操作。
- CSRF防护:ASP.NET MVC的
@Html.AntiForgeryToken()必须加上,防止跨站请求伪造攻击。 - MVC模式遵循:尽量不要在视图中直接操作数据库,数据获取逻辑应该放在Controller或Service层,通过ViewModel传递给视图。
内容的提问来源于stack exchange,提问作者dapet
相关产品推荐
相关产品推荐

