# ValidateAntiForgeryToken 返回 400 错误排查

**URL:** <https://www.isharkfly.com/t/validateantiforgerytoken-400/18193>\
**Category:** .Net\
**Tags:** c-sharp\
**Created:** [October 15, 2025, 8:29pm UTC](https://www.isharkfly.com/t/validateantiforgerytoken-400/18193 "2025-10-15T20:29:40Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![hex](https://www.isharkfly.com/user_avatar/www.isharkfly.com/hex/32/21953_2.png) [@hex](https://www.isharkfly.com/u/hex)\
**Post date:** [October 15, 2025, 8:29pm UTC](https://www.isharkfly.com/t/validateantiforgerytoken-400/18193/1 "2025-10-15T20:29:40Z")

</div>

.net ValidateAntiForgeryToken return bad request 错误的原因

---

<div class="post-metadata">

**Author:** ![hex](https://www.isharkfly.com/user_avatar/www.isharkfly.com/hex/32/21953_2.png) [@hex](https://www.isharkfly.com/u/hex)\
**Post date:** [October 15, 2025, 8:29pm UTC](https://www.isharkfly.com/t/validateantiforgerytoken-400/18193/2 "2025-10-15T20:29:41Z")

</div>

你好！`[ValidateAntiForgeryToken]` 返回 400 Bad Request 是一个非常常见的错误，它基本上意味着服务器端的 **CSRF (Cross-Site Request Forgery, 跨站请求伪造) 保护机制验证失败了**。

简单来说，这个机制的工作原理是：

1. **服务器生成一个令牌 (Token)**：当你访问一个包含表单的页面时，服务器会生成一个唯一的、加密的令牌。
2. **令牌分两部分发送给客户端** ：
  - 一部分放在 **HTTP Cookie** 中。
  - 另一部分放在页面表单的一个 **隐藏字段** (`<input type="hidden" name="__RequestVerificationToken" ...>`) 中。

3. **客户端提交表单** ：当你提交表单时（通常是 POST 请求），浏览器会同时发送 **Cookie** 和那个 **隐藏字段的值** 。
4. **服务器验证** ：服务器上的 `[ValidateAntiForgeryToken]` 属性会检查收到的 Cookie 里的令牌和表单里的令牌是否存在，并且它们是否匹配。

**如果验证失败，服务器就认为这个请求可能是伪造的、不安全的，于是拒绝处理，并返回一个 400 Bad Request 错误。**

* * *

### 导致 400 Bad Request 错误的常见原因及解决方法

以下是最常见的原因，你可以逐一排查：

#### 1. 表单中忘记添加 `AntiForgeryToken`

这是最常见的新手错误。如果你的 View (视图) 中的 `<form>` 标签内没有生成隐藏的令牌字段，那么提交时自然会验证失败。

**解决方法：**  
确保在你的 Razor 视图的 `<form>` 标签内部添加了 `@Html.AntiForgeryToken()` ([ASP.NET](http://ASP.NET) MVC) 或者 `<input asp-for="__RequestVerificationToken" type="hidden" />` 的等效标签助手。

**[ASP.NET](http://ASP.NET) Core MVC / Razor Pages 示例：**

```html
<form asp-action="Create" method="post">
    @Html.AntiForgeryToken()
    
    <!-- 其他表单元素 -->
    <input type="text" name="productName" />
    <button type="submit">Submit</button>
</form>

```

或者使用 Tag Helper (更推荐在 [ASP.NET](http://ASP.NET) Core 中使用)：

```html
<form asp-action="Create" method="post">
    <!-- Tag Helper 会自动为 POST 表单生成 AntiForgeryToken，通常无需手动添加 -->
    <!-- 但如果你的 form 标签没有 asp-action/asp-controller 等，就需要手动确认 -->
    
    <!-- 其他表单元素 -->
    <input type="text" name="productName" />
    <button type="submit">Submit</button>
</form>

```

> **注意** ：在 [ASP.NET](http://ASP.NET) Core 中，如果 `<form>` 标签使用了 `method="post"` 和 `asp-action` 或 `asp-controller` 等 Tag Helper，框架会自动为你生成 AntiForgeryToken。但如果没有使用这些 Tag Helper，则需要手动添加 `@Html.AntiForgeryToken()`。

#### 2. 使用 AJAX (如 `fetch`, `jQuery.ajax`) 提交数据

AJAX 请求不会自动提交表单中的隐藏字段。你需要手动读取令牌的值，并将其添加到请求头 (Request Header) 或请求体 (Request Body) 中。

**解决方法：**  
在页面上先获取令牌的值，然后在 AJAX 请求中将其作为请求头发送。

**Step 1: 在你的 Razor 视图中，将令牌放在一个容易被 JavaScript 获取的地方，比如一个隐藏输入框或 meta 标签。**  
`@Html.AntiForgeryToken()` 已经帮你做了这件事，你只需要用 JS 去读取它。

**Step 2: 在 JavaScript 中读取令牌并添加到请求头。**

**使用 `fetch` API 示例：**

```javascript
// 获取隐藏字段中的令牌值
const token = document.querySelector('input[name="__RequestVerificationToken"]').value;

fetch('/YourController/YourAction', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'RequestVerificationToken': token // 将令牌添加到请求头
    },
    body: JSON.stringify({ name: 'test' })
})
.then(response => {
    if (!response.ok) {
        // 请求失败
        console.error('Request failed with status:', response.status);
    }
    return response.json();
})
.then(data => {
    console.log('Success:', data);
});

```

**使用 jQuery `$.ajax` 示例：**

```javascript
var token = $('input[name="__RequestVerificationToken"]').val();

$.ajax({
    url: '/YourController/YourAction',
    type: 'POST',
    data: {
        name: 'test'
        // 注意：如果以 application/x-www-form-urlencoded 方式提交，
        // 并且 __RequestVerificationToken 在 data 中，也可以
        // __RequestVerificationToken: token 
    },
    headers: {
        'RequestVerificationToken': token // 推荐的方式是放在 Header 中
    },
    success: function (result) {
        console.log('Success:', result);
    },
    error: function (xhr, status, error) {
        console.error('Error:', xhr.status, error);
    }
});

```

#### 3. Cookie 丢失或被禁用

如果用户的浏览器禁用了 Cookie，或者 anti-forgery cookie（通常名为 `.AspNetCore.Antiforgery.*`）因某种原因丢失（例如，用户清除了浏览器数据，或者 Cookie 过期），验证也会失败。

**解决方法：**  
这通常是用户端的问题，但你可以确保你的网站正确设置了 Cookie 策略，尤其是在涉及 `iframe` 或跨域场景时，要关注 `SameSite` 属性。

#### 4. 页面缓存问题

如果你的页面被过度缓存（例如，通过代理服务器或浏览器），用户可能会得到一个包含旧的、已失效令牌的页面。当他们提交表单时，这个旧令牌与服务器期望的新令牌不匹配。

**解决方法：**  
对于包含表单的页面，应禁用或谨慎使用输出缓存。可以在 Action 上添加 `[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]` 来防止页面被缓存。

#### 5. Web Farm / 负载均衡环境配置问题

如果你将应用部署在多台服务器（Web Farm）上，并且没有配置共享的密钥环 (Data Protection Key Ring)，那么问题就会出现：

- 服务器 A 生成了一个令牌并发送给用户。
- 用户的下一个请求被负载均衡器分配给了服务器 B。
- 服务器 B 使用自己的密钥无法解密由服务器 A 生成的令牌，导致验证失败。

**解决方法：**  
你需要配置数据保护 (Data Protection) 系统，让所有服务器共享同一个密钥环。这通常通过将密钥存储在共享位置（如 Azure Blob Storage, Redis, 或网络共享文件夹）来实现。

**[ASP.NET](http://ASP.NET) Core 配置示例 (`Program.cs` 或 `Startup.cs`)：**

```csharp
using Microsoft.AspNetCore.DataProtection;

// ...

var builder = WebApplication.CreateBuilder(args);

// 将密钥存储在共享网络文件夹中
builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\keys\"))
    .SetApplicationName("YourAppName"); // 确保所有应用实例名称相同

// ...

```

### 快速排查清单

1. **检查视图** ：确保你的 `<form>` 中有 `@Html.AntiForgeryToken()`。右键查看页面源代码，搜索 `__RequestVerificationToken`，看是否存在这个隐藏字段。
2. **检查网络请求** ：打开浏览器开发者工具 (F12)，切换到 “Network” (网络) 标签。
  - **查看表单提交** ：找到你的 POST 请求，在 “Payload” 或 “Form Data” 中查看 `__RequestVerificationToken` 是否有值。
  - **查看 Cookie** ：在请求的 “Headers” → “Request Headers” 中，或 “Application” (应用) → “Cookies” 中，查找是否存在 anti-forgery cookie。

3. **检查 AJAX 调用** ：如果你用的是 AJAX，确认你已经将令牌值正确地添加到了请求头中。在开发者工具的网络请求详情里检查 `RequestVerificationToken` 请求头是否存在且有值。
4. **检查部署环境** ：如果你在生产环境遇到此问题，但在本地开发时没问题，优先怀疑是不是 Web Farm/负载均衡配置问题。

通过以上步骤，你应该能很快定位并解决 `[ValidateAntiForgeryToken]` 导致的 400 错误。

---

<div class="post-metadata">

**Author:** ![honeymoose](https://www.isharkfly.com/user_avatar/www.isharkfly.com/honeymoose/32/20796_2.png) [@honeymoose](https://www.isharkfly.com/u/honeymoose)\
**Post date:** [October 15, 2025, 8:31pm UTC](https://www.isharkfly.com/t/validateantiforgerytoken-400/18193/3 "2025-10-15T20:31:19Z")

</div>


