未授权用户提交表单后登录参数丢失问题求助
问题分析与修复方案
这个问题的核心在于:默认的AuthorizeAttribute拦截未授权的POST请求时,只会保存跳转的ReturnUrl,但不会保留表单里的提交数据。当用户登录成功后重定向回原Action时,原POST请求的表单数据已经丢失,导致model.Restaurant和NewComment.Body都为null,同时用户之前输入的评论内容也没了。
下面给你两种可行的修复思路,你可以根据自己的需求选择:
方案一:服务器端保存表单数据(适合传统页面跳转场景)
1. 自定义授权过滤器,保存POST表单数据
我们需要重写默认的授权过滤器,把用户提交的表单数据临时存在TempData里,这样登录后还能取回来:
public class SaveFormAuthorizeAttribute : AuthorizeAttribute { protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { // 只处理POST请求的表单数据 if (filterContext.HttpContext.Request.HttpMethod == HttpMethod.Post.Method) { // 将表单数据和原跳转地址存入TempData filterContext.Controller.TempData["PostedForm"] = filterContext.HttpContext.Request.Form; filterContext.Controller.TempData["OriginalReturnUrl"] = filterContext.HttpContext.Request.Url.PathAndQuery; } base.HandleUnauthorizedRequest(filterContext); } }
然后把全局过滤器里的new AuthorizeAttribute()替换成这个自定义过滤器。
2. 调整登录Action,重定向时携带上下文
在登录成功后,检查TempData里是否有保存的表单数据,如果有,就跳转到原提交地址:
[HttpPost] public ActionResult Login(LoginViewModel model, string returnUrl) { if (ModelState.IsValid && ValidateUser(model.Username, model.Password)) { FormsAuthentication.SetAuthCookie(model.Username, model.RememberMe); // 优先处理保存的表单请求 if (TempData["OriginalReturnUrl"] != null) { return Redirect(TempData["OriginalReturnUrl"].ToString()); } if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl)) { return Redirect(returnUrl); } return RedirectToAction("Index", "Home"); } ModelState.AddModelError("", "用户名或密码错误"); return View(model); }
3. 修改AddComment Action,恢复表单数据
当用户登录后回到AddComment时,从TempData里取出之前保存的表单数据,填充到ViewModel中,这样用户能看到之前输入的评论内容,再次提交就能正常绑定参数:
[HttpPost] public ActionResult AddComment(RestaurantViewModel model, Comment newComment) { // 如果参数为空,尝试从TempData恢复表单数据 if (model == null || newComment == null) { var savedForm = TempData["PostedForm"] as NameValueCollection; if (savedForm != null) { // 重建ViewModel和Comment对象 model = new RestaurantViewModel(); model.Restaurant = _context.Restaurants.Find(int.Parse(savedForm["Restaurant.Id"])); newComment = new Comment { Body = savedForm["NewComment.Body"] }; } else { // 没有数据的话直接跳回详情页 return RedirectToAction("Details", new { id = model?.Restaurant.Id ?? 0 }); } } // 原有的评论保存逻辑 var userId = User.Identity.GetUserId(); var user = _context.Users.FirstOrDefault(u => u.Id == userId); newComment.RestaurantId = model.Restaurant.Id; newComment.AuthorId = Guid.Parse(userId); newComment.AuthorName = user.UserName; newComment.DateTime = DateTime.Now; _context.Comments.Add(newComment); _context.SaveChanges(); return RedirectToAction("Details", "Restaurants", new { id = model.Restaurant.Id }); }
4. 优化ViewModel初始化
确保RestaurantViewModel里的NewComment是初始化好的,避免视图里出现空引用:
public class RestaurantViewModel { public Restaurant Restaurant { get; set; } public Comment NewComment { get; set; } public RestaurantViewModel() { // 初始化空Comment,确保TextBoxFor能正常渲染 NewComment = new Comment(); } }
方案二:客户端Ajax提交(用户体验更优)
这种方式不需要页面跳转,当用户未登录时弹出登录模态框,登录成功后自动重新提交评论,全程保留用户输入的内容:
1. 修改视图为Ajax表单
@using (Ajax.BeginForm("AddComment", "Restaurants", new AjaxOptions { HttpMethod = "POST", OnSuccess = "onCommentSuccess", OnFailure = "onCommentFailure" })) { @Html.TextBoxFor(c => c.NewComment.Body) @Html.HiddenFor(m => m.Restaurant.Id) <button type="submit">Add comment</button> } <!-- 登录模态框(示例) --> <div class="modal fade" id="loginModal"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-body"> <input type="text" id="loginUsername" placeholder="用户名"> <input type="password" id="loginPassword" placeholder="密码"> <button id="submitLogin">登录</button> </div> </div> </div> </div> <script> // 评论提交成功后的处理 function onCommentSuccess(result) { window.location.href = result.redirectUrl; } // 评论提交失败(未授权)的处理 function onCommentFailure(xhr) { if (xhr.status === 401) { // 保存当前表单数据,登录后重新提交 var formData = $('form').serialize(); $('#submitLogin').data('pendingForm', formData); // 弹出登录框 $('#loginModal').modal('show'); } } // 登录按钮点击事件 $('#submitLogin').click(function() { var username = $('#loginUsername').val(); var password = $('#loginPassword').val(); $.post('/Account/Login', { username: username, password: password }, function(res) { if (res.success) { $('#loginModal').modal('hide'); // 重新提交之前的评论表单 var pendingForm = $('#submitLogin').data('pendingForm'); $.post('/Restaurants/AddComment', pendingForm, function() { window.location.href = '/Restaurants/Details/@Model.Restaurant.Id'; }); } else { alert('登录失败,请重试'); } }); }); </script>
2. 调整AddComment Action返回Json结果
[HttpPost] public ActionResult AddComment(RestaurantViewModel model, Comment newComment) { if (!User.Identity.IsAuthenticated) { // 返回未授权状态码 return new HttpStatusCodeResult(HttpStatusCode.Unauthorized); } var userId = User.Identity.GetUserId(); var user = _context.Users.FirstOrDefault(u => u.Id == userId); newComment.RestaurantId = model.Restaurant.Id; newComment.AuthorId = Guid.Parse(userId); newComment.AuthorName = user.UserName; newComment.DateTime = DateTime.Now; _context.Comments.Add(newComment); _context.SaveChanges(); return Json(new { success = true, redirectUrl = Url.Action("Details", "Restaurants", new { id = model.Restaurant.Id }) }); }
内容的提问来源于stack exchange,提问作者bobeq
相关产品推荐
相关产品推荐

