如何在使用 Kestrel 和 React 托管的 .NET 7 上配置 ASP.NET Web API 时配置 CORS?

huangapple go评论129阅读模式
英文:

How to configure CORS when using ASP.NET Web API on NET 7 hosted using Kestrel and React?

问题

I'm working on a project using an ASP.NET Web API as the backend, and a create-react-app webpage as the frontend, and I can't get them to communicate anymore. No matter what I try, I constantly get CORS errors. They didn't seem to occur in live (i.e. with the API and Website both on IIS) but in development they refuse to work. It used to work a few days ago, but after making some changes to get it to work on IIS, it refuses to work locally using Kestrel anymore.

每次我尝试从React端使用请求(我在使用Axios.request进行HTTP请求),我都会得到Access to XMLHttpRequest at 'https://localhost:7046/api/<insert route here>' from origin 'http://localhost:3000' 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.的错误。

我尝试在Microsoft Edge中使用Chrome Web Store的CORS Unblock,但这只会改变成Response to preflight request doesn't pass access control check: It does not have HTTP ok status.。我知道路由应该是可以工作的,因为完全相同的内容在Postman中可以工作,但在浏览器中却失败了。

程序.cs:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using NLog;
using NLog.Web;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.EntityFrameworkCore;
using System.Configuration;

namespace MyNewAPI
{
    public class Program
    {
        //...(其他代码略)

        var builder = WebApplication.CreateBuilder(args);
        builder.Services.AddCors(options =>
        {
            options.AddPolicy(name: "_myAllowSpecificOrigins",
                policy =>
                {
                    policy.WithOrigins("http://localhost:3000")
                        .WithMethods("PUT", "DELETE", "GET");
                });
        });

        //...(其他代码略)

        app.UseCors();

        //...(其他代码略)
    }
}

React中的Axios请求:

import axios from 'axios';

const MyAPI = {

  async getToken() {
    const data = JSON.stringify({
      clientKey: process.env.REACT_APP_API_KEY,
      clientSecret: process.env.REACT_APP_API_SECRET
    });

    const config = {
      method: "post",
      maxBodyLength: Infinity,
      withCredentials: false,
      url: process.env.REACT_APP_API_BASE_URL + "/api/Authorize/token",
      headers: {
        "Content-Type": "application/json"
      },
      data: data
    };

    const response = await axios.request(config);
    return response.data.token;
  },
}

export default MyAPI;

编辑:

为了测试,我将builder Cors部分更改为以下内容(我知道这是不安全的,我认为这应该让任何东西都通过,然后我可以在离开我的开发环境之前将其调整):

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
        policy =>
        {
            policy.AllowAnyOrigin()
            .AllowAnyMethod()
            .AllowAnyHeader();
        });
});

我还将MyAllowSpecificOrigins添加到了app.UseCors(),但没有改变任何东西。

英文:

I'm working on a project using an ASP.NET Web API as the backend, and a create-react-app webpage as the frontend, and I can't get them to communicate anymore. No matter what I try, I constantly get CORS errors. They didn't seem to occur in live (i.e. with the API and Website both on IIS) but in development they refuse to work. It used to work a few days ago, but after making some changes to get it to work on IIS, it refuses to work locally using Kestrel anymore.

Every time I try to use a request from the React side (I'm using Axios.request for HTTP requests) I get Access to XMLHttpRequest at &#39;https://localhost:7046/api/&lt;insert route here&gt;&#39; from origin &#39;http://localhost:3000&#39; has been blocked by CORS policy: Response to preflight request doesn&#39;t pass access control check: No &#39;Access-Control-Allow-Origin&#39; header is present on the requested resource.

I tried using CORS Unblock from the Chrome Web Store in Microsoft Edge but this just changes it to Response to preflight request doesn&#39;t pass access control check: It does not have HTTP ok status. I know the route should be working as the exact same thing works using Postman, but in browser it just fails.

Program.cs:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using NLog;
using NLog.Web;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.EntityFrameworkCore;
using System.Configuration;

namespace MyNewAPI
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var logger = NLog.LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
            logger.Debug(&quot;API starting...&quot;);
            try
            {
                var MyAllowSpecificOrigins = &quot;_myAllowSpecificOrigins&quot;;

                var builder = WebApplication.CreateBuilder(args);
                builder.Services.AddCors(options =&gt;
                {
                    options.AddPolicy(name: &quot;_myAllowSpecificOrigins&quot;,
                        policy =&gt;
                        {
                            policy.WithOrigins(&quot;http://localhost:3000&quot;)
                                .WithMethods(&quot;PUT&quot;, &quot;DELETE&quot;, &quot;GET&quot;);
                        });
                });
                // Add services to the container.
                builder.Services.AddSqlServer&lt;Context1&gt;(builder.Configuration.GetConnectionString(&quot;DefaultConnection&quot;), o =&gt; o.CommandTimeout(3600));
                builder.Services.AddSqlServer&lt;Context2&gt;(builder.Configuration.GetConnectionString(&quot;AbelConnection&quot;), o =&gt; o.CommandTimeout(3600));
                builder.Services.AddMySql&lt;MySqlContext&gt;(builder.Configuration.GetConnectionString(&quot;MilogConnection&quot;), ServerVersion.AutoDetect(builder.Configuration.GetConnectionString(&quot;MilogConnection&quot;)));
                builder.Services.Configure&lt;DatabaseSettings&gt;(builder.Configuration.GetSection(&quot;MongoConnection&quot;));
                builder.Services.AddSingleton&lt;VMFarmService&gt;();
                builder.Services.AddControllers();
                // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
                builder.Services.AddEndpointsApiExplorer();
                builder.Services.AddSwaggerGen();

                builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =&gt;
                {
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuer = false,
                        ValidateAudience = false,
                        ValidateLifetime = true,
                        ValidateIssuerSigningKey = true,
                        ValidIssuer = builder.Configuration[&quot;Jwt:Issuer&quot;],
                        ValidAudience = builder.Configuration[&quot;Jwt:Audience&quot;],
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration[&quot;Jwt:SecretKey&quot;]))
                    };
                });

                builder.Logging.ClearProviders();
                builder.Host.UseNLog();


                var app = builder.Build();

                // Configure the HTTP request pipeline.
                if (app.Environment.IsDevelopment())
                {
                    app.UseSwagger();
                    app.UseSwaggerUI();
                }
                app.UseRouting();
                //app.UseHttpsRedirection();

                app.UseCors();
                app.UseAuthentication();

                app.UseAuthorization();


                app.MapControllers();

                app.Run();
            }
            catch (Exception exception)
            {
                // NLog: catch setup errors
                logger.Error(exception, &quot;Stopped program because of exception:&quot;);
                throw;
            }
            finally
            {
                // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
                NLog.LogManager.Shutdown();
            }
        }
    }
}

Axios request in react:

import axios from &#39;axios&#39;;

const MyAPI = {

  async getToken() {
    const data = JSON.stringify({
      clientKey: process.env.REACT_APP_API_KEY,
      clientSecret: process.env.REACT_APP_API_SECRET
    });

    const config = {
      method: &quot;post&quot;,
      maxBodyLength: Infinity,
      withCredentials: false,
      url: process.env.REACT_APP_API_BASE_URL + &quot;/api/Authorize/token&quot;,
      headers: {
        &quot;Content-Type&quot;: &quot;application/json&quot;
      },
      data: data
    };

    const response = await axios.request(config);
    return response.data.token;
  },
}

export default MyAPI;

EDIT:

For testing I changed the builder Cors section to the below (I know this is insecure, figure this should let anything through then I can tighten it down before moving off my dev environment):

var MyAllowSpecificOrigins = &quot;_myAllowSpecificOrigins&quot;;

                var builder = WebApplication.CreateBuilder(args);
                builder.Services.AddCors(options =&gt;
                {
                    options.AddPolicy(name: MyAllowSpecificOrigins,
                                      policy =&gt;
                                      {
                                          policy.AllowAnyOrigin()
                                          .AllowAnyMethod()
                                          .AllowAnyHeader();
                                      });
                });

I also added MyAllowSpecificOrigins to app.UseCors()but it made no difference.

答案1

得分: 0

你需要在 app.UseCors 中指定策略的名称。该方法有两个重载:string policyNameAction<CorsPolicyBuilder> configurePolicy

你可以将 app.UseCors() 更改为以下方式之一:

app.UseCors("_myAllowSpecificOrigins")

或者你可以应用 CORS 策略构建器:

app.UseCors(x => x.WithOrigins("http://localhost:3000")
               .AllowAnyMethod()
               .AllowAnyHeader());

当代码转移到生产环境时,你可以移除 CORS 配置,或者在开发和生产环境中根据需要进行设置:

if (isDev)
    app.UseCors("_myAllowSpecificOrigins");
英文:

You need to specify the name of the Policy in the app.UseCors. There are 2 overloads also for this method : string policyName and Action<CorsPolicyBuilder> configurePolicy

You can either change you app.UseCors() to

app.UseCors(&quot;_myAllowSpecificOrigins&quot;) 

or you can apply the cors policy builder

app.UseCors(x =&gt; x.WithOrigins(&quot;http://localhost:3000&quot;)
               .AllowAnyMethod()
               .AllowAnyHeader());

You can when the code moves to prod remove the Cors configuration or have a setting that can be changed when in development or in Production

if(isDev)
 app.UseCors(&quot;_myAllowSpecificOrigins&quot;) 

答案2

得分: 0

根据您的情境和配置,请在您的 launchsettings.json 文件中将以下属性设置为 true,以便根据需要响应预检请求。

"windowsAuthentication": true,
"anonymousAuthentication": true,

定义请求头:

headers: {
    "Content-Type": "application/json",
    "Access-Control-Allow-Origin": "*"
},

注意: 或者您可以这样设置 "Access-Control-Allow-Origin": "http://localhost:3000"

配置 CORS:

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
        builder =>
        {
            builder.WithOrigins("http://localhost:3000")
            .WithMethods("PUT", "DELETE", "GET");
        });
});

使用中间件引用:

app.UseCors("MyAllowSpecificOrigins")

注意: 请参考此官方文档以进行 CORS 中间件配置

英文:

Based on your scenario and configuration, please set following property to true within your launchsettings.json file in order to respond preflight request accordingly.

 &quot;windowsAuthentication&quot;: true,
 &quot;anonymousAuthentication&quot;: true,

Define Request Header:

  headers: {
        &quot;Content-Type&quot;: &quot;application/json&quot;,
        &quot;Access-Control-Allow-Origin&quot;: &quot;*&quot;
      },   

Note: Or you can set like this &quot;Access-Control-Allow-Origin&quot;: &quot;http://localhost:3000&quot;

Configure CORS:

var MyAllowSpecificOrigins = &quot;_myAllowSpecificOrigins&quot;;
    builder.Services.AddCors(options =&gt;
    {
        options.AddPolicy(name:MyAllowSpecificOrigins,
            builder =&gt;
            {
                builder.WithOrigins(&quot;http://localhost:3000&quot;)
                .WithMethods(&quot;PUT&quot;, &quot;DELETE&quot;, &quot;GET&quot;);
            });
    });

Use Middleware Reference:

app.UseCors(&quot;MyAllowSpecificOrigins&quot;)

Note: Please refer to this official document for CORS middleware configuration

答案3

得分: 0

感谢回答。

事实证明,问题一直都是由用户错误引起的。我在我的控制器中输入了错误的值,意味着它试图加载一个不存在的策略!

以防其他人遇到与我一样的问题,请确保在上述行的引号中,它与Program.cs中的策略名称匹配。

英文:

thanks for the answers.

Turns out all along the issue was due to user error. I'd entered the wrong value in the [EnableCors(&quot;_myAllowSpecificOrigins&quot;)] sections in my controllers meaning it was trying to load a policy that didn't exist!

Just in case anyone else runs into the same issue I did, make sure that in the speech marks in the above line that it matches the name of your policy in Program.cs.

huangapple
  • 本文由 发表于 2023年6月9日 13:00:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/76437336.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定