Category Archives: Webfejlesztés

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: ,,

WebAPI dokumentáció kibővítése

Írtam már arról, hogy a Microsoft.AspNet.WebApi.HelpPage NuGet csomaggal milyen könnyen generálhatunk dokumentációt a WebAPI-nkhoz. Mint minden készen kapott megoldással, ezzel is akkor kezdődnek a nehézségek, amikor az ember olyasmit szeretne, amire helyből nincs felkészítve. Szerencsére ebben az esetben a teljes forráskód rendelkezésre áll, így viszonylag egyszerűen testreszabhatjuk a működést és a megjelenést.

Példaként tegyük fel, hogy az API-nk úgy épül fel, hogy minden action hiba esetén a hozzá tartozó enum valamelyik elemét adja vissza hibakódnak a hozzá tartozó hibaüzenettel együtt, és a feladat az, hogy ezek a hibakódok megjelenjenek a dokumentációnkban.

Egy saját attribútummal – amit itt ErrorCodeType-nak hívtam, –  könnyen hozzákapcsolhatjuk a hibakódokat tartalmazó enumot (itt MyErrorCodeEnum) az actionhöz:

[ErrorCodeType(typeof(MyErrorCodeEnum))]

Ezt az információt a következő lépésekkel tehetjük bele a dokumentációba:

1. A Models mappába hozzunk létre egy új osztályt, ami majd egy konkrét hibaág leírására lesz alkalmas, hívjuk ezt ErrorCodeDescription-nek:

public class ErrorCodeDescription
{
  public int Code { get; set; }
  public string Message { get; set; }
}

 

2. A HelpPageApiModel.cs fájl írja le azt a view-modellt, ami alapján a dokumentáció oldal megjelenik. Ebbe az osztályba vegyünk fel egy új tulajdonságot, ami az összes hibalehetőséget fogja tartalmazni egy listában:

public List<ErrorCodeDescription> ErrorResponses { get; set; }

 

3. A HelpPageConfigurationExtensions.cs fájlban található az a GenerateApiModel metódus, ami felépíti a HelpPageApiModel típusú apiModel nevű változóban található view-modellt. Itt a meglévőek mintájára készíthetünk egy saját metódust, ami kiolvassa a csatolt attribútumot, és az alapján felépíti az ErrorResponses listát, például így:

private static void GenerateErrorResponses(HelpPageApiModel apiModel)
{
  ErrorCodeTypeAttribute attribute = apiModel.ApiDescription.ActionDescriptor
    .GetCustomAttributes<ErrorCodeTypeAttribute>().FirstOrDefault();
  Type enumType = attribute.ErrorCodeEnumType;
  string[] names = Enum.GetNames(attribute.ErrorCodeEnumType);

  foreach (string name in names)
  {
    apiModel.ErrorResponses.Add(new ErrorCodeDescription
    {
      Code = (int) Enum.Parse(enumType, name),
      Message = "TODO"
    });
  }
}

 

4. A modell kirenderelését a Views/Help/DisplayTemplates/HelpPageApiModel.cshtml nézet végzi. A meglévő kódok mintájára ide felvehetjük az alábbi blokkot, ami az ErrorResponses lista megjelenítését az ErrorResponses partial view-ra bízza:

@if (Model.ErrorResponses.Any()) 
{ 
  <h3>Error Responses</h3> 
  @Html.DisplayFor(m => m.ErrorResponses, "ErrorResponses") 
}

 

5. Természetesen létre kell hoznunk ugyanebben a mappában egy ErrorResponses.cshtml fájlt, ami @model-ként egy List<ErrorCodeDescription> példányt fog kapni, amit utána úgy jelenítünk meg a weboldalon, ahogy csak szeretnénk.

 

Ez elsőre sok lépésnek tűnik, de valójában egészen logikus, érdemes megbarátkozni vele.

 

Technorati-címkék: ,

WebAPI dokumentáció generálása több szerelvényből

A Microsoft.AspNet.WebApi.HelpPage NuGet csomag segítségével nagyon könnyen generálhatunk egészen barátságos dokumentációt a WebAPI-nkhoz. A megoldás alapja, hogy a WebAPI projekt kommentjeiből a C# fordítót felhasználva XML fájlt készítünk, amit aztán egy MVC controller felolvas, és barátságos HTML oldalakká fordít.

A problémák akkor jönnek, ha a a REST API publikus interfészén olyan típusok találhatóak, amik nem a WebAPI projektben vannak, hanem egy másik DLL-ben, ugyanis a másik DLL – tipikusan library – kommentjeiből generált XML fájl ugyan bekerül az API bin mappájába, de a webdeploy már nem viszi magával, tehát futási időben nem lesz ott a megfelelő helyen.

A probléma két lépésben orvosolható:

  1. A library XML fájljának bele kell kerülnie a webdeploy csomagba.
  2. A controllert át kell írni, hogy több XML fájllal is működjön.

 

A webdeploy csomag kiegészítése

A webdeploy csomag úgy készül, hogy fordítás után összemásolódik az összes fájl egy mappába, majd ennek a mappának a tartalmából áll össze a csomag. Mivel ezt a folyamatot az MSBuild vezérli, kis trükkel újabb fájlt is be tudunk tenni ebbe az ideiglenes mappába. Mindössze a WebAPI projekthez tartozó .csproj fájlt kell kiegészítenünk:

<Target Name="CopyAdditionalFiles">
  <Message Text="Copy additional files..." Importance="high"/>

  <ItemGroup>
    <_CustomFiles Include="$(ProjectDir)bin\MyLib.xml" />
    <FilesForPackagingFromProject Include="%(_CustomFiles.Identity)">
      <DestinationRelativePath>bin\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
    </FilesForPackagingFromProject>
  </ItemGroup>
</Target>

<PropertyGroup>
  <CopyAllFilesToSingleFolderForMsdeployDependsOn>
    CopyAdditionalFiles;
    $(CopyAllFilesToSingleFolderForMsdeployDependsOn);
  </CopyAllFilesToSingleFolderForMsdeployDependsOn>
</PropertyGroup>

A fenti módosítás Visual Studio 2012-től kezdve működik, a korábbi verziókhoz képest ugyanis átnevezték a CopyAllFilesToSingleFolderForMsdeployDependsOn elemet.

 

Dokumentáció generálása több XML fájlból

A fent említett NuGet csomag egyetlen XML fájlból tud dokumentációt előállítani. Ha azt szeretnénk, hogy több fájllal is megbirkózzon, akkor módosítanunk kell az XmlDocumentationProvider osztály konstruktorát:

public XmlDocumentationProvider(IEnumerable<string> documentPaths)
{
  if (documentPaths == null)
  {
    throw new ArgumentNullException("documentPaths");
  }

  XDocument finalDoc = null;
  foreach (string documentPath in documentPaths)
  {
    if (finalDoc == null)
    {
      finalDoc = XDocument.Load(File.OpenRead(documentPath));
    }
    else
    {
      XDocument additionalDoc = XDocument.Load(File.OpenRead(documentPath));
      finalDoc.Root.XPathSelectElement("/doc/members")
        .Add(additionalDoc.Root.XPathSelectElement("/doc/members").Elements());
    }
  }

  _documentNavigator = finalDoc.CreateNavigator();
}

A módosítás eredményeként a konstruktornak már több fájl útvonalát kell átadni a korábbi egy helyett. Az elsőt ugyanúgy fogja betölteni, mint korábban, a többit pedig utána fogja fűzni a memóriában. A végeredmény egyetlen nagy XML dokumentum, így a NuGet csomag többi részét nem is kell módosítanunk.

Természetesen a HelpPageConfig.cs-ben már tömb paraméterrel kell meghívnunk a konstruktort, például:

string[] documentPaths = 
{
  HttpContext.Current.Server.MapPath("~/bin/MyAPI.xml"),
  HttpContext.Current.Server.MapPath("~/bin/MyLib.xml")
};
config.SetDocumentationProvider(new XmlDocumentationProvider(documentPaths));

 

Technorati-címkék: ,,

GUID definiálása XSD-ben

XML dokumentumokban gyakran előfordul, hogy egy elem vagy attribútum GUID értéket tartalmaz. Mivel egy XML a hozzá tartozó XSD nélkül fabatkát sem ér, nem ritka feladat, hogy XSD-ben elő kellene írnunk, hogy az adott érték csak GUID lehet. Az XSD sokféle beépített típust támogat, de sajnos GUID nincs, így marad a klasszikus regexes megoldás:

<xs:schema ...>

  <xs:simpleType name="guid">
    <xs:restriction base="xs:string">
      <xs:pattern value="[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-
[0-9a-fA-F]{4}-[0-9a-fA-F]{12}" />
    </xs:restriction>
  </xs:simpleType>

Ha ez megvan, akkor pontosan úgy hivatkozhatunk a saját guid típusunkra, mint a beépítettekre:

<xs:attribute name="id" type="guid" use="required" />

Érdemes megfigyelni, hogy a regex elején és végén nincs $ és ^ jel, ugyanis a minta mindig a teljes értékre vonatkozik.

 

Technorati-címkék: ,

HTTPS: rendesen vagy sehogy

Volt szerencsém Bolognába látogatni nyáron, és örömmel láttam, hogy a repülőtéren ingyenes wifi szolgáltatás van. Lehet, hogy már ekkor gyanakodnom kellett volna, de igazából akkor kezdett a dolog furcsa lenni, amikor a felkapcsolódás után ez az oldal fogadott a böngészőben:

airport-ssl-warning

A hibaüzenet szerint az oldalhoz tartozó SSL tanúsítvány “kicsit” hibás:

  • Nem megbízható a kiállítója.
  • Lejárt az érvényessége.
  • Másik weboldalra szól.

Valójában csak akkor lehetne hibásabb, ha már vissza is vonták volna a tanúsítványt.

Aki mégis továbblép, az ezzel az oldallal találkozhat a bolognai reptéren:

airport-webpage

Igazán eredeti dizájn. Oké, Bologna nem egy metropolisz, de a helyi egyetemen simán találhattak volna egy unatkozó diákot, aki egy hétvége alatt pofásabb weboldalt tud összedobni.

Ekkor már kíváncsi lettem és kis kísérletezéssel hamar kiderült, hogy a weboldalnak semmi köze ahhoz, hogy működik-e az internet hozzáférés, vagy sem.

Sok már itt a gyanús tényező:

  • Tanúsítvány
  • IP cím
  • Dizájn
  • Telefonszám gyűjtögetés

Ez volt az a pillanat, amikor konkrétan felálltam és körülnéztem, hátha ott ül valahol Troy Hunt az ő Pineapple-jével.

Egy rendes SSL tanúsítvány ezeket a kétségeimet könnyen eloszlathatta volna. De az ilyen, érvénytelen tanúsítvány semmire nem jó, mindössze annyit garantál, hogy az átlagfelhasználót idegesíteni fogja az oldal, a szakértőbb felhasználók pedig aggódni fognak az adataik biztonsága miatt. Aki HTTPS-re adja a fejét, csinálja rendesen, különben csak az ellenkezőjét éri el vele.

 

Technorati-címkék: ,

JSON vagy nem JSON, ez itt a kérdés

Amikor egy REST API-hoz készítünk unit tesztet könnyen előfordulhat, hogy meg kell vizsgálnunk, hogy a szervertől kapott válasz olyan formában van-e, mint amiben kértük. Például egy stringről el kell döntenünk, vagy korrekt JSON-t tartalmaz-e.

Sok olyan megoldást lehet találni a neten, ami azt mondja, hogy elég, ha ellenőrizzük, hogy a kapott string < vagy { karakterrel kezdődik-e, hiszen a JavaScript Object Notation mindenképpen objektumokról vagy tömbökről szól. Ezzel a módszerrel nem csak az a baj, hogy egyetlen karakterből aligha tudjuk megmondani, hogy érvényes JSON stringről van-e szó, hanem az is, hogy érvényes lehet az a string is, ami nem tömböt vagy objektumot, hanem csak egy egyszerű értéket tartalmaz. Ami pedig a json.org szerint lehet:

json-value

A fapados karakter vizsgálgatás helyett szükségünk lenne egy korrektebb IsValidJson függvényre. Sajnos ilyet sehol nem találtam készen, de a Newtonsoft JSON.NET könyvtárral sikerült így megoldani a problémát:

try
{
    JToken.Parse(input);
    return true;
}
catch (JsonReaderException)
{
    return false;
}

Van jobb ötlete valakinek?

 

Technorati-címkék: ,,

Mixed content warning

Szomorú, amikor így széthullik egy oldal a böngészőben, például Chrome-ban:

mixed content chrome

Hogy miért? Hát nem nyilvánvaló, ott van a magyarázat. Segítek:

mixed content chrome warning small

Úgy hívják, hogy mixed content warning, és warning, azaz figyelmeztetés létére nem éppen feltűnő. Lássuk ugyanezt Firefoxban:

mixed content FF

Megvan?

mixed content FF blocked small

Az Internet Explorer kevésbé finomkodik, azonnal a felhívja a felhasználó figyelmét:

mixed content warning

Bár itt nem egy pajzs ikont kell keresgélnünk (amit egyébként túl sokszor, túl sok mindenre használtak már), hanem rögtön kapunk egy szöveges üzenetet, semennyivel sem jobb a helyzet. Ezt az üzenetet, és úgy általában a problémát, ugyanis egy átlagos felhasználó nem érti. Sőt, nem csak hogy a felhasználók nem értik, de a fejlesztők sincsenek tisztában a biztonsági vonatkozásokkal, különben nem lennének ilyen problémás oldalak.

Pedig arra kellene csak figyelni, hogy ha az oldal https:// protokollon töltődik be, akkor az oldalra betöltődő összes (igen, az összes) tartalom is https-en jöjjön, ne legyen egyetlen http:// link sem az oldal kódjában. Ha külső domainről töltesz be tartalmat, és nem tudsz relatív URL-t használni, akkor kezdd az URL-t “//”-rel, és a böngésző azt a protokollt fogja használni, amit az oldal is használ. Ezt a hívják, hogy “protocol relative”, vagy “scheme relative” vagy “scheme-less relative” URL, és már az URI általános formáját leíró RFC 3986-ban is szerepel (2005. január), és természetesen a böngészők is értik.

Ideje lenne kijavítgatni az oldalainkat, a böngészőknek pedig előbb-utóbb teljesen blokkolni az ilyen defektes oldalakat.

 

Technorati-címkék: ,,