从JavaScript调用ASP.NET Razor Page Handler方法遇400错误
ASP.NET Razor Page 调用OnPost Handler时400 Bad Request错误排查与解决
问题说明
通过JavaScript调用ASP.NET Razor Page的OnPost Handler方法时,持续收到400 Bad Request错误,Razor Page模型仅包含OnPost和OnGet两个Handler,需求是通过JS发起POST请求执行业务逻辑。
现有代码
JavaScript 请求代码
async function fetchMakeCredentialOptions(formData) { let response = await fetch('Account/PasswordLessRegistration?handler=Submit', { method: "POST", body: formData, header: { 'Accept': 'application/json' } }); let data = await response.json(); return data; }
Razor Page C# 代码
namespace IdentityServerHost.Pages.PasswordLessLogin { public class Index : PageModel { private IFido2 _fido2; public static readonly DevelopmentInMemoryStore PasswordlessStore = new DevelopmentInMemoryStore(); private readonly AdminIdentityDbContext _context; [BindProperty] public InputModel Input { get; set; } public Index(IFido2 fido2, AdminIdentityDbContext context) { _fido2 = fido2; _context = context; } private string FormatException(Exception e) { return string.Format("{0}{1}", e.Message, e.InnerException != null ? " (" + e.InnerException.Message + ")" : ""); } public async Task<IActionResult> OnGet() { return Page(); } public IActionResult OnPost([FromForm] string username, [FromForm] string displayName, [FromForm] string attType, [FromForm] string authType, [FromForm] bool requireResidentKey, [FromForm] string userVerification) { try { // 用户必须已存在于Identity系统中 var identityUser = _context.Users.FirstOrDefault(user => user.UserName == "anis"); if (identityUser == null) { throw new Exception("用户未找到"); } if (!HttpContext.User.IsAuthenticated()) { throw new Exception("用户未认证"); }; // 1. 通过用户名从数据库获取用户(示例中自动创建不存在的用户) var user = PasswordlessStore.GetOrAddUser(username, () => new Fido2User { DisplayName = displayName, Name = username, Id = Encoding.UTF8.GetBytes(username) // 需要用户ID的字节表示 }); // 2. 获取用户已有的密钥 var existingKeys = PasswordlessStore.GetCredentialsByUser(user).Select(c => c.Descriptor).ToList(); // 3. 创建凭证选项 var authenticatorSelection = new AuthenticatorSelection { RequireResidentKey = requireResidentKey, UserVerification = userVerification.ToEnum<UserVerificationRequirement>() }; if (!string.IsNullOrEmpty(authType)) authenticatorSelection.AuthenticatorAttachment = authType.ToEnum<AuthenticatorAttachment>(); var exts = new AuthenticationExtensionsClientInputs() { Extensions = true, UserVerificationIndex = true, Location = true, UserVerificationMethod = true, BiometricAuthenticatorPerformanceBounds = new AuthenticatorBiometricPerfBounds { FAR = float.MaxValue, FRR = float.MaxValue } }; var options = _fido2.RequestNewCredential(user, existingKeys, authenticatorSelection, attType.ToEnum<AttestationConveyancePreference>(), exts); // 4. 临时存储选项,可使用会话/内存缓存/Redis/数据库 HttpContext.Session.SetString("fido2.attestationOptions", options.ToJson()); // 5. 将选项返回给客户端 return new JsonResult(options); } catch (Exception e) { return new JsonResult(new CredentialCreateOptions { Status = "error", ErrorMessage = FormatException(e) }); } return new JsonResult(true); } } }
FormData 构造代码
var data = new FormData(); data.append('username', username); data.append('displayName', displayName); data.append('attType', attestation_type); data.append('authType', authenticator_attachment); data.append('userVerification', user_verification); data.append('requireResidentKey', require_resident_key);
错误原因与修复方案
1. Handler 名称不匹配
JS请求中指定了handler=Submit,但C#中的方法是OnPost(无Handler后缀)。Razor Pages中,自定义Handler需要遵循OnPost{HandlerName}的命名规则,或者请求时不指定handler参数(调用默认的OnPost)。
修复方式二选一:
- 去掉JS请求中的
?handler=Submit,直接请求对应页面路径 - 将C#中的方法名改为
OnPostSubmit,保持请求中的handler=Submit参数
2. 请求头拼写错误
JS代码中header应为复数headers,拼写错误导致请求头设置失效。
3. 缺少Anti-Forgery Token验证
Razor Pages默认启用Anti-Forgery验证,POST请求必须携带__RequestVerificationToken,否则会返回400错误。
添加方式二选一:
- 在FormData中添加令牌:
data.append('__RequestVerificationToken', document.querySelector('input[name="__RequestVerificationToken"]').value); - 在请求头中添加令牌:
headers: { 'Accept': 'application/json', 'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value }
注意:页面中需要先通过@Html.AntiForgeryToken()生成令牌输入框。
4. 路由路径可能错误
JS请求路径是Account/PasswordLessRegistration,但C#的Page位于PasswordLessLogin/Index,需要确认实际路由路径是否正确。可以通过查看页面的实际URL,或者在Razor Page顶部添加@page "/Account/PasswordLessRegistration"来指定路由。
修复后的完整JS代码示例
async function fetchMakeCredentialOptions(formData) { // 添加Anti-Forgery Token到FormData formData.append('__RequestVerificationToken', document.querySelector('input[name="__RequestVerificationToken"]').value); let response = await fetch('PasswordLessLogin/Index', // 修正为正确的路由路径 { method: "POST", body: formData, headers: // 修正拼写为headers { 'Accept': 'application/json' } }); let data = await response.json(); return data; }
内容的提问来源于stack exchange,提问作者Anis Bouzidi
相关产品推荐
相关产品推荐

