-
Notifications
You must be signed in to change notification settings - Fork 25.2k
Add note to call UseRouting after UsePathBase #25769
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a couple of nits and bits.
Co-authored-by: Luke Latham <[email protected]>
I've hit this issue today and the proposed workaround is not working for me. This was a .net 3.1 mvc that was migrated to use the new web app builder but I retained the StartUp class. Here's the code from Program where the builder and StartUp are created: var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
ApplicationName = typeof(Program).Assembly.FullName,
Args = args,
EnvironmentName = environment,
});
builder.Configuration.AddConfiguration(config);
builder.Host.UseSerilog();
builder.WebHost.UseUrls("http://*:58471");
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.Limits.MinRequestBodyDataRate = new MinDataRate(
bytesPerSecond: 80, gracePeriod: TimeSpan.FromSeconds(20));
serverOptions.Limits.MinResponseDataRate = new MinDataRate(
bytesPerSecond: 80, gracePeriod: TimeSpan.FromSeconds(20));
});
var startup = new Startup(config);
startup.ConfigureServices(builder.Services);
var app = builder.Build();
startup.Configure(app, environment); This is the method in StartUp that is called by Program to initialize the app after it is created: public void Configure(IApplicationBuilder app, string environment)
{
Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true;
// The request path has to be set if the application is running in a
// container and being exposed on a path other than /. In k8s we set
// SiteConfig__PathBase to /sms in most projects.
if (!string.IsNullOrEmpty(Cfg.SiteConfig.PathBase))
{
Log.Information("Setting PathBase to {PathBase}.", Cfg.SiteConfig.PathBase);
app.UsePathBase(Cfg.SiteConfig.PathBase);
app.UseStaticFiles(new StaticFileOptions
{
RequestPath = Cfg.SiteConfig.PathBase
});
app.Use((context, next) =>
{
if (context.Request.Path.StartsWithSegments(Cfg.SiteConfig.PathBase, out var remainder))
{
context.Request.Path = remainder;
context.Request.PathBase = new PathString(Cfg.SiteConfig.PathBase);
}
return next();
});
}
else
{
app.UseStaticFiles();
}
if (environment == "Development")
{
app.UseDeveloperExceptionPage();
// If you want saving views to not require a recompile,
// see https://kmatyaszek.github.io/2020/02/29/how-to-enable-browser-link-in-aspnetcore3-app.html
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCookiePolicy();
app.UseSerilogRequestLogging();
// Must call UseAuthentication between UserRouting and UseEndpoints or will throw an error page
app.UseAuthentication();
app.UseAuthorization();
if (Cfg.SiteConfig.EnableHangfire)
{
Log.Information("Setting up Hangfire dashboard.");
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[] { new Extensions.HangfireAuthorizationFilter() },
AppPath = environment == "Development" ? "/" : "/sms"
});
app.UseHangfireDashboard();
if (!string.IsNullOrEmpty(Cfg.SiteConfig.RefreshTargetsCron))
RecurringJob.AddOrUpdate<HangfireHelper>("refresh_targets",
h => h.RefreshTargets(), Cfg.SiteConfig.RefreshTargetsCron);
else
RecurringJob.RemoveIfExists("refresh_targets");
if (!string.IsNullOrEmpty(Cfg.SiteConfig.SendRemindersCron))
RecurringJob.AddOrUpdate<HangfireHelper>("reminders",
r => r.SendReminders(), Cfg.SiteConfig.SendRemindersCron);
else
RecurringJob.RemoveIfExists("reminders");
if (!string.IsNullOrEmpty(Cfg.SiteConfig.MakeIvrCron))
RecurringJob.AddOrUpdate<HangfireHelper>("ivr_reminders",
h => h.RunIvr(), Cfg.SiteConfig.MakeIvrCron);
else
RecurringJob.RemoveIfExists("ivr_reminders");
if (!string.IsNullOrEmpty(Cfg.SiteConfig.TwilioTasksCron))
RecurringJob.AddOrUpdate<HangfireHelper>("twilio_tasks",
h => h.RunTwilioTasks(), Cfg.SiteConfig.TwilioTasksCron);
else
RecurringJob.RemoveIfExists("twilio_tasks");
if (!string.IsNullOrEmpty(Cfg.SiteConfig.TextInviteCron))
RecurringJob.AddOrUpdate<HangfireHelper>("text_invitation",
h => h.SendTextInvitations(), Cfg.SiteConfig.TextInviteCron);
else
RecurringJob.RemoveIfExists("text_invitation");
if (!string.IsNullOrEmpty(Cfg.SiteConfig.RefreshReportsCron))
RecurringJob.AddOrUpdate<HangfireHelper>("refresh_reports",
h => h.RefreshReports(), Cfg.SiteConfig.RefreshReportsCron);
else
RecurringJob.RemoveIfExists("refresh_reports");
}
app.UseEndpoints(endpoints =>
{
endpoints.MapReverseProxy();
endpoints.MapHub<InboundCallHub>("/hubs/callHub");
endpoints.MapHub<InboundCallHub>("/hubs/inboundCallHub");
endpoints.MapHub<MonitoringHub>("/hubs/monitoringHub");
endpoints.MapHub<OutboundCallHub>($"/hubs/outboundCallHub");
endpoints.MapControllerRoute("default", "{controller=Account}/{action=Index}/{id?}");
endpoints.MapControllerRoute("home", "{controller=Home}/{action=Index}/{id?}");
});
app.UseMvc(routes => {
routes.MapRoute(
name: "Default",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" }
);
routes.MapRoute(
name: "Error404",
template: "{*url}",
defaults: new { controller = "Error", action = "Handle404" }
);
});
}
} |
Here's the log entry from the container showing that it is receiving the configuration value for PathBase:
When I try to navigate to /sms the app redirects to a path off the root and loses the /sms prefix: /Account/Login?ReturnUrl=%2F Where it should have redirected to /sms/Account/Login?ReturnUrl=%2F |
Can you do this? var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
ApplicationName = typeof(Program).Assembly.FullName,
Args = args,
EnvironmentName = environment,
ContentRootPath = !string.IsNullOrEmpty(config.SiteConfig.PathBase) ? config.SiteConfig.PathBase : null,
}); |
I don't see how this would help as the docs indicate that ContentRootPath is the physical local path to the application files (i.e., config json. dlls, etc.). The property config.SiteConfig.PathBase is populated with /sms, which is the path under the website that this application is supposed to show up as. |
What I am trying to deal with is an app that is hosted in a container which is exposed via an ingress in a k8s cluster under a path other than root (/sms in this case). This code around UsePathBase() used to work under .NET 3.1 when we validated our plans for going to k8s. We are now trying to actually migrate under .NET 6 and the new builder and realizing that the new style builder has this regression in it. |
So it looks like this solves part of the issue, but the little dance I was around UseStaticFiles may not be working. Going to deploy this to the cluster to see what happens. |
Actually, that was a false hit. I called dotnet run but forgot to hit save on the Program.cs file (using VS Code). Once I actually had the code change in place and tried to run I got this (as I'd originally expected):
|
I am going to post this on dotnet/aspnetcore#38448. |
We plan to fix
UsePathBase()
in .NET 7 so it's unnecessary to callUseRouting()
afterwards like we did forUseExceptionHandler()
,UseStatusCodePagesWithReExecute()
andUseRewriter()
in .NET 6. dotnet/aspnetcore#35426For now, we want to document that explicitly calling
UseRouting()
is necessary forUsePathBase()
to work withWebApplication
.Partially addresses: dotnet/aspnetcore#38448