Search code examples
c#.netasp.net-coremodular-monolith

How to map controllers from application part to subpath?


I define modules for my app, and I add resources as application parts to the application.

However, I'd like to map all endpoints from each module to its own subpath.

How can I achieve this?


Solution

  • We can implement this requirementby using IApplicationModelConvention. You can check the test result first and tell me is it you want(I am using the official repo).

    enter image description here

    If this is what you want to achieve, please follow my steps.

    Step 1. Download the repo and create ModuleRoutingConvention.cs file.

    using System.Linq;
    using Microsoft.AspNetCore.Mvc.ApplicationModels;
    
    namespace AppPartsSample
    {
        public class ModuleRoutingConvention : IApplicationModelConvention
        {
            public void Apply(ApplicationModel application)
            {
                foreach (var controller in application.Controllers)
                {
                    var namespaceParts = controller.ControllerType.Namespace?.Split('.');
                    if (namespaceParts != null && (namespaceParts.Contains("DependentLibrary") || namespaceParts.Contains("Plugin")))
                    {
                        var moduleName = namespaceParts[0];
    
                        foreach (var selector in controller.Selectors.Where(s => s.AttributeRouteModel != null).ToList())
                        {
                            selector.AttributeRouteModel = new AttributeRouteModel()
                            {
                                Template = $"{moduleName}/{selector.AttributeRouteModel.Template}"
                            };
                        }
                        foreach (var selector in controller.Selectors.Where(s => s.AttributeRouteModel == null).ToList())
                        {
                            selector.AttributeRouteModel = new AttributeRouteModel()
                            {
                                Template = $"{moduleName}/[controller]/[action]"
                            };
                        }
                    }
                }
            }
        }
    }
    

    Step 2. Modify the EntityTypes.cs file, like below.

    using System.Collections.Generic;
    using System.Reflection;
    
    namespace AppPartsSample.Model
    {
        #region snippet
        public static class EntityTypes
        {
            public static IReadOnlyList<TypeInfo> Types => new List<TypeInfo>()
            {
                typeof(Features).GetTypeInfo(),
                typeof(Home).GetTypeInfo(),
                typeof(NoGood).GetTypeInfo(),
                typeof(Hello).GetTypeInfo(),
            };
    
            public class Features { }
            public class Home { }
    
            public class NoGood { }
            public class Hello { }
        }
        #endregion
    }
    

    Step 3. Register it in your Startup.cs

       public void ConfigureServices(IServiceCollection services)
       {
           //services.AddMvc()
           //        .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
           //        .ConfigureApplicationPartManager(apm =>
           //            apm.FeatureProviders.Add(new GenericControllerFeatureProvider()));
    
           services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2).ConfigureApplicationPartManager(apm =>
           {
               apm.FeatureProviders.Add(new GenericControllerFeatureProvider());
           }).AddMvcOptions(options =>
           {
               options.Conventions.Add(new ModuleRoutingConvention());
           });
       }
    

    Step 4. Add attributes on the controllers in your Class Library Project.

    enter image description here

    enter image description here