I am working with an Angular 2 app with asp.net core back end. I am trying to print a pdf (client side code below). When I run on our dev server, everything is ok; however, running on production I get
Failed to load api_url: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin url is therefore not allowed access.
Everything I have seen mentions something about CORS policy, but I don't understand how this could be working fine on one server, but not on another. Also, it appears to be retrieving fine when hitting other API endpoints.
Client-side api call:
getPDF(pickupId: string): void {
this.printingSub.next(true);
this._http.get(this._dataUrl + 'pickupsheet?pickupid=' + pickupId + '&barcode=true', { responseType: ResponseContentType.Blob })
.catch(error => this.handleError(error))
.subscribe((response: Response) => {
this.pdfBlob = new Blob([response.blob()], { type: 'application/pdf' });
const blobUrl = URL.createObjectURL(this.pdfBlob);
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = blobUrl;
document.body.appendChild(iframe);
iframe.contentWindow.print();
this.printingSub.next(false);
});
}
Startup.cs
public class Startup
{
public IConfiguration Configuration { get; }
public IConfigurationSection AppSettings { get; }
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile(#"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
AppSettings = Configuration.GetSection("appSettings");
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
ConfigureDatabase();
ConfigurePolicies(services);
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddOptions();
services.Configure<AppAccessSettings>(s =>
{
s.Env = AppSettings.GetSection("env").Value;
s.EnableAuth = bool.Parse(AppSettings.GetSection("enableAuth").Value);
});
services.AddMvc().AddJsonOptions(options =>
options.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver()
);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
// Configure JWT authentication
Authentication.SetVarFromFile(AppSettings.GetSection("authFile").Value);
Authentication.SetAuth(ref app, AppSettings.GetSection("audience").Value);
app.UseCors("CorsPolicy");
app.UseMvc();
}
private void ConfigureDatabase()
{
string dbSource = AppSettings.GetSection("env").Value;
OracleEnv.Connection = Configuration.GetSection("connectionStrings").GetSection(dbSource).Value;
}
private void ConfigurePolicies(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
services.AddAuthorization(options =>
{
options.AddPolicy("EnableAuth",
policy => policy.Requirements.Add(new AuthRequirement(Configuration)));
});
services.AddSingleton<IAuthorizationHandler, UserAuthHandler>();
}
}
private void ConfigurePolicies(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
services.AddAuthorization(options =>
{
options.AddPolicy("EnableAuth",
policy => policy.Requirements.Add(new AuthRequirement(Configuration)));
});
services.AddSingleton<IAuthorizationHandler, UserAuthHandler>();
}
Pickup Sheet API Method
[Route("PickupSheet")]
public IActionResult GetPickupSheet(string pickupId, bool barCode)
{
PbReportGenerator rpt = new PbReportGenerator();
byte[] report = rpt.RetrievePDFReport(232, new Dictionary<string, string>
{
{ pickupId, "string" },
{ (barCode ? 1 : 0).ToString(), "int" }
});
var stream = new MemoryStream(report);
var response = File(stream, "application/pdf", String.Format("Report232_{0}.pdf", pickupId));
return response;
}
You need to set withCredentials with each request:
this._http.get(URL, { responseType: ResponseContentType.Blob, withCredentials: true })
Related
Been following this tutorial in order to implement Google authentication in my web API but on the client side (using React and axios to do the request) the authentication process gets interrupted with this CORS issue and I'm struggling to sort it out:
Access to XMLHttpRequest at 'https://accounts.google.com/o/oauth2/v2/auth?(etc)' (redirected from 'https://localhost:44320/Photo/b997d788-3812-41d0-a09d-1a597eee9bad') from origin 'https://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
This is the Startup.cs file:
namespace rvc
{
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.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
});
});
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie(options =>
{
options.LoginPath = "/account/google-login";
}).AddGoogle(options =>
{
options.ClientId = "clientId";
options.ClientSecret = "secret";
});
services.AddScoped<PhotoService>();
services.AddScoped<TagService>();
services.AddScoped(_ => new BlobServiceClient(Configuration.GetConnectionString("AzureBlobStorage")));
services.AddDbContext<Data.DataContext>(x => x.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
});
services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "rvc", Version = "v1" }); });
}
// 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.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "rvc v1"));
}
app.UseHttpsRedirection();
if (env.IsProduction())
{
app.UseSpa(spa => { });
app.UseFileServer(new FileServerOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(env.ContentRootPath, "client")),
EnableDefaultFiles = true
});
}
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}
}
}
The Route("google-login") gets called but the Url.Action("GoogleResponse") is not reached. These are the Google Authentication methods:
namespace rvc.Controllers;
[AllowAnonymous, Route("account")]
public class AccountController : Controller
{
[Route("google-login")]
public IActionResult GoogleLogin()
{
var properties = new AuthenticationProperties {RedirectUri = Url.Action("GoogleResponse")};
return Challenge(properties, GoogleDefaults.AuthenticationScheme);
}
[Route("google-response")]
public async Task<IActionResult> GoogleResponse()
{
var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
var claims = result.Principal?.Identities.FirstOrDefault()
?.Claims.Select(claim => new
{
claim.Issuer,
claim.OriginalIssuer,
claim.Type,
claim.Value
});
return Json(claims);
}
}
This is probably because from the server you use redirect, which triggers CORS (even if from your server you allow it).
you have to return the redirect URL to your front-end in some other way, capture it from the front-end app and then call the URL you need to invoke.
I am trying to setup IdentityServer4 for the first time, and am following the steps in the docs for adding a JS client. I must have something configured incorrectly, but I can't figure out what it is. The flow is as follows:
User hits "login" on Client A
Client A sends login request to IS4 (upon debugging, the "returnUrl" parameter seems correct)
using the IS4 extension method I am signing the user in (HttpContext.SignInAsync)
user is redirected to "connect/authorize/callback" which redirects them to the login method again and the circular reference continues until the browser stops it and throws error.
Relevant code:
startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<UsersContext>(o =>
o.UseSqlServer(Configuration.GetConnectionString("UsersRuntime")));
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddIdentityServer(o =>
{
})
.AddTestUsers(Identity.Users.Get())
.AddConfigurationStore(o =>
{
o.ConfigureDbContext = b => b.UseSqlServer(Configuration.GetConnectionString("UsersRuntime"),
sql => sql.MigrationsAssembly(migrationsAssembly));
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = b => b.UseSqlServer(Configuration.GetConnectionString("UsersRuntime"),
sql => sql.MigrationsAssembly(migrationsAssembly));
})
.AddDeveloperSigningCredential();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
InitializeDatabase(app);
app.UseRouting();
app.UseDefaultFiles();
app.UseStaticFiles();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseHttpStatusCodeExceptionMiddleware();
}
else
{
app.UseHttpStatusCodeExceptionMiddleware();
app.UseHsts();
}
app.UseCors(MyAllowSpecificOrigins);
if (env.IsProduction())
{
app.UseHttpsRedirection();
}
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHealthChecks("/health");
});
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Users API");
c.RoutePrefix = string.Empty;
});
}
private void InitializeDatabase(IApplicationBuilder app)
{
using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
serviceScope.ServiceProvider.GetRequiredService<UsersContext>().Database.Migrate();
var persistedGrantDbContext =
serviceScope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>();
persistedGrantDbContext.Database.Migrate();
var configDb = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
configDb.Database.Migrate();
var testClient = new Client
{
ClientId = "TestClient",
RequireClientSecret = false,
AllowOfflineAccess = true,
AlwaysIncludeUserClaimsInIdToken = true,
AllowedIdentityTokenSigningAlgorithms = new List<string>{SecurityAlgorithms.RsaSha256},
UpdateAccessTokenClaimsOnRefresh = true,
RefreshTokenExpiration = (int)TokenExpiration.Sliding,
AllowedGrantTypes = new List<string>
{
IdentityServerConstants.PersistedGrantTypes.AuthorizationCode
},
AllowedScopes = new List<string>
{
"Read",
"Write"
},
AllowedCorsOrigins = new List<string>
{
"https://localhost:5003"
},
RedirectUris = new List<string>{"https://localhost:5003/callback.html"}
};
configDb.Clients.Add(testClient.ToEntity());
configDb.SaveChanges();
var resource = new ApiResource
{
Name = "TestApi",
ShowInDiscoveryDocument = true,
AllowedAccessTokenSigningAlgorithms = new List<string>{SecurityAlgorithms.RsaSha256},
Scopes = new List<string>
{
"Read",
"Write"
}
};
configDb.ApiResources.Add(resource.ToEntity());
var readScope = new ApiScope("Read");
var writeScope = new ApiScope("Write");
configDb.ApiScopes.AddRange(new []{readScope.ToEntity(), writeScope.ToEntity()});
configDb.SaveChanges();
}
}
login controller
[Route("account/login")]
[Produces("application/json")]
[ApiController]
public class LoginControllerOidc: ControllerBase
{
[HttpGet]
public async Task<IActionResult> Get(string returnUrl)
{
await HttpContext.SignInAsync(new IdentityServerUser("Test")
{
DisplayName = "Test Display Name",
AdditionalClaims = new List<Claim>
{
new Claim("additionalClaim", "claimValue")
}
});
return Redirect(returnUrl);
}
}
config for oidc-client.js
var config = {
authority: "https://localhost:5001",
client_id: "TestClient",
redirect_uri: "https://localhost:5003/callback.html",
response_type: "code",
scope:"Read Write",
post_logout_redirect_uri : "https://localhost:5003/index.html"
};
Redirect Issue Screenshot
I'm at a loss for what it left to do. Following the docs I think I have everything setup correctly. Guides I am following can be found Here (adding javascript client) and Here (sign in)
The cookie is being set correctly (I think) as seen here
The problem that I was having was due to the subjectId in the controller not matching a subjectId in the TestUsers.
I trying to add identity server for my web API as its identity server4 documentation. when I was trying to call API from my console application it's every time returns InternalServerError.
Here is My Identity server Config.cs
public static class Config
{
// register api
public static IEnumerable<ApiScope> ApiScopes => new List<ApiScope>
{
// in here add your api name
new ApiScope("api1", "My API")
};
// register client which is going to access api. eg: front-end application, mobile apps etc. can add multiple client.
public static IEnumerable<Client> Clients => new List<Client>
{
new Client
{
// which is going to access
ClientId = "client",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ClientCredentials,
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},
// scopes that client has access to
AllowedScopes = { "api1" }
}
};
}
and here the identity server startup file configuration service and configure functions
public void ConfigureServices(IServiceCollection services)
{
// uncomment, if you want to add an MVC-based UI
services.AddControllersWithViews();
var builder = services.AddIdentityServer()
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients);
builder.AddDeveloperSigningCredential();
builder.AddDeveloperSigningCredential();
}
public void Configure(IApplicationBuilder app)
{
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// uncomment if you want to add MVC
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
// uncomment, if you want to add MVC
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
and here is my API startup file's congurationService and configure functions
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://localhost:14030/";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};
}
);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
here is my API controller
[Route("identity")]
public class IdentityController : ControllerBase
{
[HttpGet]
[Authorize]
public IActionResult Get() => Ok(new JsonResult(from c in User.Claims select new { c.Type, c.Value }));
}
and here is my console application client request a api
static async System.Threading.Tasks.Task Main(string[] args)
{
// discover endpoints from metadata
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("http://localhost:14030");
if (disco.IsError)
{
Console.WriteLine(disco.Error);
return;
}
// request token
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "client",
ClientSecret = "secret",
Scope = "api1"
});
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
return;
}
Console.WriteLine(tokenResponse.Json);
Console.WriteLine("\n\n");
// call api
var apiClient = new HttpClient();
apiClient.SetBearerToken(tokenResponse.AccessToken);
var response = await apiClient.GetAsync("https://localhost:5001/identity");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
}
else
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
}
}
what are the mistakes should I have to fix. Im really appreciates your valuable answers and efforts.
Thank
I got the code working, I would do the following:
use HTTPS here, not HTTP:
var disco = await
client.GetDiscoveryDocumentAsync("http://localhost:14030");
Remove the duplicate lines of in IdentityServer startup class:
builder.AddDeveloperSigningCredential();
I would add in your API startup.cs
services.AddAuthorization();
Remove the trailing / at the end of the URL here:
options.Authority = "https://localhost:14030/";
To get more debugging output from your API, you can add the following two trace lines to your appsettings.Development.json file:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.Authentication": "Trace",
"Microsoft.AspNetCore.Authorization": "Trace"
}
}
}
If you want to validate the Audience (and using IdentityServer4 v4.00) you can add:
services.AddControllers();
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://localhost:14030";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidAudiences = new[] {"https://localhost:14030/resources"},
ValidateAudience = true
};
}
);
Complete error is as shown below.
Access to XMLHttpRequest at 'https://localhost:44361/connect/token' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I have enabled cores policy in my start up class in .net core web api as shown below.
My ConfigureService method
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddDbContext<LEAFDDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), builder =>
{
builder.EnableRetryOnFailure(5, TimeSpan.FromSeconds(10), null);
}));
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<LEAFDDbContext>()
.AddDefaultTokenProviders();
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
Installer.ConfigureServices(services);
//services.AddCors();
//services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
//services.AddSingleton<IAuthenticationSchemeProvider, CustomAuthenticationSchemeProvider>();
//services.AddAuthentication(options =>
//{
// //options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
// //options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
// //options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
// options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
// options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
//})
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Events.OnRedirectToLogin = ctx =>
{
// if it is an ajax/api request, don't redirect
// to login page.
if (!(IsAjaxRequest(ctx.Request) || IsApiRequest(ctx.Request)))
{
ctx.Response.Redirect(ctx.RedirectUri);
return Task.CompletedTask;
}
ctx.Response.StatusCode = StatusCodes.Status401Unauthorized;
return ctx.Response.WriteAsync("Unauthorized");
};
})
.AddOAuthValidation()
//services.AddAuthentication()
.AddOpenIdConnectServer(options =>
{
options.Provider = new AuthorizationProvider();
// Enable the authorization and token endpoints.
options.AuthorizationEndpointPath = "/connect/authorize";
options.TokenEndpointPath = "/connect/token";
options.AllowInsecureHttp = true;
// Note: to override the default access token format and use JWT, assign AccessTokenHandler:
//
options.AccessTokenHandler = new JwtSecurityTokenHandler
{
InboundClaimTypeMap = new Dictionary<string, string>(),
OutboundClaimTypeMap = new Dictionary<string, string>()
};
//
// Note: when using JWT as the access token format, you have to register a signing key.
//
// You can register a new ephemeral key, that is discarded when the application shuts down.
// Tokens signed using this key are automatically invalidated and thus this method
// should only be used during development:
//
options.SigningCredentials.AddEphemeralKey();
//
// On production, using a X.509 certificate stored in the machine store is recommended.
// You can generate a self-signed certificate using Pluralsight's self-cert utility:
// https://s3.amazonaws.com/pluralsight-free/keith-brown/samples/SelfCert.zip
//
//options.SigningCredentials.AddCertificate("7D2A741FE34CC2C7369237A5F2078988E17A6A75");
});
}
Configure method
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
//app.UseOAuthValidation();
app.UseAuthentication();
app.UseMvc();
app.UseMiddleware();
app.UseCors("CorsPolicy");
//app.UseCors(builder => builder.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().AllowCredentials());
app.UseWelcomePage();
}
This is working as expected for all other calls except 'https://localhost:44361/connect/token' : When getting the token.
I'm sending all the requests from Angular 7 web app to .net core web api.
As a work around i have installed cross-origin resource sharing extension from google chrome and I need a code level change to fix this issue permanently.
This is the final solution i came up with.
Instead of calling "https://localhost:44361/connect/token" directly from Angular 7 app, I have created a method in API controller that will invoke above call and get the token. So when ever i need all i need is to call the newly created method in API controller.
New api call as follows
[HttpPost]
[Route("/api/[controller]/Token")]
[EnableCors("CorsPolicy")]
public async Task<ContentResult> Token(LoginTokenRequestModel model)
{
string UserId = string.Empty;
try
{
using (HttpClient httpClient = new HttpClient())
{
if (!String.IsNullOrEmpty(model.Email))
{
var content = new FormUrlEncodedContent(new KeyValuePair<string, string>[]{
new KeyValuePair<string, string>("grant_type", model.grant_type),
new KeyValuePair<string, string>("client_secret", model.client_secret),
new KeyValuePair<string, string>("client_id", model.client_id),
new KeyValuePair<string, string>("username", model.Email),
new KeyValuePair<string, string>("password", model.Password),
});
string apiURL = "https://localhost:44361";
var response = await httpClient.PostAsync(apiURL + "/connect/token", content);
var resultContent = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
var token = JsonConvert.DeserializeObject<object>(resultContent);
return new ContentResult
{
Content = token.ToString(),
ContentType = "text/plain",
StatusCode = 200
};
}
return new ContentResult
{
Content = JsonConvert.DeserializeObject<object>(resultContent).ToString(),
ContentType = "text/plain",
StatusCode = 200
};
}
}
}
catch (Exception ex)
{
return new ContentResult
{
Content = ex.Message,
ContentType = "text/plain",
StatusCode = 400
};
}
return new ContentResult
{
Content = "",
ContentType = "text/plain",
StatusCode = 400
};
}
This resolved my issue.
Use app.UseCors() before app.useMvc(). Order matters when it comes to middleware invocation. Personally, I keep it in environment determining if-else.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseCors("CorsDevPolicy");
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMiddleware(typeof(ErrorHandlingMiddleware));
app.UseMvc();
}
I am using browserlink to edit CSS styles and it works pretty nice. Unfortunately, if I change something in .cshtml files, my browser automatically refresh on save, but changes are not visible.
If I close my application and open again, changes are visible.
It seems like my application is caching views somehow somewhere and not reloading changes I do into my files.
This is really not a browser caching problem. The application really sends unchanged html result.
How can I disable such a caching feature in development?
I am using latest ASP NET Core MVC libraries.
EDIT:
if I change anything in _layout, the website is updated without any problem.
EDIT 2: Startup functions
public void ConfigureServices(IServiceCollection services)
{
services.AddWebSocketManager();
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var path = System.AppDomain.CurrentDomain.BaseDirectory;
var machineName = Environment.MachineName;
var confBuilder = new ConfigurationBuilder();
IConfigurationBuilder conf = confBuilder.SetBasePath(path);
if (env == "Development")
{
conf = conf.AddJsonFile($"appsettings.json", optional: true, reloadOnChange: true);
conf = conf.AddJsonFile($"appsettings.Development.json", optional: true, reloadOnChange: true);
conf = conf.AddJsonFile($"appsettings.{machineName}.json", optional: true, reloadOnChange: true);
conf = conf.AddJsonFile($"appsettings.External.json", optional: true, reloadOnChange: true);
}
else
{
conf = conf.AddJsonFile($"appsettings.json", optional: true, reloadOnChange: true);
conf = conf.AddJsonFile($"appsettings.Production.json", optional: true, reloadOnChange: true);
conf = conf.AddJsonFile($"appsettings.External.json", optional: true, reloadOnChange: true);
}
Configuration = conf.Build();
services.AddSingleton(provider => Configuration);
CoreStarter.OnServiceConfiguration?.Invoke(Configuration, services);
var settings = new JsonSerializerSettings();
settings.ContractResolver = new SignalRContractResolver();
var serializer = JsonSerializer.Create(settings);
services.Add(new ServiceDescriptor(typeof(JsonSerializer),provider => serializer,ServiceLifetime.Transient));
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.TryAddSingleton<IContextService, ContextService>();
services.TryAddSingleton<ICryptoService, CryptoService>();
services.TryAddSingleton<IAuthorizationHandler, AuthenticationHandler>();
services.TryAddSingleton<IHttpService, RestApiService>();
if (services.Any(x => x.ServiceType == typeof(IIdentityService)))
{
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
{
options.Events.OnRedirectToLogin = (context) =>
{
context.Response.StatusCode = 401;
return Task.CompletedTask;
};
options.Events.OnRedirectToAccessDenied = (context) =>
{
context.Response.StatusCode = 401;
return Task.CompletedTask;
};
var sharedCookiePath = Configuration.GetJsonKey<string>("SharedCookiePath");
if (!String.IsNullOrWhiteSpace(sharedCookiePath))
{
options.DataProtectionProvider = DataProtectionProvider.Create(new DirectoryInfo(sharedCookiePath));
}
});
}
services.AddCors();
services.AddLogging(builder =>
{
builder.AddConsole().AddDebug();
});
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info {Title = "CoreR API", Version = "v1"});
});
services.AddDistributedMemoryCache();
services.AddSession();
services.AddMemoryCache();
services.AddSingleton<IAssemblyLocator, BaseAssemblyLocator>();
services.AddSignalR(options =>
{
options.Hubs.EnableDetailedErrors = true;
});
var mvcBuilder = services.AddMvc(config =>
{
if (services.Any(x => x.ServiceType == typeof(IIdentityService)))
{
var policyBuilder = new AuthorizationPolicyBuilder();
policyBuilder.RequireAuthenticatedUser();
policyBuilder.AddRequirements(new AuthenticationRequirement());
var policy = policyBuilder.Build();
config.Filters.Add(new AuthorizeFilter(policy));
}
})
.AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
});
var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
foreach (var assembly in assemblies)
{
mvcBuilder.AddApplicationPart(assembly);
}
ServiceProvider = services.BuildServiceProvider();
}
public void Configure(IApplicationBuilder app, IServiceProvider serviceProvider, IHostingEnvironment env)
{
var embeddedProvider = new EmbeddedFileProvider(Assembly.GetExecutingAssembly());
var physicalProvider = env.ContentRootFileProvider;
var compositeProvider = new CompositeFileProvider(physicalProvider, embeddedProvider);
app.UseCors(o => o.AllowAnyOrigin().AllowCredentials().AllowAnyMethod().AllowAnyHeader());
app.UseAuthentication();
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
app.UseDefaultFiles(new DefaultFilesOptions()
{
FileProvider = compositeProvider,
DefaultFileNames = new List<string>() { "default.html"},
});
app.UseStaticFiles(new StaticFileOptions {FileProvider = compositeProvider});
app.UseSession();
app.UseWebSockets();
app.UseSignalR();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "CoreR API");
});
app.UseMvc(routes =>
{
routes.MapRoute("Default", "api/{controller}/{action}/{id?}");
});
CoreStarter.OnConfiguration?.Invoke(Configuration, app, serviceProvider, env);
}
according to Razor file compilation in ASP.NET Core If you want enable run time compilation only for local development:
1.Conditionally reference the Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation package based on the active Configuration value:
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.0" Condition="'$(Configuration)' == 'Debug'" />
2. Update the project's Startup.cs file ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
IMvcBuilder builder = services.AddRazorPages();
if (env.IsDevelopment())
{
builder.AddRazorRuntimeCompilation();
}
}