Building XML Web Services for the Microsoft .NET Platform phần 2 pps

38 238 0
Building XML Web Services for the Microsoft .NET Platform phần 2 pps

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

40 <soap:Body> <StockReport> <Symbol>MSFT</Symbol> <Price>74.56</Price> </StockReport> </soap:Body> </soap:Envelope> The SOAP message contains a Digest element in the header that the remote application can use to ensure that the message has not been tampered with. If the client is doing a routine check to see what her stock closed at, she might not be concerned about validating the message. But if the price of the stock triggers an event within the financial software package, she might be more interested in validating the message. For example, it would be unfortunate if the financial software package were to automatically liquidate her portfolio as the result of receiving a bogus message sent by some 14-year-old kid. mustUnderstand Attribute Because headers are optional, the recipient of the message can choose to ignore them. However, some information that can be embedded in the header should not be ignored by the intended recipient. If the header is not understood or cannot be handled properly, the application might not function properly. Therefore, you need a way to distinguish between header information that is informative and header information that is critical. You can specify whether the message recipient must understand an element in the header by specifying the mustUnderstand attribute with a value of 1 in the root of the header element. For example, the SOAP message might request that a remote application perform an action on the client’s behalf. The following example updates a user’s account information within the scope of a transaction: <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <TransactionId soap:mustUnderstand="1">123</TransactionId> </soap:Header> <soap:Body> <UpdateAccountInfo> <email>sshort@microsoft.com</email> <firstName>Scott</firstName> <lastName>Short</lastName> </UpdateAccountInfo> </soap:Body> </soap:Envelope> The recipient of the message must update the user’s account information within the scope of the client’s transaction. If the transaction is aborted, the remote application must roll back the requested changes to the user’s account information. Therefore, I encoded the transaction 41 ID within the header and set the mustUnderstand attribute to 1. The remote application must either honor the transaction or not process the message. actor Attribute A SOAP message can be routed through many intermediaries before it reaches its final destination. For example, the previous document might be rout ed through an intermediary responsible for creating a transaction context. In this case, you might want to clearly specify that the TransactionId header is intended to be processed by the transaction intermediary rather than by the default actor. The SOAP specification provides the actor attribute for annotating SOAP headers intended for certain intermediaries. The value of this attribute is the Uniform Resource Identifier (URI) of the intermediary for which the portion of the message is intended. If a header is intended to be processed by the next intermediary to receive the SOAP message, the actor attribute can be set to http://schemas.xmlsoap.org/soap/actor/next. Otherwise the actor attribute can be set to a URI that identifies a specific intermediary. Here is an example: <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <TransactionId soap:mustUnderstand="1" actor="urn:TransactionCoordinator>123</TransactionId> </soap:Header> <soap:Body> <TransferFunds> <Source>804039836</Source> <Destination>804039836</Destination> <Amount>151.43</Amount> </GetWeather> </soap:Body> </soap:Envelope> Because the TransactionId header element is intended for the transaction coordinator intermediary, its actor attribute is set to the intermediary’s URI. The mustUnderstand attribute has also been set so that if the transaction coordinator intermediary does not understand the TransactionId header element, it must raise an error. If the message is passed to another recipient, any header elements designated for the intermediary must be removed before the message is forwarded. The intermediary can, however, add additional header elements before forwarding the message to the next recipient. In this example, the transaction coordinator intermediary must remove the router element before forwarding it to the billing application. One important point to note is that routing the message directly to the default actor is not considered an error. Setting the mustUnderstand attribute to 1 in combination with setting the actor attribute to urn:TransactionCoordinator does not ensure that the message will be routed through the intermediary. It means only that if the message does reach the transaction coordinator intermediary, it must comprehend the TransactionId header entry or throw an error. 42 In the preceding example, the intermediary needs to perform a critical task before the message is routed to the default actor. Recall that if the message does reach the transaction coordinator intermediary, it must remove the TransactionId header before forwarding the message. Therefore, the default actor can check to see whether the TransactionId header exists, which would indicate that the message was not passed through its appropriate intermediaries. However, determining whether all of the headers were processed after the message reached the default actor is not always ideal. What if the SOAP request needs to be routed through the intermediaries shown here? The request to transfer funds must pass through a router intermediary before the funds are transferred. Suppose the router charges the customer a processing fee for forwarding the request to the appropriate banking Web service. However, before funds are deducted, the message should be routed through the transaction coordinator to initiate a transaction before any data is modified. Therefore the router intermediary and the default actor should perform all work in the scope of the transaction. Because the banking Web service is the default actor, it can check the headers to see whether the message was routed through the necessary intermediaries. But what if the banking Web service discovers that the message was never routed through the transaction manager intermediary? If an error occurred during the funds transfer, you might not be able to undo the work performed by the router intermediary. Worse yet, the SOAP message might have been routed through the router intermediary before being routed through the transaction coordinator. If this is the case, there might be no way to tell that the procurement application performed its work outside the scope of the transaction. Unfortunately, SOAP does not provide any mechanism to ensure that the message travels through all intended intermediaries in the proper order. In the “Futures” chapter, I will discuss one of the emerging protocols for addressing this problem. The Body Element A valid SOAP message must have one Body element. The body contains the payload of the message. There are no restrictions on how the body can be encoded. The message can be a simple string of characters, an encoded byte array, or XML. The only requirement is that the contents cannot have any characters that would invalidate the resulting XML document. The SOAP specification describes a method of encoding that can be used to serialize the data into the message’s body. It is a good idea to conform to an established encoding scheme such as this because it allows the sender to more easily interoperate with the recipient using a well-known set of serialization rules. (I describe this encoding method later in the chapter.) SOAP messages can generally be placed into two categories: procedure- oriented messages and document-oriented messages. Procedure-oriented messages provide two- way communication and are commonly referred to as remote procedure call (RPC) messages. The body of an RPC message contains information about the requested action from the server and any input and output parameters. Document-oriented messages generally facilitate one-way communication. Business documents such as purchase orders are examples of document-oriented messages. Let’s take a closer look at each of these document types. 43 Two SOAP messages are paired together to facilitate an RPC method call with SOAP: the request message and the corresponding response message. Information about the targeted method along with any input parameters is passed to the server via a request message. The server then invokes some behavior on behalf of the client and returns the results and any return parameters. Most of the examples in this chapter relate to RPC method invocations, and they all follow the SOAP specification’s guidelines for encoding RPC messages. A business document such as a purchase order or an invoice can be encoded within the body of a SOAP message and routed to its intended recipient. The recipient of the document might or might not send an acknowledgment message back to the sender. (The “SOAP Encoding” section later in this chapter describes how to use serialization rules to encode the data contained within these business documents.) Because business documents often span across multiple companies, organizations such as BizTalk.org and RosettaNet serve as facilitators and repositories for schemas that define common document exchanges. Later in the book, I will describe how to leverage the .NET platform to create and consume both RPC and document -oriented messages. Fault Element Everything does not always go as planned. Sometimes the server will encounter an error while processing the client’s message. SOAP provides a standard way of communicating error messages back to the client. Regardless of which encoding style was used to create the message, the SOAP specification mandates the format for error reporting. The body of the message must contain a Fault element with the following structure: <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <soap:Fault> <soap:faultcode>Client.Security</soap:faultcode> <soap:faultstring>Access denied.</soap:faultstring> <soap:faultactor>http://abc.com</soap:faultactor> <soap:detail> <MyError> <Originator>File System</Originator> <Resource>MySecureFile.txt</Resource> </MyError> </soap:detail> </soap:Fault> </soap:Body> </soap:Envelope> The fault code contains a value that is used to programmatically determine the nature of the error. The SOAP specification defines a set of fault codes that you can use to describe basic SOAP errors. The fault codes are listed in Table 3-1. Table 3-1: Base SOAP Fault Codes 44 Fault Code Description VersionMismatch An invalid namespace for the SOAP envelope element was specified. MustUnderstand An immediate child element within the SOAP header containing a mustUnderstand attribute set to 1 was either not understood or not obeyed by the server. Client The content of the message was found to be the root cause of the error. Possible root causes of errors resulting in a Client fault code include a malformed message or incomplete information in the message. Server The root cause of the error was not directly attributable to the content of the message. Examples of errors resulting in a Server fault code include the server not being able to obtain the appropriate resources (such as a database connection) to process the message or a logical error during the processing of the message. You can append more specific fault codes to the core SOAP fault codes listed in the table by using the “dot” notation and ordering the individual fault codes from least specific to most specific. For example, if the server is unable to open a database connection that is required to process the client’s message, the following fault code might be generated: <faultcode>Server.Database.Connection</faultcode> Because the error was not the direct result of the client’s message, the base fault code is Server. A more descriptive fault code is appended to the end of the base fault code. In my example, I define a category of codes for the database and a fault code specific to connection-related errors. The faultstring element should contain a human-readable string that describes the error encountered. Here is a faultstring value for the error connecting to the database: <faultstring>Unable to open connection to the database.</faultstring> You can use the optional faultactor element to indicate the exact source of the error. The only exception is if an intermediary generated the error. If the error was generated at any point other than the final recipient of the SOAP message, the faultactor element must contain a URI that identifies the source of the error. Otherwise, the URI can be omitted. Using SOAP RPC Messages One of the original design goals of SOAP was to provide an open and standard way to facilitate RPCs using Internet technologies such as XML and HTTP. In this section, I explain the method of encoding RPC-style messages described in version 1.1 of the SOAP specification. As I stated earlier in the chapter, the SOAP specification does not dictate the way messages should be encoded, and encoding RPC-style messages is no exception. Section 7 of the SOAP 1.1 specification describes the recommended way to encode the request and response messages. The developer is free to create her own method of encoding RPC communication. In this section, however, I limit the discussion to the “standard” method of encoding RPC-style SOAP messages. 45 To facilitate the request/response behavior needed by RPC, you need two SOAP messages: one for the request and one for the response. Here is how the request message would be encoded for a simple C# function that adds two numbers: public int Add(int x, int y) { return x + y; } The Add method accepts two integers as input parameters and passes the result back to the client as a return parameter. The input parameters must be packaged within the body of the request message so that they can be sent to the target application. This is accomplished by packaging the parameters in a struct-like format. Here is the resulting request message for Add(1, 2): <?xml version="1.0"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <Add> <x>1</x> <y>2</y> </Add> </soap:Body> </soap:Envelope> The Body element contains an Add element. Each of the input parameters is represented as a subelement within the Add element. The order of the x and y elements must match the order in which the parameters are specified in the method signature. In other words, placing y before x would be invalid. Furthermore, the names and the types of the Add, x, and y elements must be the same as the target method and its parameters. I will explain data typing in the next chapter. For now, suffice it to say that the body of the request message must be in a format expected by the remote application. Now that I have created a properly formatted request message, take a look at the response generated by the remote application: <?xml version="1.0"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <AddResult> <result>1</result> </AddResult> </soap:Body> </soap:Envelope> The response message returned by the remote application contains the result of the Add method. The return parameter is once again encoded in a struct-like format within the body of the SOAP message. The naming convention of the subelement within the body is the name of the method with Result appended to it. However, this naming convention is not 46 dictated by the specification. The first (and in this case, only) parameter contains the return parameter of the method call. As with the AddResult element, the name of the element that contains the return parameter is not dictated by the specification. What if more than one parameter is returned to the client? Let’s take a look at a slight variation of the Add method. Add2 returns the sum of the two numbers via an output parameter. public int Add2(int x, int y, out int sum) { sum = x + y; return sum; } Calling Add2(1, 2) produces the following SOAP message: <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <Add2> <x>1</x> <y>2</y> </Add2> </soap:Body> </soap:Envelope> Notice that the third parameter, sum , does not get encoded. Because sum is declared as an output parameter, there is no reason to send its initial value to the remote application. Here is the response: <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <Add2Response> <Add2Result>3</Add2Result> <sum>3</sum> </Add2Response> </soap:Body> </soap:Envelope> The response message contains the value of two parameters. As I mentioned earlier, the return parameter must always be listed first. I called the element containing the return parameter Add2Result to demonstrate that the name is not relevant. The value of the sum parameter is listed next. SOAP Encoding 47 SOAP Encoding defines the way data can be serialized within a SOAP message. SOAP Encoding builds on the types defined in the XML specification, which defines a standard way of encoding data within an XML document. SOAP Encoding clarifies how data should be encoded and covers items not explicitly covered in the XML specification, such as arrays and how to properly encode references. Simple Types Simple types include strings, integers, date/time, Booleans, and so on. The SOAP specification defers to the “Built-in datatypes” section of the “XML Schema Part 2: Datatypes” specification. I will talk about the XML built-in data types in the next chapter. An instance of a data type is encoded as an XML element. For example, an integer called Age would be encoded as follows: <Age>31</Age> Note that for RPC messages, the name of the element must correlate with the name of the parameter. Compound Types Often, it is not sufficient to pass simple types such as integers and strings as parameters; you need to pass compound types such as structures or arrays. In this section, I explain how SOAP Encoding handles compound types. Structures A structure is a collection of types that serve as a template for logically grouping data. For example, let’s say you need to create a function that calculates the volume of a rectangular solid. Instead of passing the length, the width, and the height of the cube as separate parameters, you can logically group the dimensional data into a RectSolid structure. Then the method that calculates the volume of the solid can accept an instance of the RectSolid structure. Here is an example: public struct RectSolid { public int length; public int width; public int height; } public int CalcVolume(RectSolid r) { return (r.length * r.width * r.height); } First I define a structure that contains the dimensions of a solid. Then I define the area. A request to calculate the volume of a rectangular solid that has a length of 2, a width of 3, and a height of 1 can be encoded as follows: <?xml version="1.0" encoding="utf-8"?> 48 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <CalcVolume> <r> <length>2</length> <width>3</width> <height>1</height> </r> </CalcVolume> </soap:Body> </soap:Envelope> As you can see, structures map nicely to XML. Each of the variables contained within the instance of the RectSolid structure is serialized as a child element of r. As you will see in the next chapter, this follows the method of encoding structures defined in Part 1 of the XML specification. Arrays Another common compound data type is the array. As of this writing, the XML specification does not specify how an array should be encoded. The SOAP 1.1 specification fills in the gaps. Here is an example: public int AddArray(int[] numbers) { int total = 0; foreach(int number in numbers) { total += number; } return total; } The AddArray method accepts an array of integers and returns the total. Here is how a client can call the AddArray function: int[] a = {1, 2, 3}; int total; total = AddArray(a); The call to AddArray produces the following request message: <?xml version="1.0" encoding="utf-8"?> 49 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http:// www.w3.org/2001/XMLSchema-instance"> <soap:Body> <AddArray> <a soap-enc:arrayType="xsi:int[3]"> <int>1</int> <int>2</int> <int>3</int> </a> </AddArray> </soap:Body> </soap:Envelope> The array is represented by a single element within the body tag. The element must contain the soap-enc:arrayType attribute. The value of the attribute describes the contents of the array and its dimensions. In the preceding example, xsi:int[3] specifies that the array contains three integers. In the next chapter, I will describe XML Schema and type definitions in more detail. Each value in the array is listed as a subelement. The names of the subelements are not relevant, but often the names of the elements within the array will correlate with the type of data they contain. SOAP-encoded arrays can contain different elements of different types. The following code returns an array containing an integer, a float, and a string: object[] stuff = new object[3]; stuff[0] = (int)100; stuff[1] = (float)2.456; stuff[2] = (string)"Kitchen Sink"; CollectThings(stuff); public void CollectThings(object[] things) { // } An array of objects called stuff is created, and then values of three different types are assigned to each of its three elements. The resulting response SOAP message is encoded as follows: <?xml version="1.0" encoding="utf-8"?> [...]... http://www.w3.org /20 01/XMLSchema.xsd’> 123 .45 For illustrative purposes, the instance document references two namespaces, the Commerce namespace and the XML Schema Instance namespace Because the URI for the Commerce schema is in the form of a URN, it is not resolvable However, even though the URI for the XML Schema Instance schema is in the form of a URL, it is not directly resolvable either Therefore,... of their limitations and lack of industry support The recommended way to express schemas for XML- based Web services is via XML Schema XML Schema comprises two specifications managed by the W3C, XML Schema Part 1: Structures (http://www.w3.org/TR/xmlschema-1/) and XML Schema Part 2: Datatypes (http://www.w3.org/TR/xmlschema -2/ ) As of May 2, 20 01, both specifications are recommendations of the W3C 65 XML. .. the MIME type is set to text /xml For RPC-style messages, the HTTP body contains the SOAP response message Otherwise, the HTTP body would be empty 62 Because the example request message results in a response message being generated from the server, the HTTP body contains the results In either case, the status reported in the first line of the HTTP header must contain a value between 20 0 and 29 9 In the. .. URI resolves to the Web Services Description Language (WSDL) document for the Web service I will cover WSDL in Chapter 5 The value of the SOAPAction header can be blank if the intent of the SOAP message is conveyed in the HTTP request header entry The HTTP request is the first entry in the header and contains the action (in this case, always POST) and the targeted URI If the URI in the HTTP request... enough information to create schemas for your Web services In this chapter, I will create a schema for the Commerce Web service This schema will describe the expected format of the request and response message of the PurchaseItem method Describing XML Documents You can use a schema to describe the structure of an XML document and its type information The two dominant technologies for defining an XML schema... the soap-enc:offset element specifies that the array has been offset by five Therefore, the contents of the array contain the sixth through the tenth elements 54 What if the elements to be encoded within the array are not adjacent to each other? Another means of partially encoding arrays is to use the sparse array syntax For example, say you want to create a message that contains the names of all the. .. communicates the intent of the SOAP message, either of the following entries would be valid: SOAPAction: "" SOAPAction: The HTTP response is used to communicate the results of the SOAP request HTTP/1.1 20 0 OK Server: Microsoft- IIS/5.0 Date: Sun, 25 Mar 20 01 19:44:55 GMT Content-Type: text /xml Content-Length: 24 3 < ?xml version="1.0" encoding="utf-8"?> ... namespace of the schema for the Commerce Web service For now, it contains the definition for the Amount element I will enhance it later 70 < ?xml version=‘1.0’?> . modified. Therefore the router intermediary and the default actor should perform all work in the scope of the transaction. Because the banking Web service is the default actor, it can check the headers. must remove the TransactionId header before forwarding the message. Therefore, the default actor can check to see whether the TransactionId header exists, which would indicate that the message. changes to the user’s account information. Therefore, I encoded the transaction 41 ID within the header and set the mustUnderstand attribute to 1. The remote application must either honor the transaction

Ngày đăng: 14/08/2014, 10:22

Từ khóa liên quan

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan