locked
Admin Part (place) RRS feed

  • Question

  • User-2014110218 posted

    My website needs an Admin part. Where should I put it?

    - Create an Admin folder in Controllers and Views?
    - No Admin folders at all?
    - In another Solution?

    What about the routes?

    Do you have any suggestions?

    Thanks!


     

    Thursday, January 31, 2008 11:33 AM

Answers

  • User961349301 posted

    When I create web apps, I always seperate admin from public sites because of security reasons. But that is for enterprise applications. You can if you want use a Admin folder, but the <location> element will not work with the Rout handler in this drop what I know, but you can put a web.config in the Admin folder to demand a login.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, January 31, 2008 11:49 AM
  • User1262457295 posted

    I agree with Fredrik N, I would locate your admin in another project in your solution, running as a separate website on your server (this is what I usually do. It also means I can shut that site off).

    But, in the name of completeness and maybe simplicity, there is a way to do it as a sub folder.

    In /Views/Admin/ add Web.Config with the following data:

    <system.web>
    <authentication>
    <allow users="*" />
    <deny users="?" />
    </authentication>
    </system.web>

    Now your files cannot be requested from the web. But your controllers can still render them, so you can now create an admin controller. In the admin controller you can override OnPreAction to perform a security check and throw a security exception if you are not authorized, thereby stopping your response, and allowing you to catch the exception and render an error page instead.

    I'm not absolutely certain about the security of this method, there may be a hole in it somewhere (and that somewhere is almost certainly either your controller or the controller base class, as the web.config will do what it is supposed to...it is from the WebForms side of things, and is known to work). I would not trust this for a US military site, but for a real simple web app with fairly insensitive data it will probably work.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, January 31, 2008 2:02 PM
  • User-1794225439 posted

    ...for a real simple web app with fairly insensitive data it will probably work.
    This is, IMNSHO, one of the main problems with a lot of the stuff that MS puts out, and this first version of MVC is no different. I realize this question was more than likely asked from this very perspective (a simple site) but the question is perfectly in line with a major shortcoming of this framework (and others) when you contrast it against the stuff that P&P recommends for enterprise development. MS has stated that SMB developers are their main clientèle, but I would then counter with why is that? I work in an IT department (one of many here) for a large Fortune 500 company, and due to the corporate mentality that we all love so much, I work with mostly junior developers trying to create and maintain large enterprise systems. I am, in effect, trying to train myself to be an architect, because there's just nobody else doing it. I would submit that there is a rather large, unaccounted for, portion of MS's clientèle that are just like me: enterprise developers working on large systems with very little or no architectural oversight or guidance. And we turn to the community at large, and MS in particular, to find some of that guidance. And yet, we find that MS is at odds with itself in some ways. Or putting out stuff that just isn't aimed at us. It can be very frustrating. I know this is just the first drop of MVC, so I'm hoping the "if you're doing enterprise development you'll want to use MVC" statement holds true in the end.

    Anyway, now that that's out there... the main point I'm driving at is P&P's guidance concerning composite applications. "Simple" apps will maybe need one little separate admin section, but what about a framework for a large enterprise system that needs many different modules, the ability to plug and unplug modules depending on the customized LOB application built from the framework? Duplicate controllers named Home, indeed!

    Here's what I've done to enable module (or area) separation and routing, maybe it will be of value to you in your simple(r) application or to others trying to build a more complex system. I unfortunately had to start from the top, since the routing engine doesn't handle this stuff in any way that I could figure out. So I wrote my own. I'm using Spring.Net for dependency injection, so you can make direct instantiations of the appropriate classes instead to eliminate that complexity if you aren't into DI. I've omitted some details (methods, properties) for brevity (and this is still a lot of code! Sorry).

    public class ModuleMvcRouteHandler : MvcRouteHandler
    {
    protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
    ModuleMvcHandler handler = new ModuleMvcHandler();
    handler.RequestContext = requestContext;
    return handler;
    }
    }

    class ModuleMvcHandler : IHttpHandler, IRequiresSessionState
    {
    void IHttpHandler.ProcessRequest(HttpContext httpContext)
    {
    this.ProcessRequest(httpContext);
    }

    /// <summary>
    /// Handles the http request
    /// </summary>
    /// <param name="httpContext"></param>
    protected virtual void ProcessRequest(HttpContext httpContext)
    {
    if (this.RequestContext == null)
    {
    throw new InvalidOperationException("The MvcHandler requires that a valid RequestContext be set.");
    }

    string routeModule = GetRequiredString(this.RequestContext.RouteData, "module");
    string routeController = GetRequiredString(this.RequestContext.RouteData, "controller");

    IController controllerInstance = this.GetControllerInstance(routeModule + "." + routeController);
    if (controllerInstance == null)
    {
    throw new InvalidOperationException(string.Format("The controller for path '{0}' could not be found or it does not implement the IController interface.",
    new object[] { this.RequestContext.HttpContext.Request.Path }));
    }

    ControllerContext controllerContext = new ControllerContext(this.RequestContext, controllerInstance);
    controllerInstance.Execute(controllerContext);
    }

    /// <summary>
    /// Creates an instance of the requested controller using the configured IModuleControllerFactory
    /// </summary>
    /// <param name="controllerName"></param>
    /// <returns></returns>
    protected virtual IController GetControllerInstance(string controllerName)
    {
    // instantiate the controller factory IApplicationContext context = ContextRegistry.GetContext(); IModuleControllerFactory controllerFactory = (IModuleControllerFactory)context.GetObject("IModuleControllerFactory");

    // have the factory build the controller return controllerFactory.CreateController(this.RequestContext, controllerName);
    }
    }

    class ModuleControllerFactory : IModuleControllerFactory {
    IController IModuleControllerFactory.CreateController(RequestContext context, string controllerName) {
    return this.CreateController(context, controllerName);
    }

    public virtual IController CreateController(RequestContext context, string controllerName) {
    IApplicationContext objectFactory = ContextRegistry.GetContext();
    IController controller = (IController)objectFactory.GetObject(controllerName, typeof(IController));
    controller = null;

    if (controller != null && typeof(Controller).IsAssignableFrom(controller.GetType())) {
    // can't do this yet, ViewPage.ViewData is Internal :(
    // have to fully qualify view paths in RenderView until can implement custom ViewFactory
    //IViewFactory viewFactory = (IViewFactory)objectFactory.GetObject("IModuleViewFactory", typeof(IViewFactory));
    } return controller;
    }
    }
     
    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, January 31, 2008 7:55 PM
  • User-2014110218 posted

    Thanks to all of you.
    As my application is "pretty" simple,  I decided to have an Admin Project in my existing Solution,
    with the following route:

      

    1    RouteTable.Routes.Add(new Route
    2    {
    3        Url = "Admin/[controller]/[action]/[id]",
    4        Defaults = new { action = "Index", id = (string)null },
    5        RouteHandler = typeof(MvcRouteHandler)
    6    });
    
     


     
    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, February 1, 2008 3:54 AM

All replies

  • User961349301 posted

    When I create web apps, I always seperate admin from public sites because of security reasons. But that is for enterprise applications. You can if you want use a Admin folder, but the <location> element will not work with the Rout handler in this drop what I know, but you can put a web.config in the Admin folder to demand a login.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, January 31, 2008 11:49 AM
  • User961349301 posted

    Sorry, the web.config will not work in the Admin folder because the files aren't requested. So in your case I should have seperated the Admin secion as an own application and use Controllers and View in the admin, maybe there is a solution now, but I should probably never put an Admin folder as a sub to a public site.. but that is me ;)

    Thursday, January 31, 2008 11:57 AM
  • User1262457295 posted

    I agree with Fredrik N, I would locate your admin in another project in your solution, running as a separate website on your server (this is what I usually do. It also means I can shut that site off).

    But, in the name of completeness and maybe simplicity, there is a way to do it as a sub folder.

    In /Views/Admin/ add Web.Config with the following data:

    <system.web>
    <authentication>
    <allow users="*" />
    <deny users="?" />
    </authentication>
    </system.web>

    Now your files cannot be requested from the web. But your controllers can still render them, so you can now create an admin controller. In the admin controller you can override OnPreAction to perform a security check and throw a security exception if you are not authorized, thereby stopping your response, and allowing you to catch the exception and render an error page instead.

    I'm not absolutely certain about the security of this method, there may be a hole in it somewhere (and that somewhere is almost certainly either your controller or the controller base class, as the web.config will do what it is supposed to...it is from the WebForms side of things, and is known to work). I would not trust this for a US military site, but for a real simple web app with fairly insensitive data it will probably work.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, January 31, 2008 2:02 PM
  • User-1794225439 posted

    ...for a real simple web app with fairly insensitive data it will probably work.
    This is, IMNSHO, one of the main problems with a lot of the stuff that MS puts out, and this first version of MVC is no different. I realize this question was more than likely asked from this very perspective (a simple site) but the question is perfectly in line with a major shortcoming of this framework (and others) when you contrast it against the stuff that P&P recommends for enterprise development. MS has stated that SMB developers are their main clientèle, but I would then counter with why is that? I work in an IT department (one of many here) for a large Fortune 500 company, and due to the corporate mentality that we all love so much, I work with mostly junior developers trying to create and maintain large enterprise systems. I am, in effect, trying to train myself to be an architect, because there's just nobody else doing it. I would submit that there is a rather large, unaccounted for, portion of MS's clientèle that are just like me: enterprise developers working on large systems with very little or no architectural oversight or guidance. And we turn to the community at large, and MS in particular, to find some of that guidance. And yet, we find that MS is at odds with itself in some ways. Or putting out stuff that just isn't aimed at us. It can be very frustrating. I know this is just the first drop of MVC, so I'm hoping the "if you're doing enterprise development you'll want to use MVC" statement holds true in the end.

    Anyway, now that that's out there... the main point I'm driving at is P&P's guidance concerning composite applications. "Simple" apps will maybe need one little separate admin section, but what about a framework for a large enterprise system that needs many different modules, the ability to plug and unplug modules depending on the customized LOB application built from the framework? Duplicate controllers named Home, indeed!

    Here's what I've done to enable module (or area) separation and routing, maybe it will be of value to you in your simple(r) application or to others trying to build a more complex system. I unfortunately had to start from the top, since the routing engine doesn't handle this stuff in any way that I could figure out. So I wrote my own. I'm using Spring.Net for dependency injection, so you can make direct instantiations of the appropriate classes instead to eliminate that complexity if you aren't into DI. I've omitted some details (methods, properties) for brevity (and this is still a lot of code! Sorry).

    public class ModuleMvcRouteHandler : MvcRouteHandler
    {
    protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
    ModuleMvcHandler handler = new ModuleMvcHandler();
    handler.RequestContext = requestContext;
    return handler;
    }
    }

    class ModuleMvcHandler : IHttpHandler, IRequiresSessionState
    {
    void IHttpHandler.ProcessRequest(HttpContext httpContext)
    {
    this.ProcessRequest(httpContext);
    }

    /// <summary>
    /// Handles the http request
    /// </summary>
    /// <param name="httpContext"></param>
    protected virtual void ProcessRequest(HttpContext httpContext)
    {
    if (this.RequestContext == null)
    {
    throw new InvalidOperationException("The MvcHandler requires that a valid RequestContext be set.");
    }

    string routeModule = GetRequiredString(this.RequestContext.RouteData, "module");
    string routeController = GetRequiredString(this.RequestContext.RouteData, "controller");

    IController controllerInstance = this.GetControllerInstance(routeModule + "." + routeController);
    if (controllerInstance == null)
    {
    throw new InvalidOperationException(string.Format("The controller for path '{0}' could not be found or it does not implement the IController interface.",
    new object[] { this.RequestContext.HttpContext.Request.Path }));
    }

    ControllerContext controllerContext = new ControllerContext(this.RequestContext, controllerInstance);
    controllerInstance.Execute(controllerContext);
    }

    /// <summary>
    /// Creates an instance of the requested controller using the configured IModuleControllerFactory
    /// </summary>
    /// <param name="controllerName"></param>
    /// <returns></returns>
    protected virtual IController GetControllerInstance(string controllerName)
    {
    // instantiate the controller factory IApplicationContext context = ContextRegistry.GetContext(); IModuleControllerFactory controllerFactory = (IModuleControllerFactory)context.GetObject("IModuleControllerFactory");

    // have the factory build the controller return controllerFactory.CreateController(this.RequestContext, controllerName);
    }
    }

    class ModuleControllerFactory : IModuleControllerFactory {
    IController IModuleControllerFactory.CreateController(RequestContext context, string controllerName) {
    return this.CreateController(context, controllerName);
    }

    public virtual IController CreateController(RequestContext context, string controllerName) {
    IApplicationContext objectFactory = ContextRegistry.GetContext();
    IController controller = (IController)objectFactory.GetObject(controllerName, typeof(IController));
    controller = null;

    if (controller != null && typeof(Controller).IsAssignableFrom(controller.GetType())) {
    // can't do this yet, ViewPage.ViewData is Internal :(
    // have to fully qualify view paths in RenderView until can implement custom ViewFactory
    //IViewFactory viewFactory = (IViewFactory)objectFactory.GetObject("IModuleViewFactory", typeof(IViewFactory));
    } return controller;
    }
    }
     
    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, January 31, 2008 7:55 PM
  • User-1794225439 posted

    And these routes are now fully supported. 

    RouteTable.Routes.Add(new Route
    {
      Url = "[module]/[controller].mvc/[action]/[id]",
      Defaults = new { action = "Index", id = (string)null },
      RouteHandler = typeof(ModuleMvcRouteHandler)
    });
    
    RouteTable.Routes.Add(new Route
    {
      Url = "Default.aspx",
      Defaults = new { module = "Shell", controller = "Home", action = "Index", id = (string)null },
      RouteHandler = typeof(ModuleMvcRouteHandler)
    });
     
    Thursday, January 31, 2008 8:02 PM
  • User-1794225439 posted

    I forgot to mention that my solution is fully partitioned into modules (small sampling):

    Solution
       Admin\
        Admin.Views
        Admin.Controllers
        Admin.Interfaces
      Shell\
        Shell.Views
        Shell.Controllers
        Shell.Interfaces
        Shell.WebRoot

    The WebRoot project is where Global.asax lives, and I use MSBuild tasks to copy all files (content and build output) from the module projects into their appropriate place in the WebRoot (dll's into bin\, etc.) since I'm using dependency injection and so don't have any project references (except for the Interfaces projects).
     

    Thursday, January 31, 2008 8:09 PM
  • User-1794225439 posted

     That didn't tie together very well, so here's an example of a controller registration for Spring.Net. 

    <!-- Controllers -->
    <object id="Shell.Home" type="Shell.Controllers.HomeController, Shell.Controllers" />
    
     
    Thursday, January 31, 2008 8:13 PM
  • User-2014110218 posted

    Thanks to all of you.
    As my application is "pretty" simple,  I decided to have an Admin Project in my existing Solution,
    with the following route:

      

    1    RouteTable.Routes.Add(new Route
    2    {
    3        Url = "Admin/[controller]/[action]/[id]",
    4        Defaults = new { action = "Index", id = (string)null },
    5        RouteHandler = typeof(MvcRouteHandler)
    6    });
    
     


     
    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, February 1, 2008 3:54 AM
  • User961349301 posted

    dimi3:

    Great! That is better, at least how I should have done it. Seperate the Admin from the public app..

    Friday, February 1, 2008 4:54 AM
  • User-1794225439 posted

    Thanks to all of you.
    As my application is "pretty" simple,  I decided to have an Admin Project in my existing Solution,
    with the following route:

    1    RouteTable.Routes.Add(new Route
    2 {
    3 Url = "Admin/[controller]/[action]/[id]",
    4 Defaults = new { action = "Index", id = (string)null },
    5 RouteHandler = typeof(MvcRouteHandler)
    6 });
    This is what I looked at doing at first, but I want to allow modules to be added or excluded easily, and all pages/controllers are fully qualified with their module as part of their path (for views) or namespace (for controllers) so I thought it was a better idea to make it default supported functionality.
    Friday, February 1, 2008 2:07 PM
  • User1262457295 posted

    Two points.

    1. CTP (the release stage of MVC right now) stands for Community technical preview. Community, I think, means the community of developers. These releases are meant for developers to play with and get to use and break, with the intent of using, but not necessarily for production purposes. I have no problem developing client's site with CTP products, but I do so with the understanding that the expected ship date of the completed version is before the expected ship date of my site, so that I can get the full version before going public. CTP versions are wonderful things, and they often work very well (some better than others). But by their nature, you cannot assume that Microsoft has done all its homework and created a truly complete and ready to roll framework. The security setup I mentioned probably works, and it is probably very secure, but you can't be absolutely sure yet, and at enterprise level, that's not good enough.
    2. If you can override MvcRouteHandler, why couldn't we make a new MvcRouteHandler that takes authentication into consideration?
     
    public class SecureMvcRouteHandler : MvcRouteHandler
    {
        protected override IHttpHandler GetHttpHandler( RequestContext requestContext )
        {
            if ( !requestContext.HttpContext.User.Identity.IsAuthenticated )
            {
                throw new System.Security.Authentication.AuthenticationException( "Not authenticated" );
            }
            return base.GetHttpHandler( requestContext );
        }
    }
     
    Saturday, February 2, 2008 10:08 AM
  • User-1794225439 posted

    1. CTP (the release stage of MVC right now) stands for Community technical preview. Community, I think, means the community of developers. These releases are meant for developers to play with and get to use and break, with the intent of using, but not necessarily for production purposes. I have no problem developing client's site with CTP products, but I do so with the understanding that the expected ship date of the completed version is before the expected ship date of my site, so that I can get the full version before going public. CTP versions are wonderful things, and they often work very well (some better than others). But by their nature, you cannot assume that Microsoft has done all its homework and created a truly complete and ready to roll framework. The security setup I mentioned probably works, and it is probably very secure, but you can't be absolutely sure yet, and at enterprise level, that's not good enough.
    Was this directed at me? If so, you missed the part where I said I know this is the first drop of a CTP. You also missed where I said this first drop of this CTP correlates perfectly with the overall trend of MS's stuff which my complaint is actually directed at. You also missed the part where I expressed my hopes that this one will be different.
    Sunday, February 3, 2008 1:53 AM