我已经参加了我的第一个MVC和C#项目,所以我很感激任何指导。
我创建了一项新功能,可以检查用户在登录时是否接受过安全培训。如果用户没有,则会将用户定向到他们只是同意/不同意的培训页面规则。如果用户同意,则登录完成。如果用户不同意,他/她将被注销。
我遇到的问题是,当我在训练视图中选择同意/不同意按钮时,我得到以下 它应该将我引导到主页或注销用户。
控制器
public ActionResult UserSecurityTraining(int ID, string returnUrl)
{
// check if user already has taken training (e.g., is UserInfoID in UserSecurityTrainings table)
var accountUser = db.UserSecurityTraining.Where(x => x.UserInfoID == ID).Count();
// If user ID is not in UserSecurityTraining table...
if (accountUser == 0)
{
// prompt security training for user
return View("UserSecurityTraining");
}
// If user in UserSecurityTraining table...
if (accountUser > 0)
{
return RedirectToLocal(returnUrl);
}
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> UserSecurityTrainingConfirm(FormCollection form, UserSecurityTraining model)
{
if (ModelState.IsValid)
{
if (form["accept"] != null)
{
try
{
// if success button selected
//UserSecurityTraining user = db.UserSecurityTraining.Find(); //Create model object
//var user = new UserSecurityTraining { ID = 1, UserInfoID = 1, CreatedDt = 1 };
logger.Info("User has successfully completed training" + model.UserInfoID);
model.CreatedDt = DateTime.Now;
db.SaveChanges();
//return RedirectToAction("ChangePassword", "Manage");
}
catch (Exception e)
{
throw e;
}
return View("SecurityTrainingSuccess");
}
if(form["reject"] != null)
{
return RedirectToAction("Logoff", "Account");
}
}
return View("UserSecurityTraining");
}
查看
@model ECHO.Models.UserSecurityTraining
@{
ViewBag.Title = "Security Training";
Layout = "~/Views/Shared/_LayoutNoSidebar.cshtml";
}
<!--<script src="~/Scripts/RequestAccess.js"></script>-->
<div class="container body-content">
<h2>@ViewBag.Title</h2>
<div class="row">
<div class="col-md-8">
@using (Html.BeginForm("UserSecurityTrainingConfirm", "Account", FormMethod.Post, new { role = "form" }))
{
<fieldset>
@Html.AntiForgeryToken()
Please view the following security training slides:<br><br>
[INSERT LINK TO SLIDES]<br><br>
Do you attest that you viewed, understood, and promise to follow the guidelines outlined in the security training?<br><br>
<input type="submit" id="accept" class="btn btn-default" value="Accept" />
<input type="submit" id="reject" class="btn btn-default" value="Reject" />
</fieldset>
}
</div><!--end col-md-8-->
</div><!--end row-->
</div><!-- end container -->
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
答案 0 :(得分:1)
我认为您没有提供足够的代码来正确诊断此特定错误。通常,此防伪例外是由于身份验证状态的更改。当您在页面上调用@Html.AntiForgeryToken()
时,在使用令牌的响应中也会设置cookie。重要的是,如果用户已通过身份验证,则使用该用户的身份来撰写该令牌。然后,如果该用户的身份验证状态在设置cookie和将表单发布到验证该令牌的操作之间发生更改,则该令牌将不再匹配。这可以是用户在cookie设置后进行身份验证,也可以在设置cookie后注销。换句话说,如果用户在页面加载时是匿名的,但在提交表单之前登录,则它仍然会失败。
同样,我没有看到任何明显会引起这种情况的代码,但我们也没有全面了解用户如何登录到第一个视图中的地方。
尽管如此,有一些非常明显的错误可能会或可能不会导致这个特定的问题,但肯定会在某个时候引起问题。首先,您的按钮没有name
属性。根据您的操作代码,您似乎认为id
属性将出现在FormCollection
中,但事实并非如此。您需要分别将name="accept"
和name="reject"
添加到按钮中,以便当前代码正常运行。
其次,在用户成功接受时,您应该重定向到加载SecurityTrainingSuccess
视图的操作,而不是直接返回该视图。这部分PRG(Post-Redirect-Get)模式确保不重播提交。任何和所有后期操作都应该在成功时重定向。
第三,至少开箱即用,LogOff
将是一个后期操作,这意味着你无法重定向到它。重定向总是通过GET。从技术上讲,你可以使LogOff
响应GET以及代替POST,但这是一种反模式。应始终通过POST(或更合适的动词,如PUT,DELETE等)处理原子动作,但永远不要GET。
最后,虽然很小,但一般来说,不建议使用FormCollection
。对于这样一个简单的表单,你可以将你的帖子绑定为params:
public ActionResult UserSecurityTrainingConfirm(string accept, string reject, ...)
然而,引入单个布尔值可能更合乎逻辑且更加万无一失:
public ActionResult UserSecurityTrainingConfirm(bool accepted, ...)
然后,您的按钮可能只是:
<button type="submit" name="accepted" value="true" class="btn btn-default">Accept</button>
<button type="submit" name="accepted" value="false" class="btn btn-default">Reject</button>
这基本上使它们像无线电一样。点击的那个会提交其值,因此accepted
参数将相应地为true或false。另外,请注意我将您切换为真正的button
元素。使用input
按钮是一种不好的做法,特别是当你真正需要它来提交一个值时,因为值和显示本身是绑在一起的。使用button
元素,您可以发布任何您想要的内容,并且仍然可以使用您想要的任何文本进行标记。