sobota, 16 kwietnia 2011

70-513 - Odcinek 4 - MessageContractAttribute

Podczas tworzenia serwisu w WCF, udekorowanie pewnej struktury danych atrybutem „DataContract” pozwala zdefiniować strukturę danych, która będzie przesyłana w ciele wiadomości SOAP. Dzięki używaniu [MessageContract] zamiast [DataContract], użytkownik przy tworzeniu swojego serwisu ma większą kontrolę nad wiadomościami SOAP oraz ich nagłówkami (headers).

Są dwa główne powody podjęcia decyzji o wykorzystaniu [MessageContract]:
  • W celu większej kontroli nad tworzeniem ciała wiadomości SOAP, w szczególności jak taka wiadomość ma być zserializowana
  • W celu dostarczenia i dostępu do „custom headers”
W celu zdefiniowania [MessageContract] wykorzystywane są następujące atrybuty: [MessageContract], [MessageHeader], [MessageBodyMember].

[MessageContract]

Ten atrybut może być zastosowany do klas i struktur w celu zdefiniowania własnej struktury wiadomości. Posiada on kilka ciekawych właściwości:

  • IsWrapped – definiuje czy ciało wiadomości posiada „wrapper” (pakowacz?). Szczerze powiedziawszy nie znalazłem nigdzie dokładnych informacji w jakim celu należy używać tej właściwości,
  • HasProtectionLevel – określa czy wiadomość posiada ProtectionLevel, 
  • ProtectionLevel – określa czy wiadomość ma być zaszyfrowana (encrypted), podpisana (sign) czy to i to (dostępne wartości: None, Sign, EncryptAndSign)
  • WrapperName – określa nazwę ciała wrapper’a
  • WrapperNamespace – określa przestrzeń nazwa dla ciała wrappera
Zatem, jaka jest różnica między [DataContract] a [MessageContract] ? Generalnie rzecz biorąc, [DataContract] określa określa typ danych używany przez serwis – opisuje parametry i/lub zwracane typy. Z kolei [MessageContract] używany jest w przypadku gdy chcemy wyraźnie określić format XML’a (wiadomości SOAP) przesyłanej z/do serwisu. Generalnie, atrybutu [MessageContract] należy używać w sytuacji gdy chcemy zdefiniować wiadomość SOAP z niestandardowym nagłówkiem (np. gdy w nagłówku przesyłamy nr licencji) lub w przypadku gdy chcemy stworzyć wiadomość z różnych typów [DataContract].
O co chodzi z wrapper’em? Generalnie wrapper jest to pewnego rodzaju „pakowacz” dzięki któremu możemy na przykład kontrolować strukturę danych zawartą w tym wrapperze. W przypadku gdy używamy [DataContract], WCF domyślnie serializuje wiadomość tak jakby opakowywanie domyślnie zachodziło (tak jabyśmy ustawili właściwość IsWrapped=true). W tym przypadku wszelkie parametry wiadomości przychodzących i typy zwracane w wiadomościach wychodzących wrzucane są do wrapper’a, gdy tych parametrów brak i/lub typ zwracany z metody to void, element wrapper będzie pusty. W przypadku gdy używamy [MessageContract], a w właściwość IsWrapper jest ustawiona na false, takie opakowanie nie będzie następowało.
Przykład zdefiniowania typu danych z użyciem atrybutu [MessageContract] może wyglądać następująco:

[MessageContract] 
public class HelloResponseMessage 
   private string localResponse = String.Empty; 
  private string extra = String.Empty; 
 [MessageBodyMember(Name = "ResponseToGreeting", Namespace = "http://www.examples.com")] 
public string Response 
  get { return localResponse; } 
  set { localResponse = value; } 
[MessageHeader(Name = "OutOfBandData", Namespace = "http://www.examples.com", MustUnderstand=true )] 
public string ExtraValues 
  get { return extra; } 
  set { this.extra = value; } 
}

[MessageHeader] i [MessageBodyMember]

Każdy element odpowiedniej struktury danych udekorowany tym atrybutem będzie wchodził w skład nagłówka wiadomości SOAP (SOAP header). Jego właściwości są następujące:
  • Name – określa nazwę serializowanego elementu
  • Namespace – określa przestrzeń nazw serializowanego elementu 
  • HasProtectionLevel - określa czy wiadomość posiada ProtectionLevel
  • ProtectionLevel – określa czy wiadomość ma być zaszyfrowana (encrypted), podpisana (sign) czy to i to (dostępne wartości: None, Sign, EncryptAndSign)
  • Actor –
  • MustUnderstand – określa czy odbiorca wiadomości (określony poprzez właściwość Actor) zobowiązany jest przetworzyć nagłówek. W przypadku gdy właściwość ta jest ustawiona na true a Actor i tak nie będzie mógł przetworzyć nagłówka, Actor zwraca SOAP fault. Właściwość ta jest mapowana na atrybut mustUnderstand wiadomości SOAP
  • Relay
Atrybut [MessageBodyMember] określa, że dany element struktury danych udekorowany tym atrybutem będzie wchodził w skład wiadomośći SOAP.
Przykład
Jak to wszystko wygląda w praktyce? Najlepszy będzie przykład skopiowany z MSDN'a:


[ServiceContract(Namespace = "Microsoft.WCF.Documentation")]
interface IMessagingHello
{
  [OperationContract(Action = "http://GreetingMessage/Action", ReplyAction = "http://HelloResponseMessage/Action")]
  HelloResponseMessage Hello(HelloGreetingMessage msg);
}
[MessageContract]
public class HelloResponseMessage
{
  private string localResponse = String.Empty;
  private string extra = String.Empty;

   [MessageBodyMember(Name = "ResponseToGreeting", Namespace="http://www.examples.com")]
  public string Response
  {
    get { return localResponse; } 
    set { localResponse = value; }
}

  [MessageHeader(Name = "OutOfBandData",Namespace="http://www.examples.com",MustUnderstand=true)]
  public string ExtraValues
  { 
    get { return extra; }
    set { this.extra = value; }
  }
}

[MessageContract]
public class HelloGreetingMessage
{
  private string localGreeting;
  
  [MessageBodyMember(Name = "Salutations", Namespace = "http://www.examples.com")]
  public string Greeting
  {
    get { return localGreeting; }
    set { localGreeting = value; }
  }
}

Brak komentarzy:

Prześlij komentarz