none
Custom HTML Help Page for httpGet for an IIS6 Hosted WCF Service

    Question

  • Hi,

     

    I am trying to get a custom HTML help page working for a WCF service hosted by IIS6 with ASP.NET compatiblity enabled by using the httpHelpPageUrl attribute such as

     

    <behaviors>

    <serviceBehaviors>

    <behavior name="MXGet">

    <serviceMetadata httpGetEnabled="true" />

    <serviceDebug includeExceptionDetailInFaults="true" httpHelpPageEnabled="true" httpHelpPageUrl="/MyServiceHelp.htm" />

    </behavior>

    </serviceBehaviors>

    </behaviors>

     

    But whenever a value is assigned to the httpHelpPageUrl attribute, an http request to http://localhost/Myservice.svc brings up the WSDL directly instead of the "you have created a service" default help page.   I was expecting to see my custom help page, but it didn't show up.

     

    Does anyone know how the httpHelpPageUrl attribute is supposed to work, or how to get it to work? I could not find any sample that uses this attribute either.

     

    Thanks,

    Wenbin Zhang

     

     

    Sunday, June 03, 2007 6:52 PM

Answers

  • I believe the httpHelpPageUrl lets you move the default WCF help page to another location.  If you want to host your own help page at the base address, you probably need to turn off httpGetEnabled and then at the service base address host your own http-GET WCF endpoint that serves up HTML content.
    Sunday, June 03, 2007 10:02 PM
  • Here is sample code:

     

    Code Snippet

    using System;

    using System.ServiceModel;

    using System.ServiceModel.Description;

    using System.ServiceModel.Dispatcher;

    using System.ServiceModel.Channels;

    using System.Xml;

     

    [ServiceContract]

    public interface IMyContract

    {

        [OperationContract]

        string echo(string s);

    }

     

    [ServiceContract]

    public interface IMyHelpPageContract

    {

        [OperationContract(Action="*", ReplyAction="*")]

        Message Help();

    }

     

    [ServiceBehavior(ConfigurationName = "foo")]

    public class MyService : IMyContract, IMyHelpPageContract

    {

        public string echo(string s) { return s; }

        public Message Help()

        {

            return new MyHelpPageMessage();

        }

    }

     

    abstract class ContentOnlyMessage : Message

    {

        MessageHeaders headers;

        MessageProperties properties;

     

        protected ContentOnlyMessage()

        {

            this.headers = new MessageHeaders(MessageVersion.None);

        }

     

        public override MessageHeaders Headers

        {

            get

            {

                if (IsDisposed)

                {

                    throw new ObjectDisposedException("blah");

                }

                return this.headers;

            }

        }

     

        public override MessageProperties Properties

        {

            get

            {

                if (IsDisposed)

                {

                    throw new ObjectDisposedException("blah");

                }

                if (this.properties == null)

                {

                    this.properties = new MessageProperties();

                }

                return this.properties;

            }

        }

     

        public override MessageVersion Version

        {

            get

            {

                return headers.MessageVersion;

            }

        }

     

        protected override void OnBodyToString(XmlDictionaryWriter writer)

        {

            OnWriteBodyContents(writer);

        }

    }

     

    class MyHelpPageMessage : ContentOnlyMessage

    {

        public MyHelpPageMessage()

            : base()

        {

        }

     

        protected override void OnWriteBodyContents(XmlDictionaryWriter writer)

        {

            // write the HTML you want on the help page here.  could read from external file if you want

            writer.WriteStartElement("HTML");

            writer.WriteStartElement("HEAD");

            writer.WriteRaw("");

            writer.WriteEndElement(); //HEAD

            writer.WriteRaw(@"

    This is my own help page


    I can put whatever content I want here

    ");

            writer.WriteEndElement(); //HTML

        }

    }

     

    public class Repro

    {

        public static void Main()

        {

            ServiceHost service = new ServiceHost(typeof(MyService));

            service.Description.Behaviors.Remove<ServiceDebugBehavior>();  // need to turn off default help page so as to get our own

            service.Open();

     

            Console.WriteLine("Service open at {0} - press a key to continue", service.BaseAddresses[0]);

            Console.ReadKey();

     

            ChannelFactory<IMyContract> cf = new ChannelFactory<IMyContract>("c1");

            IMyContract proxy = cf.CreateChannel();

            Console.WriteLine(proxy.echo("Hello World"));

            ((IClientChannel)proxy).Close();

            service.Close();

            Console.WriteLine("Done, press a key");

            Console.ReadKey();

        }

    }

     

    Here is the config:

     

    Code Snippet

    <?xml version="1.0" encoding="utf-8"?>

    <configuration>

      <system.serviceModel>

        <services>

          <service name="foo">

            <host>

              <baseAddresses>

                <add baseAddress="http://localhost:8000/Service/"/>

              </baseAddresses>

            </host>

            <endpoint address="E1"

                      binding="basicHttpBinding"

                      bindingConfiguration="b"

                      contract="IMyContract"/>

            <endpoint address=""

                      binding="customBinding"

                      bindingConfiguration="helpPage"

                      contract="IMyHelpPageContract"/>

          </service>

        </services>

        <client>

          <endpoint address="http://localhost:8000/Service/E1"

                    binding="basicHttpBinding"

                    bindingConfiguration="b"

                    contract="IMyContract"

                    name="c1"/>

        </client>

        <bindings>

          <customBinding>

            <binding name="helpPage">

              <textMessageEncoding messageVersion ="None" />

              <httpTransport/>

            </binding>

          </customBinding>

          <basicHttpBinding>

            <binding name="b"/>

          </basicHttpBinding>

        </bindings>

      </system.serviceModel>

    </configuration>

     

    Run it, and then hit the base address in a browser, and you'll see the custom help page.

     

    See the sample http://msdn2.microsoft.com/en-us/library/aa395208.aspx for some more details on sending back arbitrary responses over HTTP GET.

     

    (Doing this kind of stuff (GET/POX) will be much easier in Orcas.)

     

    Monday, June 04, 2007 9:23 PM
  •  Edgardo Rossetto wrote:
    Is there any way to call WriteRaw() directly? I'm getting "Text cannot be written outside the root element."

     

    Code Block

    protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
    {
        writer.WriteStartElement("<html>");
        writer.WriteRaw("<head></head><body><p>Hi</p></body>");

        writer.WriteEndElement();
    }

     

    worked for me.
    Tuesday, January 22, 2008 3:42 AM

All replies

  • I believe the httpHelpPageUrl lets you move the default WCF help page to another location.  If you want to host your own help page at the base address, you probably need to turn off httpGetEnabled and then at the service base address host your own http-GET WCF endpoint that serves up HTML content.
    Sunday, June 03, 2007 10:02 PM
  • Brian, thanks for the quick response.

     

    I'd really appreciate some sample code/configuration for that.

     

    Thnaks,

    Wenbin

    Monday, June 04, 2007 1:20 PM
  • Here is sample code:

     

    Code Snippet

    using System;

    using System.ServiceModel;

    using System.ServiceModel.Description;

    using System.ServiceModel.Dispatcher;

    using System.ServiceModel.Channels;

    using System.Xml;

     

    [ServiceContract]

    public interface IMyContract

    {

        [OperationContract]

        string echo(string s);

    }

     

    [ServiceContract]

    public interface IMyHelpPageContract

    {

        [OperationContract(Action="*", ReplyAction="*")]

        Message Help();

    }

     

    [ServiceBehavior(ConfigurationName = "foo")]

    public class MyService : IMyContract, IMyHelpPageContract

    {

        public string echo(string s) { return s; }

        public Message Help()

        {

            return new MyHelpPageMessage();

        }

    }

     

    abstract class ContentOnlyMessage : Message

    {

        MessageHeaders headers;

        MessageProperties properties;

     

        protected ContentOnlyMessage()

        {

            this.headers = new MessageHeaders(MessageVersion.None);

        }

     

        public override MessageHeaders Headers

        {

            get

            {

                if (IsDisposed)

                {

                    throw new ObjectDisposedException("blah");

                }

                return this.headers;

            }

        }

     

        public override MessageProperties Properties

        {

            get

            {

                if (IsDisposed)

                {

                    throw new ObjectDisposedException("blah");

                }

                if (this.properties == null)

                {

                    this.properties = new MessageProperties();

                }

                return this.properties;

            }

        }

     

        public override MessageVersion Version

        {

            get

            {

                return headers.MessageVersion;

            }

        }

     

        protected override void OnBodyToString(XmlDictionaryWriter writer)

        {

            OnWriteBodyContents(writer);

        }

    }

     

    class MyHelpPageMessage : ContentOnlyMessage

    {

        public MyHelpPageMessage()

            : base()

        {

        }

     

        protected override void OnWriteBodyContents(XmlDictionaryWriter writer)

        {

            // write the HTML you want on the help page here.  could read from external file if you want

            writer.WriteStartElement("HTML");

            writer.WriteStartElement("HEAD");

            writer.WriteRaw("");

            writer.WriteEndElement(); //HEAD

            writer.WriteRaw(@"

    This is my own help page


    I can put whatever content I want here

    ");

            writer.WriteEndElement(); //HTML

        }

    }

     

    public class Repro

    {

        public static void Main()

        {

            ServiceHost service = new ServiceHost(typeof(MyService));

            service.Description.Behaviors.Remove<ServiceDebugBehavior>();  // need to turn off default help page so as to get our own

            service.Open();

     

            Console.WriteLine("Service open at {0} - press a key to continue", service.BaseAddresses[0]);

            Console.ReadKey();

     

            ChannelFactory<IMyContract> cf = new ChannelFactory<IMyContract>("c1");

            IMyContract proxy = cf.CreateChannel();

            Console.WriteLine(proxy.echo("Hello World"));

            ((IClientChannel)proxy).Close();

            service.Close();

            Console.WriteLine("Done, press a key");

            Console.ReadKey();

        }

    }

     

    Here is the config:

     

    Code Snippet

    <?xml version="1.0" encoding="utf-8"?>

    <configuration>

      <system.serviceModel>

        <services>

          <service name="foo">

            <host>

              <baseAddresses>

                <add baseAddress="http://localhost:8000/Service/"/>

              </baseAddresses>

            </host>

            <endpoint address="E1"

                      binding="basicHttpBinding"

                      bindingConfiguration="b"

                      contract="IMyContract"/>

            <endpoint address=""

                      binding="customBinding"

                      bindingConfiguration="helpPage"

                      contract="IMyHelpPageContract"/>

          </service>

        </services>

        <client>

          <endpoint address="http://localhost:8000/Service/E1"

                    binding="basicHttpBinding"

                    bindingConfiguration="b"

                    contract="IMyContract"

                    name="c1"/>

        </client>

        <bindings>

          <customBinding>

            <binding name="helpPage">

              <textMessageEncoding messageVersion ="None" />

              <httpTransport/>

            </binding>

          </customBinding>

          <basicHttpBinding>

            <binding name="b"/>

          </basicHttpBinding>

        </bindings>

      </system.serviceModel>

    </configuration>

     

    Run it, and then hit the base address in a browser, and you'll see the custom help page.

     

    See the sample http://msdn2.microsoft.com/en-us/library/aa395208.aspx for some more details on sending back arbitrary responses over HTTP GET.

     

    (Doing this kind of stuff (GET/POX) will be much easier in Orcas.)

     

    Monday, June 04, 2007 9:23 PM
  • Thanks for snippet Brian.

    Is there any way to call WriteRaw() directly? I'm getting "Text cannot be written outside the root element."

    I tried with this:
    Code Snippet

    protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
    {
        XmlWriterSettings settings = new XmlWriterSettings();
        settings.ConformanceLevel = ConformanceLevel.Fragment;

        XmlWriter writer2 = XmlWriter.Create(writer, settings);

        writer2.WriteRaw("<html><head></head><body><p>Hi</p></body></html>");
    }


    But the XmlWriter.Settings.ConformanceLevel is still set to "Document"

    Also, I'm working with Orcas Beta 2, any idea if doing this is any easier with the new bits?

    As far as I can tell, the webHttpBinding "WebMessageFormat" enum only has Json and Xml, no "Raw". I also tried implementing IXmlSerializable in a class so that I could put the HTML there but the "WebMessageBody" enum options always include some kind of wrapper around the body. Sad

    Thanks
    Monday, August 13, 2007 9:49 AM
  • I have a custom message encoder than can spit out raw data - e.g. in our sample app it spits out arbitrary video streams.

     

    Monday, August 13, 2007 1:47 PM
  •  Edgardo Rossetto wrote:
    Is there any way to call WriteRaw() directly? I'm getting "Text cannot be written outside the root element."

     

    Code Block

    protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
    {
        writer.WriteStartElement("<html>");
        writer.WriteRaw("<head></head><body><p>Hi</p></body>");

        writer.WriteEndElement();
    }

     

    worked for me.
    Tuesday, January 22, 2008 3:42 AM
  • Hello Brian,

    I am in the similar situation but I need to display a Help Page from HTML file that has also external images and .css file on disk...
    If you could help me with this I will really appreciate it.

    Thanks
    Monday, December 14, 2009 10:10 AM
  • There's a slight problem with this approach. WCF seems to always return a HTTP-Content of application/xml. This means that most browsers will render the content as xml, not html. 
    Sunday, July 18, 2010 1:22 PM
  • Any examples to show IIS deployment with SVC file? I was able to get root of website working (https://localhost/) but SVC page (https://localhost/Service.svc) still shows standard wcf text. Thanks!


    Manish

    Monday, March 25, 2013 11:35 PM