Creating custom SOAP headers in VB.NET
![]()
This is rather horrible, and the documentation isn't very good. I was able to get this to log the soap messages; then to amend them. However you cannot both log and amend in the same stage of the outgoing message! (this tripped me up repeatedly)
A sample vb.net project connecting to the DPS web service is available for download:
You need to create a new class within your project (in this case !TraceExtension, which writes the soap to a log file). To enable the soap extension you need to add lines to your app.config. Details of these are in the vb project as comments.
Note that there is little on the web. Your best guide is the VB.Net help on soap extensions. A VB.NET example for this is here: http://msdn2.microsoft.com/en-us/library/system.web.services.protocols.soapextension.aspx
1. Start up VB.NET and create a new WindowsApplication.
2. Add a web reference to your WSDL in the usual way and let it generate stuff.
3. Add a button, a Listbox and a textbox on the form, and double-click on the button to open the code window
4. Stick the code into your method to call a method on your webservice (DPS is the web service I am accessing and hlwus006 is the name I gave it):
'-- Instantiate the service Dim dps007 As hlwus006.DPS = New hlwus006.DPS Dim dps007reply As hlwus006.ListPropertiesReply Dim reply As hlwus006.PropertyDef Dim replies As hlwus006.PropertyDef() ListBox1.Items.Clear() TextBox1.Clear() '-- Run the list Properties method on the service dps007reply = dps007.ListProperties '-- Display the list of DPS models in a listbox replies = dps007reply.PropertyDefs For Each reply In replies ListBox1.Items.Add(reply.name + " " + reply.desc + " ") Next(This particular web service is returning a series of strings)
5. Give it a run and check everything is working OK.
In VB.Net Express Edition, look up the helpfile section on Soap Extensions. Basically this is a way to intercept and modify the soap message on its way in and out of your client.
1. Find the app.config for your application. It will be in the base directory of your folder. Open it.
2. Insert a new section at the bottom, above the last tag:
<system.web> <webServices> <soapExtensionTypes> <add type="DPSModels.TraceExtension, DPSModels" group="0" priority="1" /> </soapExtensionTypes> </webServices> </system.web>where DPSModels is the name of your project. TraceExtension can be anything, but is the name of the class which you are about to create.
3. Add a new class TraceExtension to your project. Open it. Now copy the example code from http://msdn2.microsoft.com/en-us/library/system.web.services.protocols.soapextension.aspx.
This inspects the stream and writes them to a file. We can then modify this to change the soap header.
Imports System
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.IO
' Define a SOAP Extension that traces the SOAP request and SOAP response
' for the XML Web service method the SOAP extension is applied to.
Public Class TraceExtension
Inherits SoapExtension
Private oldStream As Stream
Private newStream As Stream
Private m_filename As String
' Save the Stream representing the SOAP request or SOAP response into
' a local memory buffer.
Public Overrides Function ChainStream(ByVal stream As Stream) As Stream
oldStream = stream
newStream = New MemoryStream()
Return newStream
End Function
' When the SOAP extension is accessed for the first time, the XML Web
' service method it is applied to is accessed to store the file
' name passed in, using the corresponding SoapExtensionAttribute.
Public Overloads Overrides Function GetInitializer(ByVal methodInfo As _
LogicalMethodInfo, _
ByVal attribute As SoapExtensionAttribute) As Object
Return CType(attribute, TraceExtensionAttribute).Filename
End Function
' The SOAP extension was configured to run using a configuration file
' instead of an attribute applied to a specific XML Web service
' method. Return a file name based on the class implementing the Web
' Service's type.
Public Overloads Overrides Function GetInitializer(ByVal WebServiceType As _
Type) As Object
' Return a file name to log the trace information to, based on the
' type.
Return "C:\" + WebServiceType.FullName + ".log"
End Function
' Receive the file name stored by GetInitializer and store it in a
' member variable for this specific instance.
Public Overrides Sub Initialize(ByVal initializer As Object)
m_filename = CStr(initializer)
End Sub
' If the SoapMessageStage is such that the SoapRequest or SoapResponse
' is still in the SOAP format to be sent or received over the network,
' save it out to file.
Public Overrides Sub ProcessMessage(ByVal message As SoapMessage)
Select Case message.Stage
Case SoapMessageStage.BeforeSerialize
Case SoapMessageStage.AfterSerialize
WriteOutput(message)
Case SoapMessageStage.BeforeDeserialize
WriteInput(message)
Case SoapMessageStage.AfterDeserialize
End Select
End Sub
' Write the SOAP message out to a file.
Public Sub WriteOutput(ByVal message As SoapMessage)
newStream.Position = 0
Dim fs As New FileStream(m_filename, FileMode.Append, _
FileAccess.Write)
Dim w As New StreamWriter(fs)
w.WriteLine("-----Request at " + DateTime.Now.ToString())
w.Flush()
Copy(newStream, fs)
w.Close()
newStream.Position = 0
Copy(newStream, oldStream)
End Sub
' Write the SOAP message out to a file.
Public Sub WriteInput(ByVal message As SoapMessage)
Copy(oldStream, newStream)
Dim fs As New FileStream(m_filename, FileMode.Append, _
FileAccess.Write)
Dim w As New StreamWriter(fs)
w.WriteLine("----- Response at " + DateTime.Now.ToString())
w.Flush()
newStream.Position = 0
Copy(newStream, fs)
w.Close()
newStream.Position = 0
End Sub
Sub Copy(ByVal fromStream As Stream, ByVal toStream As Stream)
Dim reader As New StreamReader(fromStream)
Dim writer As New StreamWriter(toStream)
writer.WriteLine(reader.ReadToEnd())
writer.Flush()
End Sub
End Class
' Create a SoapExtensionAttribute for our SOAP Extension that can be
' applied to an XML Web service method.
_
Public Class TraceExtensionAttribute
Inherits SoapExtensionAttribute
Private m_filename As String = "c:\log.txt"
Private m_priority As Integer
Public Overrides ReadOnly Property ExtensionType() As Type
Get
Return GetType(TraceExtension)
End Get
End Property
Public Overrides Property Priority() As Integer
Get
Return m_priority
End Get
Set(ByVal Value As Integer)
m_priority = value
End Set
End Property
Public Property Filename() As String
Get
Return m_filename
End Get
Set(ByVal Value As String)
m_filename = value
End Set
End Property
End Class
Now give this a run and check that c:\yourprojectname....txt does contain some soap message and
that it all works OK.
4. Add the following class to the end of the TraceExtension file:
'-- Define a SOAP header by deriving from the SoapHeader base class. '-- Specify in it the fields that we want Public Class MyHeader Inherits SoapHeader Public Userid As String Public ApplicationName As String End Class5. The ProcessMessage method looks like this:
Public Overrides Sub ProcessMessage(ByVal message As SoapMessage) Select Case message.Stage Case SoapMessageStage.BeforeSerialize Case SoapMessageStage.AfterSerialize WriteOutput(message) Case SoapMessageStage.BeforeDeserialize WriteInput(message) Case SoapMessageStage.AfterDeserialize End Select End SubModify it to add the header to the message:
Public Overrides Sub ProcessMessage(ByVal message As SoapMessage)
Select Case message.Stage
Case SoapMessageStage.BeforeSerialize
MsgBox("In ProcessMessage.beforeserialize")
'-- Add header
Dim hdr As New MyHeader
hdr.ApplicationName = "Roger's test client"
hdr.Userid = "wrp43058"
message.Headers.Add(hdr)
Case SoapMessageStage.AfterSerialize
MsgBox("In ProcessMessage.afterserialize")
WriteOutput(message)
Case SoapMessageStage.BeforeDeserialize
MsgBox("In ProcessMessage.beforedeserialize")
Copy(oldStream, newStream)
newStream.Position = 0
Case SoapMessageStage.AfterDeserialize
Case Else
Throw New Exception("invalid stage")
End Select
End Sub
You may find it helpful to scatter some msgbox("...") in the code so you can see where you are.
Now give it a run.
You may get a 'request is not well formed XML error'.... This means that you haven't handled the stream coming back as above (the stuff in BeforeDeserialize is mysterious but necessary).
If you look at the log file, your soap message should now have a header! --
-----Request at 11/10/2007 11:26:07 <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Header><MyHeader xmlns="http://gsk.com/cix/"><Userid>wrp43058</Userid><ApplicationName>Roger's test client</ApplicationName></MyHeader></soap:Header><soap:Body><ListProperties xmlns="http://gsk.com/cix/" /></soap:Body></soap:Envelope>
![]()
Constructive feedback is welcomed to Roger Pearse.
Written 12th October 2007.
This page has been accessed by
people since 12th October 2007.
Return to Roger Pearse's Pages