Legacy .net core frameworks used UseMvc()
for adding global route prefix. How to make it for asp.net core 3
without UseMvc()
?
You could refer to below demo in asp.net core 3.0 to set global route prefix with api version.You could set any prefix as you like by changing services.AddControllersWithViews(o => { o.UseGeneralRoutePrefix("api/v{version:apiVersion}"); });
1.Create a custom MvcOptionsExtensions
public static class MvcOptionsExtensions
{
public static void UseGeneralRoutePrefix(this MvcOptions opts, IRouteTemplateProvider routeAttribute)
{
opts.Conventions.Add(new RoutePrefixConvention(routeAttribute));
}
public static void UseGeneralRoutePrefix(this MvcOptions opts, string
prefix)
{
opts.UseGeneralRoutePrefix(new RouteAttribute(prefix));
}
}
public class RoutePrefixConvention : IApplicationModelConvention
{
private readonly AttributeRouteModel _routePrefix;
public RoutePrefixConvention(IRouteTemplateProvider route)
{
_routePrefix = new AttributeRouteModel(route);
}
public void Apply(ApplicationModel application)
{
foreach (var selector in application.Controllers.SelectMany(c => c.Selectors))
{
if (selector.AttributeRouteModel != null)
{
selector.AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(_routePrefix, selector.AttributeRouteModel);
}
else
{
selector.AttributeRouteModel = _routePrefix;
}
}
}
}
2.Register in Startup.cs( you need to install package Microsoft.AspNetCore.Mvc.Versioning ,current version for 3.0 is 4.0.0-preview8.19405.7)
public void ConfigureServices(IServiceCollection services) {
//MVC service registration
//https://learn.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.0&tabs=visual-studio#mvc-service-registration
services.AddControllersWithViews(o = >{
o.UseGeneralRoutePrefix("api/v{version:apiVersion}");
});
services.AddApiVersioning(o = >o.ReportApiVersions = true);
}
// 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();
}
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints = >{
endpoints.MapControllerRoute(
name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}
3.Controller:
[ApiVersion("1")]
[ApiVersion("2")]
[Route("test")]
[ApiController]
public class TestController : ControllerBase
{
[HttpGet("version"), MapToApiVersion("1")]
public IActionResult GetV1()
{
return new OkObjectResult("Version One");
}
}
4.Result
Calling /api/v1/test/version
results in "Version One".
I solved this in 3.1 with just the below in my startup Configure()
:
app.UsePathBase(new PathString("/api"));
UseRouting()
middleware. If added afterwards it will not work. –
Stovepipe UseRouting()
because then it's executed at the start of the pipeline, at least that's the case in .NET 6. Please refer to this issue for the right order of calls for this solution to work github.com/dotnet/aspnetcore/issues/… –
Bandstand As @alastairtree answered you can use app.UsePathBase
to achieve this.
You need to place this call before registering the middleware that needs to be prefixed.
If you need to register other middleware after that, that should not be prefixed, you can reset the prefix to /
.
Full example:
app.UsePathBase(new PathString("/api"));
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
app.UsePathBase(new PathString("/"));
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
UsePathBase
seems to be that both the prefixed and non-prefixed paths result in the endpoint. This may or may not be a problem, depending on your use case. Personally I switched to adding the prefix to every controller: [Route("api/[controller]")]
–
Fennec How to add global route prefix in asp.net core 6 Still works:
app.UsePathBase(new PathString("/api/service"));
app.UseRouting();
The commonly suggested UsePathBase
option does not actually add a prefix to your routes, rather, it is ignoring(effectively) the specified path base by modifying requests, so that they still end up hitting the controllers that don't have /api
in their route; those routes without /api
will still exist and be valid, resulting in duplicate endpoints.
The intended purpose of this middleware is to remove an optional prefix from a request. I find using it to achieve what we want here bastardising it.
Better options are either:
Set a global conventional controller route template:
app.MapControllerRoute(name: "default", pattern: "api/{controller}/{action}");
Note: for conventional routing to work, your controllers cannot have the
ApiController
attribute, which enforces attribute routing (second option below).
Use a base controller class with your route pattern, and inherit it in all your controllers:
[ApiController] [Route("api/[controller]")] public class BaseApiController : ControllerBase { } //This will have the route "api/WeatherForecast" public class WeatherForecastController: BaseApiController { .... }
I found this option to be the most flexible, especially if you're using Swagger, as it doesn't support conventional route templates.
© 2022 - 2024 — McMap. All rights reserved.
app.UseEndpoints()
, what global route prefix do you want to implement? – Wreckfish