Skapa ett SOAP verktyg med .NET

Under seminariena som levererats under hösten har jag använt mig av ett verktyg för att kunna skicka egenskapade SOAP-meddelanden till XML Web Services för att på så sätt undersöka eventuella brister i validering och hantering av information som finns i meddelandena. Jag har fått mycket förfrågningar om det verktyget går att distribuera ut, men blivit bedd att inte skicka ut det eftersom det är ett internt verktyg. Istället har jag valt att skriva en kort artikel om hur du själv kan skapa detta verktyg, se det som en undersökning av System.Net namnrymden med hjälp av C#.

Formuläret

Jag har valt att skapa ett formulär med några kontroller på, enligt följande layout:

Formuläret innehåller följande kontroller:

System.Windows.Forms.TextBox   tbServiceUrl;
System.Windows.Forms.TextBox   tbSoapAction;
System.Windows.Forms.TextBox   tbRequest;
System.Windows.Forms.Label     lblRequest;
System.Windows.Forms.Label     lblResponse;
System.Windows.Forms.Label     lblServiceUrl;
System.Windows.Forms.Label     lblSoapAction;
System.Windows.Forms.Button    btnSubmit;
AxSHDocVw.AxWebBrowser         wbWebResponse; 

Jag har också lagt in webbrowser kontrollen AxWebBrowser för att kunna se SOAP-meddelandet som returneras i en webbläsare och då kunna använda den transformation som automatiskt görs från XML till DHTML som i sin tur gör att jag kan navigera i resultatet enklare.

Logiken

Efter att själva formuläret är skapat så börjar vi implementera logiken, i den här versionen av verktyget kommer jag att lägga all användarinteraktion i btnSubmit.Click-händelsen men väljer också att bryta ut några valda delar i privata metoder för att göra det tydligare vad som sker. Men dubbelklicka på btnSubmit-knappen för att skapa en händelsehanterare för Click-händelsen och mata sedan in kod enligt nedan.

De klasser som vi kommer att använda för att skicka SOAP-meddelanden och sedan ta emot resultatet är HttpWebRequest och HttpWebResponse som finns definerade i System.Net namnrymden. HttpWebRequest och HttpWebResponse är Http-implementationer av de mer generella WebRequest respektive WebResponse klasserna. För att skapa en instans av HttpWebRequest objektet använder jag mig av en statisk metod på WebRequest klassen som heter Create enligt följande: 

HttpWebRequest request = null;
HttpWebResponse response = null;

try {
 
request = WebRequest.Create(tbServiceUrl.Text) as HttpWebRequest;

WebRequest.Create() metoden kräver som inparameter antingen en sträng som beskriver sökvägen till vår webbservice som ska anropas, alternativt en instans av objektet System.Uri som beskriver detsamma. I mitt fall väljer jag att skicka in den text som är inskriven i textboxen tbServiceUrl. 

Sedan sätter jag en del parametrar för förfrågan som ska skickas iväg. Jag väljer också här att göra de här parametrarna statiska, men jag antar att du snabbt inser hur du kan vidareutveckla applikationen för att bli mer flexibel. Några av dessa fält som sätts exponerar Http-headers och påverkar naturligtvis exekveringen och emottagningen av SOAP-meddelandet.

  request.Method = "POST"; 

Här anger jag att jag ska använda POST som metod för att posta meddelandet till servern.

  request.ContentType = "text/xml;";
 
request.ContentLength = (long)tbRequest.Text.Length;

Innehållet i meddelandet kommer att vara av typen “text/xml;” och jag måste också berätta hur långt meddelandet kommer att vara. 

  request.ProtocolVersion = HttpVersion.Version11;

Jag väljer vilket version av Http som ska användas genom en enumeration som finns i System.Net.HttpVersion. 

  request.Credentials = CredentialCache.DefaultCredentials;

I den här applikationen jobbar jag inte med någon speciell säkerhetshantering utan later grundinställningarna användas för detta. 

  request.Headers.Add("SOAPAction",tbSoapAction.Text);

Det sista som läggs till är den skräddarsydda http-headern SOAPAction som talar om för den anropade Web Servicen vilken metod som ska anropas och exekveras. 

Vad jag nu behöver göra är att skriva till request-objektet det innehåll som ska skickas med, alltså det XML-dokument som jag avser skicka som meddelande. Tyvärr så är det inte så enkelt som att sätta ett publikt fält på request-objektet utan istället måste jag skriva till den ström som representerar kroppen på meddelandet enligt följande kod:

  byte[] buffer;
 
Stream stream;
 
buffer = Encoding.ASCII.GetBytes(tbRequest.Text);
 
stream = request.GetRequestStream();
 
stream.Write(buffer,0,buffer.Length);
 
stream.Close();

Jag skapar helt enkel en byte-array och läser till den bufferten in innehållet i tbRequest.Text. Sedan hämtar jag strömmen som finns i request-objektet och skriver innehållet i bufferten till den strömmen innan strömmen stängs. 

  response = request.GetResponse() as HttpWebResponse;
} 

Sedan anropas metoden GetResponse som skickar iväg meddelandet till servern och returnerar ett WebResponse-objekt som konverteras till mitt HttpWebResponse-objekt. Innan jag visar det som finns i response-objektet så vill jag passa på att hantera eventuella fel som kan uppstå vid anropet och se till att även dessa visas för användaren. Därför lägger jag till följande:

catch(WebException ex) {
  if( response == null )
 
response = ex.Response as HttpWebResponse;
} 

Om någonting blir fel vid anropet till servern så kommer response-objektet att vara tomt och då väljer jag att i det fall som felet är av typen WebException att använda det returnerade WebResponse-fältet på felet och konvertera det till mitt HttpWebResponse-objekt. Sedan ser jag till att resultatet visas genom att skriva:

finally {
 
string strResponse = StreamToString(response.GetResponseStream());
 
ViewWebResponse(strResponse);
}

Hjälpfunktioner

Det sista kodstycket innehåller anrop till två hjälpfunktioner. Den ena konverterar den ström som finns i response-objektet till en sträng som är lättare att behandla och sedan har jag en hjälpmetod som visar resultatet i vår webbläsarkontroll vbWebResponse. Den första metoden skapas enligt följande: 

private string StreamToString(Stream stream) {
 
StreamReader reader = new StreamReader(stream,Encoding.UTF8);
 
return reader.ReadToEnd();
} 

I StreamToString metoden skapar jag en ny instans av en StreamReader och skickar in den ström som jag vill konvertera till en sträng i konstruktorn, sedan kan jag helt enkelt anropa ReadToEnd()-metoden som returnerar hella strömmen som en sträng som också returneras.

private void ViewWebResponse(string response) {
 
string tempPath = Path.GetTempPath() + "mySoapToolResponse";
 
object flags = null;
 
 
Cursor.Current = Cursors.WaitCursor;
 
StreamWriter tempFile = File.CreateText(tempPath);
 
tempFile.Write(response);
 
tempFile.Close();
 
wbWebResponse.Navigate(tempPath,ref flags,ref flags,ref flags,ref flags);
 
Cursor.Current = Cursors.Default;
} 

Den andra hjälpmetoden kan ses lite onödig men den behövs eftersom wbWebResponse-kontrollen inte kan läsa strängar rakt upp och ner utan istället behöver navigera till en fil eller en webbsida. I den här metoden skapar jag först en sökväg till en temporärfil i Temp-katalogen i filsystemet och skriver den inskickade strängen till denna temporärfil, stänger filen och navigerar till den med Navigate()-metoden som tyvärr kräver en del tomma object-parametrar. I metoden ser jag också till att visa för användaren att funktionen jobbar genom att uppdatera markören.

Användning

Om du nu har skrivit koden enligt de exempel som finns i denna artikel så borde du ha ett verktyg som kan användas för att testa dina webbservices med skräddardsydda XML-dokument. Ett exempel på användning kan vara enligt följande, jag har skapat en webbservice på min lokala dator i ett projekt som heter WebService där webbservice-klassen heter Service och den metod som jag vill testa är Add. Jag har också satt en namnrymd på min webbservice som är ”urn:se-microsoft-msdn:demos”. Vad jag nu gör är att jag med en webbläsare går till den sida som dokumenterar min webbservice, http://localhost/WebService/Service.asmx och väljer Add-metoden. Då får jag en sida som jag kan använda för att testa webbservicen och ännu bättre jag får också ett exempel på en SOAP-begäran som jag kan kopiera och klistra in i mitt verktyg.

 

Jag kan också i detta exempel läsa av SOAPAction. Så för att testa min webbservice kopierar jag ovanstående XML-dokument och klistrar in det i mitt verktyg, samt skriver URL och SOAPAction i avsedda textboxar. Innan jag trycker på knappen för att skicka meddelandet så byter jag ut de ”placeholders” som genererats åt mig med korrekta heltal, eftersom det är det som ska adderas. När jag sedan trycker på Submit-knappen så får jag följande resultat:

 

Sammanfattning

I den här artikeln har jag visat ett kort exempel på hur du kan skapa ett enkelt men relativt kraftfullt verktyg för att testa olika SOAP-meddelanden och se om förväntat resultat returneras. Om du har förslag på förbättringar så hör gärna av dig!