Web Services in C# and .Net
<o:p>
Web Services provide the most flexible infrastructure for creating distributed computing applications. A web service in its simplest form is an object oriented class residing on a web server, and allowing remote clients to invoke its methods. Prior to web services, programmers have relied on sockets, RPC (Remote Procedure Calls), distributed COM (DCOM), and RMI (Remote Method Invocation) technologies to create distributed applications. The major problem with the above solutions is that either they are difficult to program (e.g., sockets), or require both the client and server side to be using the same technology (e.g., DCOM is mostly windows based, and RMI is Java based requiring both the client and server programs to be written in Java). The web services architecture overcomes these limitations. The following are the main motivations behind the development of web services.
<o:p>
- The client and the web service can be remote from each other.
- The client and the web service can use a totally different operating system or programming language.
- The web service can be made available through firewalls that allow port 80 but block other ports.
<o:p>
The above goals are easily accomplished by having the client and the web service use XML (or Http Get/Post protocols for simple type of parameters in method calls) to invoke methods on the service and receive back results. The format of XML used in invoking a web service is a W3C standard known as SOAP (Simple Object Access Protocol).
Here are some of the characteristics of a web service.
- Web services do not contain any user interface, and are mainly a class or classes exposing some useful methods to a client.
- A web server is used to host the web service which uses port 80 to make the web service available to clients.
- The methods of a web service can be invoked either by Http Get, or Http Post, or using SOAP.
- Http Get/Post techniques are used when the method parameters and results are simple data types. However, for passing back and forth structures and class objects in method parameters, SOAP is the solution. .Net provides HttpGetClientProtocol, HttpPostClientProtocol and SoapHttpClientProtocol classes for using Get, Post or SOAP protocols in the client code.
- The client of a web service can either be a web browser, a web application or a desktop application (e.g., a windows form application).
- All clients to a web service require a proxy (very similar to proxy in RPC or DCOM or RMI). The job of proxy is to marshal the parameters and results in method calls to a web service.
- The client code simply invokes an object of the proxy class (which has same methods and their signatures as the methods in the web service), and calls the proxy methods. The proxy, serializes the method calls to the web service using either Http Get/Post or SOAP, and also deserializes the results back to the client.
- If the client is a web browser, or a web application, the proxy code is downloaded from the web server hosting the web service. If the client application is a desktop application. The proxy code has to be generated in advance and resides on the client.
- Since web services reside on the web server and are accessed by clients using Http (SOAP is also using Http to send back and forth XML packets) which is a stateless protocol, maintaining state between method calls is possible through application or session objects.
- The client of a web service should be able to locate (discovery) and find out the prototypes of methods (description) in web service. For this purpose, the web service provides a discovery file and a description (wsdl format) file that lists all the public methods and their prototypes in XML format. Web services can be registered with a UDDI (Universal Description, Discovery and Integration) server which can act as the yellow pages for web services.
- The web service methods can use any .Net primitive data type (e.g., int, float, Double, string, DateTime, Decimal, Boolean, Object) in parameters or return values. Arrays and ArrayLists can also be used. User defined classes or structures used in parameters or return types require special XML streaming in using SOAP.
<o:p>
<o:p>
A web service class is usually derived from System.Web.Services.WebService class. Each public method of the web service class that needs to be made available as a web service will be marked with an attribute of [WebMethod].
<o:p>
Creating a simple Web Service Using C#:
<o:p>
Create an ASP.NET Web Service type project. Name the project StInfo
using System;<o:p>
using System.Collections;<o:p>
using System.ComponentModel;<o:p>
using System.Data;<o:p>
using System.Diagnostics;<o:p>
using System.Web;<o:p>
using System.Web.Services;<o:p>
namespace StInfo<o:p>
{<o:p>
/// <summary><o:p>
/// Summary description for Service1.<o:p>
/// </summary><o:p>
public class StInfo : System.Web.Services.WebService<o:p>
{<o:p>
string [,] students =<o:p>
{<o:p>
{"062987","Andrew", "Anserson","3.45","CS"},<o:p>
{"062988","Jessica", "Johnson","3.75","CS"},<o:p>
{"062989","Monica", "Marker","3.15","EE"},<o:p>
{"062990","Michael", "Jordan","2.45","MBA"},<o:p>
{"062991","Sally", "Simpson","3.12","EE"},<o:p>
{"062987","Mark", "Mathews","2.85","CS"},<o:p>
{"062987","Sara", "Sorenson","3.52","MBA"}<o:p>
};<o:p>
<o:p>
public StInfo()<o:p>
{<o:p>
//CODEGEN: This call is required by the ASP.NET Web Services Designer<o:p>
InitializeComponent();<o:p>
}<o:p>
<o:p>
#region Component Designer generated code<o:p>
//Required by the Web Services Designer <o:p>
private IContainer components = null;<o:p>
<o:p>
/// <summary><o:p>
/// Required method for Designer support - do not modify<o:p>
/// the contents of this method with the code editor.<o:p>
/// </summary><o:p>
private void InitializeComponent(){ }<o:p>
<o:p>
protected override void Dispose( bool disposing )<o:p>
{<o:p>
if(disposing && components != null)<o:p>
{<o:p>
components.Dispose();<o:p>
}<o:p>
base.Dispose(disposing); <o:p>
}<o:p>
#endregion<o:p>
[WebMethod]<o:p>
public string GetFirstName(string StID)<o:p>
{<o:p>
// return first name for a given student ID<o:p>
for (int i = 0; i < students.GetLength(0);i++)<o:p>
{<o:p>
if (String.Compare(StID,students[i,0],true) == 0)<o:p>
return students[i,1];<o:p>
}<o:p>
return "Student does not exist with this ID";<o:p>
}<o:p>
<o:p>
[WebMethod]<o:p>
public string GetLastName(string StID)<o:p>
{<o:p>
// return first name for a given student ID<o:p>
for (int i = 0; i < students.GetLength(0);i++)<o:p>
{<o:p>
if (String.Compare(StID,students[i,0],true) == 0)<o:p>
return students[i,2];<o:p>
}<o:p>
return "Student does not exist with this ID";<o:p>
}<o:p>
<o:p>
[WebMethod]<o:p>
public float GetGPA(string StID)<o:p>
{<o:p>
// return first name for a given student ID<o:p>
for (int i = 0; i < students.GetLength(0);i++)<o:p>
{<o:p>
if (String.Compare(StID,students[i,0],true) == 0)<o:p>
return float.Parse(students[i,3]);<o:p>
}<o:p>
return 0;<o:p>
}<o:p>
<o:p>
[WebMethod]<o:p>
public string GetMajor(string StID)<o:p>
{<o:p>
// return first name for a given student ID<o:p>
for (int i = 0; i < students.GetLength(0);i++)<o:p>
{<o:p>
if (String.Compare(StID,students[i,0],true) == 0)<o:p>
return students[i,4];<o:p>
}<o:p>
return "Student does not exist with this ID";<o:p>
}<o:p>
[WebMethod]<o:p>
public string[] GetAllStudents()<o:p>
{<o:p>
// return first name for a given student ID<o:p>
string [] sts = new string[students.GetLength(0)];<o:p>
for (int i = 0; i < students.GetLength(0);i++)<o:p>
{<o:p>
sts[i]=students[i,0];<o:p>
}<o:p>
return sts;<o:p>
}<o:p>
}<o:p>
}<o:p>
SOAP<o:p>
The following is a sample SOAP request and response. The placeholders shown need to be replaced with actual values.<o:p>
<SPAN style="COLOR: black">POST /stinfo/stinfo.asmx HTTP/1.1<o:p></o:p>
<SPAN style="COLOR: black">Host: localhost<o:p></o:p>
<SPAN style="COLOR: black">Content-Type: text/xml; charset=utf-8<o:p></o:p>
<SPAN style="COLOR: black">Content-Length: <SPAN style="COLOR: darkblue">length<SPAN style="COLOR: black"><o:p></o:p>
<SPAN style="COLOR: black">SOAPAction: "http://tempuri.org/GetLastName"<o:p></o:p>
<SPAN style="COLOR: black"><o:p> </o:p>
<SPAN style="COLOR: black"><?xml version="1.0" encoding="utf-8"?><o:p></o:p>
<SPAN style="COLOR: black"><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><o:p></o:p>
<SPAN style="COLOR: black"><SPAN style="mso-spacerun: yes"> <soap:Body><o:p></o:p>
<SPAN style="COLOR: black"><SPAN style="mso-spacerun: yes"> <GetLastName xmlns="http://tempuri.org/"><o:p></o:p>
<SPAN style="COLOR: black"><SPAN style="mso-spacerun: yes"> <StID><SPAN style="COLOR: darkblue">string<SPAN style="COLOR: black"></StID><o:p></o:p>
<SPAN style="COLOR: black"><SPAN style="mso-spacerun: yes"> </GetLastName><o:p></o:p>
<SPAN style="COLOR: black"><SPAN style="mso-spacerun: yes"> </soap:Body><o:p></o:p>
<SPAN style="COLOR: black"></soap:Envelope><o:p></o:p>
<SPAN style="COLOR: black">HTTP/1.1 200 OK<o:p></o:p>
<SPAN style="COLOR: black">Content-Type: text/xml; charset=utf-8<o:p></o:p>
<SPAN style="COLOR: black">Content-Length: <SPAN style="COLOR: darkblue">length<SPAN style="COLOR: black"><o:p></o:p>
<SPAN style="COLOR: black"><o:p> </o:p>
<SPAN style="COLOR: black"><?xml version="1.0" encoding="utf-8"?><o:p></o:p>
<SPAN style="COLOR: black"><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><o:p></o:p>
<SPAN style="COLOR: black"><SPAN style="mso-spacerun: yes"> <soap:Body><o:p></o:p>
<SPAN style="COLOR: black"><SPAN style="mso-spacerun: yes"> <SPAN style="mso-spacerun: yes"> <GetLastNameResponse xmlns="http://tempuri.org/"><o:p></o:p>
<SPAN style="COLOR: black"><SPAN style="mso-spacerun: yes"> <GetLastNameResult><SPAN style="COLOR: darkblue">string<SPAN style="COLOR: black"></GetLastNameResult><o:p></o:p>
<SPAN style="COLOR: black"><SPAN style="mso-spacerun: yes"> </GetLastNameResponse><o:p></o:p>
<SPAN style="COLOR: black"><SPAN style="mso-spacerun: yes"> </soap:Body><o:p></o:p>
<SPAN style="COLOR: black"></soap:Envelope><o:p></o:p>
The following is a sample HTTP POST request and response. The placeholders shown need to be replaced with actual values.<o:p>
<SPAN style="COLOR: black">POST /stinfo/stinfo.asmx/GetLastName HTTP/1.1<o:p></o:p>
<SPAN style="COLOR: black">Host: localhost<o:p></o:p>
<SPAN style="COLOR: black">Content-Type: application/x-www-form-urlencoded<o:p></o:p>
<SPAN style="COLOR: black">Content-Length: <SPAN style="COLOR: darkblue">length<SPAN style="COLOR: black"><o:p></o:p>
<SPAN style="COLOR: black"><o:p> </o:p>
<SPAN style="COLOR: darkgreen">StID<SPAN style="COLOR: black">=<SPAN style="COLOR: darkblue">string<SPAN style="COLOR: black"><o:p></o:p>
<SPAN style="COLOR: black">HTTP/1.1 200 OK<o:p></o:p>
<SPAN style="COLOR: black">Content-Type: text/xml; charset=utf-8<o:p></o:p>
<SPAN style="COLOR: black">Content-Length: <SPAN style="COLOR: darkblue">length<SPAN style="COLOR: black"><o:p></o:p>
<SPAN style="COLOR: black"><o:p> </o:p>
<SPAN style="COLOR: black"><?xml version="1.0" encoding="utf-8"?><o:p></o:p>
<SPAN style="COLOR: black"><string xmlns="http://tempuri.org/"><SPAN style="COLOR: darkblue">string<SPAN style="COLOR: black"></string><o:p></o:p>
using System;<o:p>
using System.Collections;<o:p>
using System.ComponentModel;<o:p>
using System.Data;<o:p>
using System.Drawing;<o:p>
using System.Web;<o:p>
using System.Web.SessionState;<o:p>
using System.Web.UI;<o:p>
using System.Web.UI.WebControls;<o:p>
using System.Web.UI.HtmlControls;<o:p>
<o:p>
namespace StInfoClient<o:p>
{<o:p>
/// <summary><o:p>
/// Summary description for WebForm1.<o:p>
/// </summary><o:p>
public class WebForm1 : System.Web.UI.Page<o:p>
{<o:p>
StInfo st = new StInfo();<o:p>
protected System.Web.UI.WebControls.Label Label1;<o:p>
protected System.Web.UI.WebControls.Label Label2;<o:p>
protected System.Web.UI.WebControls.Panel pnlStudent;<o:p>
protected System.Web.UI.WebControls.Label Label3;<o:p>
protected System.Web.UI.WebControls.Label Label4;<o:p>
protected System.Web.UI.WebControls.TextBox txtMajor;<o:p>
protected System.Web.UI.WebControls.TextBox txtGPA;<o:p>
protected System.Web.UI.WebControls.Label Label5;<o:p>
protected System.Web.UI.WebControls.Label Label6;<o:p>
protected System.Web.UI.WebControls.TextBox txtFirstName;<o:p>
protected System.Web.UI.WebControls.DropDownList ddlStudents;<o:p>
protected System.Web.UI.WebControls.TextBox txtLastName;<o:p>
<o:p>
private void Page_Load(object sender, System.EventArgs e)<o:p>
{<o:p>
// Put user code to initialize the page here<o:p>
if (!IsPostBack)<o:p>
{<o:p>
string []sts = st.GetAllStudents();<o:p>
ddlStudents.Items.Clear();<o:p>
foreach(string SID in sts)<o:p>
ddlStudents.Items.Add(SID);<o:p>
}<o:p>
}<o:p>
#region Web Form Designer generated code<o:p>
override protected void OnInit(EventArgs e)<o:p>
{<o:p>
// CODEGEN: This call is required by the ASP.NET Web Form Designer.<o:p>
InitializeComponent();<o:p>
base.OnInit(e);<o:p>
}<o:p>
/// <summary><o:p>
/// Required method for Designer support - do not modify<o:p>
/// the contents of this method with the code editor.<o:p>
/// </summary><o:p>
private void InitializeComponent()<o:p>
{ <o:p>
this.ddlStudents.SelectedIndexChanged += new System.EventHandler(this.ddlStudents_SelectedIndexChanged);<o:p>
this.Load += new System.EventHandler(this.Page_Load);<o:p>
}<o:p>
#endregion<o:p>
<o:p>
private void ddlStudents_SelectedIndexChanged(object sender, System.EventArgs e)<o:p>
{<o:p>
txtFirstName.Text = st.GetFirstName(ddlStudents.SelectedItem.Text);<o:p>
txtLastName.Text = st.GetLastName(ddlStudents.SelectedItem.Text);<o:p>
txtMajor.Text = st.GetMajor(ddlStudents.SelectedItem.Text);<o:p>
txtGPA.Text = (st.GetGPA(ddlStudents.SelectedItem.Text)).ToString();<o:p>
}<o:p>
}<o:p>
}<o:p>
[WebMethod(EnableSession=true)]<o:p>
public string GetUniversityName()<o:p>
{<o:p>
// if this class is not derived from WebService class<o:p>
// then we can obtain Session, Application, User<o:p>
// and Context objects as shown below<o:p>
// Application object is HttpApplicationState class<o:p>
// Note that you have to explicitly enable<o:p>
// the session for a web service as the default<o:p>
// is disabled for a method.<o:p>
try <o:p>
{<o:p>
System.Web.SessionState.HttpSessionState sess;<o:p>
sess = HttpContext.Current.Session;<o:p>
if (sess["UniversityName"] != null)<o:p>
return sess["UniversityName"].ToString();<o:p>
else<o:p>
return "No University Name Set in Session";<o:p>
}<o:p>
catch (Exception e)<o:p>
{<o:p>
return e.Message;<o:p>
}<o:p>
}<o:p>
<o:p>
[WebMethod(EnableSession=true)]<o:p>
public string SetUniversityName(string uName)<o:p>
{<o:p>
// if this class is not derived from WebService class<o:p>
// then we can obtain Session, Application, User<o:p>
// and Context objects as shown below<o:p>
// Note that you have to explicitly enable<o:p>
// the session for a web service as the default<o:p>
// is disabled for a method.<o:p>
try <o:p>
{<o:p>
System.Web.SessionState.HttpSessionState sess;<o:p>
sess = HttpContext.Current.Session;<o:p>
Session["UniversityName"]=uName;<o:p>
return ("University Name Set to " + uName);<o:p>
}<o:p>
catch(Exception e)<o:p>
{ return e.Message;}<o:p>