Web Services

.NET Web Services

A .NET Web service is a class that inherits from System.Web.Services.WebService. This class is placed in a source file and saved with an .ASMX extension. Files with an .ASMX extension are a particular flavor of ASP.NET pages. The .ASMX file contains a directive that informs the ASP.NET run time about the nature of the file, the language in use throughout, and the main class that implements the service:

<%@ WebService Language="C#" %>

The main class must match the name declared in the Class attribute and must be public:

[WebService(Namespace="bwslib/0735615780")] public class NWService : WebService {  }

Indicating the base class for a .NET Web service is not mandatory. A Web service can also be architected starting from the ground up using a new class. Inheriting the behavior of the WebService class has some advantages, though. A Web service based on WebService has direct access to some common ASP.NET objects including Application, Request, Cache, Session, and Server. These objects are packed into an HttpContext object, which includes the time when the request was made. By using the WebService base class, a Web service also sees the ASP.NET server User object, which can be employed to verify the credentials of the user who is executing methods.

The .NET CLR supports keyword-like annotations to qualify the run-time behavior of programming elements such as classes and methods. These annotations are called attributes and are saved with the assembly metadata. The .NET Framework supplies many predefined attributes and also allows you to design your own. In the preceding code snippet, the WebService tag that prefixes the Web service class is just a system-provided attribute type that influences how the Web service is constructed and exposed.

The WebService Attribute

The WebService attribute is optional and does not affect the activity of the Web service class as to what is published and executed. The WebService attribute is represented by an instance of the WebServiceAttribute class. The WebService attribute allows you to change three default settings for the Web service: the namespace, the name, and the description.

The syntax for configuring a Web service attribute is declarative and rather intuitive. Within the body of the WebService attribute, you simply insert a comma-separated list of names and values. The keyword Description identifies the description of the Web service, whereas Name points to the official name of the Web service.

[WebService( Name="Northwind Info Service", Description="The Northwind Web Service")] public class NWService : WebService {  }

Changing the name and description of the Web service is mostly a matter of consistency. The name of the Web service defaults to the name of the class that implements it, and no description is provided. The Name attribute is used to identify the service in the WSDL text that explains the behavior of the service to clients. The description is not used in the companion WSDL text but is only retrieved and displayed by the IIS default page for URLs with an .ASMX extension. The attributes just discussed originate the page shown in Figure 9-1.

Figure 9-1

The standard page that IIS shows to let you test .NET Web services.

Changing the Default Namespace

Each Web service should have a unique namespace that allows potential clients to distinguish it from other namespaces. By default, each .NET Web service is given the same namespace: http://tempuri.org. You should change it as soon as possible prior to publishing the service on the Web. Using a temporary name does not affect the overall functionality, but will affect consistency and violate Web service naming convention. Although most namespace names out there look like URLs, you don t need to use URLs. A unique name suffices. You change the default namespace of a .NET Web service by using the Namespace property of the WebService attribute, as shown in the following example in which I ve used the ISBN of this book.

[WebService( Namespace="bwslib/0735615780", Name="Northwind Info Service", Description="The Northwind Web Service")]

The namespace information is used extensively in the WSDL definition of the Web service.

note

When you read documentation about Web services, you are usually bombarded by a storm of similar-sounding acronyms: URI, URL, URN. Let me clarify them for you. A Uniform Resource Identifier (URI) is a string that unequivocally identifies a resource over the network. There are two types of URI: Uniform Resource Locator (URL) and Uniform Resource Name (URN). A URL is given by the protocol prefix, the host name or IP address, optionally the port, and the path. A URN is simply a unique descriptive string, for example, the human-readable form of a CLSID is a URN.

Defining Web Methods

A Web service class is a regular .NET class, so it can have public as well as protected or private members; however, a Web method must be public to be effectively exposed over the Web. This public method belongs to the class that is marked with the WebMethod attribute, adding another level of visibility to the members of a .NET class. The key advantage of belonging to a class marked with WebMethod is that you can use the same class to expose functionality to both internal components of the system architecture and public consumers accessing the service via a URL. A software component allowed to instantiate the Web service class sees all the public methods and does not necessarily recognize the service as a Web service. However, when the same component is invoked as part of a Web service, the IIS and ASP.NET infrastructure ensures that external callers can see only methods marked with the WebMethod attribute. Any attempt to invoke untagged methods via a URL results in a failure.

The WebMethod Attribute

The WebMethod attribute has several properties that you can use to tailor the behavior of a Web method. Table 9-1 lists the properties supported by the attribute.

Table 9-1 Properties of the WebMethod Attribute

Property

Description

BufferResponse

Set to true by default, this property indicates that ASP.NET should buffer the entire method s response before sending it to the client. You typically set the property to false only if you know that the method returns large amounts of data. If set to false, the response is buffered but only in chunks of 16 KB.

CacheDuration

Specifies the length of time ASP.NET should cache the response of the method. This information is useful when you can foresee that the method will handle several calls in a short period of time. The duration is expressed in seconds and disabled by default. (The default is 0.) Caching recognizes distinct parameter values.

Description

Provides the description for the method. The value of the property is then written to the WSDL for the service.

EnableSession

Set to false by default, this property makes available to the method the Session object of the ASP.NET environment. Depending on how Session is configured, using this property might require cookie support on the client.

MessageName

Allows you to provide a publicly callable name for the method. Set the name of this property, and the resulting SOAP messages for the method target the name you set instead of the actual name. Use this property to give distinct names to overloaded methods in the event you use the same class as part of the middle tier and a Web service.

TransactionOption

Specifies the level of COM+ transactional support you desire for the method. A Web service method can have only two behaviors regardless of the value assigned to the standard TransactionOption enumeration you select: either it does not require a transaction or it must be the root of a new one.

The following code snippet shows how to set a few method attributes:

[WebMethod(MessageName="GetAllEmployees", CacheDuration=60, Description="Information about all the employees")] public DataSet GetEmployees() { } [WebMethod(MessageName="GetSomeEmployees", CacheDuration=60, Description="Information about the specified range of employees")] public DataSet GetEmployees(int nFirstEmpID, int nLastEmpID) { } [WebMethod(MessageName="GetEmployee", Description="Information about the specified employee")] public DataSet GetEmployees(int nEmpID) { }

Notice that the Web service class has one method with three overloads. Those methods are exposed over the Web by using distinct names. Notice also that attributes must be strongly typed in the declaration. In other words, the value you assign to CacheDuration must be a true number and not a quoted string containing a number, which would be the case in the layout of an ASP.NET page.

For the declarations in the preceding code, the standard IIS page for the Web service would look like that in Figure 9-2. The nwservice.asmx sample application that produces this output is available on the companion CD.

Figure 9-2

The sample Northwind Info Service shows off the overloaded methods.

Transactional Methods

The behavior of a Web service method in the COM+ environment deserves more attention. The stateless nature of HTTP prevents Web services from enlisting themselves in running transactions. In the case of a rollback, for example, tracking and canceling performed operations would be rather problematic. For this reason, a Web method can work only outside the scope of a transaction or start a transaction. The Disabled, NotSupported, and Supported values from the TransactionOption enumeration always cause the method to execute without a transaction. Both Required and RequiresNew create a new transaction.

When the method throws an exception, or an externally thrown exception is not handled, the transaction automatically aborts. Otherwise, when no exceptions occur, the transaction automatically commits. Of course, if the method calls SetAbort, the transaction always fails.

When your method needs to work within the scope of a transaction, make sure you import the System.EnterpriseServices namespace and reference the corresponding assembly. The namespace contains the definitions that enable you to access the programming interface of the COM+ services. You access the SetAbort and SetComplete methods by using the ContextUtil class.

Under the Hood of .NET Web Services

A .NET Web service is an ASP.NET application with an .ASMX extension that is accessed over HTTP. ASP.NET is a portion of the .NET Framework that works on top of IIS, taking care of files with special extensions such as .ASPX and .ASMX. One of the critical components of the ASP.NET infrastructure is the ISAPI filter that IIS involves when it gets a call for files with a certain extension. For example, Figure 9-3 shows the settings in the IIS configuration manager that handle .ASMX and .ASPX files with a system module named aspnet_isapi.dll.

Figure 9-3

The IIS mapping between .ASMX files and the appropriate ASP.NET ISAPI filter.

Calls for Web services always come through port 80. For .NET Web services, these calls are always made directly to URLs with an .ASMX extension. IIS intercepts these calls and passes all related packets to the registered ASP.NET ISAPI filter (aspnet_isapi.dll). The filter connects to a worker process named aspnet_wp.exe, which implements the HTTP pipeline that ASP.NET uses to process Web requests. Both executables are made of plain old Win32 code. The ASP.NET layer built atop IIS is shown in Figure 9-4.

Figure 9-4

The ASP.NET architecture that processes page and Web service requests.

The connection between the IIS process (the executable named inetinfo.exe) and the HTTP pipeline (the worker executable named aspnet_wp.exe) is established by using a named pipe.

note

A named pipe is a Win32 mechanism for transferring data over a network. As its name suggests, a named pipe works just like a pipe. You enter data in one end and the same data comes out the other end. Pipes can be established both locally to connect processes and between remote machines.

After the ASP.NET worker process receives a request, it routes the request through the .NET HTTP pipeline. The entry point of the pipeline is the Http Runtime class. This class is responsible for packaging the HTTP context for the request, which is nothing more than familiar ASP objects such as Request, Response, and Server. These objects are packed into an instance of the class HttpContext. Then a .NET application is started.

The .NET application processes incoming requests for the files in the specified virtual directory. (See Figure 9-5.) While a given request is being processed, special classes that implement the IHttpModule interface are given a chance to pre-process and post-process the request. The HTTP packets coming through are examined to determine whether they are part of a SOAP request addressed to a Web service or contain the bytes needed for a page request. HTTP handlers, namely .NET classes that implement the IHttpHandler interface, are the endpoints for all HTTP requests that involve ASP.NET and Web services. A specific HTTP handler for ASP.NET pages and Web services is created on demand by made-to-measure factory modules. These modules are classes buried in the folds of the System.Web namespace and are named PageHandler Factory and WebServiceHandlerFactory, respectively. HTTP handlers are ultimately responsible for parsing and compiling the bytes of .ASPX and .ASMX files, and for generating the response to the client.

Figure 9-5

The .NET HTTP pipeline that processes a Web request.

note

In .NET, the SOAP protocol is not an exclusive privilege of Web services, and .NET Remoting can optionally use SOAP packets to bind local and remote instances of .NET objects. Remoting can be considered as the .NET counterpart of DCOM. Compared to .NET Web services, .NET Remoting provides the same interoperability but is primarily optimized for .NET to .NET communication.

Invoking .NET Web Services

A Web service is always invoked by using an ordinary HTTP packet that contains information about the method to call and the arguments to use. This HTTP packet reaches the Web server by traveling as a GET or POST command. You can invoke a Web service method by using the following three approaches:

  • A POST command that embeds a SOAP request

  • A POST command that specifies the method name and parameters

  • A GET command whose URL contains the method name and parameters

The next listing shows a sample SOAP packet in the body of an HTTP POST command. It is the script for requesting the method GetEmployee to run and uses 2 as the argument.

POST /bwslib/Chap09/NWService/nwservice.asmx HTTP/1.1 Host: expo-one Content-Type: text/xml; charset=utf-8 Content-Length: 335 Action: "bwslib/0735615780/GetEmployee" <?xml version="1.0" encoding="utf-8"?> <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/"> <soap:Body> <GetEmployee xmlns="bwslib/0735615780"> <nEmpID>2</nEmpID> </GetEmployee> </soap:Body> </soap:Envelope>

To invoke a method in a Web service, SOAP is not strictly necessary. You can employ GET or POST commands with a much simpler body. The following listing shows the HTTP packet for a method request made via the simple POST command. The request is to execute the method GetEmployee, passing a value of 2.

POST /bwslib/Chap09/NWService/nwservice.asmx/GetEmployee HTTP/1.1 Host: expo-one Content-Type: application/x-www-form-urlencoded Content-Length: 8 nEmpID=2

The next listing shows what this code would look like if you chose to employ a GET command:

GET /bwslib/Chap09/NWService/nwservice.asmx/GetEmployee?nEmpID=2 HTTP/1.1 Host: expo-one

Handling the Response

The Web service requestor receives the response in the form of an HTTP return packet. The following listing illustrates the response you get from a POST or a GET command s traditional invocation of the GetEmployee method:

HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: actual length <?xml version="1.0" encoding="utf-8"?> <DataSet xmlns="bwslib/0735615780"> <schema xmlns="http://www.w3.org/2001/XMLSchema"> schema information </schema> XML representation of the DataSet </DataSet>

If you invoke the Web service method by using an HTTP POST command but use a SOAP payload to specify the desired action, you can expect this resultant string:

HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: length <?xml version="1.0" encoding="utf-8"?> <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/"> <soap:Body> <GetEmployeeResponse xmlns="bwslib/0735615780"> <GetEmployeeResult> <xsd:schema> schema information </xsd:schema> XML representation of the DataSet </GetEmployeeResult> </GetEmployeeResponse> </soap:Body> </soap:Envelope>

The fact that Web services are based on HTTP commands and XML formatted text demonstrates that interoperability between Web services running on different platforms is a reality, though certainly not an automatic and seamless feature.

note

As you can easily guess from the packet contents you just reviewed, SOAP payloads are quite verbose when compared with simpler HTTP POST and GET commands. The benefits of using SOAP become clearer as the complexity of data increases. GET and POST commands support primitive types, including arrays and enumerations. SOAP, instead, relies on a portable and more complex type system based on XML schemas. In addition, in .NET, .NET Web services, thanks to XML serialization, also supports classes, structures, and DataSet objects and their arrays.

Invoking Web Services from Script

To give you a practical demonstration of how Web services are really just HTTP-accessible software agents, let s write a Windows Script Host (WSH) script that allows plain Microsoft Visual Basic script code to download information from a remote server. To send HTTP commands from script code, I ll use the Microsoft.XmlHttp object a native component of Microsoft Internet Explorer 5 and Microsoft XML Parser (MSXML) 3 and later versions. The following script calls the method GetEmployee by using a GET command:

Const HOST = "http://..." Set xmlhttp = CreateObject("Microsoft.XMLHTTP") xmlhttp.open "GET", _ HOST & "bwslib/chap09/nwservice/nwservice.asmx/GetEmployee?nEmpID=2", _ false ' Actually send the request synchronously xmlhttp.send "" ' Store the results into a RAW_EMPLOYEES.XML file Set fso = CreateObject("Scripting.FileSystemObject") Set f = fso.CreateTextFile("raw_employees.xml") f.Write xmlhttp.responseText f.Close

The resultant XML string the body of the response is stored in a local XML file. The next listing shows how to accomplish the same result by using the POST command:

Const HOST = "http://..." Set xmlhttp = CreateObject("Microsoft.XMLHTTP") xmlhttp.open "POST", _ HOST & "bwslib/chap09/nwservice/nwservice.asmx/GetEmployee", _ false ' Set the Content-Type header to the specified value xmlhttp.setRequestHeader "Content-Type", _ "application/x-www-form-urlencoded" ' Set the body and send the request synchronously xmlhttp.send "nEmpID=2" ' Store the results into a RAW_EMPLOYEES.XML file Set fso = CreateObject("Scripting.FileSystemObject") Set f = fso.CreateTextFile("raw_employees.xml") f.Write xmlhttp.responseText f.Close

When using the POST command, you have to use a URL without parameters and store the parameter information in the body of the message. In addition, you also must indicate the content type of the message. The CallGetEmployeeGET.vbs and CallGetEmployeePOST.vbs sample scripts are available on the companion CD.

In addition to using WSH script, you can leverage Microsoft WebService DHTML behaviors (available at http://msdn.microsoft.com/downloads/samples/internet/asp_dot_net_servercontrols/webcontrolsVPDC/Default.asp) to call into a Web service from a client-side HTML page. All these techniques, however, are too low-level and not integrated with the development environment. The .NET Framework allows you to import any Web service in the existing project through proxy classes. A proxy class is just a .NET class that mirrors the programming interface of the Web service so that the application can interact with the remote service, even though it is totally unaware of the target location and platform.

Creating Proxy Classes

The availability of a proxy class makes calling a method on a Web service much easier. The class implementation takes care of all the work necessary to remote the call over the wire to the desired Web service method. The proxy class is automatically generated by using a system provided tool: the Web Services Description Language Tool (wsdl.exe). Microsoft Visual Studio .NET saves you from learning about the tool s numerous command line switches by providing a context menu item named Add Web Reference, shown in Figure 9-6.

Figure 9-6

Adding a reference to a Web service in Visual Studio .NET.

The command line shown in the next code listing creates a C# source file named NWServiceProxy.cs that contains a class named after the Web service in this case, NorthwindInfoService. The class is wrapped by a namespace named BWSLib.

wsdl.exe /out:NWServiceProxy.cs /namespace:BWSLib url

The name of the proxy class is the name of the Web service. If the name contains blanks, as in the name Northwind Info Service (the name of our sample Web service), the blanks are removed.

The /out switch allows you to specify a name for the class file. By default, the class is named after the Web service name. The default language is C#, but the /language option allows you to change it.

The /namespace option lets you indicate the namespace that will host the class. Visual Studio .NET defaults to a namespace that comes from the host name. For example, if you reference a Web service on http://expo-one, the default namespace for the proxy class is expo_one. Notice that a dash (-) in the host name is always replaced with the more Internet-friendly underscore character (_). Changing the default namespace is as easy as renaming the corresponding node in the Visual Studio Solution box. When you use the wsdl.exe utility from the command line, no namespace is used unless you explicitly specify it.

caution

Although the way you use proxy classes seems to closely match in-process communication, bear in mind that each method call on a Web service proxy class results in a round-trip to the Web server hosting the service.

Choosing the Transport Protocol

Another parameter you can set by using the command line is the protocol for carrying out an operation. The proxy class defaults to SOAP, which supports the most extensive set of data types when compared with the HTTP GET or POST command. The protocol you choose influences the declaration of the class because the class inherits from a particular protocol class. For example, when you choose SOAP, the proxy class for our Northwind Web service looks like this:

public class NorthwindInfoService : System.Web.Services.Protocols.SoapHttpClientProtocol {  }

HttpPostClientProtocol and HttpGetClientProtocol are the base classes used when you choose the HTTP POST or GET command. As mentioned earlier, the actual URL invoked varies slightly according to the transport protocol. In the case of HTTP GET, the URL un-encodes the method name and the arguments. In the case of HTTP POST, the URL terminates with the method name. Of course, the source code of the proxy class reflects this difference.

Accessing the Underlying URL

When the wsdl.exe utility creates the proxy class, the name of the URL you specify is typically hard-coded. However, you can change this setting at any time and even programmatically. The URL is exposed using a read/write property on the proxy class. The name of the property is Url. The property is inherited from the base class WebClientProtocol one of the proxy s ancestors. The proxy class sets the Url property in the constructor:

public NorthwindInfoService() { this.Url = "http://expo-one/bwslib/chap09/nwservice/nwservice.asmx"; }

In situations in which the URL cannot be determined unequivocally, or might change on a per-user basis or because of other run-time factors, you can ask the wsdl.exe utility not to hard-code the URL in the source. By using the /urlkey command line switch, you instruct the utility to dynamically read the Web service URL from the application s configuration file. If you use a switch such as /urlkey:ActualUrl, the proxy class constructor changes as follows:

using System.Configuration;  public NorthwindInfoService() { String urlSetting = ConfigurationSettings.AppSettings["ActualUrl"]; if ((urlSetting != null)) this.Url = urlSetting; else // Defaults to the URL used to build the proxy this.Url = "http://expo-one/bwslib/chap09/nwservice/nwservice.asmx"; }

ConfigurationSettings.AppSettings is a special property that provides access to the application settings defined in the <appSettings> section of the configuration file. Configuration files are XML files that allow you to change settings without recompiling the application. Configuration files also allow administrators to apply security and restriction policies that affect how applications run on various machines.

The name and location of the configuration file depends on the nature of the application. For ASP.NET pages and Web services, the file is named web.config and is located in the root directory of the application. You can also have other web.config files located in child directories. Child configuration files inherit the settings defined in configuration files located in parent directories. For Microsoft Windows Forms applications, the configuration file takes the name of the executable plus a .CONFIG extension. Such a file must reside in the same folder as the main executable.

Using Proxy Classes

No matter how you generated the proxy class, you use it as you would any other class in the .NET Framework. Visual Studio .NET automatically adds the class to the project. If you generate the class, you add it manually to the project. The source code of the proxy class can be freely edited in the same way that the code of any other class belonging to the project can be edited. Let s see how to use a Web service class in an ASP.NET page.

To start, you add a reference to the proxy class in the ASP.NET page by using the @ Assembly directive. You can choose to pre-compile the class into an assembly and link by using the Name attribute:

<%@ Assembly Name="NWServiceProxy" %>

You can also link the proxy class via source code, resorting to the Src attribute:

<%@ Assembly src="/books/2/368/1/html/2/NWServiceProxy.cs" %>

Once the assembly is correctly linked to the page, you can use the proxy class in your code as you would do with local, native classes. The following code shows how to call one of the overloads of the NorthwindInfoService class s GetEmployees method:

DataSet ds = new DataSet(); BWSLib.NorthwindInfoService nwis = new BWSLib.NorthwindInfoService(); ds = nwis.GetEmployees(1, 9);

Notice that the proxy class does not take into account the nicknames you might have set to distinguish, at the level of HTTP messages, the possible overloads of a given method. Previously, I marked each of the three overloads of the GetEmployees method with distinct nicknames by using the WebMethod class s MessageName attribute. Although these names are the only way to invoke the method via a URL, they are invalid entries in the proxy class programming interface. However, the proxy class code uses those nicknames to arrange proper HTTP calls by using the desired protocol. Figure 9-7 shows the output of the ProxyClass.aspx sample application available on the companion CD, which uses the Northwind Info Service.

Figure 9-7

An ASP.NET page that connects to the Northwind Info Service.

In Figure 9-8, the same Web service serves a Windows Forms application by using a different proxy class.

Figure 9-8

A Windows Forms application serviced by the Northwind Info Service.



Building Web Solutions with ASP. NET and ADO. NET
Building Web Solutions with ASP.Net and ADO.NET
ISBN: 0735615780
EAN: 2147483647
Year: 2002
Pages: 75
Authors: Dino Esposito

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net