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

.NET 5 Core API与Angular前端POST请求CORS问题求助

解决.NET 5 API POST请求CORS跨域问题

问题概述

已完成Oracle数据库、.NET 5 Core API及Angular前端搭建,API运行正常,Angular的GET请求可正常执行,但POST请求触发CORS错误,确认授权环节无问题。

报错信息

Access to XMLHttpRequest at 'https://localhost:44342/api/User/insertUser' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

现有代码片段

Angular服务POST请求

insertUser(model: Users_Viewmodel) {
  console.log(model);
  return this.http.post<Users_Viewmodel>(
    `${this.apiUrl}/api/User/insertUser`, model
  );
}

API端CORS配置(原Startup.cs片段)

// outside of my ConfigureServices method
readonly string MyAllowOrigins = "_myAllowOrigins";


//  in my ConfigureServices method
services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowOrigins,
        policy =>
        {
            policy.WithOrigins("http://localhost:4200")
                .AllowAnyHeader()
                .AllowCredentials()
                .AllowAnyMethod();
        });
});

// In my Configure method
app.UseCors(MyAllowOrigins);

API控制器代码

[HttpPost("insertUser")]
[Authorize]
public IActionResult InsertUser([FromBody] Users_Viewmodel model)
{
    try
    {
        UserRepo.InsertUser(model);
        return Ok();
    }
    catch (Exception ex)
    {
        return BadRequest(ex.Message);
    }
}

完整Startup.cs代码

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Server.IISIntegration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MyApp.Api.Helpers;
using MyApp.Api.MiddleWare;
using MyApp.WebSocketHub;

namespace MyApp.Api
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        readonly string MyAllowOrigins = "_myAllowOrigins";

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Add session-related configuration at the beginning of the method
            services.AddDistributedMemoryCache(); // Add this line

            services.AddSession(options =>
            {
                options.Cookie.Name = "SessionVars";
                options.IdleTimeout = TimeSpan.FromMinutes(30);
                options.Cookie.HttpOnly = true;
                options.Cookie.IsEssential = true;
            });

            services.AddHttpContextAccessor();
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.AddSingleton<SessionManager>();

            services.AddCors(options =>
            {
                options.AddPolicy(name: MyAllowOrigins,
                        policy =>
                        {
                            policy.WithOrigins("http://localhost:4200")
                                .AllowAnyHeader()
                                .AllowCredentials()
                                .AllowAnyMethod();
                        });
            });

            services.AddSignalR()
                .AddJsonProtocol(options => {
                    options.PayloadSerializerOptions.PropertyNamingPolicy = null;
                });

            services.AddAuthentication(IISDefaults.AuthenticationScheme);
            services.AddMvc(config =>
            {
                var policy = new AuthorizationPolicyBuilder()
                    .RequireAuthenticatedUser()
                    .Build();
                config.Filters.Add(new AuthorizeFilter(policy));
            });

            services.AddControllers()
                .AddJsonOptions(options =>
                {
                    options.JsonSerializerOptions.PropertyNamingPolicy = new AllUppercaseNamingPolicy();
                });

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "MyApp.Api", Version = "v1" });
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHttpContextAccessor httpContextAccessor)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "MyApp.Api v1"));
            }

            app.UseCors(MyAllowOrigins);

            app.UseHttpsRedirection();

            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthentication();

            app.UseAuthorization();
            
            app.UseSession();
            
            app.UseMiddleware<OpcAuthorizeMiddleware>();

            SessionManager.Initialize(httpContextAccessor);
            
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapHub<WebSocketHub.WebSocketHub>("/ws");
                endpoints.MapControllers();
            });
        }
    }
}

排查与解决方案

POST请求属于复杂请求,浏览器会先发OPTIONS预检请求验证跨域权限。当前问题核心是预检请求未得到正确的CORS响应头,原因在于中间件顺序错误或自定义授权中间件拦截了预检请求。

方案1:调整中间件顺序(优先推荐)

在.NET 5中,CORS中间件必须放在UseRouting之后、UseAuthorization及自定义中间件之前,确保所有请求(包括OPTIONS预检)都能先应用CORS策略。修改Configure方法的中间件顺序:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHttpContextAccessor httpContextAccessor)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseSwagger();
        app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "MyApp.Api v1"));
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();

    // 将UseCors移至UseRouting之后,确保路由匹配后应用CORS策略
    app.UseCors(MyAllowOrigins);

    app.UseAuthentication();
    app.UseAuthorization();
    app.UseSession();
    
    app.UseMiddleware<OpcAuthorizeMiddleware>();

    SessionManager.Initialize(httpContextAccessor);
    
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapHub<WebSocketHub.WebSocketHub>("/ws");
        endpoints.MapControllers();
    });
}

方案2:放行自定义授权中间件中的OPTIONS请求

若无法调整中间件顺序,修改OpcAuthorizeMiddleware,让它直接放行OPTIONS预检请求,不做授权校验:

public class OpcAuthorizeMiddleware
{
    private readonly RequestDelegate _next;

    public OpcAuthorizeMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // 直接放行OPTIONS预检请求
        if (context.Request.Method.Equals("OPTIONS", StringComparison.OrdinalIgnoreCase))
        {
            context.Response.Headers.Add("Access-Control-Allow-Origin", "http://localhost:4200");
            context.Response.Headers.Add("Access-Control-Allow-Headers", "*");
            context.Response.Headers.Add("Access-Control-Allow-Methods", "*");
            context.Response.StatusCode = StatusCodes.Status200OK;
            await context.Response.CompleteAsync();
            return;
        }

        // 执行原有授权逻辑
        await _next(context);
    }
}

额外验证点

  • 确认Angular请求未携带未被允许的自定义请求头(已配置AllowAnyHeader(),通常无需额外处理)
  • 检查API运行地址https://localhost:44342是否正确,协议、端口与配置一致

内容的提问来源于stack exchange,提问作者Justin de Gois

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 08:57:07