I'm building a web site with Angular and ASP.NET Core.
On some pages I want to get data from a Web API. When I run the app, the browser (Firefox) shows that
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at ...(the url) (Reason: missing token ‘authorization’ in CORS header ‘Access-Control-Allow-Headers’ from CORS preflight channel).
I tried other browsers, got the same error.
For authorization consideration, I use a HttpInterceptor
to insert an authorization header for each request from Angular frontend.
Then I looked into my ASP.NET Core backend. I set the CORS policy as app.UseCors(builder => { builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); });
, but it still doesn't work.
I tested the API with Postman, it works fine.
Where's going wrong?
The Startup.cs
file.
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.AddMvc().AddJsonOptions(
opt => opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
services.AddEntityFrameworkSqlServer();
services.AddCors();
services.AddSignalR();
services.AddDbContext<ApplicationDbContext>(opt =>
{
opt.UseSqlServer(Configuration.GetConnectionString("Remote"));
});
services.AddIdentity<ApplicationUser, IdentityRole>(opts =>
{
opts.Password.RequireDigit = true;
opts.Password.RequireLowercase = true;
opts.Password.RequireUppercase = true;
opts.Password.RequireNonAlphanumeric = false;
opts.Password.RequiredLength = 7;
}).AddEntityFrameworkStores<ApplicationDbContext>();
services.AddAuthentication(opts =>
{
opts.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
opts.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opts.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = Configuration["Auth:Jwt:Issuer"],
ValidAudience = Configuration["Auth:Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Auth:Jwt:Key"])),
ClockSkew = TimeSpan.Zero,
RequireExpirationTime = true,
ValidateIssuerSigningKey = true,
ValidateAudience = true
};
});
services.AddAuthorization(options =>
{
options.AddPolicy("NonUser", policy => policy.RequireRole("RestrauntOwner", "RestrauntAdmin", "SystemAdmin"));
});
}
// 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();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseCors(builder => { builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); });
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
app.UseSignalR(route =>
{
route.MapHub<OrderHub>("/orderhub");
});
app.UseCookiePolicy();
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
}
It's weird. I heve been developing this on my Windows PC for some time. I cloned the project on my MacBook, it worked fine without any problems on macOS.
If you can't quite get it working here's a couple tips:
If it's HTTP you must put HTTP, and HTTPS must be HTTPS.
Port number must be included too and correct.
http://localhost:5000
https://localhost:5001
So don't put a CORS rule for "http://localhost:5001"
if you're using the above settings because that isn't the same URL!
Look in the browser, or Fiddler to get it exactly right and make sure it is what you expect. If switching between HTTP and HTTPS it's very easy to get confused.
"authorization"
or "Authorization"
or "aUtHoRiZaTiOn"
if you want.WithMethods
or AllowAnyMethod
it won't work. This is very easy to miss - especially for just a GET request.For app.UseSpa
(if you're using Microsoft's hosting mechanism for SPA) you can do this
app.UseSpa(spa =>
{
// see https://go.microsoft.com/fwlink/?linkid=864501
// CORS just for the SPA
spa.ApplicationBuilder.UseCors(builder =>
{
// Must specify Methods
builder.WithMethods("GET");
// Case insensitive headers
builder.WithHeaders("AuthoriZatioN");
// Can supply a list or one by one, either is fine
builder.WithOrigins("http://localhost:5000");
builder.WithOrigins("https://localhost:5001");
});
Fiddler will show you the required conditions for CORS to succeed. Here it is authorization
header and GET
method.
You can hit R
on a previous request to rerun it after changing server configuration. That way you don't have to keep firing up your browser.
Make sure to look at Headers
in the response section because the actual content will be 0 bytes even when successful.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With