Wednesday 19 October 2011

Custom message header

This article explains about customizing the wcf message flowing between service and client.
There are certain scenario in which you to pass some information from client to service, but not as parameter in operation contracts. Example, logging system at the service we need to log user or machine information, which made request to the service. In this kind of scenario we should not pass user or machine information as parameter in operation contract. Instead we can pass the information through message flowing between client and service vice versa. The information we need to send can be appended with message header and it can be received at the server side.
Let as create sample service and client application, in which client will send “User name” information through request message and service will respond with confirmation message.
I have created Math service with Add and Subtract functionality. Client consuming this service will send his user name information as string with requested message. Once request reached the service, it will read the information from the message header and display using console window. When service responding to the client, along with operation result, it will also send confirmation message to client through message header.
Step 1: Create IMathService interface decorated with Service and Operational contract attribute
IMathService.vb
<ServiceContract()> _
Public Interface IMathService
    <OperationContract()> _
    Function Add(ByVal a As Integer, ByVal b As Integer) As Integer
    <OperationContract()> _
    Function Subtract(ByVal a As Integer, ByVal b As Integer) As Integer
End Interface
Step 2:In this class, we have implemented Add and Subtract functionality.
PrintRequestedUserID() method will read the “UserID” message header from incoming message using OperationContext. This User information is displayed in console window.
SendResponseWithMessage() method will send a confirmation message to the client as Message header through Operation context.
MathService.vb
Public Class MathService
    Implements IMathService

    Public Function Add(ByVal a As Integer, ByVal b As Integer) As Integer 
    Implements IMathService.Add
        'This method call will retrive message send from client using MessageHeader
        PrintRequestedUserID()
        'This method call will send message to client using MessageHeader
        SendResponseWithMessage()
        Return a + b

    End Function

    Public Function Subtract(ByVal a As Integer, ByVal b As Integer) As Integer 
    Implements IMathService.Subtract
        'This method call will retrive message send from client using MessageHeader
        PrintRequestedUserID()
        'This method call will send message to client using MessageHeader
        SendResponseWithMessage()
        Return a - b
    End Function

    Private Sub PrintRequestedUserID()
        Dim userID As String = String.Empty
        'Read the message header using "Name" and "NameSpace"
        userID = OperationContext.Current.IncomingMessageHeaders
                                    .GetHeader(Of String)("UserID", "ns")
        Console.WriteLine("Requested user: " + userID)
    End Sub

    Private Sub SendResponseWithMessage()
        'Creating new message header with "Content" value assigned in constructor
        Dim mess As New MessageHeader(Of String)("This is sample message from service")
        'Assigning Name and NameSpace to the message header value at server side
        Dim header As System.ServiceModel.
                    Channels.MessageHeader = mess.GetUntypedHeader("ServiceMessage", "ns")
        'Adding message header with OperationContext 
        'which will be received at the client side
        OperationContext.Current.OutgoingMessageHeaders.Add(header)
    End Sub
End Class
Step 3: Hosting the MathService using console application
MyServiceHost.vb
       Module MyServiceHost

    Sub Main()
        'Hosting the Math service using console application
        Dim host As New ServiceHost(GetType(MyService.MathService))
        host.Open()
        Console.WriteLine("Service is running... Press  to exit.")
        Console.ReadLine()
    End Sub

End Module

Web.Config
      <system.serviceModel>
    <services><service name="MyService.MathService" 
    behaviorConfiguration="MyServiceBehavior">
        <endpoint address ="MathService" binding="basicHttpBinding" 
        contract="MyService.IMathService"/>
        <endpoint  address="mex" binding="mexHttpBinding" 
        contract="IMetadataExchange"/>
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8090/MyService"/>
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors >
        <behavior name ="MyServiceBehavior">
          <serviceMetadata httpGetEnabled ="true"/>
            <serviceDebug includeExceptionDetailInFaults ="True"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

Step 4: Created console client application which add “UserID” as message header to service using Operation context before calling Add() functionality. Once the response is received from the service, it is trying to read the confirmation message from service using Operation context.
Sub Main()
        'Creating proxy class for service
        Dim proxy As IMathService = Nothing
        proxy = ChannelFactory(Of IMathService).CreateChannel(New BasicHttpBinding(), 
                    New EndpointAddress("http://localhost:8090/MyService/MathService"))

        'Lifetime of OperationContextScope defines the scope for OperationContext.
        Dim scope As OperationContextScope = Nothing
        scope = New OperationContextScope(proxy)
       
        'Creating new message header with "Content" value assigned in constructor
        Dim mess As New MessageHeader(Of String)
                         (System.Security.Principal.WindowsIdentity.GetCurrent().Name)
        'Assigning Name and NameSpace to the message header value at client side
        Dim header As System.ServiceModel.Channels.MessageHeader 
                                    = mess.GetUntypedHeader("UserID", "ns")
        'Adding message header with OperationContext 
        'which will be received at the server side
        OperationContext.Current.OutgoingMessageHeaders.Add(header)

        'Making service call
        Console.WriteLine("Sum of {0},{1}={2}", 1, 2, proxy.Add(1, 2))
        'Displaying confrimation message from service
        Console.WriteLine("Response Message: " + OperationContext.Current.
                    IncomingMessageHeaders.GetHeader(Of String)("ServiceMessage", "ns"))
        Console.ReadLine()
    End Sub

End Module

<ServiceContract()> _
Public Interface IMathService
    Inherits IClientChannel

    <OperationContract()> _
    Function Add(ByVal a As Integer, ByVal b As Integer) As Integer
    <OperationContract()> _
    Function Subtract(ByVal a As Integer, ByVal b As Integer) As Integer
End Interface

Step 5: Run the MyServiceHost
Step 6: Run the MyClientApplication
Below figure shows the message flowing between service and client
Client application output
Console hosted service output screen
Conclusion:
This article explain about customizing the wcf message header

No comments:

Post a Comment