Example #1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="T:Ceen.Mvc.ControllerRouter"/> class.
        /// </summary>
        /// <param name="config">The configuration to use</param>
        /// <param name="types">The types to use, must all derive from <see cref="T:Ceen.Mvc.Controller"/>.</param>
        public ControllerRouter(ControllerRouterConfig config, IEnumerable <Type> types)
        {
            if (types == null)
            {
                throw new ArgumentNullException($"{types}");
            }
            if (config == null)
            {
                throw new ArgumentNullException($"{config}");
            }

            // Make sure the caller cannot edit the config afterwards
            m_config = config.Clone();

            var variables = new RouteParser(m_config.Template, !m_config.CaseSensitive, null).Variables;

            if (variables.Count(x => x.Key == m_config.ControllerGroupName) != 1)
            {
                throw new ArgumentException($"The template must contain exactly 1 named group called {m_config.ControllerGroupName}");
            }
            if (variables.Count(x => x.Key == m_config.ActionGroupName) != 1)
            {
                throw new ArgumentException($"The template must contain exactly 1 named group called {m_config.ActionGroupName}");
            }

            types = types.Where(x => x != null).Distinct().ToArray();
            if (types.Count() == 0)
            {
                throw new ArgumentException($"No controller entries to load from \"{types}\"");
            }

            var wrong = types.FirstOrDefault(x => !typeof(Controller).IsAssignableFrom(x));

            if (wrong != null)
            {
                throw new ArgumentException($"The type \"{wrong.FullName}\" does not derive from {typeof(Controller).FullName}");
            }

            wrong = types.FirstOrDefault(x => x.IsAbstract || x.IsGenericTypeDefinition);
            if (wrong != null)
            {
                throw new ArgumentException($"The type \"{wrong.FullName}\" cannot be instantiated");
            }

            m_routeparser = BuildParse(types.Select(x => (Controller)Activator.CreateInstance(x)).ToArray(), m_config);
        }
Example #2
0
        /// <summary>
        /// Gets the name of an item
        /// </summary>
        /// <returns>The item name.</returns>
        /// <param name="self">The type to get the name for.</param>
        /// <param name="config">The configuration to use</param>
        public static string GetItemName(this MethodInfo self, ControllerRouterConfig config)
        {
            var nameattr = self.GetCustomAttributes(typeof(NameAttribute), false).Cast <NameAttribute>().FirstOrDefault();

            // Extract an assigned name
            if (nameattr != null)
            {
                return(nameattr.Name);
            }

            var name = self.Name;

            if (config.LowerCaseNames)
            {
                name = name.ToLowerInvariant();
            }

            return(name);
        }
Example #3
0
        /// <summary>
        /// Initializes a new instance of the <see cref="T:Ceen.Mvc.ControllerRouter"/> class.
        /// </summary>
        /// <param name="config">The configuration to use</param>
        /// <param name="instances">The instances to use.</param>
        public ControllerRouter(ControllerRouterConfig config, IEnumerable <Ceen.Mvc.Controller> instances)
        {
            if (instances == null || instances.Any(x => x == null))
            {
                throw new ArgumentNullException($"{instances}");
            }
            if (config == null)
            {
                throw new ArgumentNullException($"{config}");
            }

            // Make sure the caller cannot edit the config afterwards
            m_config = config.Clone();

            var variables = new RouteParser(m_config.Template, !m_config.CaseSensitive, null).Variables;

            if (variables.Count(x => x.Key == m_config.ControllerGroupName) != 1)
            {
                throw new ArgumentException($"The template must contain exactly 1 named group called {m_config.ControllerGroupName}");
            }
            if (variables.Count(x => x.Key == m_config.ActionGroupName) != 1)
            {
                throw new ArgumentException($"The template must contain exactly 1 named group called {m_config.ActionGroupName}");
            }

            if (instances.Count() == 0)
            {
                throw new ArgumentException($"No instances were added as routes", nameof(instances));
            }

            var duplicates = instances.GroupBy(x => x.GetType()).Where(x => x.Count() > 1);

            if (duplicates.Any())
            {
                throw new ArgumentException($"The type \"{duplicates.First().Key}\" has {duplicates.First().Count()} instances");
            }

            m_routeparser = BuildParse(instances, m_config);
        }
Example #4
0
        private static RouteParser BuildParse(IEnumerable <Controller> controllers, ControllerRouterConfig config)
        {
            var controller_routes =
                controllers.SelectMany(x =>
            {
                string name;
                var nameattr = x.GetType().GetCustomAttributes(typeof(NameAttribute), false).Cast <NameAttribute>().FirstOrDefault();
                // Extract controller name
                if (nameattr != null)
                {
                    name = nameattr.Name;
                }
                else
                {
                    name = x.GetType().Name;
                    if (config.ControllerSuffixRemovals != null)
                    {
                        foreach (var rm in config.ControllerSuffixRemovals)
                        {
                            while (!string.IsNullOrWhiteSpace(rm) && name.EndsWith(rm, StringComparison.InvariantCultureIgnoreCase))
                            {
                                name = name.Substring(0, name.Length - rm.Length);
                            }
                        }
                    }

                    if (config.LowerCaseNames)
                    {
                        name = name.ToLowerInvariant();
                    }
                }

                var routes = x.GetType().GetCustomAttributes(typeof(RouteAttribute), false).Cast <RouteAttribute>().Select(y => y.Route);

                // Add default route, if there are no route attributes
                if (routes.Count() == 0)
                {
                    routes = new[] { string.Empty }
                }
                ;

                return(routes.Distinct().Select(y => new
                {
                    Controller = x,
                    ControllerRoute = y
                }));
            }
                                       ).ToArray();

            var interface_expanded_routes =
                controller_routes.SelectMany(x =>
            {
                var interfaces     = x.Controller.GetType().GetParentInterfaces <IControllerPrefix>();
                var interfacenames = interfaces
                                     .Select(y => x.Controller.GetType().GetParentInterfaceSequence(y).Reverse().Where(z => z != x.Controller.GetType() && z != typeof(IControllerPrefix)))
                                     .Select(y => string.Join("/", y.Select(z => z.GetItemName(config))));

                if (interfacenames.Count() == 0)
                {
                    interfacenames = new[] { string.Empty }
                }
                ;

                return(interfacenames.Distinct().Select(y => new
                {
                    Controller = x.Controller,
                    ControllerRoute = x.ControllerRoute,
                    InterfacePath = (y ?? string.Empty)
                }));
            }
                                             ).ToArray();


            var target_method_routes =
                interface_expanded_routes.SelectMany(x =>
            {
                return
                (x.Controller.GetType()
                 .GetMethods(BindingFlags.Public | BindingFlags.Instance)
                 .Where(y => y.ReturnType == typeof(void) || typeof(IResult).IsAssignableFrom(y.ReturnType) || typeof(Task).IsAssignableFrom(y.ReturnType))
                 .Select(y => new
                {
                    Controller = x.Controller,
                    ControllerRoute = x.ControllerRoute,
                    InterfacePath = x.InterfacePath,
                    Method = y
                }));
            }
                                                     ).ToArray();

            var target_routes =
                target_method_routes.SelectMany(x =>
            {
                var routes = x.Method.GetCustomAttributes(typeof(RouteAttribute), false).Cast <RouteAttribute>().Select(y => y.Route);

                // Add default route, if there are no route attributes
                if (routes.Count() == 0)
                {
                    routes = new[] { string.Empty }
                }
                ;

                return(routes.Select(y => new
                {
                    Controller = x.Controller,
                    ControllerRoute = x.ControllerRoute,
                    InterfacePath = x.InterfacePath,
                    Method = x.Method,
                    MethodRoute = y
                }));
            }
                                                ).ToArray();

            // Now we have the cartesian product of all route/controller/action pairs, then build the target strings
            var tmp = new RouteParser(config.Template, !config.CaseSensitive, null);
            var defaultcontrollername = tmp.GetDefaultValue(config.ControllerGroupName);
            var defaultactionname     = tmp.GetDefaultValue(config.ActionGroupName);

            var target_strings =
                target_routes.SelectMany(x =>
            {
                var methodverbs = x.Method.GetCustomAttributes(typeof(HttpVerbFilterAttribute), false).Cast <HttpVerbFilterAttribute>().Select(b => b.Verb.ToUpperInvariant());
                var entry       = new RouteEntry(x.Controller, x.Method, methodverbs.ToArray(), null);

                var path = new RouteParser("/", !config.CaseSensitive, entry);

                if (!string.IsNullOrWhiteSpace(x.InterfacePath))
                {
                    if (x.InterfacePath.StartsWith("/", StringComparison.Ordinal))
                    {
                        path = new RouteParser(x.InterfacePath, !config.CaseSensitive, entry);
                    }
                    else
                    {
                        path = path.Append(x.InterfacePath, !config.CaseSensitive, entry);
                    }
                }

                var ct = string.IsNullOrWhiteSpace(x.ControllerRoute) ? config.Template : x.ControllerRoute;

                if (!string.IsNullOrWhiteSpace(ct))
                {
                    if (ct.StartsWith("/", StringComparison.Ordinal))
                    {
                        path = new RouteParser(ct, !config.CaseSensitive, entry);
                    }
                    else
                    {
                        if (!path.Path.EndsWith("/", StringComparison.Ordinal))
                        {
                            ct = "/" + ct;
                        }
                        path = path.Append(ct, !config.CaseSensitive, entry);
                    }
                }

                var mt = x.MethodRoute;
                if (!string.IsNullOrWhiteSpace(mt))
                {
                    if (mt.StartsWith("/", StringComparison.Ordinal))
                    {
                        path = new RouteParser(mt, !config.CaseSensitive, entry);
                    }
                    else
                    {
                        path = path.Bind(config.ActionGroupName, string.Empty, !config.CaseSensitive, true);
                        if (!path.Path.EndsWith("/", StringComparison.Ordinal))
                        {
                            mt = "/" + mt;
                        }

                        path = path.Append(mt, !config.CaseSensitive, entry);
                    }
                }

                var controllername = x.Controller.GetType().GetItemName(config);
                var actionname     = x.Method.GetItemName(config);

                var controllernames = new List <string>();
                var actionnames     = new List <string>();

                if (!config.HideDefaultController || controllername != defaultcontrollername)
                {
                    controllernames.Add(controllername);
                }
                if (!config.HideDefaultAction || actionname != defaultactionname)
                {
                    actionnames.Add(actionname);
                }
                if (!string.IsNullOrEmpty(defaultcontrollername) && controllername == defaultcontrollername)
                {
                    controllernames.Add(string.Empty);
                }
                if (!string.IsNullOrEmpty(defaultactionname) && actionname == defaultactionname)
                {
                    actionnames.Add(string.Empty);
                }

                // Cartesian product with controller and action names bound
                return(controllernames
                       .SelectMany(y => actionnames.Select(
                                       z => path
                                       .Bind(config.ControllerGroupName, y, !config.CaseSensitive, true)
                                       .Bind(config.ActionGroupName, z, !config.CaseSensitive, true)
                                       .PrunePath()
                                       .ReplaceTarget(x.Controller, x.Method, methodverbs.ToArray())
                                       )
                                   ));
            }
                                         )
                .Distinct(x => x.ToString())
                .ToArray();

            var merged = RouteParser.Merge(target_strings);

            if (config.Debug)
            {
                Console.WriteLine("All target paths:");
                foreach (var x in target_strings)
                {
                    Console.WriteLine(x);
                }

                Console.WriteLine("Map structure:");
                Console.WriteLine(merged.ToString());
            }

            return(merged);
        }
Example #5
0
 /// <summary>
 /// Initializes a new instance of the <see cref="T:Ceen.Mvc.ControllerRouter"/> class.
 /// </summary>
 /// <param name="config">The configuration to use</param>
 /// <param name="types">The types to use, must all derive from <see cref="T:Ceen.Mvc.Controller"/>.</param>
 public ControllerRouter(ControllerRouterConfig config, params Type[] types)
     : this(config, (types ?? new Type[0]).AsEnumerable())
 {
 }
Example #6
0
 /// <summary>
 /// Initializes a new instance of the <see cref="T:Ceen.Mvc.ControllerRouter"/> class.
 /// </summary>
 /// <param name="config">The configuration to use</param>
 /// <param name="assemblies">The assemblies to scan for controllers.</param>
 public ControllerRouter(ControllerRouterConfig config, IEnumerable <Assembly> assemblies)
     : this(config, assemblies.SelectMany(x => x.GetTypes()).Where(x => x != null && typeof(Controller).IsAssignableFrom(x)))
 {
 }
Example #7
0
 /// <summary>
 /// Initializes a new instance of the <see cref="T:Ceen.Mvc.ControllerRouter"/> class.
 /// </summary>
 /// <param name="config">The configuration to use</param>
 /// <param name="assembly">The assembly to scan for controllers.</param>
 public ControllerRouter(ControllerRouterConfig config, Assembly assembly)
     : this(config, assembly == null ? new Assembly[0] : new [] { assembly })
 {
 }
Example #8
0
        /// <summary>
        /// Gets the name of an item
        /// </summary>
        /// <returns>The item name.</returns>
        /// <param name="self">The type to get the name for.</param>
        /// <param name="config">The configuration to use</param>
        public static string GetItemName(this Type self, ControllerRouterConfig config)
        {
            var nameattr = self.GetCustomAttributes(typeof(NameAttribute), false).Cast <NameAttribute>().FirstOrDefault();

            // Extract an assigned name
            if (nameattr != null)
            {
                return(nameattr.Name);
            }

            var name = self.Name;

            if (typeof(Controller).IsAssignableFrom(self) && self.IsClass)
            {
                if (config.ControllerSuffixRemovals != null)
                {
                    foreach (var rm in config.ControllerSuffixRemovals)
                    {
                        while (!string.IsNullOrWhiteSpace(rm) && name.EndsWith(rm, StringComparison.InvariantCultureIgnoreCase))
                        {
                            name = name.Substring(0, name.Length - rm.Length);
                        }
                    }
                }
                if (config.ControllerPrefixRemovals != null)
                {
                    foreach (var rm in config.ControllerPrefixRemovals)
                    {
                        while (!string.IsNullOrWhiteSpace(rm) && name.StartsWith(rm, StringComparison.InvariantCultureIgnoreCase))
                        {
                            name = name.Substring(rm.Length);
                        }
                    }
                }
            }
            else if (typeof(IControllerPrefix).IsAssignableFrom(self) && self.IsInterface)
            {
                if (config.InterfaceSuffixRemovals != null)
                {
                    foreach (var rm in config.InterfaceSuffixRemovals)
                    {
                        while (!string.IsNullOrWhiteSpace(rm) && name.EndsWith(rm, StringComparison.InvariantCultureIgnoreCase))
                        {
                            name = name.Substring(0, name.Length - rm.Length);
                        }
                    }
                }
                if (config.InterfacePrefixRemovals != null)
                {
                    foreach (var rm in config.InterfacePrefixRemovals)
                    {
                        while (!string.IsNullOrWhiteSpace(rm) && name.StartsWith(rm, StringComparison.InvariantCultureIgnoreCase))
                        {
                            name = name.Substring(rm.Length);
                        }
                    }
                }
            }

            if (config.LowerCaseNames)
            {
                name = name.ToLowerInvariant();
            }

            return(name);
        }
Example #9
0
 /// <summary>
 /// Initializes a new instance of the <see cref="T:Ceen.Mvc.ControllerRouter"/> class.
 /// </summary>
 /// <param name="config">The configuration to use</param>
 /// <param name="assemblies">The assemblies to scan for controllers.</param>
 public ControllerRouter(ControllerRouterConfig config, IEnumerable <Assembly> assemblies)
     : this(config, assemblies.SelectMany(x => x.GetTypes()).Where(x => x != null && typeof(Controller).IsAssignableFrom(x) && !x.IsAbstract && !x.IsGenericTypeDefinition))
 {
 }
Example #10
0
        private static RouteParser BuildParse(IEnumerable <Controller> controllers, ControllerRouterConfig config)
        {
            var target_routes =
                ParseControllers(controllers, config)

                // Attach custom controller routes
                .Concat(
                    controllers.OfType <ManualRoutingController>().SelectMany(x => x.Routes)
                    )
                .ToArray();

            // Now we have the cartesian product of all route/controller/action pairs, then build the target strings
            var tmp = new RouteParser(config.Template, !config.CaseSensitive, null);
            var defaultcontrollername = tmp.GetDefaultValue(config.ControllerGroupName);
            var defaultactionname     = tmp.GetDefaultValue(config.ActionGroupName);

            var target_strings =
                target_routes.SelectMany(x =>
            {
                // Provide a hook point for the dynamic routing option
                if (x.Controller is IIDynamicConfiguredController idc)
                {
                    x = idc.PatchRoute(x);
                }

                var entry = new RouteEntry(x.Controller, x.Method, x.Verbs, null);

                var path = new RouteParser("/", !config.CaseSensitive, entry);

                if (!string.IsNullOrWhiteSpace(x.InterfacePath))
                {
                    if (x.InterfacePath.StartsWith("/", StringComparison.Ordinal))
                    {
                        path = new RouteParser(x.InterfacePath, !config.CaseSensitive, entry);
                    }
                    else
                    {
                        path = path.Append(x.InterfacePath, !config.CaseSensitive, entry);
                    }
                }

                var ct = string.IsNullOrWhiteSpace(x.ControllerPath) ? config.Template : x.ControllerPath;

                if (!string.IsNullOrWhiteSpace(ct))
                {
                    if (ct.StartsWith("/", StringComparison.Ordinal))
                    {
                        path = new RouteParser(ct, !config.CaseSensitive, entry);
                    }
                    else
                    {
                        if (!path.Path.EndsWith("/", StringComparison.Ordinal))
                        {
                            ct = "/" + ct;
                        }
                        path = path.Append(ct, !config.CaseSensitive, entry);
                    }
                }

                var mt = x.MethodPath;
                if (!string.IsNullOrWhiteSpace(mt))
                {
                    if (mt.StartsWith("/", StringComparison.Ordinal))
                    {
                        path = new RouteParser(mt, !config.CaseSensitive, entry);
                    }
                    else
                    {
                        path = path.Bind(config.ActionGroupName, string.Empty, !config.CaseSensitive, true);
                        if (!path.Path.EndsWith("/", StringComparison.Ordinal))
                        {
                            mt = "/" + mt;
                        }

                        path = path.Append(mt, !config.CaseSensitive, entry);
                    }
                }

                var controllername = x.Controller.GetType().GetItemName(config);
                var actionname     = x.Method.GetItemName(config);

                var controllernames = new List <string>();
                var actionnames     = new List <string>();

                if (!config.HideDefaultController || controllername != defaultcontrollername)
                {
                    controllernames.Add(controllername);
                }
                if (!config.HideDefaultAction || actionname != defaultactionname)
                {
                    actionnames.Add(actionname);
                }
                if (!string.IsNullOrEmpty(defaultcontrollername) && controllername == defaultcontrollername)
                {
                    controllernames.Add(string.Empty);
                }
                if (!string.IsNullOrEmpty(defaultactionname) && actionname == defaultactionname)
                {
                    actionnames.Add(string.Empty);
                }

                // Cartesian product with controller and action names bound
                return(controllernames
                       .SelectMany(y => actionnames.Select(
                                       z => path
                                       .Bind(config.ControllerGroupName, y, !config.CaseSensitive, true)
                                       .Bind(config.ActionGroupName, z, !config.CaseSensitive, true)
                                       .PrunePath()
                                       .ReplaceTarget(x.Controller, x.Method, x.Verbs)
                                       )
                                   ));
            }
                                         )
                .Distinct(x => x.ToString())
                .ToArray();

            var merged = RouteParser.Merge(target_strings);

            if (config.Debug)
            {
                Console.WriteLine("All target paths:");
                foreach (var x in target_strings)
                {
                    Console.WriteLine(x);
                }

                Console.WriteLine("Map structure:");
                Console.WriteLine(merged.ToString());
            }

            return(merged);
        }
Example #11
0
        /// <summary>
        /// Creates partial routes for all controllers in the sequence
        /// </summary>
        /// <param name="controllers">The controllers to create partial routes for</param>
        /// <param name="config">The configuration to use</param>
        /// <returns>The partial routes</returns>
        public static PartialParsedRoute[] ParseControllers(IEnumerable <Controller> controllers, ControllerRouterConfig config)
        {
            var controller_routes = controllers
                                    .Where(x => !(x is ManualRoutingController))
                                    .SelectMany(x =>
            {
                string name;
                var nameattr = x.GetType().GetCustomAttributes(typeof(NameAttribute), false).Cast <NameAttribute>().FirstOrDefault();
                // Extract controller name
                if (nameattr != null)
                {
                    name = nameattr.Name;
                }
                else
                {
                    name = x.GetType().Name;
                    if (config.ControllerSuffixRemovals != null)
                    {
                        foreach (var rm in config.ControllerSuffixRemovals)
                        {
                            while (!string.IsNullOrWhiteSpace(rm) && name.EndsWith(rm, StringComparison.InvariantCultureIgnoreCase))
                            {
                                name = name.Substring(0, name.Length - rm.Length);
                            }
                        }
                    }

                    if (config.LowerCaseNames)
                    {
                        name = name.ToLowerInvariant();
                    }
                }

                var routes = x.GetType().GetCustomAttributes(typeof(RouteAttribute), false).Cast <RouteAttribute>().Select(y => y.Route);

                // Add default route, if there are no route attributes
                if (routes.Count() == 0)
                {
                    routes = new[] { string.Empty }
                }
                ;

                return(routes.Distinct().Select(y => new
                {
                    Controller = x,
                    ControllerRoute = y
                }));
            }
                                                ).ToArray();

            var interface_expanded_routes =
                controller_routes.SelectMany(x =>
            {
                var interfaces     = x.Controller.GetType().GetParentInterfaces <IControllerPrefix>();
                var interfacenames = interfaces
                                     .Select(y => x.Controller.GetType().GetParentInterfaceSequence(y).Reverse().Where(z => z != x.Controller.GetType() && z != typeof(IControllerPrefix)))
                                     .Select(y => string.Join("/", y.Select(z => z.GetItemName(config))));

                if (interfacenames.Count() == 0)
                {
                    interfacenames = new[] { string.Empty }
                }
                ;

                return(interfacenames.Distinct().Select(y => new
                {
                    Controller = x.Controller,
                    ControllerRoute = x.ControllerRoute,
                    InterfacePath = (y ?? string.Empty)
                }));
            }
                                             ).ToArray();


            var target_method_routes =
                interface_expanded_routes.SelectMany(x =>
            {
                return
                (x.Controller.GetType()
                 .GetMethods(BindingFlags.Public | BindingFlags.Instance)
                 // Only target the methods that return something useful
                 .Where(y =>
                        y.ReturnType == typeof(void) ||
                        typeof(IResult).IsAssignableFrom(y.ReturnType) ||
                        typeof(Task).IsAssignableFrom(y.ReturnType)
                        )
                 // Remove properties and others
                 .Where(y => !y.IsSpecialName)
                 .Select(y => new
                {
                    Controller = x.Controller,
                    ControllerRoute = x.ControllerRoute,
                    InterfacePath = x.InterfacePath,
                    Method = y
                }));
            }
                                                     ).ToArray();

            return
                (target_method_routes.SelectMany(x =>
            {
                var routes = x.Method.GetCustomAttributes(typeof(RouteAttribute), false).Cast <RouteAttribute>().Select(y => y.Route);

                // Add default route, if there are no route attributes
                if (routes.Count() == 0)
                {
                    routes = new[] { string.Empty }
                }
                ;

                // Get any accepted verbs
                var verbs = x.Method.GetCustomAttributes(typeof(HttpVerbFilterAttribute), false).Cast <HttpVerbFilterAttribute>().Select(b => b.Verb.ToUpperInvariant()).ToArray();

                return routes.Select(y => new PartialParsedRoute()
                {
                    Controller = x.Controller,
                    ControllerPath = x.ControllerRoute,
                    InterfacePath = x.InterfacePath,
                    Method = x.Method,
                    MethodPath = y,
                    Verbs = verbs
                });
            }
                                                 )
                 .ToArray());
        }
Example #12
0
 /// <summary>
 /// Initializes a new instance of the <see cref="T:Ceen.Mvc.ControllerRouter"/> class.
 /// </summary>
 /// <param name="config">The configuration to use</param>
 /// <param name="instances">The instances to use.</param>
 public ControllerRouter(ControllerRouterConfig config, params Controller[] instances)
     : this(config, (instances ?? new Controller[0]).AsEnumerable())
 {
 }