設計同時支援SOAP與REST的WCF服務

by vivid 11. 二月 2015 11:06

.NET Magazine國際中文電子雜誌
者:許薰尹
稿:張智凱
文章編號:N150215701
出刊日期:2015/02/11
開發工具:Visual Studio 2013 Ultimate Update 4C#
版本:.NET Framework 4.5.xASP.NET

這篇文章探討的不是一個新主題,也不是新技術,起因於最近被詢問到該如何設計一個能同時支援SOAP與REST的WCF服務。提問者因為對WCF不甚了解,所以採取的作法是設計兩個服務介面,一個專走SOAP,一個專走REST。因此部分程式碼重複地出現了。

實際上這個問題只要在WCF新增多個端點就可以解決,並不需要多此一舉,設計兩個服務介面來做相同的事情。以下將以Step-by-Step步驟說明該如何利用Visual Studio 2013新增一個WCF服務,並利用組態編輯工具設定兩個服務開放兩個端點(Endpoint),一個以SOAP方式交換資料,一個則改用REST讓用戶端存取,最後再分別利用WCF用戶端與jQuery來呼叫WCF服務。

啟動Visual Studio 2013開發環境,從 Visual Studio 2013的主選單點選「File」-「New」-「Web Site」項目,開啟「New Web Site」對話窗。確認視窗上方.NET Framework的目標版本為「.NET Framework 4.5.1」,選取Visual C#程式語言,然後選取「WCF Service」,將「Web Location」設為「File System」,設定存檔資料夾後,然後按下「OK」按鈕,請參考下圖所示:

clip_image002

圖 1:建立WCF網站。

預設此範本專案將會把服務裝載在IIS中執行。專案中包含一個IService.cs檔案,其中定義一個IService 介面套用ServiceContract Attribute代表服務合約,Service.cs檔案則包含Service 服務實作的程式碼,我們以範本專案為例,分別示範呼叫需傳簡單參數的GetData方法,與需傳複雜型別參數的GetDataUsingDataContract方法。

為了要開放用戶端透過AJAX來叫用服務,我們需修改範本專案程式碼,在本例中我們修改GetData與GetDataUsingDataContract方法定義,在方法上方新增WebInvoke Attribute,開放REST用戶端可以使用HTTP POST來叫用服務,範例中設定ResponseFormat指明資料交換使用JSON格式:

[ServiceContract]
public interface IService {
 
  [OperationContract]
  [WebInvoke( Method = "POST" ,
BodyStyle = WebMessageBodyStyle.Wrapped ,
ResponseFormat = WebMessageFormat.Json )]
  string GetData( int value );
 
  [OperationContract]
  [WebInvoke( Method = "POST" ,
BodyStyle = WebMessageBodyStyle.Wrapped ,
ResponseFormat = WebMessageFormat.Json )]
  CompositeType GetDataUsingDataContract( CompositeType composite );
 
}
[DataContract]
public class CompositeType {
  bool boolValue = true;
  string stringValue = "Hello ";
 
  [DataMember]
  public bool BoolValue {
    get { return boolValue; }
    set { boolValue = value; }
  }
 
  [DataMember]
  public string StringValue {
    get { return stringValue; }
    set { stringValue = value; }
  }
}

範本專案中GetDataUsingDataContract方法使用到一個CompositeType 自定類別,CompositeType 類別上方套用DataContract Attribute;BoolValue 、StringValue 兩個屬性上方套用DataMember Attribute,以便WCF做序列化處理。

服務實作

範本專案Service.cs檔案之中包含了服務實做的程式碼 - Service 類別,GetData將回傳一個字串;而GetDataUsingDataContract將會回傳一個CompositeType 複雜型別,程式碼參考如下:

public class Service : IService {
  public string GetData( int value ) {
    return string.Format( "You entered: {0}" , value );
  }
 
  public CompositeType GetDataUsingDataContract( CompositeType composite ) {
    if ( composite == null ) {
      throw new ArgumentNullException( "composite" );
    }
    if ( composite.BoolValue ) {
      composite.StringValue += "Suffix";
    }
    return composite;
  }
}

 

定義服務與端點

首先我們要利用組態檔案,定義服務的資訊。在「Server Explorer」視窗中,WCF服務網站的Web.Config檔案上方按滑鼠右鍵,點選「Edit WCF Configuration」項目,進行WCF服務組態,請參考下圖所示:

clip_image004

圖 2:組態WCF服務。

在「Services」節點上按滑鼠右鍵,從快捷選單中選取「New Service」項目,新增一個服務,請參考下圖所示:

clip_image006

圖 3:新增一個服務。

設定服務的名稱為「Service」,服務的名稱需和Service.cs檔案中實作IService的類別名稱一致,請參考下圖所示:

clip_image008

圖 4:設定服務的名稱為「Service」。

接下來我們要明確定義三個服務的端點(Endpoint),一個MEX端點開放Metadata供想叫用服務的用戶端程式存取,以便於產生叫用服務所需的代理程式;一個端點使用basicHttpBinding,以SOAP做溝通;一個端點使用webHttpBinding,以REST方式做呼叫。

我們要先定義一個Metadata端點,開放服務的描述資訊。在「Endpoint」節點上方,按滑鼠右鍵,從快捷選單中選取「New Service Endpoint」項目,請參考下圖所示:

clip_image010

圖 5:新增MEX服務端點。

設定「Address」為「mex」;「Binding」設定為「mexHttpBinding」;Contract設定為「IMetadataExchange」,請參考下圖所示:

clip_image012

圖 6:定義一個Metadata端點。

新增一個使用SOAP的服務端點,在「Endpoint」節點上方按滑鼠右鍵,從快捷選單中選取「New Service Endpoint」項目,請參考下圖所示:

clip_image014

圖 7:新增一個使用SOAP的服務端點。

設定「Address」為「soap」;Binding設定為「basicHttpBinding」;Contract設定為「IService」,請參考下圖所示:

clip_image016

圖 8:設定SOAP端點。

設定端點行為

由於REST端點需要透過端點行為開放用戶端存取,在定義REST端點之前,我們先來定義一個端點行為。在「Endpoint Behavior」節點上方按滑鼠右鍵,從快捷選單中選取「New Endpoint Behavior Configuration」項目,請參考下圖所示:

clip_image018

圖 9:設定端點行為。

為端點行為設定取一個名稱,本例為「restBev」,按下畫面中的「Add」按鈕,開啟擴充程式新增介面,請參考下圖所示:

clip_image020

圖 10:新增端點行為。

選取「webHttp」,然後按下「Add」按鈕,請參考下圖所示:

clip_image022

圖 11:加入webHttp。

目前畫面如下:

clip_image024

圖 12:加入webHttp。

新增一個使用REST的服務端點,在「Endpoint」節點上方,按滑鼠右鍵,從快捷選單中選取「New Service Endpoint」項目,請參考下圖所示:

clip_image026

圖 13:新增一個使用REST的服務端點。

設定「Address」為「rest」;「Behavior Configuration」設定為「restBev」,「Binding「設定為「webHttpBinding」;Contract設定為「IService」,請參考下圖所示:

clip_image028

圖 14:新增一個使用REST的服務端點。

完成設定之後,按選取「File」- 「Save」儲存組態檔案,新建立的組態檔案的內容如下:

<?xml version="1.0"?>
<configuration>
 
  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="false" targetFramework="4.5.1" />
    <httpRuntime targetFramework="4.5.1"/>
  </system.web>
  <system.serviceModel>
    <services>
      <service name="Service">
        <endpoint address="mex" binding="mexHttpBinding" bindingConfiguration=""
          contract="IMetadataExchange" />
        <endpoint address="soap" binding="basicHttpBinding" bindingConfiguration=""
          contract="IService" />
        <endpoint address="rest" behaviorConfiguration="restBev" binding="webHttpBinding"
          bindingConfiguration="" contract="IService" />
      </service>
    </services>
    <behaviors>
      <endpointBehaviors>
        <behavior name="restBev">
          <webHttp />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
      <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <!--
        To browse web app root directory during debugging, set the value below to true.
        Set to false before deployment to avoid disclosing web app folder information.
      -->
    <directoryBrowse enabled="true"/>
  </system.webServer>
 
</configuration>

 

測試服務

設定完成之後,使用瀏覽器執行服務測試,確認服務設定是否正確。在「Solution Explorer」視窗,點選Service.svc檔案,按滑鼠右鍵,從快捷選單中選取「View In Browser」項目,得到的執行結果如下圖所示,將啟動服務說明頁面:

clip_image030

圖 15:測試服務頁。

接著我們使用WCF Test Client程式來測試呼叫服務。從「Solution Explorer」視窗- 選取Service.svc檔案,按CTRL+F5執行Service.svc檔案,執行結果參考如下,Visual Studio 2013會自動啟動WCF Test Client工具程式,讓您進行測試,只要輸入右方要傳遞到服務的value值,然後按下「Invoke」按鈕,執行結果就會顯示在右下方,請參考下圖所示:

clip_image032

圖 16:使用WCF Test Client測試呼叫服務。

REST用戶端

WCF服務端已經設定完成,我們可以開始進行用戶端的設計動作。在網站中加入一個新HTML網頁,從「Visual Studio 2013」-「WEBSITE」-「Add New Item」,請參考下圖所示:

clip_image034

圖 17:加入一個新HTML網頁。

在「Add New Item」視窗中選取「HTML Page」,設定檔案名稱,然後按下「Add」按鈕,請參考下圖所示:

clip_image036

圖 18:選取「HTML Page」。

使用Nuget套件管理員下載「jQuery」2.1.3版函式庫。在「Solution Explorer」視窗選取專案名稱。從Visual Studio 2013開發工具「TOOLS」-「Library Package Manager」-「Package Manage Console」開啟「Package Manage Console」視窗,然後在提示字元中輸入install-package指令,並使用「-version」指定安裝jQuery 2.1.3版本:

install-package jQuery -Version 2.1.3

得到的執行結果如下圖所示:

clip_image038

圖 19:安裝jQuery 2.1.3版本。

在</body>標籤之上引用jQuery,接著加入<script>區塊,於jQuery的ready事件撰寫程式碼利用jQuery 的.ajax()方法來叫用WCF服務:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body>
  <div id="result">
 
  </div>
  <script src="Scripts/jquery-2.1.3.js"></script>
  <script>
 
 
      $( function () {
 
        $.ajax( {
          type: "POST",
          url: "http://localhost:2039/Service.svc/rest/GetData",
          data: JSON.stringify( { value: 100 } ),
          contentType: "application/json;charset=utf-8",
          dataType: "json",
          success: function ( data ) {
            console.log( data );
            $( "#result" ).text( data.GetDataResult );
          }
        } );
      } )
 
 
  </script>
</body>
</html>

 

叫用服務時只要將參數包裝成JavaScript物件,利用 JSON.stringify序列化為JSON字串指定給data屬性。dataType指明傳送給服務的是JSON格式資料;contentType要求伺服端傳遞JSON格式資料。Success事件觸發時,從後方的匿名函式第一個參數中可以得到伺服端的執行結果。

從「Solution Explorer」視窗- 選取html檔案,按CTRL+F5執行,執行結果參考如下:

clip_image040

圖 20:執行結果。

使用複雜型別參數

若要叫用WCF服務的GetDataUsingDataContract方法,你需要傳遞一個composite參數到服務端。我們只需要定義一個JavaScript物件包含一個composite屬性,屬性的名稱與伺服端GetDataUsingDataContract方法的參數變數名稱相同。composite屬性的值是一個物件,包含兩個屬性:BoolValue與StringValue,這兩個屬性和伺服端CompositeType類別的BoolValue與StringValue屬性名稱需一致:

 
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body>
  <div id="result">
 
  </div>
  <script src="Scripts/jquery-2.1.3.js"></script>
  <script>
 
    $( function () {
 
      $.ajax( {
        type: "POST",
        url: "http://localhost:2039/Service.svc/rest/GetDataUsingDataContract",
        data: JSON.stringify( { composite: { BoolValue: false, StringValue: "hello" } } ),
        contentType: "application/json;charset=utf-8",
        dataType: "json",
        success: function ( data ) {
          console.log( data );
          $( "#result" ).text( data.GetDataUsingDataContractResult.StringValue + "," + data.GetDataUsingDataContractResult.BoolValue );
        }
      } );
    } )
 
  </script>
</body>
</html>

 

從「Solution Explorer」視窗- 選取html檔案,按CTRL+F5執行,執行結果參考如下:

clip_image042

圖 21:叫用服務結果。

SOAP用戶端

接著我們來加入一個新的主控台應用程式,當作是SOAP用戶端程式。從 Visual Studio 2013的主選單點選「FILE」-「Add」-「New Project」,請參考下圖所示:

clip_image044

圖 22:加入一個新的主控台應用程式。

然後選取「Console Application」,設定專案名稱,然後按下「Add」按鈕,請參考下圖所示:

clip_image046

圖 23:設定專案名稱。

產生叫用服務的代理程式碼。在Visual Studio 2013「Solution Explorer」視窗中,點選主控台專案,按下滑鼠右鍵,從快捷選單之中,點選「Add」-「Service Reference...」,請參考下圖所示:

clip_image048

圖 24:加入服務參考。

點選「Discover」按鈕,下方會自動列出服務資訊,再點選「OK」按鈕,請參考下圖所示:

clip_image050

圖 25:探索服務。

當在WCF Client加入Web Service參考後,會自動在組態檔將所需要的端點條件設定好,其中最主要的就是<client>項目endpoint中的address、binding和contract,以下是工具自動產生的web.config組態設定的內容:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" />
    </startup>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_IService" />
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:2039/Service.svc/soap" binding="basicHttpBinding"
                bindingConfiguration="BasicHttpBinding_IService" contract="ServiceReference1.IService"
                name="BasicHttpBinding_IService" />
        </client>
    </system.serviceModel>
</configuration>

最後在Main方法中撰寫程式建立WCF Client物件叫用WCF服務,只要建立代理程式ServiceClient類別實體,叫用它的方法,適當地傳遞參數資料即可:

namespace WCFConsoleClient {
  class Program {
    static void Main( string[ ] args ) {
      Console.WriteLine( "Press any key to call WCF Service...");
      Console.ReadLine( );
 
      ServiceReference1.ServiceClient p = new ServiceReference1.ServiceClient( );
      Console.WriteLine( p.GetData( 10 ) );
 
      ServiceReference1.CompositeType param = new ServiceReference1.CompositeType( );
      param.BoolValue = false;
      param.StringValue = "hello";
      ServiceReference1.CompositeType r = p.GetDataUsingDataContract( param );
      Console.WriteLine( r.StringValue );
      Console.WriteLine( r.BoolValue );
 
 
      param.BoolValue = true;
      param.StringValue = "hello ";
      r = p.GetDataUsingDataContract( param );     
      Console.WriteLine( r.StringValue );
      Console.WriteLine( r.BoolValue );
 
 
      Console.ReadLine( );
 
    }
  }
}

 

設定兩個專案同時執行,點選「Solution Explorer」視窗中的Sloution檔案,按滑鼠右鍵,從快捷選單中選取「Properties」項目,選取「Multiple startup projects」設定兩個專案同時執行,先執行WCF網站,再執行主控台應用程式,然後按下「OK」按鈕,請參考下圖所示:

clip_image052

圖 26:設定兩個專案同時執行。

按CTRL+F5執行方案,主控台應用程式將會印出呼叫服務的執行結果,請參考下圖所示:

clip_image054

圖 27:叫用服務。

Tags:

.NET Magazine國際中文電子雜誌 | WCF | jQuery | 許薰尹Vivid Hsu

評論 (46) -

good student
good student United States
2015/11/27 上午 04:05:03 #

老師:
可以請教為何我按照你的步驟,只是一開始開的是 "專案" 而非 "網站", ajax 回傳的結果如下 ? 改回 "網站就又可以了"。要如何使用 "專案"的方式執行 ?
Cannot process the message because the content type 'application/json;charset=utf-8' was not the expected type 'text/xml; charset=utf-8'."

回覆

cours de theatre
cours de theatre United States
2017/9/30 下午 01:00:03 #

Great article post.Really thank you! Want more.

回覆

can ho osimi
can ho osimi United States
2017/10/7 上午 01:44:04 #

Great, thanks for sharing this article. Much obliged.

回覆

can ho bien vung tau
can ho bien vung tau United States
2017/10/9 下午 07:31:52 #

Great, thanks for sharing this article.Much thanks again. Want more.

回覆

Google cheat 2017
Google cheat 2017 United States
2017/10/12 下午 10:10:39 #

I think this is a real great article post.Really looking forward to read more. Much obliged.

回覆

explanation
explanation United States
2017/10/14 下午 05:26:42 #

A big thank you for your article post.Really thank you! Great.

回覆

dragon city hack android 1
dragon city hack android 1 United States
2017/10/15 下午 04:57:36 #

Say, you got a nice blog post.Really looking forward to read more. Great.

回覆

you can find out more
you can find out more United States
2017/10/17 下午 04:15:54 #

This is one awesome blog. Will read on...

回覆

sletrokor review
sletrokor review United States
2017/10/17 下午 09:47:27 #

Thanks for sharing, this is a fantastic blog post. Will read on...

回覆

VigRx Plus Review
VigRx Plus Review United States
2017/10/19 上午 08:51:06 #

This is one awesome article.Thanks Again. Keep writing.

回覆

visit homepage
visit homepage United States
2017/10/19 下午 07:55:50 #

Say, you got a nice blog article.Really looking forward to read more. Cool.

回覆

prix carte grise
prix carte grise United States
2017/10/21 上午 08:53:55 #

Thanks a lot for the blog post.Really thank you! Really Great.

回覆

phenocal review
phenocal review United States
2017/11/1 下午 09:21:14 #

Muchos Gracias for your article.Thanks Again. Keep writing.

回覆

phentaslim
phentaslim United States
2017/11/3 上午 09:15:28 #

Thanks again for the article post.Much thanks again. Really Great.

回覆

throbbing back pain
throbbing back pain United States
2017/11/15 上午 06:55:22 #

I loved your blog.Really looking forward to read more. Cool.

回覆

avocat criminel quebec
avocat criminel quebec United States
2017/11/16 下午 05:30:09 #

I am so grateful for your post.Much thanks again. Keep writing.

回覆

I loved your article.Really thank you! Keep writing.

回覆

implement binary tree
implement binary tree United States
2017/11/22 上午 02:03:08 #

I really like and appreciate your article.Much thanks again. Cool.

回覆

fashion
fashion United States
2017/11/23 下午 08:56:25 #

I loved your blog post.Really thank you!

回覆

Adwords Management Auckland
Adwords Management Auckland United States
2017/11/25 下午 05:55:25 #

Thank you for your post.Really looking forward to read more.

回覆

Chad Boonswang SEO
Chad Boonswang SEO United States
2017/11/26 下午 11:06:34 #

Major thankies for the article.Really looking forward to read more. Really Cool.

回覆

fake cash for cars
fake cash for cars United States
2017/11/29 下午 03:31:04 #

Im thankful for the blog post.Much thanks again. Fantastic.

回覆

can ho go vap
can ho go vap United States
2017/11/29 下午 10:05:06 #

Great, thanks for sharing this blog article. Keep writing.

回覆

aged corporations
aged corporations United States
2017/11/30 下午 11:57:21 #

Appreciate you sharing, great article.Thanks Again. Fantastic.

回覆

porno
porno United States
2017/12/1 下午 03:36:48 #

Great post can make continuous improvement, thanks reveal, the actual build up associated with understanding would be to maintain understanding, interest is actually the start of prosperity.

回覆

Business Credit For Small Business Loan
Business Credit For Small Business Loan United States
2017/12/3 上午 03:56:00 #

Thanks again for the blog.Really looking forward to read more. Keep writing.

回覆

Great article post.Really thank you! Great.

回覆

I think this is a real great article post.Thanks Again. Keep writing.

回覆

my legalsite
my legalsite United States
2017/12/10 下午 06:20:48 #

I think this is a real great post.Really thank you! Really Cool.

回覆

find
find United States
2017/12/14 下午 03:18:55 #

Thank you ever so for you article. Really Cool.

回覆

Hanukkah
Hanukkah United States
2017/12/14 下午 09:49:24 #

I really enjoy the blog article.Really looking forward to read more. Cool.

回覆

canon driver software
canon driver software United States
2017/12/16 下午 05:01:52 #

I really liked your blog article. Awesome.

回覆

tips lose weight
tips lose weight United States
2017/12/16 下午 11:17:08 #

Appreciate you sharing, great post.Really thank you! Awesome.

回覆

Im grateful for the article post.Much thanks again.

回覆

Analysis
Analysis United States
2017/12/17 下午 04:43:36 #

Great, thanks for sharing this article post.Thanks Again. Really Cool.

回覆

business
business United States
2017/12/20 下午 05:54:27 #

I am so grateful for your article post.Really looking forward to read more.

回覆

canon drivers
canon drivers United States
2017/12/23 下午 01:03:21 #

Thanks for the article.Much thanks again. Keep writing.

回覆

&#216;&#162;&#219;Œ&#217;†&#217;‡ &#216;&#168;&#216;&#186;&#217;„
آینه بغل United States
2017/12/26 下午 12:46:20 #

I am so grateful for your blog article.Really looking forward to read more. Awesome.

回覆

SOCCER HIGHLIGHTS
SOCCER HIGHLIGHTS United States
2017/12/26 下午 06:44:09 #

I cannot thank you enough for the blog article.Much thanks again. Fantastic.

回覆

canon drivers
canon drivers United States
2017/12/27 下午 03:12:26 #

Thanks so much for the article post.Much thanks again. Fantastic.

回覆

I really liked your article post.Thanks Again. Really Cool.

回覆

hp drivers
hp drivers United States
2018/1/3 上午 05:13:48 #

Thanks so much for the article.Much thanks again. Cool.

回覆

casino bonus codes
casino bonus codes United States
2018/1/4 下午 03:45:49 #

Thanks again for the blog.Thanks Again. Fantastic.

回覆

hp driver
hp driver United States
2018/1/5 下午 06:44:53 #

Very neat blog article.Much thanks again. Really Cool.

回覆

FBA
FBA United States
2018/1/6 上午 10:50:50 #

Thank you ever so for you blog article.Thanks Again. Awesome.

回覆

web hosting
web hosting United States
2018/1/10 下午 12:34:28 #

Im grateful for the blog. Much obliged.

回覆

新增評論




  Country flag
biuquote
  • 評論
  • 線上預覽
Loading






NET Magazine國際中文電子雜誌

NET Magazine國際中文電子版雜誌,由恆逸資訊創立於2000,自發刊日起迄今已發行超過500篇.NET相關技術文章,擁有超過40000名註冊讀者群。NET Magazine國際中文電子版雜誌希望藉於電子雜誌與NET Developer達到共同學習與技術新知分享,歡迎每一位對.NET 技術有興趣的朋友們多多支持本雜誌,讓作者群們可以有持續性的動力繼續爬文。<請加入免費訂閱>

月分類Month List