At its simplest, creating a web service in .NET can be almost trivially easy. I’m going to start with a simple inventory query service. Example 10-2 shows the basic ASP.NET skeleton for such a service.
<%@ WebService Language="C#" Class="InventoryQuery" %> using System.Web.Services; [WebService(Namespace="http://angushardware.com/InventoryQuery")] public class InventoryQuery : WebService { [WebMethod] public int GetNumberInStock(string productCode) { return 0; } }
Let’s break this skeleton down into its basic components.
The presence of
the @ WebService
directive in a file with the
.asmx
extension tells the ASP.NET provider that
the web service is located at InventoryQuery.asmx
,
that the web service’s source code is written in C#,
and that the implementation is in the class named
InventoryQuery
. The code could also be written in
JScript .NET (JS
) or Visual Basic .NET
(VB
). Additionally, the code could actually reside
in a separate file, compiled into an assembly located in the
.Bin
directory relative to the
.asmx
file:
<%@ WebService Language="C#" Class="InventoryQuery" %>
There is no restriction on the name of the assembly containing the
class that implements a web service, and multiple web services may
exist in the same directory. However, if multiple assemblies in the
.Bin
directory each contain a class with the name
listed in an .asmx
file, there is no guarantee
which one will be used when that web service is invoked.
The
WebService
attribute comes from the
System.Web.Services
namespace, and indicates that
the class in question represents the implementation of a web service.
The Namespace
property sets the default namespace
for the web service. The WebService
attribute also
has Name
and Description
properties, which allow you to set the public name of the web
service, and give it a short textual description. The
Name
property defaults to the name of the class. A
class that implements a web service does not actually need to have
the WebService
attribute; any class can implement
a web service:
using System.Web.Services; [WebService(Namespace="http://angushardware.com/")]
Although the Namespace
property is optional, if
you leave it off the ASP .NET provider will use http://tempuri.org/
as the default, and it
will generate many strong hints that you should change the namespace.
Web service implementations
can extend the WebService
type. The
WebService
type provides access to state
information through its Application
,
Context
, Server
,
Session
, and User
properties.
Although extending WebService
is not required for
a web service implementation, I have chosen to do so in this example:
public class InventoryQuery : WebService {
Although the names are the same, the WebService
attribute and the WebService
type are completely
different beasts. If it helps you to keep the distinction clear,
remember that while attribute names always end with
Attribute
, they also have an alias to the name
without Attribute
on the end. So the
WebService
attribute type is actually formally
called WebServiceAttribute
, whereas the
WebService
type is just called
WebService
.
Finally, the GetNumberInStock
method represents
the InventoryQuery
web service’s
GetNumberInStock
message itself. Right now it will
always return 0, since I’ve only created a stub
method.
The WebMethod
attribute indicates that the method it is attached to implements a
particular web service message. By default, the name of the message
is the name of the method itself, although the
WebMethod
attribute has a
MessageName
property that allows you to override
the name. WebMethod
also has an optional
Description
property:
[WebMethod] public int GetNumberInStock(string productCode) { return 0; }
The WebMethod
attribute is the only attribute that
is absolutely required to implement a web service using ASP.NET. If
no method within a class has the WebMethod
attribute, the ASP.NET provider has no way of knowing what messages
the web service supports.
To see the
InventoryQuery
web service in action, make sure
the InventoryQuery.asmx
file is in C:dotNetAndXml
(or whatever directory you
set as the application directory in your web server), and navigate
your web browser to http://localhost/dotNetAndXml/InventoryQuery.asmx
.
You should see the page in Figure 10-3.
This HTML page is generated by the
ASP.NET provider, based on the metadata included in the
.asmx
file and the class that implements the web
service. If either the WebService
attribute or the
WebMethod
attribute included a
Description
property, the descriptive text would
be displayed here as well. If any more methods were exposed by
attaching the WebMethod
attribute to them, they
would all be listed on this page as well.
Clicking on the “Service
Description” link opens a new window containing the
WSDL file that the ASP.NET provider has automatically generated.
Example 10-3 shows the generated WSDL for the
InventoryQuery
web service. Note the similarities
to Example 10-1.
<?xml version="1.0" encoding="utf-8"?> <definitions xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:s0="http://angushardware.com" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" targetNamespace="http://angushardware.com" xmlns="http://schemas.xmlsoap.org/wsdl/"> <types> <s:schema elementFormDefault="qualified" targetNamespace="http://angushardware.com"> <s:element name="GetNumberInStock"> <s:complexType> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="productCode" type="s:string" /> </s:sequence> </s:complexType> </s:element> <s:element name="GetNumberInStockResponse"> <s:complexType> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="GetNumberInStockResult" type="s:int" /> </s:sequence> </s:complexType> </s:element> <s:element name="int" type="s:int" /> </s:schema> </types> <message name="GetNumberInStockSoapIn"> <part name="parameters" element="s0:GetNumberInStock" /> </message> <message name="GetNumberInStockSoapOut"> <part name="parameters" element="s0:GetNumberInStockResponse" /> </message> <message name="GetNumberInStockHttpGetIn"> <part name="productCode" type="s:string" /> </message> <message name="GetNumberInStockHttpGetOut"> <part name="Body" element="s0:int" /> </message> <message name="GetNumberInStockHttpPostIn"> <part name="productCode" type="s:string" /> </message> <message name="GetNumberInStockHttpPostOut"> <part name="Body" element="s0:int" /> </message> <portType name="InventoryQuerySoap"> <operation name="GetNumberInStock"> <input message="s0:GetNumberInStockSoapIn" /> <output message="s0:GetNumberInStockSoapOut" /> </operation> </portType> <portType name="InventoryQueryHttpGet"> <operation name="GetNumberInStock"> <input message="s0:GetNumberInStockHttpGetIn" /> <output message="s0:GetNumberInStockHttpGetOut" /> </operation> </portType> <portType name="InventoryQueryHttpPost"> <operation name="GetNumberInStock"> <input message="s0:GetNumberInStockHttpPostIn" /> <output message="s0:GetNumberInStockHttpPostOut" /> </operation> </portType> <binding name="InventoryQuerySoap" type="s0:InventoryQuerySoap"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" /> <operation name="GetNumberInStock"> <soap:operation soapAction="http://angushardware.com/GetNumberInStock" style="document" /> <input> <soap:body use="literal" /> </input> <output> <soap:body use="literal" /> </output> </operation> </binding> <binding name="InventoryQueryHttpGet" type="s0:InventoryQueryHttpGet"> <http:binding verb="GET" /> <operation name="GetNumberInStock"> <http:operation location="/GetNumberInStock" /> <input> <http:urlEncoded /> </input> <output> <mime:mimeXml part="Body" /> </output> </operation> </binding> <binding name="InventoryQueryHttpPost" type="s0:InventoryQueryHttpPost"> <http:binding verb="POST" /> <operation name="GetNumberInStock"> <http:operation location="/GetNumberInStock" /> <input> <mime:content type="application/x-www-form-urlencoded" /> </input> <output> <mime:mimeXml part="Body" /> </output> </operation> </binding> <service name="InventoryQuery"> <port name="InventoryQuerySoap" binding="s0:InventoryQuerySoap"> <soap:address location="http://127.0.0.1/dotNetAndXml/InventoryQuery.asmx" /> </port> <port name="InventoryQueryHttpGet" binding="s0:InventoryQueryHttpGet"> <http:address location="http://127.0.0.1/dotNetAndXml/InventoryQuery.asmx" /> </port> <port name="InventoryQueryHttpPost" binding="s0:InventoryQueryHttpPost"> <http:address location="http://127.0.0.1/dotNetAndXml/InventoryQuery.asmx" /> </port> </service> </definitions>
As you’ll recall from the earlier discussion, the WSDL document provides a complete description of the web service, including all the supported messages, types, port types, bindings, and services. In this case, the ASP.NET provider automatically supports REST-style HTTP POST and GET methods as well as SOAP over HTTP POST.
There is an alternative style for web services known as Representational State Transfer, or REST. The basic premise of REST is that the HTTP methods GET, POST, PUT, and DELETE provide all the functionality needed to interact with any resources addressable by its URI. WSDL supports REST-based web services as well as SOAP and XML-RPC.
The generated WSDL file in Example 10-3 contains more information than the one in Example 10-1. However, you can see that the only real difference is the inclusion of additional transports for HTTP GET and HTTP POST. The .NET Web Services provider creates these bindings, in addition to SOAP, automatically.
Clicking on the “GetNumberInStock” link in Figure 10-3 will bring you to the page shown in Figure 10-4. This HTML page is also generated automatically by the ASP.NET Web Services provider.
From this page, you can issue a request to the
GetNumberInStock
method of the
InventoryQuery
web service. Entering in a
value—say, “803B”—and
clicking the Invoke button causes the method to be invoked with the
given parameter.
This example uses the HTTP GET version of the web service, so the
request that was actually sent to the web service provider used the
following URL: http://127.0.0.1/dotNetAndXml/InventoryQuery.asmx/GetNumberInStock?productCode=803B
.
Because right now the C# code always returns 0, the following response is always returned:
<?xml version="1.0" encoding="utf-8" ?> <int xmlns="http://angushardware.com">0</int>
This would also be returned from the HTTP POST version. The SOAP request, however, would look quite a bit different. It would be sent with the following HTTP header and SOAP request envelope:
POST /dotNetAndXml/InventoryQuery.asmx HTTP/1.1 Host: 127.0.0.1 Content-Type: text/xml; charset=utf-8 Content-Length: 365 SOAPAction: "http://angushardware.com/GetNumberInStock" <?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> <GetNumberInStock xmlns="http://angushardware.com"> <productCode>803B</productCode> </GetNumberInStock> </soap:Body> </soap:Envelope>
The HTTP response header and SOAP response envelope would be the following:
HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: 400 <?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> <GetNumberInStockResponse xmlns="http://angushardware.com"> <GetNumberInStockResult>0</GetNumberInStockResult> </GetNumberInStockResponse> </soap:Body> </soap:Envelope>
If you scroll a little further down the page in Figure 10-4, you’ll see examples of requests and responses in all three versions of the web service.
3.149.26.176