only for RuBoard |
This section shows you how to build a fictitious web service that provides RPC-oriented operations to store and manage users' Address Book Details. Developers can integrate this service into their applications and quickly provide an Address Book Management feature to attract the users of their application. These client application developers in turn can pay a licensing fee to use the Address Book service. (Note that the example provided in this section doesn't cover any of the details regarding licensing.)
The Address Book consists of address entries. Each address entry consists of an individual's name , nickname, email address, phone number, and address. The service defines the AddressEntry object that represents an individual address entry.
Table 11.4 contains the operations provided by the Address Book web service.
Operation | Description |
---|---|
AddUser(userName,password) token= LogonUser (userName,password) | Adds an new user . Authenticates the user's credentials and returns a token if the credentials supplied are valid. For a call with invalid credentials, this operation sends a SOAP fault to the client. The token is an encrypted string that the client needs to cache and send with all subsequent requests to the web service. This token expires after 20 minutes and the requests to an operation will fail. In such a situation, the client should call the LogonUser() operation to obtain a fresh token and retry the operation with the new token. |
addrList = GetAddressList(token) | Returns all the address entries in the user's Address Book. This operation does not return all the fields in an address entry but only a summary that contains the name, email, and phone number. Other details can be obtained by using the AddAddressEntry() operation for a single address entry. |
addrEntry = GetAddressEntry (token, addrEntryID) | Returns all the details of an address entry, including name, nickname, email, phone number, and address. |
AddAddressEntry(token,addrEntry) | Adds a new address entry to the user's Address Book. It checks for a duplicate nickname and sends SOAP fault in the case of a duplicate nickname. |
UpdateAddressEntry (token,addrEntry) | Updates an existing address entry in the user's Address Book. It checks for a duplicate nickname and sends a SOAP fault in the case of a duplicate nickname. |
DeleteAddressEntry (token,addrEntryID) | Deletes an existing address entry in the user's Address Book. |
Table 11.5 contains the application specific errors returned by the web service.
Error Code | Messsage | Description |
---|---|---|
1001 | Invalid User | This error is sent in the following cases:
|
1002 | Duplicate Nickname | This error is sent when the client sends a nickname that already exists. |
The Address Book web service stores the data on a SQL Server 7.0 (or later) database. Figure 11.16 shows the two tables, Users and AddressBook , used by this web service.
Listing 11.8 shows the Address Book web service. The code shows the web service class AddressBookService with the two custom SOAP Exception classes and a database configuration information class. The AddressBookService class is designed to handle the business processes and the two classes, AddressEntry and AddressBookUser , represent the business data.
<%@ WebService Language="c#" Class="Service.AddressBookService" %> using System; using System.Web.Services; using System.Web.Services.Protocols; using System.Xml; namespace Service { [WebService(Namespace="http://newriders.com/webservices/")] public class AddressBookService : WebService { //Add a New Users of the Web service [WebMethod] public bool AddUser(string userName,string password) { return AddressBookUser.CreateUser(userName, password ); }//AddUser [WebMethod] public string LogonUser(string userName,string password) { int userID ; if( (userID = AddressBookUser.Authenticate(userName,password))== 0 ) throw new InvalidAddressBookUserException(Context.Request.Url.AbsoluteUri); return AddressBookUser.GetToken( userID ); }//AddUser [WebMethod] public bool AddAddressEntry(string token, AddressEntry addrEntry) { int userID = 0; if( ( userID = AddressBookUser.Authenticate(token) )==0) throw new InvalidAddressBookUserException(Context.Request.Url.AbsoluteUri); try { if (AddressEntry.Create( userID, addrEntry )) return true; else return false; } catch(Exception ex ) { if( ex.Message.Equals("Duplicate Nickname") ) throw new DuplicateNickNameException(Context.Request.Url.AbsoluteUri); else throw ex; } } [WebMethod] public bool UpdateAddressEntry(string token, AddressEntry addrEntry) { int userID = 0; if( ( userID = AddressBookUser.Authenticate(token) )==0) throw new InvalidAddressBookUserException(Context.Request.Url.AbsoluteUri); try { if (AddressEntry.Update( userID, addrEntry )) return true; else return false; } catch(Exception ex ) { if( ex.Message.Equals("Duplicate Nickname") ) throw new DuplicateNickNameException(Context.Request.Url.AbsoluteUri); else throw ex; } } [WebMethod] public bool DeleteAddressEntry(string token, int addrEntryID ) { int userID = 0; if( ( userID = AddressBookUser.Authenticate(token) )==0) throw new InvalidAddressBookUserException(Context.Request.Url.AbsoluteUri); if (AddressEntry.Delete( userID, addrEntryID )) return true; else return false; } [WebMethod] public AddressEntry GetAddressEntry(string token, int addrEntryID) { if( AddressBookUser.Authenticate(token) == 0 ) throw new InvalidAddressBookUserException(Context.Request.Url.AbsoluteUri); return new AddressEntry( addrEntryID ) ; } [WebMethod] public AddressEntry[] GetAddressList(string token) { int userID = 0; if( ( userID = AddressBookUser.Authenticate(token) )==0) throw new InvalidAddressBookUserException(Context.Request.Url.AbsoluteUri); return AddressEntry.GetAddressList(userID ); } } //Database configuration details public class DBConfig { public static string CONNECT_STRING { get { return "Server=NewRiders;uid=sa;pwd=;database=AddressBook"; } } } //Exception thrown on Invalid Logon credentials or an Expired Token public class InvalidAddressBookUserException : SoapException { private static XmlNode node; static InvalidAddressBookUserException() { XmlDocument doc = new System.Xml.XmlDocument(); node = doc.CreateNode(XmlNodeType.Element, SoapException.DetailElementName.Name, SoapException.DetailElementName.Namespace); //node.InnerText = "Invalid User"; node.InnerXml = "<e:myfaultdetails xmlns:e='http://newriders.com/webservices/addressbook/ faults' >" +" <message>Invalid User</message>" +" <errorcode>1001</errorcode>" +"</e:myfaultdetails>"; } public InvalidAddressBookUserException(string actor): base("Invalid User", SoapException.ClientFaultCode,actor,node ) { } } //Exception thrown for a create request with an already existing //NickName public class DuplicateNickNameException : SoapException { private static XmlNode node; static DuplicateNickNameException() { XmlDocument doc = new System.Xml.XmlDocument(); node = doc.CreateNode(XmlNodeType.Element, SoapException.DetailElementName.Name, SoapException.DetailElementName.Namespace); //node.InnerText = "Duplicate Nickname"; node.InnerXml = "<e:myfaultdetails xmlns:e='http://newriders.com/webservices/addressbook/ faults' >" +" <message>Duplicate Nickname</message>" +" <errorcode>1002</errorcode>" +"</e:myfaultdetails>"; } public DuplicateNickNameException(string actor): base("Duplicate Nickname", SoapException.ClientFaultCode,actor,node ) { } } }
The preceding two custom SOAP Exception classes, InvalidAddressBookUser Exception and DuplicateNickNameException inherits from the System.Web. Services.Protocols.SoapException class and creates the application-specific error information in the detail element within the SOAP Fault element.
Listing 11.9 shows the AddressBookUser class used by the web service for user management and authentication.
using System; using System.Data.SqlClient ; using System.Web.Services.Protocols; namespace Service { public class AddressBookUser { //Create new User with a unique userName public static bool CreateUser(string userName,string password) { bool created = false ; SqlConnection conn = new SqlConnection(DBConfig.CONNECT_STRING); SqlCommand command = conn.CreateCommand(); command.CommandText = "SELECT count(*) FROM Users WHERE UserName='"+userName+"'"; conn.Open(); if ( (int) command.ExecuteScalar() == 0 ) { //Add the User command.CommandText = "INSERT INTO Users (UserName,Password) VALUES " + "('"+ userName +"','"+ password +"')"; command.ExecuteNonQuery(); created = true; } command.Dispose(); conn.Close(); return created; } //Authenticate user credentials public static int Authenticate(string userName,string password) { int userID = 0; SqlConnection conn = new SqlConnection(DBConfig.CONNECT_STRING); SqlCommand command = conn.CreateCommand(); command.CommandText = "SELECT UserID FROM Users WHERE UserName='"+userName+"'" + "AND Password= '"+password+"'"; conn.Open(); SqlDataReader reader = command.ExecuteReader(); if( reader.Read() ) { userID = reader.GetInt32(0); } reader.Close(); conn.Close(); return userID; } //Authenticate user credentials using the token and check for an expired //token public static int Authenticate(string token ) { int userID = 0; token = Decrypt(token); DateTime dtToken = Convert.ToDateTime(token); DateTime dtNow = DateTime.Now; if( dtNow.Subtract(dtToken).TotalMinutes > 20 )//expire after 20 mins { return userID; } Object tempID ; SqlConnection conn = new SqlConnection(DBConfig.CONNECT_STRING); SqlCommand command = conn.CreateCommand(); command.CommandText = "SELECT UserID FROM Users WHERE Token='"+token+"'"; conn.Open(); tempID = command.ExecuteScalar(); if (tempID!=null) { userID = (int) tempID; } conn.Close(); return userID; } //Fetch a fresh token public static string GetToken( int userID ) { //For the token you can use the combination //"System.DateTime.Now.ToString()+userName+password" //to make it unique and secure. Here we only use the current time string token = Encrypt(System.DateTime.Now.ToString() ); SqlConnection conn = new SqlConnection(DBConfig.CONNECT_STRING); SqlCommand command = conn.CreateCommand(); command.CommandText = "UPDATE Users SET Token = '"+ token +"'" + "WHERE UserID= "+ userID +" "; conn.Open(); command.ExecuteNonQuery(); conn.Close(); return token; } private static string Encrypt(string str) { //Add your Encryption logic here return str; } private static string Decrypt(string str) { //Add your Decryption logic here return str; } } }
Listing 11.10 shows the AddressEntry class that implements the operations related to the address entries in the Address Book.
using System; using System.Data.SqlClient; namespace Service { public class AddressEntry { public int EntryID = 0; public string NickName; public string FirstName; public string LastName; public string EmailID; public string Phone; public Location AddrLocation; //Default public constrcutor essential for searlization public AddressEntry() { } public AddressEntry(int entryID) { SqlConnection conn = new SqlConnection(DBConfig.CONNECT_STRING); SqlCommand command = conn.CreateCommand(); command.CommandText = "SELECT * FROM AddressBook WHERE AddressEntryID='"+entryID +"'"; conn.Open(); SqlDataReader reader = command.ExecuteReader(); if(reader.Read() ) { this.EntryID = entryID; NickName = reader.GetString(2); FirstName = reader.IsDBNull(3)? null:reader.GetString(3); LastName = reader.IsDBNull(4)? null:reader.GetString(4); EmailID = reader.IsDBNull(5)? null:reader.GetString(5); Phone = reader.IsDBNull(6)? null:reader.GetString(6); AddrLocation = new Location(); AddrLocation.Street = reader.IsDBNull(7)? null:reader.GetString(7); AddrLocation.City = reader.IsDBNull(8)? null:reader.GetString(8); AddrLocation.State = reader.IsDBNull(9)? null:reader.GetString(9); AddrLocation.Zip = reader.IsDBNull(10)? null:reader.GetString(10); AddrLocation.Country = reader.IsDBNull(11)? null:reader.GetString(11); } reader.Close(); conn.Close(); if( this.EntryID == 0 ) throw new Exception("Invalid EntryID."); } //Create a new Address Entry public static bool Create( int userID, AddressEntry addrEntry ) { Location loc = addrEntry.AddrLocation ; String sql; SqlConnection conn = new SqlConnection(DBConfig.CONNECT_STRING); SqlCommand command = conn.CreateCommand(); command.CommandText = "SELECT Count(*) FROM AddressBook WHERE UserID='"+userID +"'" +" AND NickName= '"+ addrEntry.NickName +"' "; conn.Open(); if ( (int) command.ExecuteScalar() > 0 ) { conn.Close(); throw new Exception("Duplicate Nickname"); } if (loc != null) { sql = "INSERT INTO AddressBook" + "(UserID,NickName,FirstName,LastName,EmailID,Phone,Street,City,State,Zip,Country )" + "VALUES ("+userID+",'"+addrEntry.NickName+"','"+addrEntry.FirstName+"'," + "'"+ addrEntry.LastName +"','"+addrEntry.EmailID +"','"+ addrEntry.Phone+ "', " + "'"+ loc.Street +"','"+ loc.City +"','"+ loc.State +"','"+loc.Zip +"','" + loc.Country +"' )"; } else { sql = "INSERT INTO AddressBook" + "(UserID,NickName,FirstName,LastName,EmailID,Phone )" + "VALUES ("+userID+",'"+addrEntry.NickName+"','"+addrEntry.FirstName+"'," + "'"+ addrEntry.LastName +"','"+addrEntry.EmailID +"','"+addrEntry.Phone +"' )" ; } command.CommandText = sql; command.ExecuteNonQuery(); conn.Close(); return true; } //Update existing Address Entry public static bool Update( int userID, AddressEntry addrEntry ) { Location loc = addrEntry.AddrLocation ; String sql; SqlConnection conn = new SqlConnection(DBConfig.CONNECT_STRING); SqlCommand command = conn.CreateCommand(); command.CommandText = "SELECT Count(*) FROM AddressBook WHERE UserID='"+ userID +"'" +" AND AddressEntryID != '"+ addrEntry.EntryID +"' AND NickName= '"+ addrEntry.NickName +"'"; conn.Open(); if ( (int) command.ExecuteScalar() > 0 ) { conn.Close(); throw new Exception("Duplicate NickName"); } if (loc != null) { sql = "UPDATE AddressBook " + " SET NickName = '"+addrEntry.NickName+"', FirstName = '"+addrEntry.FirstName+"'," + " LastName = '"+ addrEntry.LastName +"' ,EmailID = '"+addrEntry.EmailID +"'," + " Phone ='"+ addrEntry.Phone+ "', Street= '"+ loc.Street +"'," + " City = '"+ loc.City +"' ,State = '"+ loc.State +"' ,Zip= '"+ loc.Zip +"'," + " Country = '"+ loc.Country +"'" + " WHERE UserID = '"+ userID +"' AND AddressEntryID = '"+addrEntry.EntryID +"'"; } else { sql = "UPDATE AddressBook" + " SET NickName = '"+addrEntry.NickName+"', FirstName = '"+addrEntry.FirstName+"'," + " LastName = '"+ addrEntry.LastName +"' ,EmailID = '"+addrEntry.EmailID +"'," + " Phone ='"+ addrEntry.Phone+ "'," + " WHERE UserID = '"+ userID +"' AND AddressEntryID = '"+addrEntry.EntryID +"'"; } command.CommandText = sql; command.ExecuteNonQuery(); conn.Close(); return true; } //Delete existing Address Entry public static bool Delete( int userID, int entryID ) { SqlConnection conn = new SqlConnection(DBConfig.CONNECT_STRING); SqlCommand command = conn.CreateCommand(); command.CommandText = "DELETE FROM AddressBook WHERE UserID='"+ userID+"'" +" AND AddressEntryID = '"+ entryID +"' "; conn.Open(); command.ExecuteNonQuery(); conn.Close(); return true; } //Get all existing Address entires for a specific user public static AddressEntry[] GetAddressList(int userID) { AddressEntry[] addressList = null ; int addrCount = 0; SqlConnection conn = new SqlConnection(DBConfig.CONNECT_STRING); SqlCommand command = conn.CreateCommand(); command.CommandText = "SELECT Count(*) FROM AddressBook WHERE UserID='"+userID +"'"; conn.Open(); addrCount = (int) command.ExecuteScalar(); if( addrCount != 0 ) { addressList = new AddressEntry[addrCount]; command.CommandText = "SELECT AddressEntryID,NickName,FirstName,LastName, EmailID," + "Phone FROM AddressBook WHERE UserID='"+ userID +"' ORDER BY NickName" ; SqlDataReader reader = command.ExecuteReader(); int i=0; while( reader.Read() ) { addressList[i] = new AddressEntry(); addressList[i].EntryID = reader.GetInt32(0); addressList[i].NickName = reader.GetString(1); addressList[i].FirstName = reader.IsDBNull(2)? null:reader.GetString(2); addressList[i].LastName = reader.IsDBNull(3)? null:reader.GetString(3); addressList[i].EmailID = reader.IsDBNull(4)? null:reader.GetString(4); addressList[i++].Phone = reader.IsDBNull(5)? null:reader.GetString(5); } reader.Close(); } conn.Close(); return addressList; } } public class Location { public string Street; public string City; public string State; public string Zip; public string Country; } }
only for RuBoard |