locked
SPWebConfigModification -> Adding a new HttpModule above built-in HttpModules RRS feed

  • Question

  • Hello,

    Using a feature & the SPWebConfigModification class, I'm trying to find a way to register an HttpModule in SharePoint and have the declaration come before the built-in module declarations.

    I can successfully add my new HttpModule to the modules tag right now, but it ends up being the last module registered, and I need mine to be registered first.

    Here is the current result:  (My Module is called "MySiteRedirectModule")

     
     <modules runAllManagedModulesForAllRequests="true">
    <remove name="AnonymousIdentification" />
    <remove name="FileAuthorization" />
    ...(More Remove tags)
    <add name="SPRequestModule" preCondition="integratedMode" type="Microsoft.SharePoint.ApplicationRuntime.SPRequestModule, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
    <add name="ScriptModule" preCondition="integratedMode" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    <add name="SharePoint14Module" preCondition="integratedMode" />
    ...(More Add tags)
    <add name="MySitesRedirectModule" type="Common.MySitesRedirectModule, Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=123123" />
    </modules>

     

    But I need it to be:(Notice that I've got the MySiteRedirectModule at the top of the add tags)

    <modules runAllManagedModulesForAllRequests="true">
    <remove name="AnonymousIdentification" />
    <remove name="FileAuthorization" />
    ...(More Remove tags)
    <add name="MySitesRedirectModule" type="Common.MySitesRedirectModule, Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=123123" />
    <add name="SPRequestModule" preCondition="integratedMode" type="Microsoft.SharePoint.ApplicationRuntime.SPRequestModule, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
    <add name="ScriptModule" preCondition="integratedMode" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    <add name="SharePoint14Module" preCondition="integratedMode" />
    ...(More Add tags)
    </modules>

     

    If you're wondering why I need my module to be added first -> I'm using the RewritePath() method in my BeginRequest handler.  If I get a request for a page like "/person.aspx", I'm trying to rewrite the path to "/_layouts/Folder/CustomPerson.aspx", but this won't work unless my module is processed first.


    Thanks in advance for any help!

    Jason

     

     

    Wednesday, February 2, 2011 6:29 PM

Answers

  • The developer has confirmed that there is no way in the SharePoint object model to control the order of the nodes.
    Rick - MSFT
    Sunday, February 6, 2011 3:18 AM

All replies

  • I don't able to reproduce your issue, perhaps better to share you httpmodule code. I have created a new SharePoint Server 2010 WebApplication and then a new Publishing SiteCollection. I develop custom HttpModule as mentioned here http://msdn.microsoft.com/en-us/library/aa719858(v=vs.71).aspx. Lastly I have make an entry in the Web.Config. As for the sequence custom module seems to be executing first. If you share the code I can give bit more concrete response. Also in the past I have written number of redirection modules and they seem to work fine. 

     <modules runAllManagedModulesForAllRequests="true">
       <remove name="AnonymousIdentification" />
       <remove name="FileAuthorization" />
       <remove name="Profile" />
       <remove name="WebDAVModule" />
       <remove name="Session" />
     
       <!-- Custom HttpModule -->
       <add name="HelloWorldModule" 
               type="HelloWorldModule, HelloWorldModule" />
       
       <add name="SPRequestModule" preCondition="integratedMode" type="Microsoft.SharePoint.ApplicationRuntime.SPRequestModule, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
       <add name="ScriptModule" preCondition="integratedMode" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
       <add name="SharePoint14Module" preCondition="integratedMode" />
       <add name="StateServiceModule" type="Microsoft.Office.Server.Administration.StateModule, Microsoft.Office.Server, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
       <add name="RSRedirectModule" type="Microsoft.ReportingServices.SharePoint.Soap.RSRedirectModule, RSSharePointSoapProxy, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" />
       <add name="PublishingHttpModule" type="Microsoft.SharePoint.Publishing.PublishingHttpModule, Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      </modules>
      <handlers>
    


    Microsoft SharePoint MVP |http://razirais.wordpress.com| MCT| MCPD SharePoint 2010 Developer| MCITP SharePoint Administrator 2010
    Wednesday, February 2, 2011 10:38 PM
  • If I understand you correctly, you made your web.config entry by hand, is that correct?  If so, that's a big problem...As I mentioned in my post, I'm using a Feature & the SPWebConfigModification class to automate the web.config modifications. 


    As a side note -> If I make the web.config modifications by hand & place my HttpModule registration before the other HttpModules, my code works just fine.  That's why I'm wondering if this is possible with the SPWebConfigModification class.


    Jason

    Wednesday, February 2, 2011 11:03 PM
  • Yes by hand and I have also tested it using IIS 7.5 Manager to register the module and both worked same way. In your case the best way to tell is to look at your code that you used to register HttpModule. SPWebConfigModification may cause some issues as mentioned at http://blog.crsw.com/2007/09/20/how-to-modify-the-web-config-file-in-sharepoint-using-spwebconfigmodification/ and http://blogs.devhorizon.com/reza/?p=459
    Microsoft SharePoint MVP |http://razirais.wordpress.com| MCT| MCPD SharePoint 2010 Developer| MCITP SharePoint Administrator 2010
    Wednesday, February 2, 2011 11:12 PM
  • Here's the code I used to register my HttpModule (via a Feature Receiver):

      [Guid("c407b744-5826-4672-8ed8-dff775e58440")]
      public class MySitesEventReceiver : SPFeatureReceiver
      {
        private const string _cWebConfigModificationOwner = "MySites.MySitesRedirectModule";
    
        SPWebConfigModification spWebConfigModification = new SPWebConfigModification()
        {
          // The owner of the web.config modification, useful for removing a 
          // group of modifications
          Owner = _cWebConfigModificationOwner,
          // Make sure that the name is a unique XPath selector for the element 
          // we are adding. This name is used for removing the element
          Name = "add[@name='MySitesRedirectModule']",
          // We are going to add a new XML node to web.config
          Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode,
          // The XPath to the location of the parent node in web.config
          Path = "configuration/system.webServer/modules[0]",
          // Sequence is important if there are multiple equal nodes that 
          // can't be identified with an XPath expression
          Sequence = 0,
          // The XML to insert as child node, make sure that used names match the Name selector
          Value = "<add name='MySitesRedirectModule' type='Common.MySitesRedirectModule, Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ea5eab43fffd1a2a' />"
        };
    
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
          SPWebApplication webApp = properties.Feature.Parent as SPWebApplication;
          if (webApp != null)
          {
            AddWebConfigModifications(webApp, spWebConfigModification);
          }
        }
    
        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
          SPWebApplication webApp = properties.Feature.Parent as SPWebApplication;
          if (webApp != null)
          {
            RemoveWebConfigModificationsByOwner(webApp, _cWebConfigModificationOwner);
          }
        }
        
        // Uncomment the method below to handle the event raised after a feature has been installed.
    
        //public override void FeatureInstalled(SPFeatureReceiverProperties properties)
        //{
        //}
    
    
        // Uncomment the method below to handle the event raised before a feature is uninstalled.
    
        //public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
        //{
        //}
    
        // Uncomment the method below to handle the event raised when a feature is upgrading.
    
        //public override void FeatureUpgrading(SPFeatureReceiverProperties properties, string upgradeActionName, System.Collections.Generic.IDictionary<string, string> parameters)
        //{
        //}
    
        /// <summary>
        /// Add a collection of web modifications to the web application
        /// </summary>
        /// <param name="webApp">The web application to add the modifications to</param>
        /// <param name="modifications">The collection of modifications</param>
        private void AddWebConfigModifications(SPWebApplication webApp, SPWebConfigModification modification)
        {
          System.Diagnostics.Debugger.Break();
    
          webApp.WebConfigModifications.Insert(0, modification);
    
          // Commit modification additions to the specified web application
          webApp.Update();
    
          // Push modifications through the farm
          SPFarm.Local.Services.GetValue<SPWebService>(webApp.Parent.Id).ApplyWebConfigModifications();
        }
    
        /// <summary>
        /// Remove modifications from the web application
        /// </summary>
        /// <param name="webApp">The web application to remove the modifications from</param>
        /// <param name="owner"Remove all modifications that belong to the owner></param>
        private void RemoveWebConfigModificationsByOwner(SPWebApplication webApp, string owner)
        {
          Collection<SPWebConfigModification> modificationCollection = webApp.WebConfigModifications;
          Collection<SPWebConfigModification> removeCollection = new Collection<SPWebConfigModification>();
    
          int count = modificationCollection.Count;
          for (int i = 0; i < count; i++)
          {
            SPWebConfigModification modification = modificationCollection[i];
            if (modification.Owner == owner)
            {
              // collect modifications to delete
              removeCollection.Add(modification);
            }
          }
    
          // now delete the modifications from the web application
          if (removeCollection.Count > 0)
          {
            foreach (SPWebConfigModification modificationItem in removeCollection)
            {
              webApp.WebConfigModifications.Remove(modificationItem);
            }
    
            // Commit modification removals to the specified web application
            webApp.Update();
    
            // Push modifications through the farm
            SPFarm.Local.Services.GetValue<SPWebService>(webApp.Parent.Id).ApplyWebConfigModifications();
          }
        }
    
      }

    Wednesday, February 2, 2011 11:30 PM
  • I have forwarded this to a former developer on the SharePoint team who knows SPWebConfigModifications class very well. My hunch is that you are seeing default behavior that cannot be changed with the SharePoint object model and you will have to resort to one of the .NET XML document handling APIs.  But with luck he will respond with an easy solution.
    Rick - MSFT
    Thursday, February 3, 2011 8:37 PM
  • Thank you!  I suspect that this is the default behavior, but I'd be happy to get that confirmed!

    Jason
    Thursday, February 3, 2011 8:40 PM
  • The developer has confirmed that there is no way in the SharePoint object model to control the order of the nodes.
    Rick - MSFT
    Sunday, February 6, 2011 3:18 AM
  • Thanks for the confirmation Rick!
    Monday, February 7, 2011 2:54 PM
  • How about the following in HttpModule.Init:

     

     IList<EventHandler> l = GetCurrentDelegatesForEvent(context, "Error", "EventErrorRecorded", "Events");

                // Remove existing handlers.
                foreach (EventHandler eh in l)
                {
                    context.Error -= eh;
                }

                // Add our handler first.
                context.Error += new EventHandler(HandleApplicationError);

                // Add removed handlers after ours.
                foreach (EventHandler eh in l)
                {
                    context.Error += eh;
                }

     

    and

     

            /// <summary>
            /// Use reflection to obtain list of current delegates for an event.
            /// </summary>
            /// <param name="context"></param>
            /// <param name="eventName"></param>
            /// <param name="eventObjectFieldName"></param>
            /// <param name="eventsPropertyName"></param>
            /// <returns></returns>
            private IList<EventHandler> GetCurrentDelegatesForEvent(HttpApplication context, string eventName, string eventObjectFieldName, string eventsPropertyName)
            {
                IList<EventHandler> l = new List<EventHandler>();

                try
                {
                    BindingFlags bf = BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;

                    Type dt = null;
                    Type t = context.GetType();
                    foreach (EventInfo ei in t.GetEvents(bf))
                    {
                        if (ei.Name == eventName)
                        {
                            dt = ei.DeclaringType;
                            break;
                        }
                    }

                    if (dt != null)
                    {

                        FieldInfo fiEER = dt.GetField(eventObjectFieldName, bf);

                        if (fiEER != null)
                        {
                            object eer = fiEER.GetValue(context);
                            if (eer != null)
                            {
                                PropertyInfo piEvents = dt.GetProperty(eventsPropertyName, bf);

                                if (piEvents != null)
                                {
                                    object ehlo = piEvents.GetValue(context, null);

                                    if (ehlo != null)
                                    {
                                        EventHandlerList ehl = ehlo as EventHandlerList;

                                        if (ehl != null)
                                        {
                                            object handler = ehl[eer];

                                            if (handler != null)
                                            {
                                                EventHandler mHandler = handler as EventHandler;
                                                if (mHandler != null)
                                                {
                                                    foreach (EventHandler eh in mHandler.GetInvocationList())
                                                    {
                                                        l.Add(eh);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    Log.Error("Failure obtaining current delegates for Error Event", ex);
                }

                return l;
            }

     

     

     

     

     

    • Proposed as answer by Badajoz95 Tuesday, November 13, 2012 11:30 PM
    Friday, February 25, 2011 1:29 AM
  • Wow - That's an interesting approach, but we ended up just making the web.config modification part of our initial configuration process.  (We're using a custom app to deploy packages, so we just added a module on to that to modify the web.config file(s).
    Friday, February 25, 2011 4:20 PM
  • You could use the HttpModuleSectionCode

    // Get the Web application configuration.
    Configuration configuration = WebConfigurationManager.OpenWebConfiguration("/aspnetTest");
    
    // Get the HttpModulesSection.
    HttpModulesSection httpModulesSection = (HttpModulesSection) configuration.GetSection("system.web/httpModules");
    
    

    Get a list of all the modules currently configured, get the names of each module, (e.g. foreach loop)

    Then Create SPWebConfigurationModification objects for each of the existing HTTPModules,

    And then use the SP class to remove them all.

    Next run EnsureChildNode on your httpmodule,

    Then use EnsureChildNode to Add the originals back which should then go After your Module.

    Make sure you set sequence on your Config Modification objects, give yours a sequence of 0 and set the ones you removed to like 1 or something, so the config class will do yours first.

     


    My Blog: http://www.thesug.org/Blogs/ryan_mann1/default.aspx Website: Under Construction
    Saturday, February 26, 2011 5:15 AM
  • Another thing you could do,

     

    Create your own custom web.configuration section by Inheriting ConfigurationSection in System.Configuration.  Then in your config sections constructor,

    Use

    // Get the HttpModulesSection.
    HttpModulesSection httpModulesSection = (HttpModulesSection) configuration.GetSection("system.web/httpModules");
    
    

    To insert your httpmodule entry at the top of the collection.

    Then in your web config modification, Add your custom ConfigSection to

    configSection/sectionGroup @Name="SharePoint"

    The SharePoint section group is at the top of the Web.Config, so it loads before the HttpModules Section,

    If I understand correctly, the entire file is loaded into memory before it's parsed, and it's parsed in memory, So you could do the modification after it's loaded without actually storing your httpmodule in the actual file.

     

    This is just a theory, I haven't tested this idea yet, but it sounds solid.


    My Blog: http://www.thesug.org/Blogs/ryan_mann1/default.aspx Website: Under Construction
    Saturday, February 26, 2011 5:21 AM
  • Wow - a couple of good ideas...Unfortunately, I'm under a deadline and don't have time to test them out right now-> I guess we'll have to leave these posts here, and if someone else has the same issue/question, they'll be able to try it out!


    Thanks!

    Monday, February 28, 2011 7:21 PM
  • Very good job APOLK66 !

    I implemented your solution in my Init Http Module and your solution works very well :)

    Thank you again !

    Saturday, June 18, 2011 11:40 AM
  • 
    
    

    apolk66's answer word for me.

    Resequencing the event handler seems like a good work around to not being able to do this using the SPWebConfigModification class, and manually hacking the web.config is not an option I would promote.


    • Edited by Badajoz95 Tuesday, November 13, 2012 11:33 PM
    Tuesday, November 13, 2012 11:33 PM