.NET Core 2 Web API: CORS issues

2020-05-07 04:08发布

问题:

So I have a front-end Angular app where I fill in a small form and want to send the data to my Web API which also runs on localhost. My Angular app runs on http://localhost:4200/ and my web api runs on https://localhost:44302/api/input .

On form submit, the following method(s) will be called:

public uploadFormData(): void {
    const form = $('#inputForm')[0];
    const files = ($('#fileUpload') as any);
    const formData = new FormData();
    const file = files[0];

    formData.append('tenant', form[0].value);
    formData.append('title', form[1].value);
    formData.append('file', file, file.name);
    this.uploadService.uploadForm(formData)
      .subscribe(res => this.fileUploaded(res));
  }

  private fileUploaded(data: any): void {
    $('#message').html('Response: 200 OK !');
  }  

This will go to my service, as seen here:

public uploadForm(formdata: any) {
    const url = 'https://localhost:44302/api/input';
    const headers = new Headers({'encrypt': 'multipart/form-data'});
    const options = new RequestOptions({ headers: headers });

    return this.http.post(url, formdata, options)
      .catch(this.errorHandler);
  }

  private errorHandler(error: Response) {
    return Observable.throw(error || 'Some error on server occured: ' + error);
  }  

If I remove the headers & options (and also the options from my http.post), the end result is the same: a 500 server error will be thrown saying

"No 'Access-Control-Allow-Origin' header is present on the requested 
resource. Origin 'http://localhost:4200' is therefore not allowed access. 
The response had HTTP status code 500."  

So on my server-side I have a .NET Core 2 Web API running on localhost. Here's my Startcup.cs file so you can see I have configured CORS:

// This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication(sharedOptions =>
            {
                sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddAzureAdBearer(options => Configuration.Bind("AzureAd", options));

            services.AddMvc();

            services.AddCors();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseCors(builder =>
                builder.WithOrigins("http://localhost:4200")
                //builder.AllowAnyOrigin()
                .AllowAnyMethod()
                .AllowAnyHeader());

            app.UseAuthentication();
            app.UseMvc();
        }  

Here you can find the code for my controller that will accept the input:

// POST api/input
    [HttpPost]
    public IActionResult Post(FormCollection data)
    {
        var inputdata = data;
        //var test = JsonConvert.DeserializeObject<SPInputObject>(data);

        return Ok("POST all good");
    }  

I don't even know if the "FormCollection data" is the correct parameter, because I can't get into my web api due to those CORS issues.

So can anyone see what might be the problem?

EDIT: here you can see what the response/request headers are in Chrome dev tools BEFORE the server 500 error:

Here's what I see AFTER the 500 server error:

回答1:

I think you need to mention the app.UseCors() before app.UseMvc() line code, per this blog post.

To be precise:

services.AddCors(feature => {
                feature.AddPolicy(
                    "GlobalCorsPolicy",
                    builder => builder
                                    .SetIsOriginAllowed((host) => true)
                                    .AllowAnyHeader()
                                    .AllowAnyMethod()
                                    .AllowAnyOrigin()
                                    .AllowCredentials()
                                );
            });

Out of the above, this piece of line really helped at major cost.

.SetIsOriginAllowed((host) => true)

And then, you have to explicitly set the EnableCors("GlobalCorsPolicy")] in your controller as decorative attribute. There are discussions and arguments around why to add if we are adding globally but my solution was practically solving the issue.

And without adding any browser Allow-Control-Allow-Origin plugin, if you add these in the code, it should work.

After total hell lotta attempts, i was able to get it through.

This post also helped me. I am sure this would get resolved if you follow the steps, and with few iterations on your own for your learning/understand how CORS works and to use in .net core. It for sure is different than how easier it was in .net web api (fx).



回答2:

You need to install a nuget package: "Microsoft.AspNet.WebApi.Cors", and add it to the class startup:

            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }

            public IConfiguration Configuration { get; }

            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddMvc();
                services.AddCors(options => options.AddPolicy("AllowAll", builder =>
                {
                    builder.AllowAnyOrigin();
                    builder.AllowAnyHeader();
                    builder.AllowAnyMethod();
                    builder.AllowCredentials();
                }));
            }

            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IHostingEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
                app.UseCors("AllowAll"); //<===
                app.UseMvc();
            }

Add to you controller

[EnableCors(origins: "http://localhost:4200", headers: "*", methods: "*")]

In your headers (angular):

              const httpOptions = {
                headers: new HttpHeaders({
                  'Access-Control-Allow-Origin': '*',
                  'Access-Control-Allow-Headers': 'Content-Type, Origin , Access-Control-* , X-Requested-With, Accept',
                  'Content-Type':  'application/json,charset=utf-8',
                  'Accept': 'application/json',
                  'Allow' : 'GET, POST, PUT, DELETE, OPTIONS, HEAD'
                })
              };

And finally install Allow-Control-Allow-Origin in your browser