Developer forum

Forum » Development » integrating an mvc app in dw

integrating an mvc app in dw

Remi Muller
Reply

This is experimental but maybe some devs are willing too look into this.

I tried integrating an mvc app in dw.
The main problem seems to be the routing:
//this is loaded from the: Dynamicweb.Notifications.Standard.Application.Start
routes.MapRoute(
                name: "Default",
                url: "CustomModules/mvcapp1/{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
                
            );
 
I suspect that dw authentication and or url handlers are blocking this route.
 
Would it be possible to support this kind of scenario?
That would be awesome.
 
Also have a look at this article where i got the idea:
http://www.hanselman.com/blog/IntegratingASPNETMVC3IntoExistingUpgradedASPNET4WebFormsApplications.aspx
 
FYI i am using mvc4.
 
The idea is to create a custommodule backend which is built with mvc instead of webforms and still be able to access the dw api.
 
 


Replies

 
Scott Forsyth
Reply
Hi Remi,
 
This is a good idea. I've tried to do the same and the solution that I've used for now is with two sites and then using URL Rewrite/IIS7.5 to proxy between them based on the URL.  However, I like your idea of incorporating them into the same site.
 
I'm brainstorming with some people and I'll do some testing and try to find a way around that.  Dynamicweb relies on the 404 handler for rendering the pages so it clashes with the MVC routes.  However, we may be able to reorder the Dynamicweb and MVC routes, or we can exclude certain URLs from being handled by Dynamicweb.
 
I'll get back to you by early next week, hopefully with a solution.
 
Scott
 
 
Remi Muller
Reply
Thanks for looking into the issue! :)
 
Scott Forsyth
Reply
This post has been marked as an answer

Hi Remi,

I have a working solution. I like your idea of tapping into Dynamicweb.Notifications.Standard.Application.Start, but by then the URL has already been mapped, so you'll need to tap in earlier. I'll make a disclaimer that 'this works on my machine' which doesn't promise it will work in every situation.  From what I can tell, it should work for you.

At a high level, the solution is to override global.asax and add the MVC routes. Then in Application_AuthenticateRequest only call the Dynamicweb AuthenticateRequest for URLs that aren't handled by MVC.

To override global.asax first download the Visual Studio templates. 

Info: http://developer.dynamicweb-cms.com/documentation/for-developers/visual-studio-templates.aspx
Download: http://developer.dynamicweb-cms.com/documentation/for-developers.aspx

 

Create a new Visual Studio project with File | New Project, and select Dynamicweb Custom Modules. This will create an example project, which you can delete most everything except for global.asax / global.asax.cs. Make sure that you have a reference to Dynamicweb.dll.

In global.asax.cs, add your MVC routes to Application_Start. 

Then, still in global.asax.cs set Application_AuthenticateRequest to:

        public void Application_AuthenticateRequest(object sender, EventArgs e)
        {

            //exclude all paths already entered by RouteTable.Routes (except the catchall)
            var httpContextBase = new HttpContextWrapper(HttpContext.Current);
            foreach (var routeBase in RouteTable.Routes)
            {
                var routeData = routeBase.GetRouteData(httpContextBase);
                if ((routeData != null) && ((Route)(routeData.Route)).Url != "{*catchall}")
                {
                    //exit and don't load Dynamicweb functionality for this page
                    return;
                }
            }
            
            Dynamicweb.Frontend.GlobalAsaxHandler.Application_AuthenticateRequest(sender, e);
        }

 

What this will do is check for the routes that you created and exit Application_AuthenticateRequest early if the route is already created. That prevents it from being handled by Dynamicweb. This provided a DRY solution so that you only need to create the MVC routes once, and it will work for you.

Note that in your example you called the route name "Default".  That may clash with other routes so just make sure that you have a unique name.

Also, styles, scripts and other dependencies need to be handled the same way. If you want them handled by Dynamicweb then don't create a route.  If you want them handled by MVC then create a route for them. You can also call other MVC features like BundleConfig.RegisterBundles if you so desire.

Once you have this created, copy over your new compiled DLL to the Dynamicweb's /bin folder, and replace global.asax with your new global.asax file.

Here's an example of the global.asax.cs file that I used:

 

using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace TestMvcApplication1
{
    public class TestMvcApplication : System.Web.HttpApplication
    {
        public void Application_Start(object sender, EventArgs e)
        {

            RouteTable.Routes.MapRoute(
                 name: "Default",
                 url: "CustomModules/mvcapp1/{controller}/{action}/{id}",
                 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
                 );

            RouteTable.Routes.MapRoute(
                name: "OtherTestPage",
                url: "othertestpage",
                defaults: new { controller = "Home", action = "OtherTestPage" }
                );

            // Fires when the application is started
            Dynamicweb.Frontend.GlobalAsaxHandler.Application_Start(sender, e);
        }

        public void Session_Start(object sender, EventArgs e)
        {
            // Fires when the session is started
            Dynamicweb.Frontend.GlobalAsaxHandler.Session_Start(sender, e);
        }

        public void Application_BeginRequest(object sender, EventArgs e)
        {
            // Fires at the beginning of each request
            //GlobalAsax.Application_BeginRequest(sender, e);
        }

        public void Application_AuthenticateRequest(object sender, EventArgs e)
        {

            //exclude all paths already entered by RouteTable.Routes (except the catchall)
            var httpContextBase = new HttpContextWrapper(HttpContext.Current);
            foreach (var routeBase in RouteTable.Routes)
            {
                var routeData = routeBase.GetRouteData(httpContextBase);
                if ((routeData != null) && ((Route)(routeData.Route)).Url != "{*catchall}")
                {
                    //exit and don't load Dynamicweb functionality for this page
                    return;
                }
            }
            
            Dynamicweb.Frontend.GlobalAsaxHandler.Application_AuthenticateRequest(sender, e);
        }

        public void Application_Error(object sender, EventArgs e)
        {
            // Fires when an error occurs
            Dynamicweb.Frontend.GlobalAsaxHandler.Application_Error(sender, e);
        }

        public void Session_End(object sender, EventArgs e)
        {
            // Fires when the session ends
            Dynamicweb.Frontend.GlobalAsaxHandler.Session_End(sender, e);
        }

        public void Application_End(object sender, EventArgs e)
        {
            // Fires when the application ends
            Dynamicweb.Frontend.GlobalAsaxHandler.Application_End(sender, e);
        }

        public void Application_OnPreRequestHandlerExecute(object sender, EventArgs e)
        {
            //Dynamicweb.Frontend.GlobalAsaxHandler.Application_OnPreRequestHandlerExecute(sender, e);
        }

    }
}

 

 

 

Votes for this answer: 1
 
Remi Muller
Reply
I have to try this out. It looks promising. Thanks!
 
Scott Forsyth
Reply

Hi Remi,

Have you had a chance to try this yet? I'm curious to know how it worked for you.

Scott

 
Remi Muller
Reply

I have a working prototype. The dw application needs some references to mvc and the web config needs some additions for mvc etc.

I tried using the filemanager control and binding it to a model property. I can load the control but i could not set the value. I think i need to make my own filemanager control.

I have updated the global asax code. The version above disabled dw authentication completely.

See the code:

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using System.Web.Routing;
using System.Web.Mvc;

namespace dwMVCRoutes
{
    public class Global : System.Web.HttpApplication
    {
        public void Application_Start(object sender, EventArgs e)
        {
            
            RouteTable.Routes.MapRoute(
                 name: "mvcapp1",
                 url: "CustomModules/mvcapp1/{controller}/{action}/{id}",
                 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
                 );
            
            /*
            RouteTable.Routes.MapRoute(
                name: "OtherTestPage",
                url: "othertestpage",
                defaults: new { controller = "Home", action = "OtherTestPage" }
                );
            */
            // Fires when the application is started
            Dynamicweb.Frontend.GlobalAsaxHandler.Application_Start(sender, e);
        }

        public void Session_Start(object sender, EventArgs e)
        {
            // Fires when the session is started
            Dynamicweb.Frontend.GlobalAsaxHandler.Session_Start(sender, e);
        }

        public void Application_BeginRequest(object sender, EventArgs e)
        {
            // Fires at the beginning of each request
            //GlobalAsax.Application_BeginRequest(sender, e);
            Dynamicweb.Frontend.GlobalAsaxHandler.Application_BeginRequest(sender, e);
        }

        public void Application_AuthenticateRequest(object sender, EventArgs e)
        {

            if (IsMvcRequest()) return;

            Dynamicweb.Frontend.GlobalAsaxHandler.Application_AuthenticateRequest(sender, e);
        }

        private static bool IsMvcRequest()
        {
            //exclude all paths already entered by RouteTable.Routes (except the catchall)
            var httpContextBase = new HttpContextWrapper(HttpContext.Current);
            foreach (var routeBase in RouteTable.Routes)
            {
                var routeData = routeBase.GetRouteData(httpContextBase);
                if ((routeData != null) && ((Route)(routeData.Route)).Url != "{*catchall}")
                {
                    //exit and don't load Dynamicweb functionality for this page
                    return true;
                }
            }
            return false;
        }

        public void Application_Error(object sender, EventArgs e)
        {
            // Fires when an error occurs
            Dynamicweb.Frontend.GlobalAsaxHandler.Application_Error(sender, e);
        }

        public void Session_End(object sender, EventArgs e)
        {
            // Fires when the session ends
            Dynamicweb.Frontend.GlobalAsaxHandler.Session_End(sender, e);
        }

        public void Application_End(object sender, EventArgs e)
        {
            // Fires when the application ends
            Dynamicweb.Frontend.GlobalAsaxHandler.Application_End(sender, e);
        }

        public void Application_OnPreRequestHandlerExecute(object sender, EventArgs e)
        {
            if (IsMvcRequest()) return;
            Dynamicweb.Frontend.GlobalAsaxHandler.Application_OnPreRequestHandlerExecute(sender, e);
        }
    }
}

 

 
Remi Muller
Reply

Also when using views i have to copy them into the dw application then it works.

The following code is called from a editor template. Can you pinpoint the problem of initializing the value on the Filemanager control?

see code:

public static class DynamicwebControls
    {
        public static HtmlString DwTestControl(string value)
        {
            var ctrl = new Dynamicweb.Controls.FileManager();

            ctrl.ID = "txtDwFile";
            
            StringWriter sw = new StringWriter();
            var htmlwriter = new HtmlTextWriter(sw); 
            ctrl.RenderControl(htmlwriter);
            ctrl.Value = value;
            ctrl.File = value;
            ctrl.Folder = "/Files/Images";
            
            return new HtmlString(sw.ToString());
        }
    }

 

 
Nicolai Høeg Pedersen
Reply

Hi Remi

 

Interesting! Thanks for sharing, and please post if you find more interesting stuff.

 

BR Nicolai

 
Scott Forsyth
Reply

Hi Remi,

Thanks for the update. I'll get an answer for you on this. I setup a repro so that I can see what you see and the control is loaded but it's disabled. Is that what you see too? I ran out of time though and now I'm travelling for two weeks. Are you ok if I get the answer when I get back or is it holding up a release?

Thanks,

Scott

 
Remi Muller
Reply

Hi,

I can load the filemanager control and use the file selector. Only problem is binding a value from the model.
I have been very busy lately. So let's have a look at it when your back. No rush :)

 
Scott Forsyth
Reply

Hi Remi,

That sounds good. I'll follow up when I get back.

Scott

 
Scott Forsyth
Reply

Hi Remi,

I wanted to follow-up and say that I haven't forgotten about you and the MVC integration. I keep snoozing my reminder to get back to you due to not having enough time yet. It's going to take me a while to get the full testing environment setup where I can debug this and I haven't been able to do that yet, but I haven't forgotten.

Scott

 

 
Remi Muller
Reply

Hi Scott, i have been snoozing the project as well. It is still on my radar.

 
Scott Forsyth
Reply

We'll get it figured then. I worked for a while on it a ways back but I had something off in the merged winforms and MVC project and it didn't work, so I just need to start again. Once it's figured it should be an easy answer.

 

 
Scott Forsyth
Reply

Hi Remi,

Is this still something that you are interested in? There is a lot of focus on MVC in future versions of Dynamicweb and there is some support today with some effort, but I apologize that I haven't sat down to provide you with an answer yet. Can you wait for the functionality to be a first-class citizen in Dynamicweb, or are you still working to accomplish this now?

Thanks,

Scott

 

 

 

 

 

 

 
Remi Muller
Reply

I am still interested but is did not have my attention recently. Not sure if it will on the short term.

What are your plans regarding mvc for future versions 8.4, 8.5 etc?

 
Scott Forsyth
Reply

Hi Remi,

I received your reply by email but the post is missing now so I'll reply to the ghost post. The details are being worked out, but I believe that you'll see exactly what you're looking for in the 8.4 (or maybe 8.5 but hopefully earlier) time frame. There's a lot of discussion and planning going on to make this as natural feeling and exciting for MVC developers as possible.

Scott

 

 

 
Remi Muller
Reply

Thanks for answering. Good to hear there are developments towards mvc developers.

(The post was not missing by the way. It is on page 2 ;)

 

 
Scott Forsyth
Reply

Hi Remi,

Indeed, here's the thread on page 2! 

Scott

 

You must be logged in to post in the forum