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]:
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:
[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:
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; }
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
{
}
[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;
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