/// public Securities { string urlSetting = System.Configuration.ConfigurationSettings.AppSettings ["SecuritiesWebServiceUrl"]; } The constructor sets the object’s Url property to t
Trang 1Table 6-11: Selected Properties, Methods, and Events of the SoapHttpClientProtocol
Class
group to use when connecting to the Web service A connection group provides a mechanism for allowing multiple clients within the same application to share connections open to a given Web server
CookieContainer Used to access the cookies maintained by the proxy Also
provides a mechanism for setting cookies for a particular domain
Credentials Specifies authentication credentials that can be used to
log into the Web server The supported methods of authentication include Basic Authentication, Windows NT Challenge/Response, Kerberos, and Digest
PreAuthenticate Specifies whether the authentication credentials should
be sent immediately or as a result of receiving a 401 access denied error
Proxy Contains the information necessary to connect to the
proxy server This includes the URL, port, and user name/domain/password
RequestEncoding Specifies the type of encoding that will be used when
serializing the request message The default is UTF-8
Timeout Specifies the period of time, in milliseconds, that a
synchronous Web request has to complete before the request is aborted The default is infinity (−1)
Url Specifies the address of the Web service endpoint
UserAgent Specifies the value of the user agent header in the HTTP
request
Abort Used to abort any asynchronous method calls that are
currently executing
Discover Used to dynamically discover the location of the Web
service via the DISCO file referenced by the Url property
// -// <autogenerated>
// This code was generated by a tool
Trang 2The date and time can be recorded by promptly checking the file into a source code
repository or by adding it as a comment to the generated file The WSDL document can be obtained by WSDL.exe itself You can accomplish this by using one of the optional
command-line parameters I discuss later in this section
[System.Xml.Serialization.SoapIncludeAttribute(typeof(SoapPaymentHeader))]
public class Securities : System.Web.Services.Protocols
SoapHttpClientProtocol {
public SoapPaymentHeader SoapPaymentHeaderValue;
Trang 3
public SoapReceiptHeader SoapReceiptHeaderValue;
The Securities class is defined within the BrokerageFirm namespace It is derived from the
SoapHttpClientProtocol class SoapHttpClientProtocol serves as the base class for all
ASP.NET proxies and contains the implementation necessary to communicate with most HTTP-based Web services
The Securities class is also decorated with three attributes The first is the
WebServiceBinding attribute, which serves the exact same role on the client as it does on
the Web service This attribute allows you to formally reference a particular binding defined
within another namespace The two other attributes are SoapInclude attributes They tell the XML Serializer to include the SoapPaymentHeaderValue and the SoapReceiptHeaderValue
member variables within the SOAP message
/// <remarks/>
public Securities() {
string urlSetting =
System.Configuration.ConfigurationSettings.AppSettings ["SecuritiesWebServiceUrl"];
}
The constructor sets the object’s Url property to the value of the SecuritiesWebServiceUrl
application configuration parameter defined in the application’s configuration file If the value
is not found, the Url property is set to the value contained within the HTTP extension
element that defines the address of the endpoint within the WSDL document’s service
definition
You should consider modifying the else logic to throw an exception instead of defaulting to a
hard-coded value This will make it easier to diagnose some problems within your
application For example, when you are trying to debug your application, it would be easy to
overlook the fact that the SecuritiesWebServiceUri parameter is misspelled within your
configuration file (Did you catch the misspelling?)
You might need to dynamically modify the Url property at run time For example, the
application might want to reissue its request to another server in the event of failure The Url
property is a publicly exposed read/write property, so it can be modified by the client at run time
You can also set the Url property to point to a DISCO file containing a reference to the
targeted Web service You can then call the Discover method to dynamically bind to the Web
service contained within the DISCO file I will cover DISCO files in more detail in Chapter 10
Trang 4is for synchronously invoking the Web method, and the other two are used in combination to invoke the Web method asynchronously Here is the synchronous definition for the
InstantQuote method:
[System.Web.Services.Protocols.SoapHeaderAttribute
"SoapReceiptHeaderValue", Direction=System.Web.Services Protocols.SoapHeaderDirection.Out)]
The InstantQuote method is decorated with the SoapHeader, DebuggerStepThrough, and
SoapRpcMethod attributes The DebuggerStepThrough attribute is used by the Visual Studio
.NET debugger The Visual Studio NET debugger will not stop within the method marked
with this attribute
The SoapHeader and SoapRpcMethod attributes serve the same purpose as they do when applied to a Web method The SoapHeader attribute indicates which member variable
should be serialized into the header of the SOAP message The SoapRpcMethod attribute
indicates the encoding style and the format of the message as well as the value of the SOAP
HTTPAction header
The signature of the method itself is composed of NET types that match their XML
counterparts described within the types section of the WSDL document This wrapper
method definition allows code written against the proxy to take full advantage of the features provided by the NET platform For example, if a client attempts to pass invalid parameters,
such as passing two strings to the Add method instead of two integers, the compiler will
generate errors at compile time Developers using Visual Studio NET will also have full
IntelliSense capabilities when they write code against the proxy
The implementation of the InstantQuote method packages the parameters into an array of objects and calls the Invoke method Because this method is publicly exposed, you can call
it directly However, using the method exposed by the WSDL.exe-generated proxy provides
a more convenient and natural calling convention
In many circumstances, making a synchronous call to a Web method is not ideal This is especially true for Web services accessed via the Internet, where quality and speed of the connection might be uncertain This might also be true for Web services hosted within the walls of a corporate data center For example, a Web service might be used to expose data
Trang 5contained within a mainframe A significant amount of initialization might need to be done to set up a connection to the mainframe, or the Web service might be accessed during times of peak load
The next two methods defined for the InstantQuote operation are BeginInstantQuote and
EndInstantQuote These methods are used to make an asynchronous call to the Securities
Web service’s InstantQuote Web method:
/// <remarks/>
public System.IAsyncResult BeginInstantQuote(string symbol, CurrencyType targetCurrency, System.AsyncCallback callback, object asyncState) {
By convention, the method used to invoke the asynchronous call is prefixed with Begin and
the method used to retrieve the parameters returned by the Web service is prefixed with
End The implementation invok es the BeginInvoke and EndInvoke methods, respectively
The asynchronous methods are not decorated with attributes used to describe the formatting
of the message The methodName parameter contains the name of the method that the
ASP.NET runtime will use to retrieve the formatting information If the asynchronous
message is decorated with any attributes such as SoapDocumentMethod, these attributes
will be ignored
[System.Xml.Serialization.SoapTypeAttribute("SoapReceiptHeader", "http://woodgrovebank.com/Securities/encodedTypes")]
public class SoapReceiptHeader : SoapHeader {
Trang 6public enum CurrencyType {
attribute to explicitly define type information used by the XML Serializer to map the NET
types to their XML Schema counterparts
The use of the proxy to make a synchronous method call is fairly trivial The following
example writes the price of a security passed as a command-line argument out to the
console:
Trang 7string symbol = args[0];
Securities securities = new Securities();
// Create and initialize the Payment header
SoapPaymentHeader paymentHeader = new SoapPaymentHeader(); paymentHeader.CreditCardNumber = "12345";
}
Because the Payment header is required to be passed to the InstantQuote method, I create
a new SoapPaymentHeader object Then I initialize it and set it to the
SoapPaymentHeaderValue property on the securities object The proxy is responsible for
serializing the SoapPaymentHeader object within the header of the SOAP request message Invoking the InstantQuote Web met hod asynchronously involves a little more work The
following code is contained within a WinForm application Let’s walk through an example I will write a console application that uses the Securities Web service proxy to make an
asynchronous method call:
static Securities securities = new Securities();
First I create a class that will contain the console application Then I create a static instance
of the Securities Web service proxy as a static member of the class I do this because the
Trang 8static void Main(string[] args)
{
string symbol = args[0];
SoapPaymentHeader paymentHeader = new
As you have learned, WSDL.exe will properly handle generating proxies for Web services
that support headers The generated proxy code will contain a class declaration for each
header defined by the Web service Depending on the direction of the header, instances of the header class can be either retrieved or set using an associated property defined by the proxy class for the Web service By default, the property will have the same name as the
class, with a prefix of Value If the class declaration contains an XmlType attribute
(discussed in Chapter 7), the property on the client will simply be the name given to the XML type
The proxy class will also perform client-side validation of the SOAP headers before sending
the message to the server For example, the proxy will throw a SoapException if
SoapPaymentHeaderValue was set to null when the Web method was invoked
Within the Main function, a call is made to the BeginInstantQuote method This method
accepts two parameters in addition to the securities symbol and the target currency of the
quote I also pass an instance of the AsyncCallback delegate that serves as a reference to the InstantQuoteCallback method I will define shortly This tells the Web service proxy to execute the InstantQuoteCallback method once the Web service returns If there is no
callback method that should be invoked, you can pass null for the value of the parameter
The fourth parameter is intended to pass state that should be associated with the method
once the callback has been invoked The parameter is of type object and therefore accepts
an instance of any NET type In this case, I pass the symbol of the security for which I have requested the quote
public static void InstantQuoteCallback(IAsyncResult result) {
// Obtain the results
double price = securities.EndInstantQuote(result);
Trang 9// Obtain the additional state that was sent by
// the call to BeginCallback
WebClientAsyncResult webResult =
(WebClientAsyncResult)result;
string symbol = (string)webResult.AsyncState;
// Display the results within a message box
Console.WriteLine("{0} = {1}", symbol, price);
to obtain the return value of the Web method call Next I obtain the additional state
information from the AsyncState property—in this case, the symbol passed to the Add
method Finally the price of the security is written to the console
Cookies
Proxies derived from SoapHttpClientProtocol fully support HTTP cookies However, the
proxies have cookies disabled by default To enable cookie support, you must set the
CookieContainer property on the proxy object to reference an instance of a CookieContainer
object
Earlier in the chapter, I leveraged session state to configure the target currency The client
first sets the target currency by calling SetCurrency Then the client calls InstantQuote to
obtain the price of the security Because the Web service relies on cookies to maintain
session state, clients using this Web service need to explicitly enable cookies The following code demonstrates how to enable session state:
string symbol = args[0];
Securities securities = new Securities();
// Enable session state by creating a new cookie container securities.CookieContainer = new CookieContainer();
Trang 10// Receive a quote on the requested security in UK pounds securities.SetCurrency(CurrencyType.UK_POUNDS);
Summary
ASP.NET provides a robust, feature-rich platform for easily creating and consuming Web
services For a V1 product, it is remarkably feature complete
An ASP.NET Web service is represented by an asmx file hosted within an IIS Web
application The implementation of the Web service can be contained within the asmx file or within a compiled DLL If the code appears inline within the asmx file, the ASP.NET runtime will automatically compile it the first time it is accessed
A Web service is defined by a standard public class declaration Public methods defined
within the class can be exposed by the Web service if you decorate the method with the
WebMethod attribute This attribute exposes properties that can be optionally set to control
the behavior of the ASP.NET runtime The class can also be decorated with the WebService
attribute
All ASP.NET Web services expose a SOAP interface over HTTP Depending on the
complexity of the Web service’s interface, an ASP.NET Web service might also support
HTTP GET and HTTP POST The ASP.NET runtime will automatically map data contained within requests from the client and their corresponding responses to their corresponding
.NET datatypes
The ASP.NET platform will automatically generate documentation for the Web service A
human-readable HTML version of the documentation can be obtained by calling the asmx file with no parameters A programmatic WSDL version of the documentation can be
obtained by appending &wsdl to the URL that addresses the asmx file
ASP.NET supports two distinct encoding styles, Document and RPC Document is the
default and is used primarily for document-based message exchanges between the client and the server RPC is used primarily for procedure-based communication between the
client and the server You can select RPC by using the SoapRpcService or SoapRpcMethod
attribute
You should be careful when you pass value types as parameters because the ASP.NET
platform has some inconsistencies when identity is maintained The identities of built-in
value types such as int and double are never maintained, even when passed by reference
The identity of a custom value type when passed by reference is maintained when the
encoding style is set to RPC However, the identity of custom value types passed by value is improperly maintained when the encoding style is set to RPC
Regardless of the style of encoding, SOAP formally defines how errors returned to the client should be encoded within a SOAP message The ASP.NET runtime will automatically map
Trang 11.NET exceptions into a well-formed SOAP Fault element You can also formally raise a fault
by throwing an exception of type SoapException
You can facilitate interface inheritance by referencing a port type or a binding definition from
an external namespace Of the two, ASP.NET supports referencing transport-specific
binding definitions You first reference the remote binding definition with the
WebServiceBinding attribute, and then you associate the reference to the binding with a
particular Web method via the Binding property of the SoapRpcMethod or
SoapDocumentMethod attribute
ASP.NET also provides a fairly robust state management system It supports three
configurations: In Process, Out of Process, and SQL Server Of the three, In Process is the most performant configuration You should consider Out of Process and SQL Server only if the Web service will be deployed on a Web farm Regardless of which model you use, the programming model is exactly the same
The ASP.NET platform also has good support for defining and consuming SOAP headers A
SOAP header is defined by deriving from the SoapHeader class You then use the
SoapHeader attribute to associate the header with a particular Web method ASP.NET
automatically deserializes any headers received from the client and serializes any headers sent from the Web server
Finally, the ASP.NET framework provides an interception mechanism called SOAP
extensions The SOAP extensions framework lets you examine and, if necessary, modify the contents of the SOAP messages exchanged between the client and the server
I didn’t cover a couple of key topics related to ASP.NET Web services because they deserve chapters of their own In Chapter 7, I will discuss how to control how individual parameters passed by a Web service or its client are encoded within a SOAP message In Chapter 9, I will also cover the security services provided by ASP.NET
Trang 12Chapter 7: XML Serialization
Overview
The ASP.NET runtime is built on top of a technology called XML serialization XML
serialization is responsible for serializing instances of NET types to XML and deserializing XML to instances of NET types XML serialization is also responsible for serializing NET type definitions to XML schemas and deserializing XML schemas to NET type definitions Sometimes, this default behavior might not entirely meet your needs For example, public properties and fields will be serialized into elements within the resulting XML document, but many existing and emerging Web services interfaces such as UDDI and NET My Services expose interfaces that use attributes Therefore, you need a means of controlling how NET types and instances of NET types are serialized into XML
Consider the following SOAP message, which submits a purchase order:
<Name accountNumber="12345">ABC Company</Name>
<Street>123 Some Street</Street>
<Name accountNumber="12345">ABC Company</Name>
<Street>123 Some Street</Street>
Trang 13The message contains a mixture of elements and attributes For example, the part number
for each item listed in the purchase order is serialized within the partNumber attribute of
each Item element The Name element within the billing and shipping addresses contains
the name of the company as well as an attribute that contains the company’s account
number
Controlling XML Serialization
For situations in which the default serialization support is not adequate, XML serialization
provides mechanisms for altering the way NET types are serialized to XML You do this
mostly by using the attributes defined in the System.Xml.Serialization namespace, which
Table 7-1 describes
Table 7-1: XML Serialization Attributes
XmlAnyAttribute Creates an “open” XML datatype in which any attribute can be
added to its root node
XmlAnyElement Creates an “open” XML datatype where any element can be
included as a child element
XmlArray Controls how the root node of an array is serialized
XmlArrayItem Controls how an item of an array is serialized
XmlAttribute Indicates that a public field, property, or parameter should be
serialized as an attribute
XmlElement Controls how a public field, property, or parameter is serialized as
an element
XmlEnum Controls how an enumeration is serialized
XmlIgnore Indicates that XML serialization should not serialize the
Trang 14Table 7-1: XML Serialization Attributes
member
XmlInclude Tells XML serialization to include a particular datatype definition of
a class that derives from a base class exposed by a Web service’s interface
XmlRoot Identifies a type as the root of an XML document
XmlText Specifies that the member variable be serialized as the content of
the parent element
XmlType Maps an XML type to a particular class, structure, enumeration, or
interface declaration You can use the XML serialization attributes only with literal, document- oriented SOAP
messages For example, the attributes will be ignored if the Web method is decorated with
the SoapRpcMethod attribute or is decorated with the SoapDocumentMethod attribute and has the Use property set to SoapBinding-Use.Encoded
The attributes listed in the table control how XML serialization represents NET types in
XML For Web services, the attributes control how instances of.NET types are encoded into the body of a SOAP message They also control how NET types are represented as XML datatypes in the WSDL document that describes the Web service
In this chapter, I use the preceding attributes to create the PurchaseOrder NET type, which
can be used to define how a purchase order is serialized in the body of a SOAP message I
also create the AcceptPO Web method This Web method is used to receive purchase
orders similar to the one in the previous example
Defining the Root PurchaseOrder Datatype
The first step is to define the PurchaseOrder class, which will represent the root element
within the body of the SOAP message Recall from Chapter 4 that you can set elements to
null by setting the xsi:nil attribute to true For the SOAP message to be valid, it must not
contain a null reference to the purchase order
The following code defines the PurchaseOrder class:
To define the PurchaseOrder XML datatype, I need to define a public class by the same
name I also need to ensure that the PurchaseOrder element cannot contain a null value I can do this by decorating the class with the XmlRoot attribute
Trang 15One of the properties exposed by the XmlRoot attribute is IsNullable This property indicates
whether the schema generated by XML serialization will permit instance documents to
contain a PurchaseOrder element with a null value
You can also use the XmlRoot attribute to control the behavior of XML serialization Table
7-2 describes the properties exposed by the XmlRoot attribute
Table 7-2: XmlRootAttribute Properties
Property Description
DataType Specifies the XML datatype in which the class should be encoded
ElementName Specifies the name of the root XML element
Form Specifies whether the XML element must be namespace
qualified It is set to one of three values defined by the
XmlSchemaForm enumeration: None, Qualified, or Unqualified
IsNullable Specifies whether the value of the XML element can be set to xsd:nil
Namespace Specifies the XML namespace in which the root element is
qualified
Most of the elements within the PurchaseOrder document contain attributes or child
elements themselves For example, the BillingAddress and ShippingAddress elements each contain Name, Street, City, State, and ZipCode child elements, as shown here:
<PurchaseOrder>
<BillingAddress>
<Name accountNumber="12345">ABC Company</Name>
<Street>123 Some Street</Street>
<Name accountNumber="12345">ABC Company</Name>
<Street>123 Some Street</Street>
Trang 16You can model complex structures such as the preceding one using a class hierarchy The
following example defines an Address class and then defines two fields within the
PurchaseOrder class, BillingAddress and ShippingAddress Both fields are defined as type Address
[XmlRoot(IsNullable=false)]
public class PurchaseOrder
{
[XmlElemen t(IsNullable=true, DataType="normalizedString")]
public string Comments;
[XmlElement(IsNullable=false)]
public Address BillingAddress;
[XmlElement(IsNullable=false)]
public Address ShippingAddress;
// Additional type definitions
}
public class Address
{
public CompanyName Name;
public string Street;
public string City;
public string State;
public string ZipCode;
}
The preceding code uses the XmlElement attribute to control how properties and fields are serialized as elements within the resulting XML document For example, the Comments field can contain a null value, but neither the ShippingAddress field nor the BillingAddress field
can be set to null Like the XmlRoot attribute, the XmlElement attribute exposes an
IsNullable property
The Comments element can be set to null because XML serialization does not allow you to
specify that a particular element or attribute be able to optionally occur within a document
This is because the XmlElement attribute does not provide a means of explicitly setting the
minOccurs and maxOccurs constraints on an element I discuss another potential
workaround for this situation later in the chapter
The Comments element contained within the PurchaseOrder document contains text related
to the order In addition to setting the XmlElement attribute’s IsNullable property, the
preceding code sets the DataType property for the Comments field
Trang 17XML serialization provides a default mapping between NET types and the built-in datatypes
defined by XML Schema For example, the Comments property is defined as type String By default, XML serialization will transform the String type to the XML built-in datatype string
Suppose the back-end system that will record the receipt of the purchase order does not
accept linefeeds or tabs In order to communicate this to the client, I set the datatype of the
Comments element to normalizedString By definition, normalizedString cannot contain
linefeeds or tabs
XML serialization supports all XML built-in datatypes Table 7-3 lists the supported mappings between XML datatypes and NET types
Table 7-3: Mapping Between XML Datatypes and NET Types
base64Binary Byte (array) Int Int32
dateTime DateTime negativeInteger String
duration String nonNegativeInteger String
ENTITY String nonPositiveInteger String
ENTITIES String normalizedString String
gDay String positiveInteger String
hexBinary Byte (array) token String
Even though XML serialization will transform an XML datatype into its corresponding NET
type, it will not perform any validation on the data For example, the XML datatype integer maps to the String NET type, but the client can pass non-numeric data to the Web service
Therefore, it is up to the Web service to enforce additional constraints over and above what
Trang 18The XmlElement attribute exposes additional properties that you can use to control how a
.NET type is serialized to XML Table 7-4 describes these properties
Table 7-4: XmlElementAttribute Properties
DataType Specifies the XML Schema built-in datatype in which the property
or field should be encoded
ElementName Specifies the name of the XML element
Form Specifies whether the XML element must be namespace qualified
It is set to one of three values defined by the XmlSchemaForm enumeration: None, Qualified, or Unqualified
IsNullable Specifies whether the value of the XML element can have its xsi:nil
attribute set to true
Namespace Specifies the XML namespace in which the element is defined
Type Specifies the NET type that should be used to generate the
schema that describes the element
You can decorate a property or a field with more than one XmlElement attribute Doing so
specifies that an instance document must contain an element that complies with the criteria
specified by one of the XmlElement attributes Here is an example:
public class Person
{
[XmlElement("SocialSecurityNumber")]
[XmlElement("DriversLicenseNumber")]
public string Identifier;
public string Name;
}
The preceding NET type definition creates the following XML datatype definition:
<s:complexType name="Person">
<s:sequence>
<s:choice minOccurs="1" maxOccurs="1">
<s:element minOccurs="1" maxOccurs="1"
Trang 19</s:complexType>
An instance of the Person XML datatype must contain the person’s name as well as the
person’s driver’s license number or social security number
You can use the XmlType attribute to control how NET types are serialized to an XML
datatype, and you can apply the attribute to a variety of NET type definitions, including
classes, structures, enumerations, and interface definitions You can use the XmlType
attribute to set the name of the resulting XML datatype and the namespace in which the
datatype is defined You can also use it to specify whether a datatype definition will be
generat ed within the Web service’s WSDL document
Table 7-5 describes the properties exposed by the XmlType attribute to control how XML
datatypes are generated
Table 7-5: XmlTypeAttribute Properties
IncludeInSchema Specifies whether the type will be included in the schema
Namespace Specifies the XML namespace in which the XML schema datatype is
qualified
TypeName Specifies the name of the XML datatype that describes the targeted
.NET type
You can also use the XmlIgnore attribute to exclude entities from type definitions For
example, suppose the internal implementation of the AcceptPO Web service needs to track
a processing code The following class definition adds a ProcessingCode public field to the
class for maintaining the state of this information:
public int ProcessingCode;
// Additional type definitions
}