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.

Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

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

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • 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!

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • 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

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • 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

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    I will find a way.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • 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();
    
        //...
    
    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    htavukcu created

    Thanks for replying, I will try and let you inform

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    sure

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    htavukcu created

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

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • 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.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
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 v10.8.0-preview. Updated on September 16, 2026, 14:50
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.