C# Razor Pages中无表单AJAX POST与AntiForgery的正确处理
Razor Pages 无表单AJAX POST的防伪造令牌解决方案
问题场景
在Razor Pages页面实现消息列表展示,通过JavaScript的Edit(id)方法异步加载编辑HTML片段并注入页面;使用Save(id)方法通过AJAX POST提交修改时,因未使用<form>标签,ASP.NET Core内置的防伪造令牌框架未自动生成验证令牌,导致请求触发404错误。同时需要支持并行编辑多条消息,不想通过[IgnoreAntiforgeryToken]注解规避验证(存在CSRF安全风险),也不确定是否需要为每条编辑的消息生成独立令牌。
解决方案
- 在主视图底部添加
@Html.AntiForgeryToken()生成全局可用的防伪造令牌 - 修改
Save函数,通过请求头携带该令牌完成验证 - 移除后台PageModel上的
[IgnoreAntiforgeryToken]注解 - 该令牌可重复用于多条消息的编辑提交,无异常
原主视图(View.cshtml)
@foreach (var item in Model.Messages) { <div id="message_@item.Id"> <label class="form-label">@item.Type:</label> @{ var result = Markdown.ToHtml(item.Message, pipeline); @Html.Raw(result) } </div> <div class="mb-3 text-end"> <a href="#" onclick="Edit(@item.Id)">✏</a> <a href="#" onclick="Delete(@item.Id)">❌</a> </div> } @section Scripts { <script> function Edit(messageId) { $.get( "/Messages/_EditMessage?messageId=" + messageId, function (str) { $('#message_' + messageId).html(str); } ); } function Save(messageId) { $.post( "/Messages/_EditMessage?messageId=" + messageId, { NewMessage: $('#NewMessage_' + messageId).val() }, function (str) { $('#message_' + messageId).html(str); } ); } </script> }
编辑部分视图(_EditMessage.cshtml)
@if (Model.Edit) { <div class="mb-3"> <label class="form-label">@Model.Message.Type</label> <textarea class="form-control" id="NewMessage_@Model.Message.Id" rows="@(Model.Message.Message.Count(c => c == '\n') + 1)">@Model.Message.Message</textarea> </div> <div> <button class="btn btn-outline-success" onclick="Save(@Model.Message.Id)">Save</button> </div> } else { var result = Markdown.ToHtml(Model.Message.Message, pipeline); <label class="form-label">@Model.Message.Type:</label> @Html.Raw(result) }
原后台PageModel(_EditMessage.cshtml.cs)
[IgnoreAntiforgeryToken] public class _EditMessageModel : PageModel { public TextMessage Message { get; set; } = null!; public bool Edit { get; set; } [BindProperty] public string NewMessage { get; set; } = string.Empty; public IActionResult OnGet(int messageId) { var message = MessageManager.Messages.FirstOrDefault(m => m.Id == messageId); if (message is null) return NotFound(messageId); Message = message; Edit = true; return Page(); } public IActionResult OnPost(int messageId) { var message = MessageManager.Messages.FirstOrDefault(m => m.Id == messageId); if (message is null) return NotFound(messageId); Message = message; Message.Message = NewMessage; Edit = false; return Page(); } }
修正内容
- 在主视图(View.cshtml)的末尾添加防伪造令牌:
@Html.AntiForgeryToken()
- 修改
Save函数为以下代码:
function Save(messageId) { $.ajax({ type: "POST", headers: { "RequestVerificationToken": $('input[name="__RequestVerificationToken"]').val() }, url: "/Messages/_EditMessage?messageId=" + messageId, data: { NewMessage: $('#NewMessage_' + messageId).val() } }).done(function(response){ $('#message_' + messageId).html(response); }); }
- 移除后台PageModel上的
[IgnoreAntiforgeryToken]注解,修正后的后台代码:
public class _EditMessageModel : PageModel { public TextMessage Message { get; set; } = null!; public bool Edit { get; set; } [BindProperty] public string NewMessage { get; set; } = string.Empty; public IActionResult OnGet(int messageId) { var message = MessageManager.Messages.FirstOrDefault(m => m.Id == messageId); if (message is null) return NotFound(messageId); Message = message; Edit = true; return Page(); } public IActionResult OnPost(int messageId) { var message = MessageManager.Messages.FirstOrDefault(m => m.Id == messageId); if (message is null) return NotFound(messageId); Message = message; Message.Message = NewMessage; Edit = false; return Page(); } }
内容的提问来源于stack exchange,提问作者mtnielsen
相关产品推荐
相关产品推荐

