Search code examples
asp.net-coreasp.net-web-apiasp.net-web-api-routing

ASP.NET Core 3 adding route prefix


In Startup.cs I added api/ to the start of my route pattern.

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "api/{controller}/{action=Index}/{id?}");
});

But it hasn't done anything: the old URLs continue to work and the ones that start with /api return 404. This makes no sense!

How do I get my API to be served under /api?


Solution

  • Global Route Prefix in ASP.NET Core 3.0

    Create a custom MvcOptionsExtensions

        public static class MvcOptionsExtensions
        {
            public static void UseCentralRoutePrefix(this MvcOptions opts, IRouteTemplateProvider routeAttribute)
            {
                opts.Conventions.Insert(0, new RouteConvention(routeAttribute));
            }
        }
    
        public class RouteConvention : IApplicationModelConvention
        {
            private readonly AttributeRouteModel _centralPrefix;
    
            public RouteConvention(IRouteTemplateProvider routeTemplateProvider)
            {
                _centralPrefix = new AttributeRouteModel(routeTemplateProvider);
            }
    
            public void Apply(ApplicationModel application)
            {
                foreach (var controller in application.Controllers)
                {
                    var matchedSelectors = controller.Selectors.Where(x => x.AttributeRouteModel != null).ToList();
                    if (matchedSelectors.Any())
                    {
                        foreach (var selectorModel in matchedSelectors)
                        {
                            selectorModel.AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(_centralPrefix,
                                selectorModel.AttributeRouteModel);
                        }
                    }
    
                    var unmatchedSelectors = controller.Selectors.Where(x => x.AttributeRouteModel == null).ToList();
                    if (unmatchedSelectors.Any())
                    {
                        foreach (var selectorModel in unmatchedSelectors)
                        {
                            selectorModel.AttributeRouteModel = _centralPrefix;
                        }
                    }
                }
            }
        }
    

    Codes in Startup.cs

    public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews(opt => {
                opt.UseCentralRoutePrefix(new RouteAttribute("api"));
                ;
            });
        }
    

    Test of result

    enter image description here