Calling a WCF service using SOAP in unit test or with VB Script on a classic ASP page

I created a WCF service in .Net and this was fine for .Net customers who use the Add Service Reference wizard easily enough.
However one customer was on classic ASP and he found it difficult to connect. So when we had succeeded together it was worth writing down. This is the result.
Please ignore the property naming. I don’t use strPropertyName. This isn’t working code, just an idea of what is required.

Solved.

Tips:

  • Inspect the .wsdl file
  • Note the soapAction setting
  • Note the namespace. The WCF namespace will include the interface name

Unit test written in VB.Net for SOAP

Public Sub SOAPClientHelloWorldTestMethod()

strURLSubFolder = “ServiceName.svc”
strInterfaceName = “IServiceName”
strFunction = “HelloWorldMethod”
Dim parameter1 = “parameter1”

strRequest2FunctionNameOpen = “&lt ns1:HelloWorldMethod &gt”
strRequest3Parameters = “&lt” + parameter1Value + “&gt”
strRequest4FunctionNameClose += “&lt ns1:HelloWorldMethod &gt”

CallSOAP()

End Sub

Then the SOAP call method

Private Sub CallSOAP()

‘URL to SOAP namespace and connection URL
Dim strURLBase = “http://serviceaddress.net/”
Dim strURL = strURLBase + strURLSubFolder
Dim strNamespace = “http://tempuri.org/”

strRequest1SOAPPart1 = “<?xml version=””1.0″” encoding=””UTF-8″”?>” & _

“<SOAP-ENV:Envelope ” & _

“xmlns:SOAP-ENV=””http://schemas.xmlsoap.org/soap/envelope/”&#8221; ” & _

“xmlns:ns1=””” & strNamespace & “””>” & _

“<SOAP-ENV:Body>”

strRequest5SOAPEnd = “</SOAP-ENV:Body></SOAP-ENV:Envelope>”

Dim strRequest = strRequest1SOAPPart1 + strRequest2FunctionNameOpen + strRequest3Parameters + strRequest4FunctionNameClose + strRequest5SOAPEnd
TestContext.WriteLine(“URL = ” + strURL)
TestContext.WriteLine(“Request = ” + strRequest)
Dim soapAction As String
soapAction = strNamespace & strInterfaceName & “/” & strFunction
TestContext.WriteLine(“SOAPAction = ” + soapAction)

Dim objectName As String
‘objectName = “Msxml2.XMLHTTP.3.0”
‘objectName = “MSXML2.DOMDocument.6.0”
objectName = “MSXML2.ServerXMLHTTP”

Dim soap_request As Object
soap_request = CreateObject(objectName)

Dim contentType As String
contentType = “text/xml; charset=utf-8”
‘contentType = “application/x-www-form-urlencoded”

soap_request.open(“POST”, strURL, False)
soap_request.setRequestHeader(“Content-Type”, contentType)
soap_request.setRequestHeader(“Content-Length”, Len(strRequest))
soap_request.setRequestHeader(“SOAPAction”, soapAction)

‘send the request and capture the result
soap_request.send(strRequest)
Dim strResult = soap_request.responseText
soap_request = Nothing

‘display the XML
‘response.write (strResult)
‘TestContext.WriteLine(strResult)
TestContext.WriteLine(“”)
Dim xml = XDocument.Parse(strResult)
TestContext.WriteLine(xml.ToString())

End Sub

Classic ASP

Title
<%
Dim strRequest, strResult, strElement, strURL, strNamespace,interfacename,soap_request,parameter1

‘ Define services
strURL = “http://serviceaddress.svc&#8221; ‘ Service URL
strNamespace = “http://tempuri.org/&#8221; ‘ Name space
interfacename=”IServiceName/” ‘ interface name
strElement = “GetMethod” ‘ Element

set soap_request = Server.CreateObject(“Msxml2.XMLHTTP.3.0″)
strRequest =”<?xml version=””1.0″” encoding=””UTF-8″”?>”

strRequest=strRequest& “<SOAP-ENV:Envelope xmlns:SOAP-ENV=””http://schemas.xmlsoap.org/soap/envelope/”&#8221; xmlns:ns1=”””&strNamespace&”””>”

strRequest=strRequest& “<SOAP-ENV:Body>”

strRequest=strRequest& “<ns1:HelloWorldMethod>”

strRequest=strRequest& “<ns1:parameter1>”&soap_user&”</ns1:parameter1>”

strRequest=strRequest& “</ns1:HelloWorldMethod>”

strRequest=strRequest& “</SOAP-ENV:Body>”

strRequest=strRequest& “</SOAP-ENV:Envelope>”

soap_request.open “post”, “”&strURL&””, False
soap_request.setRequestHeader “Content-Type”, “text/xml; charset=utf-8”
soap_request.setRequestHeader “SOAPAction”,strNamespace&interfacename&strElement
soap_request.setRequestHeader “Content-Length”,Len(strRequest)

Call soap_request.send(strRequest) ‘ Send the soap request and capture the result
strResult = soap_request.responseText

‘display the XML
if strResult”” then
Set ResponseXML = Nothing
end if
Set soap_request = Nothing

end if

%>

Advertisements

Learning WCF

With help from book “Learning WCF” by Michele Leroux Bustamante

Notes from working through samples:

Note 1:

Surprisingly it is possible/advisable to host the service from a different project or projects. This gives the possibility of one service to have different types of host running

Error 1:  System.ServiceModel.AddressAccessDeniedException: HTTP could not register URL http://.. Your process does not have access rights to this namespace

Solution 1:

Run Visual Studio as an administrator

Error 2: App.Config does not have context menu option “Edit WCF configuration”

Solution 2:

Use Tools > WCF Service Configuration Editor then close it

Then right-click on App.Config and the “Edit WCF configuration” will be there

 

Error 3: ASP.Net HTTP Error 403.14 – Forbidden – The Web server is configured to not list the contents of this directory

Solution 3:

  1. IIS
  2. Select the web site
  3. Double-click Directory Browsing
  4. Enable

(Nice to have an easy problem)

 

Error 4: IIS – Web site – Configuration Editor – system.web/compilation – Bug Unrecognized element folderlevelbuildproviders

Solution 4

With thanks to:

http://forums.iis.net/t/1180915.aspx which led to

http://www.hanselman.com/blog/ASPNET4BreakingChangesAndStuffToBeAwareOf.aspx

If you install ASP.Net 4 and then install IIS then you need to make sure IIS is configured to know about ASP.Net 4 otherwise IIS will have no idea how to run ASP.Net 4 applications.

There’s a simple workaround:

  • If you are already in this state, drop to the command line and navigate to the FX install directory.  Then run “aspnet_regiis –iru“.
    • Note if you are on a 64-bit machine, run this command from the 64-bit FX install directory – not the 32-bit installation directory.

or for future reference, try to enable IIS and the ASP.NET extensibility option *first* when your are building machines or VMs.  That way when VS 2010 or .NET are subsequently installed, the installation will automatically detect the presence of IIS and will auto-register with it.

 

 

How to host a service in a solution and have the client add service reference

Run without debugging – to do this use Ctrl + F5 or use the Debug menu, but the option may not be there, if you start with a VB IDE. If it is not there then you may add it using customise.

If you run with debugging as usual you will not be able to edit the client.

Hosting the service in IIS

Error no website with code 500

Not surprised that I had issues with hosting here. I had a WSS Windows SharePoint Services and SharePoint Central Administration using my IIS. Took a while to sort, what follows is not comprehensive, but a guide to what I did. I took the view that I have not used SharePoint Central Administration and so was prepared to sacrifice it. Some steps included:

IIS > Sites > Default Web Site > Right-Click > Edit Bindings.

Remove all bindings except http

Then under Default Web Site I had a folder wss -> Virtual Directories -> 80 -> my sample application landed here. Not sure why.

I removed all SharePoint (80) web sites, hope that is ok. And archived the wss folder in its entirety.

Everything started working again.

 

WCF Test Client executable

C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\WcfTestClient.exe

Useful quick test.

 

 

 

 

 

How to consume a web service in code directly

As well as using a wizard it is possible to access web services using code. These are the steps.

  1. Open a Visual Studio Command Prompt
  2. Navigate to a suitable directory e.g. c:\Temp
  3. Use WSDL. Examples
    C# is default language
    wsdl http: //  …myservice.asmx
    For VB use
    wsdl /language:vb http://.. myservice.asmx
    Your service might be .svc
  4. Press return and the wsdl program should write out a file for your consumption
  5. This creates a web service proxy, which will talk to the service.
  6. In your project create a folder say named Service
  7. Add in your new file
  8. Add reference to System.Web.Services
  9. Add code similar to:

myService MyService1 = new myService()

var result = myService.myMethod();

To create a batch file to do this automatically

Thanks to:

http://stackoverflow.com/questions/5879076/how-to-create-a-batch-file-for-visual-studio-command-prompt

call “C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat” x86
wsdl /language:vb /out:MyServiceName.vb http:// … myservice.svc
pause

Why does the consumed service return arrays when I specified a List(of T)

WCF tries hard to not create services which are .Net dependent. So by default the return of List(Of T) will return an array.

If you add a service using the .Net UI, then there is an option:

Configure Service Reference > Always Generate Message Contracts > Collection Type > Change to what you want, which may be a System.Collections.Generic.List.

There is a similar option with svcutil and probably wsdl, but I have not used this yet.

The End.

How to create a WCF Service Web Role and publish to Azure and consume it

Create the Project

Visual Studio > New Project > Visual Basic or Visual C# > Cloud > Windows Azure Cloud Service
> Select WCFServiceWebRole
If this is missing then Install “Windows Azure SDK for .Net”

Code quick start: Create and deploy a WCF service in Windows Azure
http://msdn.microsoft.com/en-us/library/windowsazure/gg651130.aspx

Code Quick Start: Create a client application that uses a WCF service deployed to Windows Azure
http://msdn.microsoft.com/en-us/library/windowsazure/gg651126.aspx

Also see:

http://rickrainey.com/2013/08/30/hosting-a-wcf-service-in-an-azure-web-role-with-http-tcp-endpoints/

Code the Project

Follow the instructions above.

You cannot overload a method. So I had GetMakes and GetMakes(VehicleTypeID) this will mean the service cannot be activated.

Deployment to Azure

Points to watch are:

  1. In ServiceConfiguration.Cloud.cscfg
    <Instances count=”2″ />
  2. In the role, e.g. WCFServiceWebRole1 on Configuration specify the storage account credentials for the Diagnostics results:
    Use the eclipse and pick existing Azure account

Change the name of a service

MyService.svc is actually 2 files, with a linked code behind file.
Using right-click MyService.svc may be opened for code or for markup. If opened for markup then the service name is exposed. If you change the name of the project then you need to change the name here to the fully qualified name.

Consume the service

Mostly fine by following the example. Notable point here.

  1. Add service reference to a project in Visual Studio
  2. Right-click and Configure Service Reference
  3. Under Data Type > Tick On the “Always generate message contracts”

Otherwise the Get..Request() or Get..Response() may not be visible.

Troubleshooting

You could remote desktop to the Azure instance and then use:

  1. the Event Viewer – Application to view errors.
  2. IIS > Management > Configuration Editor
    If this will not open, then it may display what error was found in the Web.Config file. Correct this.

For debugging In Web.confg you may add the following section so that actual errors appear on web inspection:

  <system.web>
    <customErrors mode="Off"/>
  </system.web>

but configSections must be first and there should be only one open and close tag for configuration.

 

Error 1

If debugging then it would crash OnStart when calling AzureLocalStorageTraceListener.GetLogDirectory. Issue here was in ServiceDefinition.csdef there was no log defined.

Error 1 Solution

    <LocalResources>
       <LocalStorage name="WCFServiceWebRole1.svclog" sizeInMB="1000" cleanOnRoleRecycle="false" />
     </LocalResources>

Error 2

Metadata contains a reference that cannot be resolved: ‘http://<ip address>:<port no>/CatalogueService.svc?wsdl’.
The document format is not recognized (the content type is ‘text/html; charset=UTF-8’).
Metadata contains a reference that cannot be resolved: ‘http://<ip address:<port no>/CatalogueService.svc’.
There was no endpoint listening at http://<ip address>:<port no>/CatalogueService.svc that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
The remote server returned an error: (404) Not Found.
If the service is defined in the current solution, try building the solution and adding the service reference again.

Error 2 Solution

I had inadvertently commented out the [ServiceContract] from the IService
Also make sure that the markup of the service is correct.

Error 3

“The remote server returned an error (500) Internal Server Error”

Error 3 Solution

Unfortunately this just means there has been an error. I changed a number of things and then it started again, i do not know which fixed the issue for me:

  1. Added an [OperationContract] to the ICatalogueService and did not explicity implement it.
  2. An issue with remote desktop publish
  3. Overloaded a method with the same name
  4. Remmed out next line
    //[ServiceContract(ProtectionLevel=ProtectionLevel.EncryptAndSign)
    [ServiceContract]
    public interface ICatalogueService

Error 4

I had a test fail possibly when returning a lot of data.

System.ServiceModel.CommunicationException: The maximum message size quota for incoming messages (65536) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element. —> System.ServiceModel.QuotaExceededException: The maximum message size quota for incoming messages (65536) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element.

Error 4 Solution

Surprisingly I didn’t change anything on the outgoing service.
Instead on the calling application in the app.config file there are some settings for the bindings.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_IMyService" closeTimeout="00:01:00"
...
                    maxBufferSize="6553600" maxBufferPoolSize="524288" maxReceivedMessageSize="6553600"
...
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://xxx.xx/MyService.svc"
                binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IMyService"
                contract="CatalogueCloudService.IMyService" name="BasicHttpBinding_IMyService" />
        </client>
    </system.serviceModel>
</configuration>

Error 5 – The type or namespace ‘..’ could not be found (are you missing a using directive or an assembly reference?) in  – …\..\Reference.cs

Solution 5

I tried removing [Serializable][DataContract] from class and remove [DataMember] from properties and then re-deployed the web service and the error went away.

Error 6 – Web.Config errors

Resulted in Internal Server Erorr 500

Solution 6

See top of troubleshooting. Use IIS and try and use the Configuration Editor. This may display your error. Possible errors include:

  1. You should have only 1 configuration open and close tag
  2. configSections should be the first element

 

Error 7 Busy (Starting role… Unhandled Exception: Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironmentException

Does not progress beyond this. You have to delete that deployment for now.

Solution to Error 7

This is similar to error 2 above.

The Web Role project in AzureLocalStorageTraceListener the name of the path must be the same as the service

publicclassAzureLocalStorageTraceListener : XmlWriterTraceListener

{
public AzureLocalStorageTraceListener() : base(Path.Combine(AzureLocalStorageTraceListener.GetLogDirectory().Path, “WCFServiceWebRole2.svclog”)) {}

publicstaticDirectoryConfiguration GetLogDirectory()
{
DirectoryConfiguration directory = newDirectoryConfiguration();
directory.Container = “wad-tracefiles”;
directory.DirectoryQuotaInMB = 10;
directory.Path = RoleEnvironment.GetLocalResource(“WCFServiceWebRole2.svclog”).RootPath;
return directory;
}
}

Error 8

The type ‘WCFServiceWebRole.VRMService’, provided as the Service attribute value in the ServiceHost directive, or provided in the configuration element system.serviceModel/serviceHostingEnvironment/serviceActivations could not be found.

Solution to Error 8

Right-Click on the service with the bug and use View Markup. The Service name may have got out of sync with the project name

<%@ServiceHostLanguage=”C#”Debug=”true”Service=”WCFServiceWebRole.MyService”CodeBehind=”MyService.svc.cs” %>

 

The name in the Azure Service Project > Roles > Your Role > Properties > Local Storage > must have the same name

 

 

Using SSL on the WCF WebRole

Links to:

If you have a subscription then see Pluralsight course: Windows Azure fundementals > Windows Azure Roles

http://rickrainey.com/2013/09/18/securing-a-wcf-service-in-an-azure-web-role-with-http-tcp-endpoints-2/

End.