webservice címkéhez tartozó bejegyzések

Visual Studio: Unable to check out the current file

Érdekes hibába futottam bele, miközben egy szolgáltatás referenciát próbáltam felvenni Visual Studioba. Az URL egy .svc fájlra mutatott, aminek a szerkezetét az Add Service Reference dialógusablak gond nélkül felismerte, ám az ablak bezárásakor a Studio a következő hibaüzenettel örvendeztetett meg:

Unable to check out the current file.  The file may be read-only or locked, or you may need to check the file out manually.

Bár az adott projekt történetesen valóban source control alatt volt, Git-ről lévén szó a “check out” kifejezés nem tűnt helyénvalónak. Az igazság az, hogy a fenti hibaüzenet teljesen rossz és semmi köze a verziókezelőhöz, és a megoldás az, hogy nem közvetlenül az Add Service Reference, hanem azon belül az Advanced gombra, majd az Add Web Reference gombra kattintva megjelenő dialógusablakot kell használni az adott szolgáltatás esetén.

 

Technorati-címkék: ,,
Reklámok

Hány kérés kell egy authentikációhoz?

Adott egy webszolgáltatás, amiről tudjuk, hogy csak Basic hitelesítéssel érhető el, de ez nem gond, hiszen szerencsére .NET-ben a generált proxy osztály Credentials tulajdonságán keresztül könnyű beállítani a szükséges felhasználónevet és jelszót:

MyService ws = new MyService
{
    Credentials = new NetworkCredential( "user", "password" )
};

Az ember azt gondolná, hogy ennek hatására olyan HTTP kérés megy majd a webszolgáltatás felé, amely tartalmazza az authenkációhoz szükséges adatokat, de a valóságban sajnos nem ez történik. Először elmegy egy olyan kérés, amiben nincs Authorization fejléc, azután ha a szerver ezt zokon veszi és 401 Authenticate válasszal tér vissza, akkor a kliens küld egy újabb kérést, ezúttal mellékelve hozzá a felhasználónevet és a jelszót. Az eredmény tehát dupla forgalom. Ha ez nem szimpatikus, és persze miért is lenne az, akkor jól jöhet a PreAuthenticate tulajdonság:

MyService ws = new MyService
{
    Credentials = new NetworkCredential( "user", "password" ),
PreAuthenticate = true };

 

Technorati-címkék: ,

PHP-s webszolgáltatás hívása .NET-ből

Úgy alakult, hogy .NET-ből kellett egy PHP-ban készült webszolgáltatást meghívnom standard SOAP interfészen keresztül, de ismét bebizonyosodott, hogy a Simple Object Access Protocolban a “simple” egyszerűen nem igaz, ugyanis a két fél összereszelése nem ment teljesen simán.

A hívás eredményeként a kliens 400 Bad Request státuszkódot kapott vissza, ami önmagában nem sokat segített a hibakeresésben. Az Apache webszerver naplójában ugyan volt egy Invalid URI in request bejegyzés, de ez sem vitt közelebb a megoldáshoz.

Mint már oly sokszor, most is a Fiddler segített. Megnézve vele a HTTP forgalmat gyanús lett, hogy a kérésben szerepel egy Expect: 100-continue fejléc, amiről később kiderült, hogy a PHP-s webszolgáltatás nem tud vele mit kezdeni. Ez egy elég érdekes fejléc, arra találták ki, hogy a kliens megkérhesse a szervert, hogy még a body felküldése előtt kiértékelje a kérésben szereplő fejléc mezőket, és egy 100 Continue válasszal jelezze, ha a fejlécek alapján hajlandó lesz feldolgozni a kérést, és a kliens bátran küldheti a body-t (bővebben lásd RFC 2616 8.2.3). Magyarul a klasszikus kérés-válasz sorrendet szépen fel lehet vele borítani:

Kliens –> Szerver:

POST example.com HTTP/1.1
kérés fejlécek
Expect: 100-Continue

Szerver –> Kliens:

HTTP/1.1 100 Continue
válasz fejlécek

Kliens –> Szerver:

kérés body

Szerver –> Kliens:

HTTP/1.1 200 OK
válasz fejlécek
válasz body

A .NET kliens mindig elküldi az Expect: 100-Continue fejléc sort, hogy spóroljon a hálózati forgalommal, sajnos azonban a jelek szerint nem minden szerver támogatja ezt a csavarást. Ezt a viselkedést a ServicePointManager osztály Expect100Continue tulajdonsága segítségével lehet felüldefiniálni:

ServicePointManager.Expect100Continue = false;

Még az is lehet, hogy így lesz gyorsabb a kódunk, hiszen a HttpWebRequest osztály alapértelmezés szerint 350 milliszekundumot vár a Continue válaszra.

 

Technorati-címkék: ,,,

Tanúsítvány ellenőrzésének kikapcsolása webszolgáltatás hívásnál

Nem szeretek alapvetően jó és szükséges biztonsági funkciók kikapcsolásáról írni, de mivel nekünk nem volt más választásunk, másnak is hasznos lehet. Adott egy SOAP kliens, ami hívna egy webszolgáltatást, csakhogy a szerveren lévő tanúsítvány nem érvényes. Szerencsére ezt a .NET Framework kliens oldalon ellenőrzi és normális esetben elszáll a hívás, csakhogy néha sajnos az a kívánság, hogy hibás tanúsítvány esetén is csont nélkül menjen minden. Szerencsére az ellenőrzést ki lehet kapcsolni web.configban a servicePointManager elem segítségével:

<configuration>
  <system.net>
    <settings>
      <servicePointManager
          checkCertificateName="false"
          checkCertificateRevocationList="false" />
    </settings>
  </system.net>
</configuration>

 

Technorati-címkék: ,

Webszolgáltatás hívás kimenő IP címének beállítása

Ma egy olyan webszolgáltatást kellett meghívnom, ami előtt egy tűzfal csak bizonyos IP című kliensekről engedélyezi a hozzáférést. Ez normális esetben nem is szokott gondot okozni, most is megbeszéltem az ottani rendszergazdákkal, hogy milyen IP címről akarom küldeni a kérést, és ők készségesen lyukat ütöttek a tűzfalba. A nehezítést az jelentette, hogy a hívó épp egy szerver, aminek több IP címe van, és persze alapból nem arról ment ki a kérés, ahonnan én akartam. A kérdés tehát, hogy hogyan lehet megadni egy webszolgáltatás hívásnál a hívó IP címét?

A dolog szerencsére megoldható, de nem olyan egyszerűen, mint szeretnénk. A Visual Studio varázslója olyan proxy osztályt generál, ami a SoapHttpClientProtocol ősosztályból származik, aminek persze egy fikarcnyi ide vágó property-je sincsen. Viszont a HTTP híváshoz ő belül a HttpWebRequest osztályt használja, amin szerencsére lehet ilyen jellegű fogódzót találni. Persze csak alapos kereséssel, ugyanis a tulajdonságot úgy hívják, hogy ServicePoint, aminek a BindIPEndPointDelegate tulajdonságát (!) kell beállítani egy delegate-re! Én bizony ezt biztosan nem így csináltam volna, de gondolom jó okuk volt rá, hogy így döntöttek.

Sajnos a belső WebRequest példány szintén nincs kivezetve a SOAP proxy osztályban, így nincs más lehetőségünk, mint a VS által generált proxy osztályból egy újabb osztályt származtatni, és ott felülírni azt a metódust, ami a WebRequest példány előállításáért felelős. Miután ezt így fejben levezette az ember, a kód már meglehetősen rövid:

class MyServiceWrapper : MyVSGeneratedServiceProxyClass
{
  public IPAddress SourceIPAddress { get; set; }

  protected override WebRequest GetWebRequest( Uri uri )
  {
    HttpWebRequest request = (HttpWebRequest) base.GetWebRequest( uri );
    request.ServicePoint.BindIPEndPointDelegate = new BindIPEndPoint(
      delegate( ServicePoint servicePoint, 
                IPEndPoint remoteEndPoint, 
                int retryCount )
      {
        return new IPEndPoint( this.SourceIPAddress, 0 );
      });
    return request;
  }
}

Persze akinek nem jön be ez a tömör névtelen metódusos megoldás, az írhat olvashatóbb kódot két metódusba:

protected override WebRequest GetWebRequest( Uri uri )
{
  HttpWebRequest request = (HttpWebRequest) base.GetWebRequest( uri );
  request.ServicePoint.BindIPEndPointDelegate = 
    new BindIPEndPoint( this.OnBindIPEndPoint );
  return request;
}


private IPEndPoint OnBindIPEndPoint( ServicePoint servicePoint, 
                                     IPEndPoint remoteEndPoint,
                                     int retryCount )
{
  return new IPEndPoint( this.SourceIPAddress, 0 );
}

Végül már csak annyi dolgunk maradt, hogy a Studio által generált osztály helyett a sajátunkat példányosítjuk és beállítjuk a SourceIPAddress tulajdonságot a kívánt IP címre.

Technorati-címkék:

Foglaltság lekérdezése az Exchange-ből

Az Exchange Web Services (EWS) segítségével tetszőleges klienssel kapcsolódhatunk az Exchange Serverhez és lekérdezhetjük a felhasználóink naptárát.

Mivel szabványos SOAP-os webszolgáltatásról van szó, az első lépés természetesen a WSDL fájl elérése, ami itt található:

http://SERVER/EWS/services.wsdl

Erre adjunk referenciát Visual Studioban (a SERVER helyére beírva az Exchange Serverünk FQDN-jét) és közben figyeljünk a http/https alkalmazására. Ha ez megvan, egy ExchangeServerBinding objektumon keresztül kapcsolódhatunk a szolgáltatáshoz:

  ExchangeServiceBinding binding = new ExchangeServiceBinding();
  binding.Credentials = CredentialCache.DefaultCredentials;
  binding.Url = @"http://SERVER/EWS/exchange.asmx";

Fontos, hogy mindenképpen hitelesíteni kell magunkat, alapértelmezés szerint anonymous felhasználó nem fér hozzá a webszolgáltatáshoz. A foglaltság lekérdezéséhez számos paramétert át kell adnunk a kérésben, például azt, hogy a cél felhasználó naptárának mely időszakára vagyunk kíváncsiak. Szerintem a legegyszerűbb egy hétfőtől péntekig tartó időszakot megadni:

  Duration duration = new Duration();
  duration.StartTime = this._firstDay;    // E hét hétfője
  duration.EndTime = this._firstDay.AddDays( 5 );

Ezen a webszolgáltatáson keresztül többféle formátumban is lekérdezhetjük a felhasználó naptárát. Nekünk most arra van szükségünk, hogy az előbb megadott időszakban, fél órás bontásokban (interval), mikor szabad vagy mikor foglalt az illető:

  FreeBusyViewOptionsType options = new FreeBusyViewOptionsType();
  options.TimeWindow = duration;
  options.RequestedView = FreeBusyViewType.FreeBusyMerged;
  options.RequestedViewSpecified = true;
  options.MergedFreeBusyIntervalInMinutes = 30;
  options.MergedFreeBusyIntervalInMinutesSpecified = true;

Meg kell adnunk azt is, hogy kinek a naptára érdekel minket (Free/Busy time Read jogosultsággal kell bírnunk a naptárához), itt több felhasználót is felsorolhatunk MailboxData objektumok formájában:

  MailboxData mailbox = new MailboxData();
  mailbox.AttendeeType = MeetingAttendeeType.Required;
  mailbox.Email = new EmailAddress();
  mailbox.Email.Address = "felhasználó@example.com";

A legfájdalmasabb rész a kérő időzónájához tartozó összes paraméter megadása. A Central European Standard Time zónához tartozó értékeket kibogarásztam a registry-ből, de persze akár változhat is:

  SerializableTimeZone timeZone = new SerializableTimeZone();
  timeZone.Bias = -60;
  timeZone.StandardTime = new SerializableTimeZoneTime();
  timeZone.StandardTime.Bias = 0;
  timeZone.StandardTime.DayOfWeek = "Sunday";
  timeZone.StandardTime.DayOrder = 5;
  timeZone.StandardTime.Month = 10;
  timeZone.StandardTime.Time = "03:00:00";
  timeZone.DaylightTime = new SerializableTimeZoneTime();
  timeZone.DaylightTime.Bias = -60;
  timeZone.DaylightTime.DayOfWeek = "Sunday";
  timeZone.DaylightTime.DayOrder = 5;
  timeZone.DaylightTime.Month = 3;
  timeZone.DaylightTime.Time = "00:00:00";

Ha ez mind megvan, összerakhatjuk őket egyetlen objektumba:

  GetUserAvailabilityRequestType request = new GetUserAvailabilityRequestType();
  request.TimeZone = timeZone;
  request.FreeBusyViewOptions = options;
  request.MailboxDataArray = new MailboxData[] { mailbox };

Végül meghívhatjuk a webszolgáltatás GetUserAvailabilty függvényét:

  GetUserAvailabilityResponseType response = binding.GetUserAvailability( request );

A válaszban többféle formában is megtaláljuk, hogy mikor foglalt és mikor szabad az illető. Egy szabad/foglalt naptár rajzolásához talán a legegyszerűbb, ha a response.FreeBusyResponseArray[ 0 ].FreeBusyView.MergedFreeBusy stringet vizsgáljuk: ez pontosan annyi karakterből áll, ahány intervallumot kértünk a szervertől és minden esetben 0 jelzi, ha még szabad az az intervallum. Az egyes értékek előbb egy napon belül mennek végig az intervallumokon, majd utána jön a következő nap.