Marc Rasmussen
Marc Rasmussen

Reputation: 20565

.net core api CORS Get works but cors gets 405

I have the following middleware:

    namespace TimeManagement
{
    public class CorsMiddleware
    {
        private readonly RequestDelegate _next;

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

        public Task Invoke(HttpContext httpContext)
        {
            httpContext.Response.Headers.Add("Access-Control-Allow-Origin", "*");
            httpContext.Response.Headers.Add("Access-Control-Allow-Credentials", "true");
            httpContext.Response.Headers.Add("Access-Control-Allow-Headers",
                "Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name");
            httpContext.Response.Headers.Add("Access-Control-Allow-Methods", "POST,GET,PUT,PATCH,DELETE,OPTIONS");
            return _next(httpContext);
        }
    }

// Extension method used to add the middleware to the HTTP request pipeline.
    public static class CorsMiddlewareExtensions
    {
        public static IApplicationBuilder UseCorsMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<CorsMiddleware>();
        }
    }
}

And the following startup class:

namespace TimeManagement
{
    public 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.AddDbContext<WorkTimeContext>(opt =>
                opt.UseInMemoryDatabase("WorkTime"));
            services.AddDbContext<TimeManagementContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("TimeManagementContext")));

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
            services.AddControllers();
        }

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

            app.UseHttpsRedirection();
            app.UseCorsMiddleware();
            app.UseRouting();
            app.UseAuthorization();

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

Then I attempt to run https://localhost:5001/api/WorkTimes GET and it returns without issues.

Now I am using an Angular frontend and from there I am trying to post. As you may know, it sends an OPTIONS first and here I get a CORS error:

enter image description here

Upvotes: 2

Views: 3481

Answers (5)

Binara Thambugala
Binara Thambugala

Reputation: 776

In here ,

https://localhost:5001/api/WorkTimes

is used by the server to return specific data set when valid credentials are sent via a GET HTTP method request.Therefore, in such a scenario, it makes no sense for the server to accept a POST request at that resource/URL, so it may respond with a 405(Method Not Allowed) status code when such a request is made.

The 405(Method Not Allowed) is an HTTP response status code indicating that the specified request HTTP method was received and recognized by the server, but the server has rejected that particular method for the requested resource.A 405 code response confirms that the requested resource is valid and exists, but the client has used an unacceptable HTTP method during the request.

This could happen in a few different circumstances:

  • The user agent is accidentally sending an incorrect HTTP method.
  • The server is expecting only a handful of valid HTTP methods for the requested resource.

Use the ASP.NET CORS middleware as below instead of custom middleware.(Asp.Net Core v3.0 sample code)

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
     if(env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();

        app.UseCors(cor=> cor
           .AllowAnyHeader()
           .WithOrigins("http://localhost:4200","https://localhost:4200"));

        app.UseRouting();
        app.UseAuthorization();

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

Upvotes: 1

Lutti Coelho
Lutti Coelho

Reputation: 2264

Cors errors can be very trick. Sometimes the browser returns this erros without really call your api. So first step you need to be sure that the browser call your API. To do this I usually add a dumb inline middleware and put a breakpoint in it. You could add a logger to it too.

The dumb middleware sample:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.Use(async (context, next) =>
    {
        await next.Invoke();
        // Do logging or other work that doesn't write to the Response.
    });

    // The others middlewares.
}

Now that you know if the problem is in your browser or in your app you can what can you do?

1) If the problem is in your browser, follow the instruction from @Eldho answer to enable it.

2) If the problem is in your app please read the rest of my answer.

Middlewares are executed in the same sequence you call it in Configure method.

Maybe HttpsRedirection is returning this error. (Big maybe here)

You can try declare your custom app.UseCorsMiddleware() before HttpsRedirection. But I suggest you to use Asp.net Cors Middlewares that already exists and works fine. Why reinvent the wheel?

This is a sample of Asp.Net Core v2.1 Cors Middleware

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy("Any", 
            builder => 
            {
                builder
                    .AllowAnyOrigin()
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials();
            });
    });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseCors("Any");
    // The others middlewares.
}

Another approach (only for development) is to use SPA Middleware to forward the requests your SPA. This way your angular app and your Asp.Net app will answer on the same port of localhost and chrome will not block any. But it just work as temporary solution. It is not recommended to use this in production.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseCors("Any");

    // The others middlewares.

    if (env.IsDevelopment())
    {
        app.UseSpa(spa =>
        {
            spa.UseProxyToSpaDevelopmentServer("http://localhost:4200");
        });
    }
}

Upvotes: 0

Eldho
Eldho

Reputation: 8301

chrome doesn't support this.

​Access to XMLHttpRequest at '...' from origin '...' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

Solution 1 [Temporary Development Solution]

chrome://flags/#out-of-blink-cors 

Disable the out-of-blink-cors flag by copying and pasting that address into the address bar of Chrome followed by [enter]. In the page that opens you'll see a highlighted so-called 'flag' (experiment). Disable it and restart Chrome (Edit: previously I said to enable the flag but only disabling seems to work)

In many cases that solves the issue. You can upvote this feature on chrome here

Solution 2 (Recommended)

Create your api under in the sub-domain

your api url will be http://localhost:4200/api/YourEndpoint

Here
Voyage is our angular application.
Under it Api where we host our API so which will be under a same domain so CORS policy is not violated.
IIS Setup

Upvotes: 0

Kahbazi
Kahbazi

Reputation: 15015

You need to send status code 200 for CORS preflight request, which your middleware is not setting right now.

Why don't you just use the ASP.NET CORS middleware which handles this? ASP.NET CORS Middleware

Upvotes: 2

Gauravsa
Gauravsa

Reputation: 6528

Can you try putting the following in web.config:

<httpHandlers>
  ... 
    <add path="*" verb="OPTIONS" type="System.Web.DefaultHttpHandler" validate="true"/>
</httpHandlers>

Upvotes: 0

Related Questions