Thursday, December 15, 2011

Working with EndPoint Behavior in WCF

Today I will make a short demo about how you can extend your Wcf service with custom behaviors. Wcf provides many ways of customizing the runtime behavior of your service. For a more detailed explanation please refer to msdn magazine .

First of all, let's find out who the players are. We will be working in the 'ServiceModelLayer' with the 'Dispatcher' in the context of the service host and the 'Proxy' in the context of a client. Their job is to translate between WCF Message Objects and .Net Method Calls.                              

WCF runtime


This pair dispatcher/proxy follow a sequence of steps during which you can insert your own code for message transformation, serialization, parameters inspection and so on.

Here is a list of 'points' where you can plug in and insert your code by implementing the following interfaces.


StageInterceptor InterfaceDescription
Parameter Inspection IParameterInspector Called before and after invocation to
inspect and modify parameter values.
Message Formatting IDispatchMessageFormatter
IClientFormatter
Called to perform serialization and
deserialization.
Message Inspection IDispatchMessageInspector
IClientMessageInspector
Called before send or after receive to
inspect and replace message contents.
Operation Selection IDispatchOperationSelector
IClientOperationSelector
Called to select the operation to invoke for the given message.
Operation Invoker IOperationInvoker Called to invoke the operation.


So now we know what kind of extensions we have and whom they apply to. In order to apply an extension (from the above list) we need to set up a behavior. There are 4 types of behaviors in wcf, each of them sharing this set of methods:
Validate - Called just before the runtime is built—allows you to perform custom validation on the service description.

AddBindingParameters - Called in the first step of building the runtime, before the underlying channel is constructed—allows you to add parameters to influence the underlying channel stack.
ApplyClientBehavior - Allows behavior to inject proxy (client) extensions. Note that this method is not present on IServiceBehavior.
ApplyDispatchBehavior - Allows behavior to inject dispatcher extensions.

One exception is that IServiceBehavior doesn't have an ApplyClientBehavior method because service behaviors can't be applied to clients.

Types of Behaviors


ScopeInterfacePotential Impact
ServiceEndpointContractOperation
ServiceIServiceBehavior
EndpointIEndpointBehavior
ContractIContractBehavior
OperationIOperationBehavior
Let's see now how the behavior and the extension get together.

My example comes from a silverlight application where I had to create a custom behavior in order to catch wcf exceptions. So to achieve this I used  IDispatchMessageInspector to change the status code of the reply to 200 (OK) and the IEndpointBehavior because I wanted to apply this extension only to this level.

Show me some code! :)

Step 1. Create the extension implementing IDispatchMessageInspector

public class SilverlightFaultMessageInspector : IDispatchMessageInspector
{
#region IDispatchMessageInspector Members

public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
// Do nothing
return null;
}

public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
if (reply.IsFault)
{
HttpResponseMessageProperty property = new HttpResponseMessageProperty();
// Here the response code is changed to 200.
property.StatusCode = System.Net.HttpStatusCode.OK;
reply.Properties[HttpResponseMessageProperty.Name] = property;
}
}
#endregion
}


Step 2. Create the behavior and apply the entension
public class FaultBehavior : BehaviorExtensionElement, IEndpointBehavior
{
#region IEndpointBehavior Members
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}

public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}

public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
SilverlightFaultMessageInspector inspector = new SilverlightFaultMessageInspector();
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
}


public void Validate(ServiceEndpoint endpoint)
{
}
public override Type BehaviorType
{
get { return typeof(FaultBehavior); }
}

protected override object CreateBehavior()
{
return new FaultBehavior();
}
#endregion
}


Step 3. Declare the behavior extension in the service config file

Inside the <system.serviceModel> create the <extensions> element if does not exist.

<extensions>
<behaviorExtensions>
  <add name="silverlightFaults" type="myservicename.FaultBehavior, myservicename, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>

NOTE: the type must be the fully qualified name of the assembly/class that you are using and must be declared on a single line, otherwise will not work (WCF known issue)

Step 4. Create an end point behavior and add the extension
<behavior name="myFaultBehavior">
   <silverlightFaults />
 </behavior>

Step 5. Crate an endpoint and assign behavior configuration attribute

<endpoint address=""
binding="basicHttpBinding"
bindingConfiguration="MyHttpBinding"
  contract="IMyContract"
behaviorConfiguration="myFaultBehavior">

</endpoint>

And that's it! You can inspect the exceptions that come from a wcf service by using e.Error property in the completed event handler. 

Friday, December 9, 2011

Streaming in WCF


There are scenarios when you must be able to send or receive large amount of data or files using a service in your application. WCF supports streaming and I will show you how to achieve that.

There are 4 types of TransferMode in WCF

using System;namespace System.ServiceModel
{
    public enum TransferMode    

    {
        Buffered = 0,
        Streamed = 1,
        StreamedRequest = 2,
        StreamedResponse = 3,
    }
}


The default one is Buffered. This means that the sender/receiver cannot process the message until the whole data is in one place. We must set our binding to Streamed to enable streaming.

Next thing is to add an operation contract that must satisfy 2 conditions:
1. The parameter that holds the data to be streamed must be the only parameter in the method.
2. At least one of the types of the parameter and return value must be either Stream, Message, or IXmlSerializable.  

In short, this means that the method must have only one parameter which is a Stream, otherwise the return type must be Stream,Message, etc...

Step 1. Create a WCF service application
In VisualStudio, File->New Project->Wcf->Wcf Service Application

Step 2. Add an operation contract with a Stream parameter
Here is my service contract:
[ServiceContract]
public interface IService1
{
    [OperationContract]
    bool SendStream(Stream stream);
}

Step 3. Create a binding in the configuration file, which supports streaming

We have to set the transferMode to Streamed and increase the message size attributes. I have used a regular basicHttpBinding.

<bindings>
      <basicHttpBinding>
        <binding name="streaming" maxReceivedMessageSize="67108864" transferMode="Streamed">         
        </binding>
      </basicHttpBinding>
    </bindings>


After the binding is created, add the endpoint and service tags like in the image above.

Step 4. Implement the operation contract in Service.svc

public bool SendStream(Stream stream) 
{
    try
    {
         using (FileStream fs = new FileStream(AppDomain.CurrentDomain.BaseDirectory + "file", FileMode.Create))
         {
             stream.CopyTo(fs);
          }
          return true;
      }
      catch (Exception ex)
      {
          return false;
       } 
}


Step 5. Critical step. 

 I lost many hours trying to understand why this almost complete configuration doesn't work with files larger than 3-4 MBytes.

The reason is that even if we enable streaming and we increase the message size, ASP.NET doesn't know about WCF limitations. So the solution is to add httpRuntime in system.web element:
<httpRuntime maxRequestLength="67108864"/>


Step 6. Create the client and consume the service
Create a simple console application and add a service reference of the created service. You must have a file called 'largefile' without any extension in the bin\debug\ folder of your console application. Mine has ~20 MBytes.


Now consume the service:

static void Main(string[] args)
        {
            try
            {
                using (ServiceReference1.Service1Client proxy = new ServiceReference1.Service1Client())
                {
                    using (FileStream fs = new FileStream(AppDomain.CurrentDomain.BaseDirectory + "largefile", FileMode.Open))
                    {
                        var result = proxy.SendStream(fs);
                    }
                }
            }
            catch (Exception ex)
            {
            }
        }

The result should be true, which means that the streaming was successful.
Happy coding! :)
Download code

Thursday, December 8, 2011

WCF Security overview

WCF framework is so huge that you can easily get lost. I spent many hours to figure out how it works and I thought that I might share my conclusions.There are 2 major types of security modes in WCF and o variation of those two:
Message level security - the default one
-the message is encrypted from end to end
Transport level security
-the communication channel is encrypted using ssl over http (https)
TransportWithMessageCredential
-it's a variation of the other two types. It provides both channel and message encryption.For HTTP, the mechanism is Secure Sockets Layer (SSL) over HTTP (HTTPS); for TCP, it is SSL over TCP or Windows.

Here is a  good article about securing services.
I will now make few step by step demos of combined security and authentication practices in WCF.

The firs one is using Basic Authentication in WCF using  TransportCredentialOnly security mode. Note that it does not provide message integrity or confidentiality. Basic authentication works well in scenarios where you want to provide access to some people inside a company for example.

In the first part we will create the service with the needed configuration and host it on iis. The next ting we will create a basic(Console) client to consume our service. 


Part I - Creating the service

Step 1. Create a new WCF Service Application
I named my "WcfBasic"



 Step 2. Create a binding

 I choose to create a basic http binding , nothing very special. Here is the element.
  <basicHttpBinding>
      <binding name="SecurityBinding"> 
        <security mode="TransportCredentialOnly">
           <transport clientCredentialType="Basic"></transport>
        </security>      </binding>    </basicHttpBinding>

Now we have to put this element in the configuration file inside  the  bindings element.
Note: if you don' have <bindings> element just create it


Step 3. Add the service and end point elements.


You must add the following element in the <services> tag in the configuration file. If there is no <services> element just create one.


<services>
    <service name="WcfSecurity.SecurityService">
        <endpoint address=""
                            binding="basicHttpBinding"                                                                                                             bindingConfiguration="SecurityBinding"
                            name="BasicHttpEndpoint"                                                                                                             contract="WcfSecurity.ISecurityService">  
        </endpoint> 
    </service>
  </services>




Save now the changes, build your project and let's try to open it in a browser.
Right click on Service1.svc -> View in Browser. You should get this error:
Security settings for this service require 'Basic' Authentication but it is not enabled for the IIS application that hosts this service. 

That's because we haven't enabled yet basic authentication for our service.

Step 4. Host the service on iis and enable basic authentication


First we have to publish the service on iis. For this you have to right click on the WcfBassic project and click Publish



It will appear a popup where we have to set up the virtual directory and other things.
Service Url: localhost
Site/application: Default Web Site/WcfBasic (here you can add other name if you like)
Check 'Mark as IIS application on destination'
Click Publish
Note: solve and build errors before publishing, otherwise the publish will fail!



After the publish is succeeded, open your IIS manager and let's enable the basic authentication. 

Step 5. Enable basic authentication in iis

Locate your application under Sites/Default Web Site. In the Features view, locate and double click the Authentication icon.



In the screen that will show we have to do 2 things: enable basic authentication, disable any other authentication type. You can use enable/disable button from the right Actions pane. It should look like this.




Now switch to 'Content View', click on Service1.svc and in the Actions pane click Browse.




And voilà! You are prompted to enter your credentials. Now you would ask but what credentials? Their are just the ones you use to log on to the machine.




If you enter the good credentials you will be directed to the service page.



Otherwise you will get unauthorized error.



That was the hard part. What's left now is to create a client to consume the service.


Part II. Creating the client to consume the service

Step 1. Create a new console application in the solution

Step 2. Add service reference.

Remember that our service is now hosted on iis. So in order to add the reference, expand the console project and right click References -> Add Service Reference. It will show up a popup where we have to enter the service url which should something like: 'http://localhost/WcfBasicAuth/Service1.svc'


When you press Ok, you will be prompted for your credentials, and you will receive a warning.


This is the request credentials pop up.




Enter your credentials and press ok. The service reference is added to your application.

Step 3. Instantiate the service and call method


The last thing to do now is to instantiate the service, set the ClientCredentials and call the method. The credentials are your logon credentials.


Note: before you can test it, you must set the console application as start project.Right click on the console project -> Set as startup project.

And here is the result if your credentials are good:


Let me know what you think. Stay close for more step by step demos using transport security mode (https with ssl certificate), custom user name and password, Authentication Service in WCF.

Download code