Home > C#, REST, WCF > Configuring the WCF Web API without using the fluent API

Configuring the WCF Web API without using the fluent API

Microsoft will add first class support for REST web services into the next version of WCF. A preview of this can be downloaded from: http://wcf.codeplex.com. After having seen the presentation of Glenn Block at the Belgium Microsoft Techdays, I wanted to start playing a little bit with the API and a couple of days ago I finally found the time to do it.

I am quite happy with the way how they will handle REST in .Net and it will make developing real REST services (instead of REST like services …) more easy. The only thing that annoyed me slightly was the fact that is not possible to configure the WCF Web API from the web.config. In my company we have different environments (development, test, …) and we have to promote the assemblies from one environment to the other, but we can have different config files depending on the environment. In the classical WCF, this allows us to enable custom logging or monitoring in for example test but not in production. With the fluent API, this kind of deployment time configuration is not possible. With our deployment system, if we would want to add WCF Web message handlers or operation handlers in test, it would also go to production or we would first have to promote a modified version to test without logging.

In order to fix this, I have written a custom service behavior that allows adding message handlers using the configuration file. Of course this could easily be extended with the other configuration options of the fluent API, but I want to keep my example as simple as possible. If people would be interested in a version with complete web.config support, then please let me know.

Hooking into the WCF Web API was remarkable easy. It adds endpoints of type HttpEndpoint for the REST services to the WCF stack. The HttpEndpoint provides access to features like the active message handlers. So all I had to do was looping over the endpoints in my service behavior and if it was of type HttpEndpoint than I added all the message handlers that were defined in the config file.

public class HttpConfigBehavior : IServiceBehavior
{
    private Type[] messageHandlers;

    public HttpConfigBehavior(IEnumerable<Type> messageHandlers)
    {
        if (messageHandlers == null)
            throw new ArgumentNullException("messageHandlers");

        this.messageHandlers = messageHandlers.ToArray();
    }

    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase,
        Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
        foreach (ServiceEndpoint serviceEndpoint in endpoints)
        {
            HttpEndpoint httpEndpoint = serviceEndpoint as HttpEndpoint;
            if (httpEndpoint != null)
            {
                httpEndpoint.MessageHandlerFactory = new HttpMessageHandlerFactory(messageHandlers);
            }
        }
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }
}

In order to add in from the config file, I also had to create a BehaviorExtensionElement and the necessary configuration classes.

public class HttpConfigBehaviorExtensionElement : BehaviorExtensionElement
{
    public override Type BehaviorType
    {
        get { return typeof(HttpConfigBehavior); }
    }

    protected override object CreateBehavior()
    {
        return new HttpConfigBehavior(
            MessageHandlers.OfType<MessageHandlerConfigurationElement>().Select(e => e.Type));
    }

    [ConfigurationProperty("messageHandlers")]
    public MessageHandlerConfigurationCollection MessageHandlers
    {
        get
        {
            return (MessageHandlerConfigurationCollection)base["messageHandlers"];
        }
        set
        {
            base["messageHandlers"] = value;
        }
    }
}
[ConfigurationCollection(typeof(MessageHandlerConfigurationElement))]
public class MessageHandlerConfigurationCollection : ConfigurationElementCollection
{
    public MessageHandlerConfigurationElement this[int index]
    {
        get
        {
            return (MessageHandlerConfigurationElement)base.BaseGet(index);
        }
    }

    protected override ConfigurationElement CreateNewElement()
    {
        return new MessageHandlerConfigurationElement();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((MessageHandlerConfigurationElement)element).Name;
    }
}
public class MessageHandlerConfigurationElement : ConfigurationElement
{
    [ConfigurationProperty("name", IsRequired=true)]
    public string Name
    {
        get
        {
            return (string)base["name"];
        }
        set
        {
            base["name"] = value;
        }
    }

    [ConfigurationProperty("type", IsRequired = true)]
    [TypeConverter(typeof(TypeNameConverter))]
    public Type Type
    {
        get
        {
            return (Type)base["type"];
        }
        set
        {
            base["type"] = value;
        }
    }
}
public class TypeNameConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string);
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return destinationType == typeof(Type);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        return Type.GetType((string)value);
    }
}

We can now define WCF Web API message handlers from the config file:

<system.serviceModel>

  <behaviors>
    <serviceBehaviors>
      <behavior>
        <httpConfig>
          <messageHandlers>
            <add name="logger"
                  type="MvcApplication45.Util.LoggingChannel, MvcApplication45, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          </messageHandlers>
        </httpConfig>
      </behavior>
    </serviceBehaviors>
  </behaviors>

  <extensions>
    <behaviorExtensions>
      <add name="httpConfig" type="MvcApplication45.Util.HttpConfigBehaviorExtensionElement, MvcApplication45, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
    </behaviorExtensions>
  </extensions>

  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>

Conclusion

Although it is not provided out of the box, the WCF Web API can easily be configured using the web.config file. All that is required is some good old “classic” WCF knowledge. Your investment in learning classic WCF is thus definitely not lost. Glimlach

Advertisements
Categories: C#, REST, WCF
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: