.NET基礎拾遺(7)Web Service的開發與應用基礎

 Index :html

(1)類型語法、內存管理和垃圾回收基礎程序員

(2)面向對象的實現和異常的處理面試

(3)字符串、集合與流數據庫

(4)委託、事件、反射與特性api

(5)多線程開發基礎數組

(6)ADO.NET與數據庫開發基礎緩存

(7)WebService的開發與應用基礎服務器

1、SOAP和Web Service的基本概念

  Web Service基於SOAP協議,而SOAP自己符合XML語法規範。雖然.NET爲Web Service提供了強大的支持,但瞭解其基本機制對於程序員來講仍然是必需的。網絡

1.1 神馬是SOAP協議?

  SOAP協議的全稱是簡單對象訪問協議(Simple Object Access Protocol),SOAP致力於以XML形式提供一個簡單、輕量的用於在分散或分佈環境中交換結構化和類型信息的機制。SOAP只規範對象訪問的方式,而不限制具體實現的技術環境,這意味着SOAP協議是一種跨平臺的協議:一個.NET客戶端程序能夠按照SOAP協議訪問一個基於JavaEE技術體系結構的Web Service。SOAP訪問仍然基於HTTP協議,同時其內容又以XML形式展示。多線程

  SOAP規範由四部分組成:

  ① SOAP信封(SOAP envelop)

  ② SOAP編碼規則(SOAP encoding rules)

  ③ SOAP RPC表示(SOAP RPC representation)

  ④ SOAP綁定(SOAP binding)

  這裏不對這四部分展開介紹,經過下面的一個小例子來直觀地認識一下。

  (1)在Web服務端,打算對外提供一個公共方法來供客戶端調用,而客戶端則須要提供這個方法須要的參數,而且最終獲得返回值。假設這個方法被申明在MySimpleService.asmx文件中:

    [WebMethod]
    public string GetSumString(int para1, int para2)
    {
        int result = para1 + para2;
        return result.ToString();
    }

  (2)當客戶端試圖使用這個Web Service方法時,就須要向服務器端發出這樣的一個HTTP請求:

POST /MySimpleService.asmx HTTP/1.1
Host: localhost
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://tempuri.org/GetSumString"

<?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>
    <GetSumString xmlns="http://tempuri.org/">
      <para1>250</para1>
      <para2>250</para2>
    </GetSumString>
  </soap:Body>
</soap:Envelope>

  (3)等到Web Service服務器端接收到上面的請求以後,就能夠進行相應的邏輯處理,而且返回結果。根據SOAP協議,HTTP響應以下形式:

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>
    <GetSumStringResponse xmlns="http://tempuri.org/">
      <GetSumStringResult>500</GetSumStringResult>
    </GetSumStringResponse>
  </soap:Body>
</soap:Envelope>

  如此一來,客戶端就獲得了服務端的處理結果,換句話說,客戶端已經獲得了Web Service提供的服務。

PS:最後,再說一下SOAP協議和HTTP協議,它們的關係很是相似於網絡分層中的上下層協議,使用SOAP協議的雙方將SOAP數據包放入HTTP報文之中,而且經過HTTP協議完成實際的傳輸,換句話說,SOAP是對HTTP的一個封裝,下圖說明了這一過程:

1.2 WSDL又是什麼鬼,它有啥做用?

  (1)WSDL介紹

  WSDL(Web Service Description Language)是Web服務描述語言,它是一種由微軟、IBM、Intel等大型供應商提出的語言規範,目的就是爲了描述Web服務器所提供的服務,以供使用者參考。WSDL是一種複合XML語法規範的語言,它的設計徹底基於SOAP協議,當一個Web Service服務器指望爲使用者提供服務說明時,WSDL是最好的選擇之一。

  這裏仍以上面的實例來講明,在Web服務端提供了這樣一個方法:

    string GetSumString(int para1, int para2)

  當服務端視圖利用WSDL告訴客戶端如何使用該方法時,就會提供下面的這樣一個WSDL文件(仍然是一個XML):

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://tempuri.org/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" targetNamespace="http://tempuri.org/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
  <wsdl:types>
    <s:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/">
      <s:element name="GetSumString">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="1" maxOccurs="1" name="para1" type="s:int" />
            <s:element minOccurs="1" maxOccurs="1" name="para2" type="s:int" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:element name="GetSumStringResponse">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="GetSumStringResult" type="s:string" />
          </s:sequence>
        </s:complexType>
      </s:element>
    </s:schema>
  </wsdl:types>
  <wsdl:message name="GetSumStringSoapIn">
    <wsdl:part name="parameters" element="tns:GetSumString" />
  </wsdl:message>
  <wsdl:message name="GetSumStringSoapOut">
    <wsdl:part name="parameters" element="tns:GetSumStringResponse" />
  </wsdl:message>
  <!-- 這裏省略其餘定義 -->
</wsdl:definitions>
View Code

  如上xml所示,在<wsdl:types>節點下,WSDL定義了GetSumString方法的名字:

<s:element name="GetSumString">

  參數數量、每一個參數的類型:

<s:complexType>
   <s:sequence>
      <s:element minOccurs="1" maxOccurs="1" name="para1" type="s:int" />
      <s:element minOccurs="1" maxOccurs="1" name="para2" type="s:int" />
   </s:sequence>
</s:complexType>

  以及返回參數的類型:

<s:element name="GetSumStringResponse">
   <s:complexType>
      <s:sequence>
         <s:element minOccurs="0" maxOccurs="1" name="GetSumStringResult" type="s:string" />
      </s:sequence>
   </s:complexType>
</s:element>

  經過完整的描述,使用者就可以瞭解如何使用該Web服務了。

  (2)獲取和使用WSDL

  當Web Service服務器提供WSDL時,就能夠經過特定的工具得到WSDL文件。最直接的方式就是在URL中直接添加WSDL參數,來發送獲得WSDL文件的請求,以下所示:

  http://localhost:6105/MySimpleService.asmx?wsdl

  這時點擊回車就能夠獲得以下圖所示的WSDL結果:

1.3 Web Service中如何處理附件?

  儘管Web Service提供的方法的參數類型沒有任何限制,也就意味着全部的附件能夠經過字節數組來進行傳遞,可是把字節流直接內嵌在SOAP消息的作法有不少問題,這也曾經成爲XML語法和SOAP協議被詬病的緣由。這裏主要介紹一下XOP的概念。

  在XOP出現以前,SOAP處理二進制數據的方式都很簡單,好比當一個Web Service服務端提供了以下的方法時:

    void UploadSmallAttach(Byte[] attachment)

  客戶端調用該Web Service,只須要發出下面這樣的SOAP請求便可:

<?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>
    <UploadSmallAttach xmlns="http://tempuri.org/">
      <attachment>D0CF11E0A1B11AE100000000000000000000000003E0000300FEFF09000600000000000000000000000600000000000000000000DE0200000000000000000000001000000000000000FEFFFFFFFF00000000000000000000D80200000000000D9020000DA02000DB02000000DC020000DD0200000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</attachment>
    </UploadSmallAttach>
  </soap:Body>
</soap:Envelope>

  如上所示,其中<attachment>節點下的一大堆字符,就是某個文件的字節流。經過這種方式,確實是能夠實現傳送二進制附件的功能的,但這樣的處理過於粗略,且傳輸沒有任何優化。W3C爲此特別指定了XOP規範。

  XOP(XML-binary Optimized Packages)意爲XML二進制打包,它把二進制數據流從SOAP消息中分離出來,進行單獨打包。上述的客戶端請求若是使用XOP規範的話,將轉變爲以下結果:

<?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>
    <UploadSmallAttach xmlns="http://tempuri.org/">
      <attachment>
     <
xop:Include xmlns="http://www.w3.org/2015/10/02/xop/include" href="cid:http://www.book.com/attachment.png" />
    </
attachment> </UploadSmallAttach> </soap:Body> </soap:Envelope>

  能夠看到,本來出如今二進制字節流的地方,被轉換成了一個引用:

   <attachment>
   <xop:Include xmlns="http://www.w3.org/2015/10/02/xop/include" href="cid:http://www.book.com/attachment.png" />
  </attachment>

  這樣整個SOAP信封節點下就再也不包含任何二進制直接,而福建則被安放在另外一個MIME體中:

Content-Type: image/png
Content-Transfer-Encoding: binary
Content-ID: <sample@book.com>
D0CF11E0A1B11AE100000000000000000000000003E0000300FEFF09000600000000000000000000000600000000000000000000DE0200000000000000000000001000000000000000FEFFFFFFFF00000000000000000000D80200000000000D9020000DA02000DB02000000DC020000DD0200000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

2、使用.NET開發Web Service

  .NET爲Web Service提供了全面的支持,不管是建立Web Service仍是訪問Web Service,使用.NET都能快速有效地完成需求。

2.1 如何在.NET中建立Web Service?

  (1)使用WebMethod特性建立Web Service

  在.NET中,全部Web Service的資源被定義爲asmx文件,而在ASP.NET被安裝時,asmx文件也會在IIS中被註冊成由ASP.NET組件來處理。也就是說,一個asmx文件和其後臺代碼asmx.cs組成了一個Web Service資源。

  爲了讓咱們可以把注意力集中在邏輯的處理上,而忽略SOAP通訊的工做,.NET提供了Web Service類型和WebMethod特性。在繼承自Web Service類型的公共方法上添加WebMethod特性,就能夠申明爲一個Web Service方法。

  ① 建立一個Web服務

  ② asmx文件只是簡單地聲明瞭後臺代碼的位置,而不包含任何工做代碼。後臺代碼都在asmx.cs中:

    /// <summary>
    /// MySimpleService 的摘要說明
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // 若要容許使用 ASP.NET AJAX 從腳本中調用此 Web 服務,請取消註釋如下行。 
    // [System.Web.Script.Services.ScriptService]
    public class MySimpleService : System.Web.Services.WebService
    {

        [WebMethod]
        public string GetSumString(int para1, int para2)
        {
            int result = para1 + para2;
            return result.ToString();
        }

    }
View Code

Luckily,WebService和WebMethod爲咱們提供了徹底包裝好的SOAP處理功能,而在大多數狀況下,咱們所要作的就是繼承和使用它們。

  (2)建立自定義的類型來處理對asmx文件的請求

  在ASP.NET的處理機制中,全部的HTTP請求通道都經過管道來尋找處理程序。咱們所熟悉的WebForm和WebService,都是實現了IHttpHandler接口的Http處理程序,這致使了它們有能力處理特定的Http請求。事實上,咱們能夠經過配置Web.config來自定義Http處理程序和資源的映射匹配關係,如同下面的配置所展現的同樣:

<httpHandlers>
      <add verb="*" path="*.ashx" type="System.Web.UI.SimpleHandlerFactory"/>
      <add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory"/>
      <add verb="*" path="*.asmx" type="System.Web.Services.Protoclos.WebServiceHandlerFactory"/>
</httpHandlers>

  實現IHttpHandler接口很簡單,必須的工做就僅僅是實現一個ProcessRequest方法和一個只讀屬性:

    public void ProcessRequest(HttpContext context)
    public bool IsReusable

  在HttpContext類型的上下文對象中包含了Http請求(HttpRequest),也包含了Http返回類型對象(HttpResponse),而且容許程序員往裏面寫入但願的返回內容。IsReusable屬性則返回當前對象是否可被重用來應對全部相似的Http請求。

  鑑於此,咱們能夠本身實現一個實現IHttpHandler接口的處理程序,在配置文件中將其綁定到.asmx文件上,就能夠實現Web Service方法了。固然,爲了符合SOAP規範,咱們須要在ProcessRequest方法中解析SOAP請求,而且把返回值放入一個SOAP包中。

  下面的代碼示例展現瞭如何自定義asmx處理程序(這裏只展現瞭如何編寫實現IHttpHandler接口的類型並使其工做,省略了繁瑣的SOAP解析和組織工做)

  ① 新建一個ashx程序,實現IHttpHandler接口

    /// <summary>
    /// MySimpleHandler 的摘要說明
    /// </summary>
    public class MySimpleHandler : IHttpHandler
    {

        public void ProcessRequest(HttpContext context)
        {
            context.Response.Write("<h1>Hello Web Service!</h1>");
        }

        public bool IsReusable
        {
            get
            {
                return true;
            }
        }
    }
View Code

  ② 修改Web.config文件,加入自定義HttpHandler類型

 <add verb="*" path="*.asmx" type="MyWebApp.MySimpleHandler,MyWebApp"/>

  (3)自定義Web Service資源文件和處理程序

  事實上,咱們徹底能夠定義一種新的資源文件而不是採用asmx文件名,只要咱們爲其定製HttpHandler程序。須要作的工做爲:

  ① 定義一個新的文件格式如asnew,在IIS中匹配asnew和aspnet_isapi.dll處理程序;

  ② 自定義一個實現了IHttpHandler接口的類型,在這個類型中實現SOAP;

  ③ 在須要使用asnew文件的站點配置文件中綁定asnew和新的Http處理類型;

  這樣的方案顯然能夠爲系統帶來更大的靈活性,但同時也意味着更大的代碼量。下圖展現了三種實現Web Service的方法之間的關係:

2.2 WebMethod特性包含哪些屬性,各有神馬用處?

  WebMethod特性在Web Service中被用來申明一個公開方法,瞭解其使用方法是在正確編寫Web Service的基礎。在WebMethod特性中,一共包含了6個屬性,這6個屬性對WebMethod的使用很是重要。

  (1)BufferResponse屬性

  該屬性代表是否啓用對Web Service方法響應的緩衝。當設置爲true時,Web Service方法將響應序列化到內存緩衝區中,直到緩存區被用滿或者響應結束後,響應纔會被髮送給客戶端。相反,設置爲false時,.NET默認以16KB的塊區緩衝響應,響應在被序列化的同時將會被不斷髮送給客戶端,不管該響應是否已經徹底結束。

PS:默認BufferResponse被設置爲true。當Web Service要發送大量數據流給客戶端時,設置BufferResponse爲false時能夠防止大規模數據一次性刷新到內存,而對於小量數據,設置爲true則能夠有效地提升性能。

  (2)EnableSession屬性

  該屬性指定是否啓用會話狀態。若是爲true,則啓用,爲fasle則禁用。默認被設置爲false。

    public class MySimpleService : System.Web.Services.WebService
    {
        [WebMethod(EnableSession = true)]
        public string WithSession()
        {
            return TryGetSession();
        }

        [WebMethod(EnableSession = false)]
        public string WithoutSession()
        {
            return TryGetSession();
        }

        private string TryGetSession()
        {
            if (Session == null)
            {
                return "Session is Forbidden";
            }

            if (Session["number"] == null)
            {
                Session["number"] = 0;
            }

            Session["number"] = (int)Session["number"] + 1;
            return Session["number"].ToString();
        }
    }
View Code

  分別訪問WithSession和WithoutSession方法,結果以下圖所示:

  (3)CacheDuration屬性

  該屬性指示啓用對Web Service方法結果的緩存。服務端將會緩存每一個惟一參數集的結果,該屬性的值指定服務器端應該對結果進行多少秒的緩存處理。若是該值爲0,則禁用對結果進行緩存;若是不爲零,則啓用緩存,單位爲秒,意爲設置多少秒的緩存時間。默認該值被設爲0。

    [WebMethod(CacheDuration = 10, EnableSession = true)]
    public string WithCache()
    {
        if (Session["number"] == null)
        {
            Session["number"] = 0;
        }

        Session["number"] = (int)Session["number"] + 1;
        return Session["number"].ToString();
    }
View Code

  上面的WithCache方法設置了10秒的緩存時間,即10秒內的訪問都會獲得同樣的結果。

  (4)Description屬性

  該屬性很簡單,提供了對某個Web Service方法的說明,而且會顯示在服務幫助頁上面。

  (5)MessageName屬性

  該屬性是Web Service可以惟一肯定使用別名的重載方法。除非另外指定,默認值是方法名稱。當指定MessageName時,結果SOAP消息將反映該名稱,而不是實際的方法名稱。

  當Web Service提供了兩個同名的方法時,MessageName屬性會頗有用,這一點將會體如今WSDL中:

    [WebMethod(MessageName="HelloWorld1")]
    public string HelloWorld(int num)
    {
        return num.ToString();
    }

    [WebMethod(MessageName = "HelloWorld2")]
    public string HelloWorld()
    {
        return "Hello World!";
    }
View Code

  (6)TransactionOption屬性

  該屬性用以設置Web Service方法的事務特性,在.NET中事務模型是基於聲明性的,而不是編寫特定的代碼來處理提交和回滾事務。在Web Service中,能夠經過TransactionOption屬性來設置該方法是否須要被放入一個事務之中。若是申明瞭事務屬性,執行Web Service方法時引起異常會自動終止事務,相反若是未發生任何異常,則自動提交事務。

  事務最經常使用的一個場景就是數據庫訪問,因此該屬性在利用Web Service實現的分佈式數據庫訪問中就特別有用。

2.2 如何生成Web Service代理類型?

  (1)Web Service代理類的概念

  所謂的代理類,就是SOAP協議的代理類型,它使得咱們能夠經過調用本地的類型方法(代理類),來達到訪問Web Service方法的目的。代理類的最終目的就是將程序員從繁瑣的SOAP消息處理和XML解析中解放出來,而專一於邏輯工做。下圖說明了代理類的做用:

  (2)如何生成Web Service代理類

  在Visual Studio中提供了一個很簡單的生成Web Service代理類的方法就是Web引用,以下圖所示:

  當Web引用被添加後,一個代理類型也會自動生成。而且,當服務端的Web Service更新後,咱們只須要簡單地更新一下Web引用,就能夠方便地更新代理類型。

  在客戶端邏輯中,只須要調用代理類的對應接口就OK,十分簡單:

    class Program
    {
        static void Main(string[] args)
        {
            using (MySimpleServiceSoapClient proxy = new MySimpleServiceSoapClient())
            {
                string result = proxy.GetSumString(250, 250);
                Console.WriteLine("250+250={0}", result);
            }
            Console.ReadKey();
        }
    }
View Code

  執行結果爲:

  

2.3 簡述.NET中Web Service的異常機制

  即便有了本地的代理類,調用Web Service方法仍是調用本地方法有所區別,若是Web Service出現了異常,那麼這些異常信息就須要被封裝在SOAP信息中發送回客戶端。

  (1)SOAP中對異常的規定

  SOAP規定了異常信息的攜帶方式,那就是全被放入fault節點中。fault節點必須是Body節點的子節點,並且一個SOAP消息只能出現一個fault節點。

子節點 描述
<faultcode> 識別故障的代碼
<faultstring> 供認閱讀的有關故障的說明
<faultactor> 是誰引起異常
<detail> 存留設計Body元素的應用程序專用錯誤信息

  其中faultcode是一個錯誤碼,其取值和每一個值所表明的含義都在SOAP中有所定義,下表列出了全部faultcode及其含義:

faultcode節點值 描述
VersionMismatch SOAP Envelop元素的無效命名空間被發現
MustUnderstand Header元素的一個直接子元素沒法被理解
Client 消息被不正確地構成,或包含了不正確的信息
Server 服務器有問題,所以沒法處理進行下去

  (2)服務端對未捕獲異常的處理

  在使用WebService類型和WebMethod特性建立Web Service的狀況下,服務器端的異常都會被捕捉,而且全部異常都會被放入到SoapException類型中,而且返回給客戶端。咱們能夠在服務端代碼中直接使用SoapException異常,經過設置其屬性來告知客戶端:

  ① Message:原始異常的Message屬性

  ② Code:服務器異常碼

  ③ Actor:Web Service方法的URL

  ④ Detail:空引用,但有一個空的詳細信息元素存在於故障元素之中

  服務端會把SoapException放入Fault節點之中並返回給客戶端,以此來告知服務端發生的異常。

  (3)客戶端代理類對fault節點的處理

  若是使用.NET自動生成的Web Service代理類,那麼它將可以自動地識別fault節點,而且還原爲SoapException異常。這裏能夠經過下面的一段代碼示例來直觀地瞭解這一點:

  ① 首先在Web Service方法中直接拋出一個異常,以下代碼所示:

    [WebMethod]
    public string HelloException()
    {
        // 直接拋出一個異常,該異常會被包裝爲SoapException
        throw new Exception("發生了一個異常!");
    }
View Code

  ② 其次根據這個Web Service在本地生成對應代理類,而且經過try-catch捕捉從服務端發送來的SoapExcetpion:

    class ServiceClient
    {
        static void Main(string[] args)
        {
            using (MySimpleServiceSoapClient proxy = new MySimpleServiceSoapClient())
            {
                try
                {
                    // 這裏異常將會被代理類拋出
                    proxy.HelloException();
                }
                catch (SoapException ex)
                {
                    // 打印異常信息內容
                    Console.WriteLine("Actor:{0}", ex.Actor);
                    Console.WriteLine("CodeName:{0}", ex.Code.Name);
                    Console.WriteLine("Detail:{0}", ex.Detail.InnerText);
                    Console.WriteLine("Message:{0}", ex.Message);
                }
            }
            Console.ReadKey();
        }
    }
View Code

參考資料

(1)朱毅,《進入IT企業必讀的200個.NET面試題》

(2)張子陽,《.NET之美:.NET關鍵技術深刻解析》

(3)王濤,《你必須知道的.NET》

 

相關文章
相關標籤/搜索