
问题根源在于:ajax 请求完成后立即触发表单提交,而此时表单控件仍处于禁用状态(由 disablepopup(true) 设置),导致 asp.net core 验证失败或请求体缺失关键字段(如防伪令牌、必填项),最终返回 400 bad request。
问题根源在于:ajax 请求完成后立即触发表单提交,而此时表单控件仍处于禁用状态(由 disablepopup(true) 设置),导致 asp.net core 验证失败或请求体缺失关键字段(如防伪令牌、必填项),最终返回 400 bad request。
在 ASP.NET Core Razor Pages 应用中,表单提交依赖于完整的、可序列化的输入字段——包括隐藏的防伪令牌(__RequestVerificationToken)、模型绑定必需的隐藏字段(如 SourceId、TargetType)以及用户填写的可见字段。当你调用 disablePopup(true) 禁用整个模态框内的 和
因此,当 $.ajax().done() 中直接调用 $form.trigger('submit') 时,表单虽被提交,但关键字段(尤其是 __RequestVerificationToken 和业务字段)因被禁用而丢失,导致服务器端验证失败(如防伪令牌缺失 → 400;或模型绑定失败 → 400),而非业务逻辑错误。
你观察到 setTimeout(..., 300) 能“临时修复”,本质是利用了 JavaScript 事件循环的异步特性:.always() 回调虽注册在 .done() 之后,但在 Promise 链中执行顺序取决于回调注册时机。默认链式写法中,.always() 在 .done() 之后执行,因此 disablePopup(false) 发生在 trigger('submit') 之后,提交时控件仍为禁用状态。
✅ 正确解法:确保表单控件在提交前已恢复启用状态。最可靠的方式是将 .always() 回调置于 Promise 链最前端,保证无论请求成功或失败,控件都先被重新启用:
$('#product-transfer-submit').on('click', function () {
var $form = $(this).closest('form');
var $modal = $('#transfer-product-modal');
var $activeTab = $modal.find('a.nav-link.active');
var targetType = $activeTab.data('type');
var targetId = $modal.find($activeTab.attr('href') + ' :input').val();
$modal.find('#TargetType').val(targetType);
// ✅ 关键:将 .always() 放在链首,确保控件优先恢复启用
$.ajax({
type: 'GET',
url: '?handler=ValidateProductTransfer',
// ⚠️ 移除 contentType: 'application/json' —— GET 请求无请求体,设置该头会导致歧义
dataType: 'json',
data: {
'sourceId': $modal.find('#SourceId').val(),
'targetType': targetType,
'targetId': targetId,
'productId': $modal.find('#ProductId').val()
}
})
.always(function () {
// ✅ 无论成功/失败,先恢复控件可用性
disablePopup(false);
})
.done(function (response) {
if (response.isSuccess) {
// 此时所有 input/select 已启用,值可正常提交
$form.trigger('submit');
} else {
alert(response.error);
}
})
.fail(function (xhr) {
alert(xhr.responseText || '验证请求失败,请重试');
});
});
function disablePopup(disable) {
// 注意:禁用/启用 input/select 即可,a 标签无需 disabled 属性(语义不合法)
$("#transfer-product-modal :input").prop("disabled", disable);
}
? 关键注意事项:
- contentType 对 GET 无效:GET 请求参数通过 URL 查询字符串传递,不应设置 contentType(它仅用于 POST/PUT 等带请求体的请求)。保留该配置可能导致某些服务器中间件误判。
- 标签不支持 disabled 属性:HTML 规范中 无 disabled 属性,prop("disabled", true) 对其无效。应改用 addClass('disabled').attr('aria-disabled', 'true') 或 CSS 控制交互。
- 防伪令牌必须启用:__RequestVerificationToken 是 ASP.NET Core 抗 CSRF 的核心,若被禁用则提交必然 400。确保其所在 始终未被 disablePopup() 影响(当前代码中它位于
-
替代方案(推荐):避免全局禁用,改用按钮禁用 + 加载状态提示:
$('#product-transfer-submit').prop('disabled', true).text('验证中...'); // ... AJAX 后 .always(() => { $('#product-transfer-submit').prop('disabled', false).text('Transfer'); });
通过调整 Promise 回调顺序并清理冗余配置,即可彻底规避 400 错误,无需不可靠的 setTimeout 延迟。











