You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular 19与.NET Core 9后端重复POST请求问题求助

核心问题:Angular页面渲染重复发送POST请求导致Auth Token失效

背景信息

有数据分析背景,刚接触Web开发,使用Angular 19前端和.NET Core 9 Web API后端,已自行排查两天未解决。核心问题是每次页面渲染时,前端会向同一端点发送两次POST请求,导致用于兑换第三方API token的auth_token因重复使用失效。该POST请求含JSON体,会触发CORS预检OPTIONS请求,日志显示.NET Core的CORS中间件已正常响应(日志行:CORS policy execution successful.),但重复POST问题仍存在。

现象与日志分析

单次页面刷新会触发1次OPTIONS请求和2次POST请求,后端日志中每次POST都会输出Responded to request for account connection。关键日志片段:

info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 POST http://localhost:5281/api/accountconnect - application/json 111
...
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished HTTP/1.1 POST http://localhost:5281/api/accountconnect - 200 - application/json;+charset=utf-8 127.8122ms      
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 OPTIONS http://localhost:5281/api/accountconnect - - -
...
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished HTTP/1.1 OPTIONS http://localhost:5281/api/accountconnect - 204 - - 6.9856ms
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 POST http://localhost:5281/api/accountconnect - application/json 111
...
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished HTTP/1.1 POST http://localhost:5281/api/accountconnect - 200 - application/json;+charset=utf-8 7.0639ms  

后端代码

AccountConnectController.cs

using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using backend_webapi.Models;

namespace backend_webapi.Controllers;

[ApiController]
[Route("api/[controller]")]
public class AccountConnectController : ControllerBase
{
    [HttpPost]
    public async Task<ActionResult<AccountCredentials>> Post([FromBody] AccountCredentials credentials)
    {
        Console.Out.WriteLine("Responded to request for account connection");
        return Ok(credentials);
    }

    [HttpOptions]
    public async Task<ActionResult<AccountCredentials>> Options([FromBody] AccountCredentials credentials)
    {
        Console.Out.WriteLine("Responded to OPTIONS request");
        return Ok();
    }
}

Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddOpenApi();

builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowAngular",
        policy =>
        {
            policy.WithOrigins("http://localhost:4200")
                   .AllowAnyHeader()
                   .AllowAnyMethod();
        });
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

app.UseCors("AllowAngular");

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();
    
app.Run();

前端代码

services/local.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class DataService {
  constructor(private http: HttpClient) {}

  rootUrl = 'http://localhost:5281';

  getForecast() {
    console.log('Getting forecast...');
    console.log('Root URL:', this.rootUrl);
    console.log(this.http.get(this.rootUrl + '/weatherforecast'));
    return this.http.get(this.rootUrl + '/weatherforecast');
  }

  getAuth(code: string) {
    console.log('Getting auth creditentials...');
    console.log('code:', code);
    return this.http.post(this.rootUrl + '/api/accountconnect', {
        code: code
    });
  }
}

app/auth-nylas/auth-nylas.component.ts

import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { LoginData } from '../../inferfaces/login';
import { DataService } from '../../services/local';

@Component({
  selector: 'app-auth-nylas',
  imports: [],
  templateUrl: './auth-nylas.component.html',
  styleUrl: './auth-nylas.component.scss'
})
export class AuthNylasComponent {
  userData: LoginData = { code: '' };
  data: any;

  constructor(private route: ActivatedRoute, private dataService: DataService) {
    console.log('firing constructor...');
    this.route.queryParamMap.subscribe(params => {
      this.userData.code = params.get('code') || undefined;
    });
  }

  ngOnInit() {
    console.log('firing ngOnInit... Code:', this.userData.code);
    if (this.userData.code) {
      this.dataService.getAuth(this.userData.code).subscribe(data => {
        this.data = data;
      });
    }
  }
}

浏览器网络截图

浏览器网络标签页截图

解决思路与排查方向

  • 检查组件初始化次数:查看浏览器控制台中firing constructor...和firing ngOnInit...的输出次数,确认组件是否被重复渲染(比如路由配置重复、模板中多次引用组件)。
  • 排查路由参数订阅逻辑:queryParamMap的订阅会在路由参数变化时触发,若页面刷新时该订阅触发多次,可能导致重复调用getAuth。建议在订阅回调中直接调用请求,而非依赖ngOnInit,并添加取消订阅逻辑避免内存泄漏。
  • 移除手动OPTIONS处理:.NET Core的CORS中间件会自动处理预检OPTIONS请求,手动编写[HttpOptions]方法可能干扰默认逻辑,尝试删除该方法后测试。
  • 检查HttpClient配置:确认是否添加了自动重试的拦截器,导致请求被重复发送。
  • 查看请求发起者:在浏览器网络面板中查看两次POST请求的Initiator字段,判断是Angular代码触发还是浏览器预加载等行为导致。
  • 验证变更检测机制:Angular的变更检测可能导致组件重复执行逻辑,可尝试将组件变更检测策略设置为OnPush测试。

内容的提问来源于stack exchange,提问作者Tyler Brown

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 05:34:55