Sunny
Sunny

Reputation: 932

Problem in enabling CORS in asp net core web api v3.0

I am using asp net core 3.0 in my web API project. I have created various API's and all are accessible via Swagger or Postman. But when trying to access the same via any other client like React, Method not allowed (405 error code) is received. On investing further, I find out that at first, OPTION request is received from the React application and the net core web API application is giving the 405 status code. Further, I find out that I need to enable all the methods as well as origins from the net core application to accept all types of requests otherwise it will not accept OPTION request. To achieve this, I enabled CORS policy in startup.cs file but still had no luck. Following is my startup.cs file:

public class Startup
{
    public Startup(IConfiguration configuration)
    {

        Configuration = configuration;

        var elasticUri = Configuration["ElasticConfiguration:Uri"];

        Log.Logger = new LoggerConfiguration()
            .Enrich.FromLogContext()
            .Enrich.WithExceptionDetails()
            .WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri(elasticUri))
            {
                MinimumLogEventLevel = LogEventLevel.Verbose,
                AutoRegisterTemplate = true,
            })
        .CreateLogger();


    }

    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.Configure<IISServerOptions>(options =>
        {
            options.AutomaticAuthentication = false;
        });
        services.Configure<ApiBehaviorOptions>(options =>
        {
            //To handle ModelState Errors manually as ApiController attribute handles those automatically
            //and return its own response.
            options.SuppressModelStateInvalidFilter = true;
        });

       services.AddCors(options =>
        {
            options.AddPolicy("CorsPolicy",
                builder => builder.AllowAnyOrigin()
                .AllowAnyMethod()
                .AllowAnyHeader());
        });

        services.AddControllers(options =>
        {
            //To accept browser headers.
            options.RespectBrowserAcceptHeader = true;
        }).
         AddNewtonsoftJson(options =>
         {
             // Use the default property (Pascal) casing
             options.SerializerSettings.ContractResolver = new DefaultContractResolver();
             options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;

         }).
        AddJsonOptions(options =>
         {
             //Not applying any property naming policy
             options.JsonSerializerOptions.PropertyNamingPolicy = null;
             options.JsonSerializerOptions.IgnoreNullValues = true;

         }).
        AddXmlSerializerFormatters().
        AddXmlDataContractSerializerFormatters();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
    {
        app.UseCors("CorsPolicy");

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }      

        // Enable middleware to serve generated Swagger as a JSON endpoint.
        app.UseSwagger();

        // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
        // specifying the Swagger JSON endpoint.
        app.UseSwaggerUI(c =>
        {
            c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
        });

        app.UseRouting();

        app.UseAuthentication();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });

        //Configuring serilog
        loggerFactory.AddSerilog();

    }
}

I tried testing the same API with the OPTIONS method from POSTMAN. It is also giving the Http Status Code as 405. But when trying to access the same request using the POST method, I received the response successfully.

Is there anything wrong with the above code or something wrong with the order of middlewares being called in Configure().

Upvotes: 1

Views: 806

Answers (3)

user20108629
user20108629

Reputation:

Try this:

app.UseCors(policy =>
policy.WithOrigins("https://localhost:PORT", "https://localhost:PORT")
.AllowAnyMethod()
.WithHeaders(HeaderNames.ContentType)

);

Upvotes: 0

Khabir
Khabir

Reputation: 5844

You need to add Cors in Startup.cs file under your web api project

  1. add this variable in Startup.cs

readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

  1. add services.AddCors before services.AddControllers() in the method ConfigureServices in file Startup.cs:
services.AddCors(options =>
{
    options.AddPolicy(MyAllowSpecificOrigins,
    builder =>
    {
       builder.WithOrigins("http://localhost:4000",
       "http://www.yourdomain.com")
       .AllowAnyHeader()
       .AllowAnyMethod();
    });
});

services.AddControllers();

*** You can pass only * to allow all instead of passing http://localhost:4000","http://www.yourdomain.com in the WithOrigins method

  1. add app.UseCors before app.UseAuthentication() in the method Configure in file Startup.cs:

app.UseCors(MyAllowSpecificOrigins);

Check this Microsoft help

Upvotes: 0

Arzu Suleymanov
Arzu Suleymanov

Reputation: 691

Try to add extension method and modifying your startup class:

Extension method:

public static void AddApplicationError(this HttpResponse response, string 
    message)
{
    response.Headers.Add("Application-Error", message);
    response.Headers.Add("Access-Control-Expose-Headers", "Application-Error");
    response.Headers.Add("Access-Control-Allow-Origin", "*");
}

Startup.cs :

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler(builder =>
            {
                builder.Run(async context =>
                {
                    context.Response.StatusCode = (int) 
                 HttpStatusCode.InternalServerError;

                    var error = context.Features.Get<IExceptionHandlerFeature>();
                    if (error != null)
                    {
                        context.Response.AddApplicationError(error.Error.Message);
                        await context.Response.WriteAsync(error.Error.Message);
                    }
                });
            });
        }

P.S. in my case I had scenario also returning 405 status error, cause was, similar action methods I used and there are conflicted

For ex:

    [HttpGet]
    public ActionResult GetAllEmployees()

    [HttpGet]
    public ActionResult GetCustomers()

Hope this will help at least to show exact error message

Upvotes: 1

Related Questions