Search code examples
url-rewritingroutesdotnetnukednn9

Route used by DNN module being ignored because request URL intercepted by friendly URL provider


I have created a custom DNN module which uses routing for some functionality.

I register a single route with the pattern: Data/{table}/{action}.aspx (I use a class implementing DNN's IServiceRouteMapper, which has a RegisterRoutes method which is called as DNN starts up).

The route is registered successfully and can be seen in the RouteTable on every page request.

However, every request that should be matched to the route results in DNN showing its 404 error page. In other words, DNN seems to be deciding that the request URL ought to match a DNN page, but doesn't.

If I change the friendly URL provider configuration from urlFormat="advanced" to urlFormat="searchfriendly", then the routing works successfully.

My conclusion is that the DNN friendly URL provider (which is implemented as an HTTP module) intercepts the request first (i.e. before route matching can kick in), and when in "advanced" mode assumes it is a page URL, then tries to find a matching page in the CMS, and fails. However in "searchfriendly" mode it lets it go so route matching can look at the request.

I want to keep the friendly URL provider in "advanced" mode (because the URLs are much cleaner). I also want to find some simple solution that doesn't involve e.g. writing my own friendly URL provider!

In the web.config, URL routing appears to be in the pipeline before the friendly URL provider, so I am confused why the above happens:

<modules runAllManagedModulesForAllRequests="true">
  <remove name="UrlRoutingModule-4.0" />
  <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="managedHandler" />
  <add name="RequestFilter" type="DotNetNuke.HttpModules.RequestFilter.RequestFilterModule, DotNetNuke.HttpModules" preCondition="managedHandler" />
  <add name="UrlRewrite" type="DotNetNuke.HttpModules.UrlRewriteModule, DotNetNuke.HttpModules" preCondition="managedHandler" />

EDIT: adding the IServiceRouteMapper implementation as requested, i.e. where the route gets registered. Navigator.SiteRootFolder is a constant that returns "Data".

public void RegisterRoutes(IMapRoute mapRouteManager)
{
    // This method is called automatically by DNN on startup
    RegisterRoutes(RouteTable.Routes);
}

private static void RegisterRoutes(RouteCollection routes)
{
    if (Global.CanCreateDataContext())
    {
        Global.MetaModel.RegisterContext(
            new EFDataModelProvider(() => Global.CreateDataContext(Global.Context.DataContextAssemblyLocation)),
            new ContextConfiguration { ScaffoldAllTables = Global.Context.ScaffoldAllTables });

        Global.MetaModel.DynamicDataFolderVirtualPath = string.Format("~/{0}/DynamicData", Navigator.SiteRootFolder);

        routes.Add(
            new DynamicDataRoute(string.Format("{0}/{{table}}/{{action}}.aspx", Navigator.SiteRootFolder))
            {
                Constraints = new RouteValueDictionary(new { action = "List|Details|Edit|Insert" }),
                Model = Global.MetaModel
            });
    }
}

Solution

  • That is really a non-standard way of creating services in DNN. Normally you would use a simple route mapper that implements DotNetNuke.Web.Api.IServiceRouteMapper like this:

    public class ServiceRouteMapper : IServiceRouteMapper
    {   public void RegisterRoutes(IMapRoute mapRouteManager)
        {
            mapRouteManager.MapHttpRoute(
                moduleFolderName: "MyModule",
                routeName: "default", 
                url: "{controller}/{action}",
                namespaces: new[] { "MyCompany.Dnn.MyModule.Controllers" });
        }
    }
    

    This would result in the following route:

    /DesktopModules/MyModule/Api/Data/List (assuming you had a controller class named "DataController" and a method named "list".

    The reason this doesn't interfere with DNN's Friendly Url provider is because "/DesktopModules" is a reserved path ignored by the Url provider.

    Now, if you insist on having the completely custom routes as you have, you could add an path ignore to the Url Provider. To do this, you need to add (or update if it exists) a record in the HostSettings table with SettingName = "AUM_DoNotRewriteRegEx". Below is an example script that can add the path "/Data" to the ignore list.

    IF NOT EXISTS (SELECT * FROM HostSettings WHERE SettingName = 'AUM_DoNotRewriteRegEx' )
    BEGIN
        INSERT INTO HostSettings (SettingName, SettingValue, SettingIsSecure, CreatedByUserId, CreatedOnDate, LastModifiedByUserId, LastModifiedOnDate)
        VALUES('AUM_DoNotRewriteRegEx','/DesktopModules/|/Providers|/LinkClick\.aspx|/Data', 0, -1, GETDATE(), -1, GETDATE())
    END
    ELSE
    BEGIN
        UPDATE HostSettings SET SettingValue = (select SettingValue + '|/Data' from HostSettings where settingname = 'AUM_DoNotRewriteRegEx')
        where SettingName = 'AUM_DoNotRewriteRegEx'
    END