ASP.Net core 3.0 (3.1) set PathBase for single page application - asp.net-core

I have the following asp.net core spa application configured (react-redux template)
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UsePathBase(new PathString("/foo"));
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
}
I’d like to set pathBase for application, but app.UsePathBase(new PathString("/foo")) just ignored. On 2.2 it perfectly worked. Automatically modified index.html and all static files were moved to relative path. But on 3.0 (3.1) static + generated files are placed on root.
Generated files on .Net Core 2.2
Generated files on .Net Core 3.0
Does anyone have any ideas for solving it? Or may be some examples of Startup.cs with working pathBase?

Usually, app.UsePathBase(new PathString("/foo")); is used because the reverse proxy cuts off some prefix and causes ASP.NET Core app doesn't realize the virtual app path is start with /foo. In your scenario, if you don't have a reverse proxy that rewrite the prefix to empty string, you don't need app.UsePathBase(...).
Instead, if you your spa runs on a subpath, you could setup a middleware that branches the /foo.
Finally, you might want to add a property of homepage in your package.json so that it will generate the <base url="/foo/"/> when publishing. Or as an alternative, you could update the <base url=""> in ClientApp/public/index.html manually.
In short, add a "homepage": "/foo/" in your package.json
"private": true,
"homepage": "/foo/",
"dependencies": {
...
}
And setup a /foo branch to make SPA runs under that path:
string spaPath = "/foo";
app.Map(spaPath,appBuilder =>{
appBuilder.UseSpa(spa =>
{
spa.Options.DefaultPage = spaPath+"/index.html";
spa.Options.DefaultPageStaticFileOptions = new StaticFileOptions{
RequestPath = spaPath,
};
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
});
Don't use app.UsePathBase(new PathString("/foo")) unless you understand you do want to override the path base for all routes.

Related

ASP.NET Core MVC : make file available on server

I'm new to .NET Core and I'm working on an ASP.NET Core MVC app.
I'm trying to make available a file that was generated from backend. The physical path is (root) Cards/id/index.html. My problem is that when I execute my web app, I cannot browse that file.
For example:
localhost:Card/id/index.html
I know that i have to add something to my middleware but I can't fix it.
Here is my startup.cs code
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), #"cards")),
RequestPath = new PathString("/personal-cards")
});
app.UseRouting();
app.UseEndpoints(x => {
x.MapRazorPages();
x.MapControllerRoute(name:"Default",
pattern: "{Controller}/{action}/{id?}",
defaults: new {controller = "Home", Action="Index"});
});
}
Can you help me?
You don't need write below code.
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), #"cards")),
RequestPath = new PathString("/personal-cards")
});
You just need to put your cards folder under the wwwroot folder. Then your can access the file directly, you just need to make sure the url is correct.

How to set the redirect URI when using Microsoft sign-in in a .NET 5 application?

I have created a .NET 5 application with Microsoft sign-in based on this explanation.
It is working fine when running locally. However, something is going wrong when running the application in Amazon EKS. This became clear to me after reading error message I saw in the browser and after reading the network traffic.
This is how this looks like.
What becomes clear is that there is something wrong with "redirect_uri" (containing http instead of https). This is really frustrating as my application is using https. I use https when opening the application in my browser. It is important to mention that this does not occur when running the application locally on my laptop. What I hope for is that there is a simple way to set the "redirect_uri" property that is used in my code. In this way, I can guarantee that the right redirect uri is used.
Here is the source code I would like to change:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
var configSettings = new ConfigSettings();
Configuration.Bind("ConfigSettings", configSettings);
services.AddSingleton(configSettings);
services.AddSingleton<IAuthResponseFactory, AuthResponseFactory>();
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"));
services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
services.AddRazorPages()
.AddMicrosoftIdentityUI();
services.AddHealthChecks();
services.Configure<HealthCheckPublisherOptions>(options =>
{
options.Delay = TimeSpan.FromSeconds(2);
options.Predicate = (check) => check.Tags.Contains("ready");
});
}
// 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();
}
else
{
app.UseExceptionHandler("/Home/Error");
// 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.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health/ready", new HealthCheckOptions()
{
Predicate = (check) => check.Tags.Contains("ready")
});
endpoints.MapHealthChecks("/health/live", new HealthCheckOptions());
});
}
So how do I change my source in a way that I can set the redirect uri correctly?
Looks like you need to enable header forwarding.
Step 1: configure the ForwardedHeadersOptions
services.Configure<ForwardedHeadersOptions>(options =>
{
options.RequireHeaderSymmetry = false;
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
// TODO : it's a bit unsafe to allow all Networks and Proxies...
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
Step 2: UseForwardedHeaders in the public void Configure(IApplicationBuilder app, IHostingEnvironment env) method
app.UseForwardedHeaders();
Step 3: Only use UseHttpsRedirection for production
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
// Forward http to https (only needed for local development because the Azure Linux App Service already enforces https)
app.UseHttpsRedirection();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
See How to set redirect_uri protocol to HTTPS in Azure Web Apps and .net Core X Forwarded Proto not working

Swagger and .Net COre cannot find custom.css

I've built an API project with .Net Core 3.0, adding Swagger 5.rc4 to show API documentation.
I want to customize CSS so I've added in the startup.Configure:
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Geo API");
c.RoutePrefix = "docs";
c.DocumentTitle = "GeoData APIs";
c.DisplayRequestDuration();
c.EnableFilter();
c.InjectStylesheet("/swagger-ui/custom.css");
});
I can see the swagger page at localhost:8888/docs/index.html but I'm not able to make it load the custom.css
In my project, I've created a folder "swagger-ui" containing the file custom.css, and set to be copied to the output directory. I can correctly see it, inside the swagger-ui folder, in the bin folder when I compile but no way to see it from the browser.
I've added also app.UseStaticFiles(); but nothing has changed.
You could try steps below:
Create Asp.NET Core Web API 3.0
Edit the csproj to add reference below:
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0-rc4" />
Add swagger-ui and custom.css to wwwroot with content below:
body {
background-color:yellow
}
Change Startup.cs
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.AddControllersWithViews().AddNewtonsoftJson();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", 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();
}
else
{
app.UseExceptionHandler("/Home/Error");
// 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.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Geo API");
c.RoutePrefix = "docs";
c.DocumentTitle = "GeoData APIs";
c.DisplayRequestDuration();
c.EnableFilter();
c.InjectStylesheet("/swagger-ui/custom.css");
});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}

How to cache static files in ASP.NET Core?

I can't seem to enable caching of static files in ASP.NET Core 2.2. I have the following in my Configure:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
app.UseCors(...);
}
else {
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseSignalR(routes => { routes.MapHub<NotifyHub>("/..."); });
app.UseResponseCompression();
app.UseStaticFiles();
app.UseSpaStaticFiles(new StaticFileOptions() {
OnPrepareResponse = (ctx) => {
ctx.Context.Response.Headers[HeaderNames.CacheControl] = "public, max-age=31557600"; // cache for 1 year
}
});
app.UseMvc();
app.UseSpa(spa => {
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment()) {
spa.UseVueCli(npmScript: "serve", port: 8080);
}
});
}
When I try and Audit the production site on HTTPS using chrome I keep getting "Serve static assets with an efficient cache policy":
In the network tab there is no mention of caching in the headers, when I press F5 it seems everything is served from disk cache. But, how can I be sure my caching setting is working if the audit is showing its not?
This is working in ASP.NET Core 2.2 to 3.1:
I know this is a bit similar to Fredrik's answer but you don't have to type literal strings in order to get the cache control header
app.UseStaticFiles(new StaticFileOptions()
{
HttpsCompression = Microsoft.AspNetCore.Http.Features.HttpsCompressionMode.Compress,
OnPrepareResponse = (context) =>
{
var headers = context.Context.Response.GetTypedHeaders();
headers.CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue
{
Public = true,
MaxAge = TimeSpan.FromDays(30)
};
}
});
I do not know what UseSpaStaticFiles is but you can add cache options in UseStaticFiles. You have missed to set an Expires header.
// Use static files
app.UseStaticFiles(new StaticFileOptions {
OnPrepareResponse = ctx =>
{
// Cache static files for 30 days
ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=2592000");
ctx.Context.Response.Headers.Append("Expires", DateTime.UtcNow.AddDays(30).ToString("R", CultureInfo.InvariantCulture));
}
});
Beware that you also need a way to invalidate cache when you make changes to static files.
I have written a blog post about this: Minify and cache static files in ASP.NET Core

Client Side Deep Links with WebpackDevMiddleware 404s

I am using the WebpackDevMiddleware for Development builds to serve up a Vue.js application that uses client-side routing. The SPA application is served up from the root url just fine, but if I attempt to use any client-side deep links, I get a 404.
Note running as Production works as expected.
What I want:
http://locahost/ - serve up the vue app.
http://localhost/overlays/chat - serve up the vue app.
http://localhost/api/* - serve up the api routes handled server side.
There is a minimum viable reproduction of the problem in this repository. You can run it using vscode debugging as Development environment where the bug happens. There is also a script /scripts/local-production that will build and run as Production environment, where it works as expected.
Relevant portions of my Startup.cs looks like this:
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// In production, the Vue files will be served
// from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = Configuration["Client"];
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//set up default mvc routing
app.UseMvc(routes =>
{
routes.MapRoute("default", "api/{controller=Home}/{action=Index}/{id?}");
});
//setup spa routing for both dev and prod
if (env.IsDevelopment())
{
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
HotModuleReplacement = true,
ProjectPath = Path.Combine(env.ContentRootPath, Configuration["ClientProjectPath"]),
ConfigFile = Path.Combine(env.ContentRootPath, Configuration["ClientProjectConfigPath"])
});
}
else
{
app.UseWhen(context => !context.Request.Path.Value.StartsWith("/api"),
builder => {
app.UseSpaStaticFiles();
app.UseSpa(spa => {
spa.Options.DefaultPage = "/index.html";
});
app.UseMvc(routes => {
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Fallback", action = "Index" });
});
});
}
}
}
I was able to get around this using the status code pages middleware to handle all status codes and re-execute using the root path. This will cause the spa app to be served up for all status codes in the 400-599 range which is not quite what I want but gets me working again at least.
//setup spa routing for both dev and prod
if (env.IsDevelopment())
{
//force client side deep links to render the spa on 404s
app.UseStatusCodePagesWithReExecute("/");
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
HotModuleReplacement = true,
ProjectPath = Path.Combine(env.ContentRootPath, Configuration["ClientProjectPath"]),
ConfigFile = Path.Combine(env.ContentRootPath, Configuration["ClientProjectConfigPath"])
});
}
Hopefully, this will help someone in the future that might be bumping up against this issue.