共用方式為


設計服務合約

本主題描述什麼是服務合約、定義方式、可用的作業為何(以及基礎訊息交換的影響)、使用哪些數據類型,以及其他可協助您設計符合案例需求的作業的其他問題。

建立服務合約

服務會公開許多作業。 在 Windows Communication Foundation (WCF) 應用程式中,建立方法並將其標示 OperationContractAttribute 為 屬性,以定義作業。 然後,若要建立服務合約,請將作業分組在以 ServiceContractAttribute 屬性標示的介面內,或在以相同屬性標示的類別中定義它們。 (如需基本範例,請參閱 如何:定義服務合約。)

沒有 OperationContractAttribute 屬性的任何方法都不是服務作業,而且不會由 WCF 服務公開。

本主題描述設計服務合約時的下列決策點:

  • 是否要使用類別或介面。

  • 如何指定您要交換的數據類型。

  • 您可以使用的交換模式類型。

  • 您是否可以將明確的安全性需求納入合約的一部分。

  • 作業輸入和輸出的限制。

類別或介面

類別和介面都代表功能的群組,因此,這兩者都可以用來定義WCF服務合約。 不過,建議您使用介面,因為它們會直接建立服務合約的模型。 如果沒有實作,介面就不只定義具有特定簽章的方法群組。 實作服務合約介面,而且您已實作 WCF 服務。

Managed 介面的所有優點都適用於服務合約介面:

  • 服務合約介面可以擴充任何其他服務合約介面。

  • 單一類別可以藉由實作這些服務合約介面來實作任意數目的服務合約。

  • 您可以變更介面實作來修改服務合約的實作,而服務合約維持不變。

  • 您可以實作舊介面和新介面來建立服務版本。 舊用戶端會連線到原始版本,而較新的用戶端可以連線到較新版本。

備註

從其他服務契約介面繼承時,您無法覆寫操作屬性,例如名稱或命名空間。 如果您嘗試這樣做,您會在目前的服務合約中建立新的作業。

如需使用介面建立服務合約的範例,請參閱 如何:使用合約介面建立服務

不過,您可以使用 類別來定義服務合約,並同時實作該合約。 藉由將 ServiceContractAttributeOperationContractAttribute 分別直接套用至類別和類別上的方法來建立服務,其優點是速度和簡單性。 缺點是Managed類別不支援多個繼承,因此一次只能實作一個服務合約。 此外,對類別或方法簽章的任何修改會修改該服務的公用合約,這可以防止未修改的用戶端使用您的服務。 如需詳細資訊,請參閱 實作服務合約

如需使用 類別來建立服務合約並同時實作它的範例,請參閱 如何:使用合約類別建立服務

此時,您應該瞭解使用 介面和使用 類別來定義服務合約之間的差異。 下一個步驟是決定服務與其客戶端之間可以來回傳遞哪些數據。

參數和傳回值

每個作業都有傳回值和參數,即使這些是void。 不過,不同於本機方法,您可以在其中將對象參考從某個對象傳遞至另一個物件,服務作業不會將參考傳遞至物件。 相反地,他們會傳遞物件的複本。

這很重要,因為參數或傳回值中使用的每個類型都必須可串行化;也就是說,必須將該類型的 物件轉換成位元組數據流,以及從位元組數據流轉換成物件。

基本型別預設可串行化,如同 .NET Framework 中的許多類型一樣。

備註

作業簽章中參數名稱的值是合約的一部分,而且區分大小寫。 如果您要在本機使用相同的參數名稱,但修改已發佈元資料中的名稱,請參閱 System.ServiceModel.MessageParameterAttribute

資料合約

Windows Communication Foundation (WCF) 應用程式等服務導向應用程式的設計目的是要與Microsoft和非Microsoft平台上盡可能多的用戶端應用程式互通。 為了達到最廣泛的互作性,建議您使用 DataContractAttributeDataMemberAttribute 屬性來標記類型,以建立數據合約,這是描述服務作業交換之數據的服務合約部分。

數據契約是需要明確加入的契約:除非您明確套用數據契約屬性,否則不會序列化任何類型或數據成員。 數據合約與 Managed 程式代碼的存取範圍無關:私人數據成員可以串行化並傳送到其他地方以公開存取。 (如需數據合約的基本範例,請參閱 如何:建立類別或結構的基本數據合約。WCF 會處理基礎 SOAP 訊息的定義,以啟用作業的功能,以及將數據型別串行化至訊息主體和輸出訊息主體。 只要數據類型可串行化,您就不需要在設計作業時考慮基礎訊息交換基礎結構。

雖然一般 WCF 應用程式會使用 DataContractAttributeDataMemberAttribute 屬性來建立作業的數據合約,但您可以使用其他串行化機制。 標準 ISerializableSerializableAttributeIXmlSerializable 機制會處理資料類型串行化至基礎 SOAP 訊息,以將數據類型從一個應用程式傳送到另一個應用程式。 如果您的資料類型需要特殊支援,您可以採用更多串行化策略。 如需 WCF 應用程式中資料類型串行化選項的詳細資訊,請參閱 在服務合約中指定資料傳輸

將參數及回傳值映射至消息交換

服務操作藉由交換SOAP訊息來支援,這些訊息除了傳遞應用程式數據外,也傳遞應用程式支持特定的標準安全性、交易和會話相關功能所需的數據。 因為這是這種情況,服務作業的簽章會指定特定基礎 訊息交換模式 (MEP),以支援數據傳輸,以及作業所需的功能。 您可以在 WCF 程式設計模型中指定三種模式:要求/回復、單向和雙工訊息模式。

要求/回復

要求/回復模式是要求傳送者(用戶端應用程式)接收與要求相互關聯的回復的模式。 這是預設的 MEP,因為它支援一個作業,此作業允許將一個或多個參數傳遞至作業中,並將傳回值傳回給呼叫者。 例如,下列 C# 程式代碼範例會顯示接受一個字串並傳回字串的基本服務作業。

[OperationContractAttribute]
string Hello(string greeting);

以下是對等的Visual Basic程式碼。

<OperationContractAttribute()>
Function Hello (ByVal greeting As String) As String

此作業簽章會指定基礎訊息交換的形式。 如果沒有關聯性存在,WCF 無法判斷傳回值是針對哪個操作。

請注意,除非您指定不同的基礎訊息模式,即使是在 Visual Basic 中傳回 voidNothing)的服務操作也是要求/回應訊息交換。 作業的結果是,除非用戶端以異步方式叫用作業,否則用戶端會停止處理,直到收到傳回訊息為止,即使該訊息在正常情況下是空的。 下列 C# 程式代碼範例示範在用戶端收到空訊息回應之前不會傳回的作業。

[OperationContractAttribute]
void Hello(string greeting);

以下是對等的Visual Basic程式碼。

<OperationContractAttribute()>
Sub Hello (ByVal greeting As String)

如果作業需要很長的時間才能執行,上述範例可能會降低用戶端效能和回應性,但即使傳回 void,要求/回復作業也有優點。 最明顯的一個是 SOAP 錯誤可以在回應訊息中傳回,這表示在通訊或處理中發生某些服務相關的錯誤狀況。 服務合約中指定的 SOAP 錯誤會以 物件的形式傳遞至用戶端應用程式 FaultException<TDetail> ,其中 type 參數是服務合約中指定的類型。 這可讓用戶端輕鬆通知 WCF 服務中的錯誤狀況。 如需例外狀況、SOAP 錯誤和錯誤處理的詳細資訊,請參閱 指定及處理合約和服務中的錯誤。 若要查看要求/回復服務和用戶端的範例,請參閱 如何:建立 Request-Reply 合約。 如需了解更多有關請求-回覆模式問題的資訊,請參閱 Request-Reply Services

單向

如果 WCF 服務應用程式的用戶端不應該等待作業完成,而且不會處理 SOAP 錯誤,作業可以指定單向訊息模式。 單向操作是指用戶端呼叫操作,然後在WCF將訊息寫入網路後繼續處理。 這通常表示,除非輸出訊息中傳送的數據非常大,否則客戶端幾乎會立即繼續執行(除非傳送數據時發生錯誤)。 這種類型的訊息交換模式支援從用戶端到服務應用程式的事件類似行為。

在訊息交換中,如果只傳送一則訊息而未收到任何回應,則無法支援指定 void 以外的傳回值的服務作業;在此情況下,將擲回 InvalidOperationException 例外狀況。

沒有傳回訊息也意味著不會有任何SOAP錯誤傳回,以指出處理或通訊中的任何錯誤。 (當操作是單向操作時,傳達錯誤資訊需要雙工訊息交換模式。)

若要為傳回 void的作業指定單向訊息交換,請將 IsOneWay 屬性 true設定為 ,如下列 C# 程式代碼範例所示。

[OperationContractAttribute(IsOneWay=true)]
void Hello(string greeting);

以下是對等的Visual Basic程式碼。

<OperationContractAttribute(IsOneWay := True)>
Sub Hello (ByVal greeting As String)

這個方法與上述要求/回復範例相同,但將 屬性設定 IsOneWaytrue ,表示雖然 方法相同,但服務作業不會傳送傳回訊息,而且用戶端會在傳出訊息傳遞至通道層之後立即傳回。 如需範例,請參閱 如何:建立 One-Way 合約。 如需單向模式的詳細資訊,請參閱 One-Way 服務

雙層式

雙向模式的特點在於,無論是服務端還是用戶端,都可以獨立地使用單向或請求/回應傳訊來相互傳送訊息。 這種形式的雙向通訊適用於必須直接與用戶端通訊的服務,或提供異步體驗給訊息交換的任一端,包括類似事件的行為。

雙工模式比要求/回復或單向模式略微更為複雜,因為需要額外的機制來與用戶端通訊。

若要設計雙工合約,您也必須設計回呼合約,並將該回呼合約的類型指派給 CallbackContract 標示服務合約的屬性 ServiceContractAttribute 屬性。

若要實作雙工模式範例,您必須建立第二個介面,其中包含客戶端會呼叫的方法宣告。

如需建立服務的範例,以及存取該服務的用戶端,請參閱 如何:建立雙工合約如何:使用雙工合約存取服務。 如需工作範例,請參閱 Duplex。 如需有關使用雙工合約時遇到的問題的詳細資訊,請參閱 雙工服務

謹慎

當服務收到雙工訊息時,它會查看該傳入訊息中的ReplyTo 元素,以確定回復應傳送的位置。 如果用來接收訊息的通道未受到保護,不可信的客戶端可能會傳送針對目標計算機的惡意訊息,也可能導致該目標計算機的ReplyTo拒絕服務(DOS)。

Out 和 Ref 參數

在大部分情況下,您可以使用in參數(ByVal在 Visual Basic 中)和outref參數(ByRef在 Visual Basic 中)。 因為outref參數都表示數據是從作業中傳回,因此,下列作業簽章雖然會傳回void,仍然需要指定執行要求/回覆作業。

[ServiceContractAttribute]
public interface IMyContract
{
  [OperationContractAttribute]
  public void PopulateData(ref CustomDataType data);
}

以下是對等的Visual Basic程式碼。

<ServiceContractAttribute()> _
Public Interface IMyContract
  <OperationContractAttribute()> _
  Public Sub PopulateData(ByRef data As CustomDataType)
End Interface

唯一的例外狀況是您的簽章具有特定結構的案例。 例如,只有在用來宣告作業的方法傳回NetMsmqBinding時,才可以使用void結與客戶端通訊;此時不會有任何輸出值,無論是回傳值、refout參數。

此外,使用 outref 參數需要作業具有基礎回應消息,才能傳回修改的物件。 如果你的操作是單向操作, InvalidOperationException 執行時會拋出例外。

在合約上指定訊息保護層級

設計合約時,您也必須決定實作合約的服務訊息保護層級。 只有在訊息安全性套用至合約端點中的系結時,才需要這樣做。 如果系結已關閉安全性(也就是說,如果系統提供的系結將 設定 System.ServiceModel.SecurityMode 為 值 SecurityMode.None),則您不需要決定合約的訊息保護層級。 在大部分情況下,套用訊息層級安全性的系統提供的系結會提供足夠的保護層級,而且您不需要考慮每個作業或每個訊息的保護層級。

保護層級是一個值,指定支援服務的訊息(或訊息元件)是否已簽署、簽署和加密,或未經簽章或加密傳送。 保護層級可以設定於各種範圍:在服務層級、特定作業、該作業內的訊息,或訊息元件。 除非明確覆寫,否則在一個範圍設定的值會成為較小範圍的預設值。 如果系結組態無法提供合約所需的最低保護層級,則會引發例外狀況。 而且當合約上未明確設定保護層級值時,如果系結具有訊息安全性,系結組態會控制所有訊息的保護層級。 此為預設行為。

這很重要

決定是否將合約的不同範圍明確設定為低於完整保護層級 ProtectionLevel.EncryptAndSign,通常是一項需要在某種程度的安全性與提高效能之間進行取捨的決策。 在這些情況下,您的決策必須圍繞您的業務及其交換數據所帶來的價值。 如需詳細資訊,請參閱 保護服務

例如,下列程式代碼範例不會在合約上設定 ProtectionLevelProtectionLevel 屬性。

[ServiceContract]
public interface ISampleService
{
  [OperationContractAttribute]
  public string GetString();

  [OperationContractAttribute]
  public int GetInt();
}

以下是對等的Visual Basic程式碼。

<ServiceContractAttribute()> _
Public Interface ISampleService

  <OperationContractAttribute()> _
  Public Function GetString()As String

  <OperationContractAttribute()> _
  Public Function GetData() As Integer

End Interface

當與 ISampleService 端點中的實作互動時,預設 WSHttpBinding 為 , System.ServiceModel.SecurityMode也就是 Message,所有訊息都會加密並簽署,因為這是默認保護層級。 不過,當ISampleService服務搭配預設值BasicHttpBinding使用時,所有SecurityModeNone訊息都會以文字形式傳送,因為此系結沒有安全性,因此會忽略保護等級(亦即訊息不會加密或簽署)。 SecurityMode如果 已將 變更為 Message,則這些訊息會加密並簽署(因為現在會是系結的預設保護層級)。

如果您想要明確指定或調整合約的保護需求,請將屬性(或範圍較小的任何ProtectionLevel屬性)設定ProtectionLevel為服務合約所需的層級。 在此情況下,使用明確設定需要系結,才能支援所使用範圍的最低設定。 例如,下列程式代碼範例會針對ProtectionLevel作業明確指定一個GetGuid值。

[ServiceContract]
public interface IExplicitProtectionLevelSampleService
{
  [OperationContractAttribute]
  public string GetString();

  [OperationContractAttribute(ProtectionLevel=ProtectionLevel.None)]
  public int GetInt();
  [OperationContractAttribute(ProtectionLevel=ProtectionLevel.EncryptAndSign)]
  public int GetGuid();
}

以下是對等的Visual Basic程式碼。

<ServiceContract()> _
Public Interface IExplicitProtectionLevelSampleService
    <OperationContract()> _
    Public Function GetString() As String
    End Function

    <OperationContract(ProtectionLevel := ProtectionLevel.None)> _
    Public Function GetInt() As Integer
    End Function

    <OperationContractAttribute(ProtectionLevel := ProtectionLevel.EncryptAndSign)> _
    Public Function GetGuid() As Integer
    End Function

End Interface

實作此 IExplicitProtectionLevelSampleService 合約且具有使用預設值 WSHttpBinding 的端點 (預設值 System.ServiceModel.SecurityMode,也就是 Message) 的服務具有下列行為:

  • GetString作業訊息會加密並簽署。

  • GetInt作業訊息會以未加密和未簽署的文字形式傳送。

  • 該操作GetGuidSystem.Guid會在已加密和簽署的訊息中返回。

如需保護層級及其使用方式的詳細資訊,請參閱 瞭解保護層級。 如需安全性的詳細資訊,請參閱 保護服務

其他作業簽章需求

某些應用程式功能需要特定種類的作業簽章。 例如,NetMsmqBinding 結支援持久性服務和用戶端,讓應用程式可以在通訊過程中重新啟動,並在上一個中斷的位置繼續,而不會遺漏任何訊息。 (如需詳細資訊,請參閱 WCF 中的佇列。)不過,長期作業必須只接受一個 in 參數,而且沒有傳回值。

另一個範例是在操作中使用Stream型別。 Stream因為參數包含整個訊息本文,所以如果輸入或輸出(也就是ref參數、out參數或傳回值)的類型為Stream,則它必須是作業中指定的唯一輸入或輸出。 此外,參數或傳回型別必須是 StreamSystem.ServiceModel.Channels.MessageSystem.Xml.Serialization.IXmlSerializable。 如需數據流的詳細資訊,請參閱 大型數據和串流

名稱、命名空間和混淆

合約和作業定義中 .NET 型別的名稱和命名空間,在合約轉換成 WSDL,以及建立和傳送合約訊息時相當重要。 因此,強烈建議使用所有支援合約屬性的NameNamespace屬性,明確設定服務合約名稱和命名空間,例如ServiceContractAttributeOperationContractAttributeDataContractAttributeDataMemberAttribute和其他合約屬性。

其中一個結果是,如果未明確設定名稱和命名空間,則在元件上使用 IL 混淆會改變合約類型名稱和命名空間,並導致通常會失敗的修改後的 WSDL 和網路通信。 如果您未明確設定合約名稱和命名空間,但確實想要使用模糊化,請使用 ObfuscationAttributeObfuscateAssemblyAttribute 屬性來防止修改合約類型名稱和命名空間。

另請參閱