How to map controllers from application part to subpath?

49 Views Asked by At

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?

2

There are 2 best solutions below

1
Jason Pan On BEST ANSWER

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

2
Unseen On

You can simply separate base controllers and customize their routes. With this you can limit authorizations and produce consume types as well.

Base Controller

[ApiController]
[AllowAnonymous]
[Consumes(ApiContentTypes.ApplicationJson)]
[Produces(ApiContentTypes.ApplicationJson)]
public class BaseApiController : ControllerBase
{

}

Employee Module Base Controller

[Route("api/EmployeeModule/[Controller]/[Action]")]
[Authorize(Roles = Roles.EmployeeAdmin)]
public class EmployeeModuleController : BaseApiController 
{

}

Employee Module Sub-Controllers

public class ManagerEmployeeController : EmployeeModuleController
{

}

Another module base controller

[Route("api/Story")]
public class StoryModuleController : BaseApiController 
{

}

Sub-base controller

[Route("Instagram/[Action]")]
[Authorize(Policy = Policies.InstagramPolicy)]
public class InstagramStoryController : StoryModuleController
{
   public async Task<IResponse> GetAsync(CancellationToken ct = default)
   {
      await Task.Yield();
      return new SuccessResponse("Successfully Retrieved");
   }
}