Modular ASP.NET Web API: how to add/remove route at run time to a Web API

那年仲夏 提交于 2019-12-03 07:13:06
Tohid

I solved it.

Firstly, and thanks to @Aleksey L., a little change to the ModuleController (adding Configuration.Initializer(Configuration)):

[RoutePrefix("api/modules")]
public class ModulesController : ApiController
{
    private ModuleService _moduleService = new ModuleService();

    // Other codes

    public void Post(string id)
    {
        _moduleService.Load(id);

        Configuration.Routes.Clear();
        Configuration.MapHttpAttributeRoutes();
        Configuration.Initializer(Configuration);
    }

    // Other codes
}

Then we should extend DefaultHttpControllerSelector:

public class ModularHttpControllerSelector : DefaultHttpControllerSelector
{
    private readonly HttpConfiguration _configuration;

    public ModularHttpControllerSelector(HttpConfiguration configuration)
        : base(configuration)
    {
        _configuration = configuration;
    }

    public override IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
    {
        var result = base.GetControllerMapping();
        AddPluginsControllerMapping(ref result);
        return result;
    }

    private void AddPluginsControllerMapping(ref IDictionary<string, HttpControllerDescriptor> controllerMappings)
    {
        var custom_settings = _getControllerMapping();

        foreach (var item in custom_settings)
        {
            if (controllerMappings.ContainsKey(item.Key))
                controllerMappings[item.Key] = item.Value;
            else
                controllerMappings.Add(item.Key, item.Value);
        }
    }

    private ConcurrentDictionary<string, HttpControllerDescriptor> _getControllerMapping()
    {
        var result = new ConcurrentDictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);
        var duplicateControllers = new HashSet<string>();
        Dictionary<string, ILookup<string, Type>> controllerTypeGroups = GetControllerTypeGroups();

        foreach (KeyValuePair<string, ILookup<string, Type>> controllerTypeGroup in controllerTypeGroups)
        {
            string controllerName = controllerTypeGroup.Key;

            foreach (IGrouping<string, Type> controllerTypesGroupedByNs in controllerTypeGroup.Value)
            {
                foreach (Type controllerType in controllerTypesGroupedByNs)
                {
                    if (result.Keys.Contains(controllerName))
                    {
                        duplicateControllers.Add(controllerName);
                        break;
                    }
                    else
                    {
                        result.TryAdd(controllerName, new HttpControllerDescriptor(_configuration, controllerName, controllerType));
                    }
                }
            }
        }

        foreach (string duplicateController in duplicateControllers)
        {
            HttpControllerDescriptor descriptor;
            result.TryRemove(duplicateController, out descriptor);
        }

        return result;
    }

    private Dictionary<string, ILookup<string, Type>> GetControllerTypeGroups()
    {
        IAssembliesResolver assembliesResolver = new DefaultAssembliesResolver(); //was: _configuration.Services.GetAssembliesResolver();
        IHttpControllerTypeResolver controllersResolver = new DefaultHttpControllerTypeResolver(); //was: _configuration.Services.GetHttpControllerTypeResolver();

        ICollection<Type> controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver);
        var groupedByName = controllerTypes.GroupBy(
            t => t.Name.Substring(0, t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length),
            StringComparer.OrdinalIgnoreCase);

        return groupedByName.ToDictionary(
            g => g.Key,
            g => g.ToLookup(t => t.Namespace ?? String.Empty, StringComparer.OrdinalIgnoreCase),
            StringComparer.OrdinalIgnoreCase);
    }
}

And of course we have to replace the default HttpControllerSelector with our HttpControllerSelector, in the App_start\WebApiConfig.cs:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        GlobalConfiguration.Configuration.Services.Replace(
            typeof(System.Web.Http.Dispatcher.IHttpControllerSelector),
            new ModularHttpControllerSelector(config));

        config.MapHttpAttributeRoutes();
    }
}

If anybody is interested in how I implemented the ModuleService, I can upload the code to GitHub.

Here is the whole source code in the GitHub: https://github.com/tohidazizi/modular-web-api-poc

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!