OK
Published on

SOAP Servislerinin Tek Bir Sınıf Aracılığı ile Loglanması

Authors
  • avatar
    Name
    Oğuzhan Kırçalı
    Twitter

SOAP servislerini bilirsiniz. HTTP protokolü üzerinde XML tabanlı çalışır. URL'in sonuna WSDL eklersiniz ve bütün şeffaflığı ile neyi var neyi yok size gösterir. Yıllardır milyarlarca veriyi oradan oraya, buradan şuraya taşıyan ve günümüzde azalarak da olsa taşımaya devam eden bu servisler, yerini yeni çocuk RESTful servislere bırakıyor ancak, birçok yerde yine bir SOAP servis url'i çıkıyor ve bu servisi projenize entegre etmeniz gerekiyor.

Ekibi içinde bulunduğum bir projede birden fazla SOAP servisinin entegrasyonunu yaptım. Bu süreçte gelen ve giden verinin (XML) log kayıtlarının atılması, sizin de tahmin ettiğiniz gibi oldukça önemli bir konu olarak gündeme geldi. Bütün SOAP servisleriniz için öyle güzel bir sınıf hazırlayacağız ki, servise gitmeden önce ve servisten döndükten sonra oluşan XML'leri yakalayıp log kaydı atacağız. Hadi başlayalım!

Bu yazıda anlattığım kodlara buradan erişebilirsiniz.

Web Servisini Projeye Eklemek

Örnek olarak hazırlanmış bir web servisi kullanacağım. http://www.dneonline.com/calculator.asmx?wsdl servisini bir hayırsever hazırlamış ve deneme amaçlı kullanalım diye bize sunmuş. Bu servisi projeme eklemek için projemin referanslarına sağ tıklayıp servis referansı ekle menüsü ile açılan pencereye bu URL'i yapıştırıp Go'ya tıklıyorum. Aşağıya servisin geldiğini göreceksiniz. Uygun bir namespace verip OK diyorum. Bu işlemde servisi çağırmak ve gelen cevabı almak için gereken bütün sınıflar ve endpoint oluşacaktır.

Web Servisine Çağrıda Bulunmak

Web servisini projeye ekledikten sonra basit bir metodunu kullanarak çağrıda bulunalım. Biz iki sayıyı göndereceğiz, o da bize toplamını dönecek. Aşağıdaki kod bloğu ile web servisini çağırabilirsiniz.

var client = new CalculatorServiceRef.CalculatorSoapClient();
int a = 6;
int b = 3;
var result = client.Add(a, b);

result nesnesine 9 sonucu geldi ise servisi ekleme ve çağırma işlemini doğru yapmışsınız demektir. Sorun yaşadıysanız tarayıcıdan servis URL'ine giderek erişim kontrolü veya web.config'deki endpoint'lerin düzgün oluşup oluşmadığını kontrol edebilirsiniz.

Örnek olarak kullandığım web serviste herhangi bir kullanıcı adı/şifre istememektedir.

Loglamayı Yapacak Sınıfları Eklemek

Servisimiz çalıştığına göre servise gönderdiğimiz ve servisten gelen verileri yakalayacak olan sınıfları projemize ekleyelim. Aşağıdaki iki sınıfı, projenizde uygun gördüğünüz bir yere ekleyin.

SoapBehaviour.cs
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Linq;
using SoapLogging.Database;

namespace SoapLogging.Common
{
    public class SoapBehavior : IEndpointBehavior, IClientMessageInspector
    {
        private readonly ServiceLogRepository _logRepository;
        public SoapBehavior()
        {
            _logRepository = new ServiceLogRepository();
        }
        private int MessageDBID { get; set; }
        private string ModuleName { get; set; }
        private ServiceLog ServiceLogEntity { get; set; }

        public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {

        }
        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            // adds our inspector to the runtime
            clientRuntime.MessageInspectors.Add(this);
            clientRuntime.MaxFaultSize = 1024000;
        }
        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {

        }
        public void Validate(ServiceEndpoint endpoint)
        {
            this.ModuleName = endpoint.Contract.ConfigurationName.Split('.')[0];
        }

        object IClientMessageInspector.BeforeSendRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel)
        {
            try
            {
                this.ServiceLogEntity = new ServiceLog
                {
                    Module = this.ModuleName,
                    ActionName = request.Headers.Action.Split('/').Last(),
                    OutgoingXml = request.ToString(),
                    RequestDate = DateTime.Now
                };

                if (this.ServiceLogEntity.ActionName == "Divide")
                {
                    this.ServiceLogEntity.HintKey = "Divide process started.";
                }
            }
            catch (Exception ex)
            {
                this.ServiceLogEntity = new ServiceLog
                {
                    Module = this.ModuleName,
                    ActionName = request.Headers.Action.Split('/').Last(),
                    OutgoingXml = "Hata" + ex.Message,
                    RequestDate = DateTime.Now
                };
            }

            _logRepository.InsertOrUpdateServiceLog(this.ServiceLogEntity);

            return this.ServiceLogEntity.Id;
        }

        void IClientMessageInspector.AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
            int isOk = reply.ToString().IndexOf("search text in incoming xml");
            if (isOk > 0)
                this.ServiceLogEntity.HintKey = "some text";

            this.ServiceLogEntity.IncomingXml = reply.ToString();
            this.ServiceLogEntity.ResponseDate = DateTime.Now;
            _logRepository.InsertOrUpdateServiceLog(this.ServiceLogEntity);
        }
    }
}
SoapBehaviorExtensionElement.cs
using System;
using System.ServiceModel.Configuration;

namespace SoapLogging.Common
{
    public class SoapBehaviorExtensionElement : BehaviorExtensionElement
    {
        public SoapBehaviorExtensionElement()
        {
        }
        
        public override Type BehaviorType
        {
            get
            {
              return typeof(SoapBehavior);
            }
        }
        
        protected override object CreateBehavior()
        {
            return new SoapBehavior();
        }
    }
}

SoapBehavior.cs dosyasındaki ServiceLogRepository sınıfı projeniz için SOAP loglarını kaydedeceğiniz ve güncelleğeceğiniz sınıfır temsil etmektedir. Siz kendi metotlarınız ile bu sınıfı yer değiştirebilirsiniz.

Servis ile Loglama Sınıfını İlişkilendirmek

İki sınıfı eklemenin ardından sorunsuzca derleniyorsa projeniz, servisle SoapBehavior sınıfını ilişkilendirme adımına geçebiliriz. Bu ilişkilendirme adımının tamamını web.config dosyasında yapacağız. Web.config dosyanızı aşağıdaki şekilde güncelleyebilirsiniz.

  <system.serviceModel>
    <behaviors>
      <endpointBehaviors>
        <behavior name="SoapBehavior">
          <SoapBehavior></SoapBehavior>
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <dataContractSerializer maxItemsInObjectGraph="2147483647" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <client>
      <endpoint address="http://www.dneonline.com/calculator.asmx"
        binding="basicHttpBinding" bindingConfiguration="CalculatorSoap"
        contract="CalculatorServiceRef.CalculatorSoap" name="CalculatorSoap" behaviorConfiguration="SoapBehavior"/>
    </client>
    <extensions>
      <behaviorExtensions>
        <add name="SoapBehavior" type="SoapLogging.Common.SoapBehaviorExtensionElement, SoapLogging, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </behaviorExtensions>
    </extensions> 
    <bindings>
      <basicHttpBinding>
        <binding name="CalculatorSoap" />
      </basicHttpBinding>
    </bindings>
  </system.serviceModel>

system.serviceModel etiketleri arasına endpointBehaviors'ı ekleyin. Burada bizim davranışımızın Web.config dosyasındaki kaydını yapıyoruz. Yine system.serviceModel etiketi arasına extension sınıfımızı tanıtan satırları ekliyoruz. İlişkiyi kuran en önemli satır ise servisin endpoint etiketine eklediğimiz behaviorConfiguration="SoapBehavior" ayarıdır.

Kontrol

Yukarıdaki işlemlerin tamamını yaptıktan sonra ilişkilendirmenin doğru yapılıp yapılmadığını anlamak için SoapBehavior.cs sınıfındaki

  • BeforeSendRequest
  • AfterReceiveReply

metotlara birer breakpoint koyarak kontrol edebiliriz. ServiceLogEntity nesnesinin gelen ve giden XML'lerinin dolu olduğunu göreceksiniz. Buraya kadar anlattığım bütün aşamaları içeren projeye bu bağlantıdan erişebilirsiniz.