Open Closed

How to configure RouteTenantResolveContributor? #9306


User avatar
0
htavukcu created

Hello, I need more information about RouteTenantResolveContributor, but I couldn't find it in your documentation. Would it be possible for you to share an example with me? How can I configure it and, for example, set up Swagger routing? Thank you for your help.


10 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    If your current url is https://localhost:44303/Weather/test and route template include __tenant

    Which mean you need add __tenant to your route template

    [Route("[Controller]/{__tenant}")]
    public class WeatherController : MyProjectNameController
    {
        [HttpGet]
        public IActionResult GetWeather()
        {
            return Ok($"Current tenant is {CurrentTenant.Name}");
        }
    

    See https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing

  • User Avatar
    0
    htavukcu created

    Hi,

    I need your support on a few topics:

    1-Is there a centralized and efficient way to add the {__tenant} parameter to automatically generated endpoint routes?

    2-How can I include the {__tenant} parameter in the endpoints displayed in Swagger UI?

    3-Is there any example or documentation available regarding the necessary configuration for AuthServer and ApiHost projects to support this setup?

    I’d really appreciate your help. Thank you!

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    What is the final URL format you expect?

    Like this? https://localhost:44303/tenantname/api/abp

  • User Avatar
    0
    htavukcu created

    Yes exactly like this,https://localhost:44303/tenantname/api/abp

    https://localhost:44303/tenantname/swagger/index.html https://localhost:44303/tenantname/Account/Login

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    I will find a way.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Add a AddTenantToRouteclass then add it to aspnet core system.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc.ApplicationModels;
    using Microsoft.AspNetCore.Routing;
    using Volo.Abp.AspNetCore.MultiTenancy;
    using Volo.Abp.MultiTenancy;
    
    namespace MyCompanyName.MyProjectName.Web;
    
    public class AddTenantToRoute : IPageRouteModelConvention, IApplicationModelConvention
    {
        public void Apply(PageRouteModel model)
        {
            var selectorCount = model.Selectors.Count;
            var selectorModels = new List<SelectorModel>();
            for (var i = 0; i < selectorCount; i++)
            {
                var selector = model.Selectors[i];
                selectorModels.Add(new SelectorModel
                {
                    AttributeRouteModel = new AttributeRouteModel
                    {
                        Template = AttributeRouteModel.CombineTemplates("{__tenant:regex(^[a-zA-Z0-9]+$)}", selector.AttributeRouteModel!.Template!.RemovePreFix("/"))
                    }
                });
            }
            foreach (var selectorModel in selectorModels)
            {
                model.Selectors.Add(selectorModel);
            }
        }
    
        public void Apply(ApplicationModel application)
        {
            var controllers = application.Controllers;
            foreach (var controller in controllers)
            {
                var selector = controller.Selectors.FirstOrDefault();
                if (selector == null || selector.AttributeRouteModel == null)
                {
                    controller.Selectors.Add(new SelectorModel
                    {
                        AttributeRouteModel = new AttributeRouteModel
                        {
                            Template = AttributeRouteModel.CombineTemplates("{__tenant:regex(^[[a-zA-Z0-9]]+$)}", controller.ControllerName)
                        }
                    });
                    controller.Selectors.Add(new SelectorModel
                    {
                        AttributeRouteModel = new AttributeRouteModel
                        {
                            Template = controller.ControllerName
                        }
                    });
                }
                else
                {
                    var template = selector.AttributeRouteModel?.Template;
                    template = template.IsNullOrWhiteSpace() ? "{__tenant:regex(^[[a-zA-Z0-9]]+$)}" : AttributeRouteModel.CombineTemplates("{__tenant:regex(^[[a-zA-Z0-9]]+$)}", template.RemovePreFix("/"));
                    controller.Selectors.Add(new SelectorModel
                    {
                        AttributeRouteModel = new AttributeRouteModel
                        {
                            Template = template
                        }
                    });
                }
            }
        }
    }
    
    public class MyRouteTenantResolveContributor : RouteTenantResolveContributor
    {
        public const string ContributorName = "MyRoute";
    
        public override string Name => ContributorName;
    
        protected override Task<string?> GetTenantIdOrNameFromHttpContextOrNullAsync(ITenantResolveContext context, HttpContext httpContext)
        {
            var tenantId = httpContext.GetRouteValue(context.GetAbpAspNetCoreMultiTenancyOptions().TenantKey) ?? httpContext.Request.PathBase.ToString();
            var tenantIdStr = tenantId?.ToString()?.RemovePreFix("/");
            return Task.FromResult(!tenantIdStr.IsNullOrWhiteSpace() ? Convert.ToString(tenantIdStr) : null);
        }
    }
    
    
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        //...
        
        PostConfigure<RazorPagesOptions>(options =>
        {
            options.Conventions.Add(new AddTenantToRoute());
        });
    
        PostConfigure<MvcOptions>(options =>
        {
            options.Conventions.Add(new AddTenantToRoute());
        });
        
        context.Services.ConfigureApplicationCookie(x =>
        {
            x.Cookie.Path = "/";
        });
    
        Configure<AbpTenantResolveOptions>(options =>
        {
            options.TenantResolvers.Add(new MyRouteTenantResolveContributor());
        });
        //...
    }
    
    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        //...
        app.Use(async (httpContext, next) =>
        {
            var tenantMatch = Regex.Match(httpContext.Request.Path, "^/([^/.]+)(?:/.*)?$");
            if (tenantMatch.Groups.Count > 1 && !string.IsNullOrEmpty(tenantMatch.Groups[1].Value))
            {
                var tenantName = tenantMatch.Groups[1].Value;
                if (!tenantName.IsNullOrWhiteSpace())
                {
                    var tenantStore = httpContext.RequestServices.GetRequiredService<ITenantStore>();
                    var tenantNormalizer = httpContext.RequestServices.GetRequiredService<ITenantNormalizer>();
                    var tenantInfo = await tenantStore.FindAsync(tenantNormalizer.NormalizeName(tenantName)!);
                    if (tenantInfo != null)
                    {
                        if (httpContext.Request.Path.StartsWithSegments(new PathString(tenantName.EnsureStartsWith('/')), out var matchedPath, out var remainingPath))
                        {
                            var originalPath = httpContext.Request.Path;
                            var originalPathBase = httpContext.Request.PathBase;
                            httpContext.Request.Path = remainingPath;
                            httpContext.Request.PathBase = originalPathBase.Add(matchedPath);
                            try
                            {
                                await next(httpContext);
                            }
                            finally
                            {
                                httpContext.Request.Path = originalPath;
                                httpContext.Request.PathBase = originalPathBase;
                            }
                            return;
                        }
                    }
                }
            }
    
            await next(httpContext);
        });
        app.UseRouting();
        app.MapAbpStaticAssets();
    
        //...
    
  • User Avatar
    0
    htavukcu created

    Thanks for replying, I will try and let you inform

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    sure

  • User Avatar
    0
    htavukcu created

    Hi, Thanks for your help, it worked as expected.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Good news. I will also share a community article to explain this. https://github.com/abpframework/abp/issues/22919

    Thanks.

Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v9.3.0-preview. Updated on June 13, 2025, 11:37