Töötan ühe projekti kallal, mille juures kasutan aktiivselt Windows Communication Foundationit ja reedel tekkis väga lihtne soov. Tahtsin näha, mis sõnumeid ja vastused ma saadan ning saan kasutades välist (minu kontrolli alt väljas) olevat teenust. See informatsioon on kasulik näiteks suhtlemisel välise teenuse arendajaga (mis minu juhul oli case). Soovisin saata talle probleemi kirjelduse, mis mul oli ja kaasa panna sõnumid, mis saadan ja vastuseks saan. Väga triviaalne probleem.
Esimese mõttena proovisin sniffimisprogrammi nimega ethereal (et siis näha pakette ja loodetavasti nii teada saada). See aga ebaõnnestus, kuna tegu oli HTTPS ühendusega ja sniffimisest on ülimalt vähe kasu. Sniffimine oli esimene mõte, kuna nooruses sai kõvasti turvalisuse, wardrivimise jm. tegeletud (noorena veel oli aega selleks).
Kuna peale sõnumi väljumist WCF -st, ei ole võimalik seda lugeda, hakkasin otsima lahendust WCF -i seest. Kurvastusega pidin tunnistama, et puhtalt konfigureeritavat lahendust ei leidnud. Küll aga leidsin lahenduse, mis võimaldab plugina ehitada, mis enne sõnumi väljumist ja vastuse saabumisel näpud vahele paneb ning sõnumit/vastust vaadata ja töödelda laseb.
IClientMessageInspector
WCF -s on interface - IClientMessageInspector, mis nõuab kahe meetodi implementeerimist:
- BeforeSendRequest - enne sõnumi saatmist
- AfterReceiveReply - peale sõnumi saabumist
Ning muidugi saab meetod sisendiks ka sõnumi.
Interface -i implementatsioon oli minul järgmine:
public class RequestResponseInspector : IClientMessageInspector
{
#region IClientMessageInspector Members
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
Message originalMessage = buffer.CreateMessage();
XmlWriter writer = XmlWriter.Create("Request.xml");
originalMessage.WriteMessage(writer);
writer.Flush();
if (!(writer.WriteState == WriteState.Closed))
writer.Close();
return null;
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
MessageBuffer buffer = reply.CreateBufferedCopy(Int32.MaxValue);
reply = buffer.CreateMessage();
Message originalMessage = buffer.CreateMessage();
XmlWriter writer = XmlWriter.Create("Response.xml");
originalMessage.WriteMessage(writer);
writer.Flush();
if (!(writer.WriteState == WriteState.Closed))
writer.Close();
}
#endregion
}
Nii enne sõnumi saatmist kui ka vastuse saabumisel, kirjutan ma sõnumi XML -i faili, et siis hiljem vaadata. Nüüd, antud koodist tasub teha tähelepaneku, et ma loon originaalsest sõnumist koopia, mitte ei kirjuta seda sõnumit otse, mis meetodisse tuleb. Kui te proovite kirjutada otse, siis sõnumi saatmisel saate te vea, kuna sõnumit ei ole enam buffris - see on juba välja kirjutatud.
Järgmise sammuna, tuleks luua uus teenuse behaviour , mis antud sekkuja kliendi endpointi külge paneks. Selleks on olemas IEndpointBehavior liides (interface). Kuigi antud liidese meetoditest huvitab mind ainult ApplyClientBehavior meetod, kuna soovin kliendi poolse endpointi käitumist muuta.
Antud meetod ei tee midagi muud, kui ainult lisab loodud sekkuja (interceptor) kliendi sõnumite sekkujate kollektsiooni.
RequestResponseInspector requestResponseInspector = new RequestResponseInspector();
clientRuntime.MessageInspectors.Add(requestResponseInspector);
Ja viimase sammuna teen antud käitumsie konfigureeritavaks, et oleks mõnusam kasutada.
public class RequestResponseBehaviourElement : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new RequestResponseEndpointBehaviour();
}
public override Type BehaviorType
{
get { return typeof(RequestResponseEndpointBehaviour); }
}
}
Selleks, et antud laiendust saaks kasutada konfiguratsiooni failist, on vaja teenuse behaviouri laiendus registreerida konfiguratsioonis, kasutada seda behviouri kirjeldamisel ning siduda behaviour endpointiga.
system.serviceModel elementi lisada järgmine laienduse registreerimine.
<extensions>
<behaviorExtensions>
<add name="requestResponseInspector"
type="XPlugin.RequestResponseBehaviourElement, XPlugin,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
Tähelepanek: laienduse kirjeldamisel tuleb alati pikk assembley kirjeldus lisada. Vastasel juhul saate veateate kasutamisel. Ei oska öelda, kas see on selle versiooni kiiks või mitte, aga see on minu kogemus laienduste kasutamisest (ma olen nii mõnegi teinud).
Behaviouri kirjeldus näeks välja umbes niisugune (kui soovite ainult interceptorit lisada).
<behaviors>
<endpointBehaviors>
<behavior name="sekkujaBehaviour">
<requestResponseInspector/> <!-- Sama mis extensioni nimi -->
</behavior>
</endpointBehaviors>
</behaviors>
Ja antud behaviouri seote siis endpointiga behaviourConfiguration attribuudi kaudu.
<endpoint address=https://mingiaadress/mingiteenus binding="basicHttpBinding"
bindingConfiguration="mingiBinding" contract="MingiLepingLiides"
name="Aken" behaviorConfiguration="sekkujaBehaviour" />
Lahendus väga lihtne, kuid ülimalt kasulik, kui soovite sõnumeid logida, auditeerida või hallata, enne kui sõnum väljub teenuse suunas või kohe pärast saabumist.
Antud postitusega on kaasas ka lahenduse kood. Antud kood on aga küpsemal kujul, kui siin seletatud. Tegin plugina valmis, mis toimib nii teenuse kui kliendi poolel ja saab konfigureerida endpointi külge ning lisaks ka sõnumite (päring, vastus) salvestuskohti. Kaasas ka lahendusel näidiskonf.
Lahenduse kood.