Saturday, October 22, 2011

Creating and Consuming a Web Service


Introduction


One of the most powerful aspects of .NET is the ease with which one can create Web Services. A Web Service is an external interface provided by a Web site that can be called from other Web sites. For example, a financial company may make up to the minute stock quotes available via a Web Service for those who do their trading with that company. This information could be read from a Web page and displayed, or read from a stand-alone application on a customer's desktop computer.In this article we'll examine the two parts of a Web Service: how to create it and how to consume it. Specifically, we'll be creating a Web Service that exposes the FAQs fromASPFAQs.com.

Creating the Web Service


When creating a Web Service you must ask yourself, "What service am I trying to provide my users?" The goal of this article is to create a Web Service that will allow other Web sites to provide a listing of the FAQs from ASPFAQs.com on their site. Ideally, I want to restrict other sites to only being able to view the listing of FAQ categories and the FAQs by category. If they wish to view an "Answer" to a FAQ, I want the user to have to visit www.aspfaqs.com. Formally, my Web Service should provide other Web sites the ability to:
  1. View a listing of all of the FAQ categories
  2. View a listing of all of the FAQs for a particular category
  3. View the "Question" (but not the Answer) for a particular FAQ
Creating Web Services is quite simple. Start by creating a .asmx file (either through Visual Studio .NET or your favorite text editor (may I suggest Web Matrix, which has a template for creating Web Services)). The Web Service is created as an ordinary class; the methods that have the <WebMethod()> macro before them indicate the method is accessible via the Web Service.
For the ASPFAQs.com Web Service, we will create three Web Service-accessible methods: GetCategoriesGetFAQsInCategory, and GetFAQ, which perform the tasks (1), (2), and (3) outlined above, respectively. A private helper function, GetDataSet, is also included, which essentially populates a DataSet based on a passed in SQL query. The code for our Web Service class can be seen below:

<%@ WebService Language="VB" Class="ASPFAQs" %>
Imports System.Web.Services
Imports System.Data
Imports System.Data.SqlClient
Imports System.Configuration

Public Class ASPFAQs
Private Function GetDataSet(strSQL as String) as DataSet
'1. Create a connection
Dim myConnection as New SqlConnection(ConnectionString)

'2. Create the command object, passing in the SQL string
Dim myCommand as New SqlCommand(strSQL, myConnection)

myConnection.Open()

'3. Create the DataAdapter
Dim myDataAdapter as New SqlDataAdapter()
myDataAdapter.SelectCommand = myCommand


'4. Populate the DataSet and close the connection
Dim myDataSet as New DataSet()
myDataAdapter.Fill(myDataSet)
myConnection.Close()

'Return the DataSet
Return myDataSet
End Function


<WebMethod()> Public Function GetCategories() as DataSet
Return GetDataSet(SQL Query for Retrieving the FAQ Categories)
End Function


<WebMethod()> Public Function GetFAQsInCategory(catID as Integer) as DataSet
Return GetDataSet(SQL Query for Retrieving the FAQs for Category catID)
End Function


<WebMethod()> Public Function GetFAQ(FAQID as Integer) as DataSet
Return GetDataSet(SQL Query for Retrieving the FAQ FAQID)
End Function
End Class

Some things to note: the three Web Service-accessible methods are predicated with <WebMethod()>; at the top of the .asmx file is a @WebService directive that specifies the language and class in the file; the Web Service is named ASPFAQs, as shown by the class name. Once you have created this .asmx file and stored it on a Web-accessible directory, you can view the methods by visiting the page directly through your Web browser. For example, I named my Web Service file ASPFAQs.asmx and saved it in the /wsdirectory; so, by visiting http://aspnet.4guysfromrolla.com/ws/ASPFAQs.asmx, you can see a listing of the Web Method's public methods. Furthermore, you can "try out" the Web Methods by providing input parameters and viewing the returned results.
If you read last week's Protecting Yourself from SQL Injection Attacks article you may be concerned that in using Web Services that accept parameters that are used directly in a SQL statement you are opening yourself up to a SQL Injection attack. (The GetFAQsInCategory and GetFAQ are two such methods that may concern the alert reader.) However, SQL Injection attack is not a problem here because the Web Service code automatically ensures that the input parameter is of the correct type, which is Integer here. Hence, if a malicious user attempts to pass to the Web Service an input parameter of, say, 0 'malicious SQL statement, an error message like Cannot convert 0 'malicious SQL to System.Int32. Parameter name: type --> Input string was not in a correct format will be returned. If, however, the input string is of type String, you should be sure to sanitize the input string by replacing all single apostrophes with two successive single apostrophes.

Consuming a Web Service


For a Web site to consume a Web Service, a rather complicated and terse communication must occur between the client Web site (henceforth referred to as the "consumer") and the Web site that is providing the Web service (henceforth the "producer"). Essentially, the consumer must decide what producer's method it wishes to call. If there are input parameters, these parameters must be converted into XML to be passed along. An HTTP request is then made from the consumer to the producer, specifying the method it wishes to call and passing along the parameters in either a SOAP request, through the QueryString, or in the POST headers.The producer receives the incoming request, unpackages the input parameters, and calls the appropriate method of the specified class. This method, when complete, returns some value, which is packaged up and sent back to the consumer in an HTTP response. The consumer receives this response, unpackages the return value, completing the Web Service call.
Clearly we'd like to not have to worry about the HTTP message passing semantics at all when using Web Services. In order to remove this as a concern, we use what is called a Proxy class. Proxy classes serve as an intermediate step between the program (or Web page) on the consumer and the actual Web service on the producer. For each method in the producer's Web Service, there is a method in the Proxy class. It is the responsibility of the Proxy class to do all of the complicated message-passing tasks; essentially this complexity is hidden in the class, our Web page can simply call the methods of this class and not concern itself with the underlying semantics involved in calling a Web Service.

What in the World did he just Say?


By this point you're likely thoroughly confused, and that's understandable, this can be a confusing topic. The fundamental thing to understand is that the HTTP communications that must occur between a consumer and producer when calling a Web Service can be complicated and require much code. We'd prefer to have our Web pages that utilize Web Services be able to invoke the Web Service just as if it were a local component. In order to accomplish this, a Proxy class is used, whose public interface mirrors that of the Web Service. If you are still confused, consider viewing this PowerPoint presentation, which illustrates how to consume a Web Service. (Note that it shows how to create the Proxy class from the command-line; in this article we'll be using Visual Studio .NET.)

Creating the Proxy Class in Visual Studio .NET


Creating a Proxy class for a Web Service is a breeze in Visual Studio .NET. In your ASP.NET Web Project, simply right click on the References icon and choose to "Add a Web Reference." A dialog box will appear asking you for a URL - simply enter the URL of the Web Service, such as: http://aspnet.4guysfromrolla.com/ws/ASPFAQs.asmx. You will then see the description of the Web Service (just as if you had visited the URL directly through your Web browser). To complete this task, click the "Add Reference" button, which will automatically create the Proxy class for you and compile it.When added to your project the Proxy class's namespace will likely be the URL of your site, for example: com.4guysfromrolla.aspnet. (You can rename this if you so choose.) To call the Web Service from a Web page you use the Proxy class like you would any other local component. Imagine that we wanted to display a list of the FAQs from the ASP.NET category (which has category ID 22). We could do this by making a call to the GetFAQsInCategory method of the Web Service, passing in the parameter 22, and binding the resulting DataSet to a DataGrid, like so:

'HTML content in .aspx page...
<asp:datagrid id="dgCategoryFAQs" runat="server" />

'*** ------------------------------------------------ ***'

'code content in the code-behind page
Private Sub Page_Load(sender as Object, e as EventArgs)
'Create an instance of the Proxy class
Dim consumeWebService as com._4guysfromrolla.aspnet.ASPFAQs
Set consumeWebService = New com._4guysfromrolla.aspnet.ASPFAQs

'Bind the results of GetFAQsInCategory to dgCategoryFAQs
dgCategoryFAQs.DataSource = consumeWebService.GetFAQsInCategory(22)
dgCategoryFAQs.DataBind()
End Sub

From simply examining the code you could not determine that the call to the com._4guysfromrolla.aspnet.ASPFAQs Proxy class is, in actuality, a remote Web service call. When the Proxy class's GetFAQsInCategory method is called, the complex communications discussed previously occur (the remote HTTP request/response dialogue).

Creating a Proxy Class without Visual Studio .NET
Creating a Proxy class through Visual Studio .NET is very easy; however, if you don't have Visual Studio .NET, you can still create Proxy classes, but you must do so through the command-line. For more information on this technique be sure to read the PowerPoint presentation: Calling a Web Service from an ASP.NET Web Page.


Conclusion


In this article we examined how to create a Web Service and then how to consume it from an ASP.NET Web page using Visual Studio .NET. Microsoft has really simplified the process of producing and consuming Web Services with .NET. Creating a Web Service is as simple as creating a .asmx file and writing the code for the Web Service methods - save for the <WebMethod()> macros, the code appears nearly identical to the code one would write for a local component. Consuming a Web Service is painfully simple too, thanks to the use of Proxy classes. On top of that, the creation of Proxy classes is utterly simple through tools such as Visual Studio .NET.