解决 AJAX 成功回调中触发表单提交导致 400 错误的根本原因与正确方案

霞舞

霞舞

2026-07-31

551人浏览

原创

解决 AJAX 成功回调中触发表单提交导致 400 错误的根本原因与正确方案

问题根源在于 disablepopup(true) 禁用了表单内所有输入控件,而 .always() 回调执行晚于 .done(),导致 trigger('submit') 时表单实际提交的数据为空(禁用字段被浏览器忽略),asp.net core 验证失败返回 400。

问题根源在于 disablepopup(true) 禁用了表单内所有输入控件,而 .always() 回调执行晚于 .done(),导致 trigger('submit') 时表单实际提交的数据为空(禁用字段被浏览器忽略),asp.net core 验证失败返回 400。

在 ASP.NET Core Razor Pages 应用中,表单提交依赖完整的、启用的输入字段(尤其是隐藏的防伪令牌 __RequestVerificationToken 和必填业务字段)。当您调用 disablePopup(true) 后立即发起 AJAX 请求,并在 .done() 中直接触发 $form.trigger('submit'),此时表单控件仍处于 disabled 状态——浏览器标准行为明确规定:禁用的 。因此,最终提交的请求体中缺失关键字段(如 __RequestVerificationToken、SourceId、TargetType 等),触发服务器端模型绑定失败或防伪令牌验证失败,返回 HTTP 400 Bad Request。

您观察到 setTimeout 能“修复”问题,本质是利用了 JavaScript 事件循环的异步特性:.always() 回调(内部调用 disablePopup(false))虽注册在 .fail() 之后,但实际执行时机取决于 Promise 状态解析顺序;而 setTimeout(..., 0) 将表单提交推入下一个宏任务队列,确保 .always() 先执行、输入框被重新启用,再提交表单——但这是一种不可靠的“竞态修复”,延迟值(如 300ms)无理论依据,且易受环境影响。

正确解法:调整 jQuery Deferred 回调注册顺序,确保 disablePopup(false) 在 .done() 或 .fail() 前执行

表单提交波浪按钮
表单提交波浪按钮

一个表单按钮特效,提交时按钮内会出现波浪填充反馈。 从下载体验看,它的优势在于文件清晰、没有外部依赖、可以直接用浏览器打开。用户可以先阅读本文了解实现方式,再进入演示页体验真实交互。

下载
$('#product-transfer-submit').on('click', function () {
    disablePopup(true);

    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);

    $.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 () { // ? 关键:always 必须最先注册!
        disablePopup(false);
    })
    .done(function (response) {
        if (response.isSuccess) {
            $form.trigger('submit'); // 此时所有字段已启用,提交完整数据
        } else {
            alert(response.error);
        }
    })
    .fail(function (xhr) {
        alert(xhr.responseText || 'AJAX request failed.');
    });
});

? 关键要点总结:

  • 禁用控件 ≠ 视觉灰化,而是彻底排除在表单数据之外:disabled 属性使元素不响应交互,且不参与 form.submit() 的默认序列化过程
  • jQuery 的 .always() 不保证“绝对最后执行”:其执行时机取决于 Promise 状态解析完成时间,但若 .done() 内同步触发提交,.always() 可能尚未运行——必须显式前置注册以确保优先级。
  • GET 请求无需 contentType:contentType 仅用于设置请求体(request body)的 MIME 类型,而 GET 请求将参数编码在 URL 查询字符串中,设置 contentType 不但无效,还可能引发服务器端解析异常。
  • 防伪令牌(Antiforgery Token)极易因此失效:Razor Pages 的 __RequestVerificationToken 隐藏字段若被禁用,将不会提交,导致 [ValidateAntiForgeryToken] 特性验证失败,直接返回 400。

? 进阶建议:

  • 若需更精细控制加载状态,可改用 Promise.finally()(现代写法)或明确分离 UI 状态管理与业务逻辑;
  • 考虑使用 event.preventDefault() + 手动构造 FormData 提交,避免依赖原生表单触发机制;
  • 在服务端日志中检查 400 错误的具体原因(如 Invalid model state 或 Antiforgery token validation failed),可快速定位缺失字段。

遵循上述修正后,表单将在 AJAX 验证通过后、控件恢复启用状态下干净提交,彻底规避 400 错误,且无需任何脆弱的定时器 hack。

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.20

2432

5

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

2023.07.04

720

3

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

2023.07.04

2226

6

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

2023.09.01

638

4

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.04

1167

5

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

2023.09.04

2133

4

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

2023.09.12

2415

6

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

2023.09.20

1523

5

java访问控制修饰符介绍
java访问控制修饰符介绍

java访问控制修饰符有四种,分别是public、protected、private、默认访问修饰符。详细介绍:1、public,public是最宽松的访问控制修饰符,被修饰的类、方法和变量可以被任何其他类访问,当一个类、方法或变量被声明为public时,它们可以在任何地方被访问,无论是同一个包中的类还是不同包中的类;2、protected修饰符等等。

2023.09.20

500

7

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
jQuery官方API文档
jQuery官方API文档

共0课时 | 0人学习

Symfony教程(入门篇+基础篇)
Symfony教程(入门篇+基础篇)

共18课时 | 1.6万人学习