寫WebService有一段時間了,把自己收集的和理解的關於WebService工作原理做個總結.
WebService主要採用了Http協議,Http是個基於Tcp/Ip的應用層協議:(注:現在的大部分WebService開發已經能很好的支援socket的即時通訊了.但http依然是它的精髓.)
Http採用了"請求-----應答"模式;
Http採用了透過XMLpttpttp的......
Http通訊過程:調用.asmx句柄,XML、XSD、SOAP和WSDL處理,
XML中主要提供的有三方面:(以下文章引自dn. .net/article/19/19185.shtm)
個人感覺主要要掌握的是SOAP請求訊息和應答訊息的xml格式...
訊息分派
當.asmx句柄被查看TP管道時,透過查看管道呼叫時,透過查看管道。 .asmx檔案中的WebService聲明,確定檢查哪個.NET類別。然後它觀察到來的HTTP訊息中的信息,決定呼叫引用類別中的哪個方法。為了呼叫前面範例中的Add方法,HTTP請求訊息應像下面一樣:
上面的HTTP請求訊息中有兩則資訊可以用來決定呼叫類別中的哪個方法:SOAPAction頭或soap體中請求元素的名字。在這個例子中,每種方法都指出了發送者想要呼叫的方法名稱。
.asmx句柄使用SOAPAction頭的值來實現訊息的分派。因此,.asmx句柄查看訊息中的SOAPAction頭,使用.NET映射檢查引用類別中的方法。它只考慮標記了[WebMethod]屬性的方法,但透過查看每種方法的SOAPAction值再具體確定要呼叫哪個方法。因為我們在類別中並沒有明確的指定SOAPAction的值,.asmx句柄認為SOAPAction的值是Web服務的名稱空間加上方法名稱。而且我們也沒有指定名稱空間,所以句柄就把http://tempuri.org當作預設值。這樣Add方法的預設SOAPAction值就是http://tempuri.org/Add。
可以如下方法自訂Web服務的名稱空間。把類別標記上[WebService]屬性,用[SoapDocumentMethod]屬性標記WebMethods來指定具體的SOAPAction值。範例如下:
using System.Web.Services; using System.Web.Services.Protocols; [WebService(Namespace="http://example.org/math")] public class MathService { [WebMethod] public double Add(double x, double y) { return x + y; } [WebMethod] [SoapDocumentMethod(Action="urn:math:subtract")] public double Subtract(double x, double y) { return x - y; } ... }
現在.asmx句柄認為Add方法的SOAPAction值是http://example.org/math/Add,SubTract方法的SOAPAction值是urn:math:subtract(因為我們在類別中明確定義了) 。例如下面的HTTP請求訊息呼叫Subtract:
如果.asmx句柄沒為HTTP請求訊息找到一個SOAPAction匹配,將會拋出一個異常。如果你不想依賴SOAPAction頭來分派訊息,可以引導.asmx句柄使用請求元素名稱。採用這種方法需要為類別標記上[SoapDocumentService]屬性的RoutingStyle特性,同時也應該指出WebMethods不需要SOAPAction值(在類別中設定其值為空)。如下所示:
using System.Web.Services; using System.Web.Services.Protocols; [WebService(Namespace="http://example.org/math")] [SoapDocumentService( RoutingStyle=SoapServiceRoutingStyle.RequestElement)] public class MathService { [WebMethod] [SoapDocumentMethod(Action="")] public double Add(double x, double y) { return x + y; } [WebMethod] [SoapDocumentMethod(Action="")] public double Subtract(double x, double y) { return x - y; } ... }
在這種情況下,句柄甚至不關心SOAPAction的值,它使用請求元素的名字來決定呼叫方法。例如,在下面的HTTP請求訊息中,它希望呼叫Add方法的請求元素的名字是Add:
所以當.asmx句柄接收到HTTP訊息時它要做的第一件事就是決定如何分派訊息到對應的WebMethod。在它真正呼叫方法之前,還需要將到來的XML對應到.NET物件。
將XML映射到物件
一旦WebMethod句柄決定了呼叫哪個方法,它就會將XML訊息反串行化為.NET物件。隨著訊息分派,句柄透過reflection檢查類,然後決定怎麼處理XML訊息。 XmlSerializer類別會自動完成XML和System.Xml.Serialization名稱空間中類別的對應。
XmlSerializer能實現任何.NET公共類型到XML Schema類型的映射,有了這個適當的映射,它能自動的實現.NET物件和XML實例文檔的映射(見圖4)。 XmlSerializer受XML Schema支援功能的限制,雖無法處理所有複雜的現代物件模型(如非樹型的物件圖),卻能處理開發者常用的複雜類型。
再看前面Add的例子,XmlSerializer將把x和y元素映射為.NET的double值(呼叫Add方法時必須提供的)。 Add方法傳回一個double型別值給呼叫者,這也需要被串列化為SOAP應答訊息中的一個XML元素。
Figure 4. Mapping XML to objects
XmlSerializer也能自动处理一些复杂类型(除了上面说到的一些限制)。比如,下面的WebMethod计算两个点结构之间的距离。
using System; using System.Web.Services; public class Point { public double x; public double y; } [WebService(Namespace="urn:geometry")] public class Geometry { [WebMethod] public double Distance(Point orig, Point dest) { return Math.Sqrt(Math.Pow(orig.x-dest.x, 2) + Math.Pow(orig.y-dest.y, 2)); } }
请求此操作的SOAP消息将包含一个Distance元素,它包含了两个子元素,一个称作orig,另一个是dest,每一个都包括了x和y元素,如下所示:
这种情况下SOAP应答消息将包含一个DistanceResponse元素,它包含一个double 类型的DistanceResult子元素。
缺省的XML映射使用方法名作为请求元素名,参数名作为子元素名。每个参数的结构依赖于类型的结构。公共字段和属性的名字简单映射为子元素,如Point类中的x和y。应答元素的名字缺省为请求元素的名字后面附加上“Response”,应答元素也包含一个子元素,是请求元素名字后面附加“Result”。也有可能使用一些固定的映射属性来打破标准的XML映射。比如,你可以使用[XmlType]属性来定制类型的名字和名称空间,使用[XmlElement]和[XmlAttribute]属性来控制如何将参数或类成员分别映射为元素或属性,也可以使用[SoapDocumentMethod]属性控制怎样把方法本身映射为请求/响应消息中的元素名。比如,检查下面重新定义的Distance。
using System; using System.Web.Services; using System.Web.Services.Protocols; using System.Xml.Serialization; public class Point { [XmlAttribute] public double x; [XmlAttribute] public double y; } [WebService(Namespace="urn:geometry")] public class Geometry { [WebMethod] [SoapDocumentMethod(RequestElementName="CalcDistance", ResponseElementName="CalculatedDistance")] [return: XmlElement("result")] public double Distance( [XmlElement("o")]Point orig, [XmlElement("d")]Point dest) { return Math.Sqrt(Math.Pow(orig.x-dest.x, 2) + Math.Pow(orig.y-dest.y, 2)); } }
它所期望的SOAP请求消息如下:
.asmx句柄使用SOAP document/literal风格来实现和描述上面显示的默认映射。意思上说WSDL定义将包含literal XML schema定义,它描述了SOAP消息中用到的请求和响应元素。
.asmx句柄也能使用SOAP rpc/encoded风格。这意味着SOAP体中包含一个RPC调用的XML代表(representation),参数用SOAP编码规则来串行化。实现这些仅需将[SoapDocumentService] and [SoapDocumentMethod]替换为[SoapRpcService] and [SoapRpcMethod]属性。
正如你所看到的,我们可能完全定制一个从给定方法到SOAP消息的映射。XmlSerializer提供了一个强大的串行化引擎。
除了处理参数的反串行化,.asmx句柄也能串行化/反串行化SOAP头。SOAP头的处理不同于参数,因为它们被认为是典型的无法控制的信息,和具体的方法没有直接的联系。由于这些,头处理主要是通过中间层(interception layers),完全为WebMethods屏蔽了头处理。
然而如果想涉足于WebMethod中的头信息,你必须提供一个.NET类,从SoapHeader派生而来,它代表了头的XML schema类型。然后定义一个此类型的成员变量作为每一个头实例的占位符。最后标记每个要访问头的WebMethod,指定你想要到达的域名。
比如,下面的SOAP请求包括一个用来进行身份验证的UsernameToken头。
更多WebService工作原理相关文章请关注PHP中文网!