ASP.NET címkéhez tartozó bejegyzések

Ott a fájl, de mégis 404

Próbálnék betölteni egy ASPX fájlt, de sehogy nem akar sikerülni. Pontosabban IIS Expressen a forráskódot futtatva megy kiválóan, de IIS-re publisholva 404 lesz belőle.

Látszólag minden rendben van, ott van a fájl a szerveren, csak épp nem töltődik le. Bekapcsolom a Failed Request Tracinget, hátha látszik valami. Látszik bizony: 388 napló bejegyzés egyetlen HTTP kéréshez. Még szerencse, hogy van Request Summary nézet, ami rögtön kiemeli az egyetlen warningot:

MODULE_SET_RESPONSE_ERROR_STATUS

ModuleName: ManagedPipelineHandler

Notification: EXECUTE_REQUEST_HANDLER

HttpStatus: 404

HttpReason: Not Found

HttpSubStatus: 0

ErrorCode: The operation completed successfully. (0x0)

Önmagában nem éppen nagy segítség, de legalább kiderült belőle, hogy melyik modul a bűnös. Kis öröm. Kikeresem a bejegyzést a Complete Request Trace-ből és megnézem, mi van előtte. AspNetParse és AspNetCompile bejegyzések. Talán valami gond van az ASPX fájllal? Nem valószínű, hiszen IIS Expressben megy, ráadásul NuGet package-ből jött.

Azért csak megnézem a forráskódot. Rögtön az első sor gyanús: a @Page direktívában CodeFile szerepel. Szokatlan. Átírom CodeBehindra. Fordítok, telepítek.

Megjavul.

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

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

Microsoft Nyári Akadémia – .NET vNext

Kedden a Microsoft Nyári Akadémia programsorozat megnyitásaként a .NET és az ASP.NET következő verziójáról tartottam egy bevezető előadást. Az előadásnak a “.NOT, avagy .NET vNext” címet adtam, mert az új verzió számos gyökeres változást hoz, sok esetben teljesen átírva azt, amit a .NET-ről eddig tanultunk.

Ahhoz, hogy ezeknek a változásoknak az okait megértsük, mindenképp látnunk kell azt, hogy a Microsoft másfél évtizede rakta le a .NET platform alapjait, és ennyi idő alatt ez a szakma, különösen a webes világ, rengeteget változott. Kezdetben a cég ezekre a irányváltásokra pont olyan fürgén reagált, mint egy konténerszállító teherhajó, de azután a webes termékcsapat szépen magára talált, és a teherhajóból fürge romboló lett. A WebForms ás a .NET hiányosságait szépen sorban igyekeztek pótolni, megjelent a NuGet, az MVC, a WebPages és a WebAPI.

A végeredmény azonban nem egyetlen technológia lett, hanem egymáshoz nagyon hasonló, egymással rokon programozási keretrendszerek összessége. És bár hívhatjuk “One ASP.NET”-nek, amíg az MVC és a WebAPI más filtereket használ, vagy az MVC-ben és a WebPages-ben más a HTML helperek implementációja, addig ez az elnevezés sántít.

A webes termékcsapat nagyon jól ráérzett, hogy itt egy komolyabb vérfrissítésre van szükség. És ha már újraírják az MVC-t úgy, hogy magába olvassza a WebAPI-t és a WebPagest is, akkor át lehet gondolni az ASP.NET-nek azokat a részeit is, amik valóban közösek. Elég csak a mindenhol jelenlevő HttpContext objektumra, a System.Web.dll-re, vagy a teljes végrehajtási csővezetékre gondolni. Sőt mi több, ha figyelembe vesszük az új fordító (Roslyn) és új JIT fordító (RyuJIT) képességeit, akkor fel kell hogy merüljön a BCL és a CLR cseréjének kérdése is.

A Microsoft nem kisebb dologra vállalkozott, mint hogy a .NET következő verziójára refaktorálja a BCL-t, kicseréli a runtime-ot, átdefiniálja az egységnyi kód fogalmát, elpusztítja a GAC-ot, újraírja az ASP.NET platformot, és hozzá természetesen a teljes eszközparkot. Mindezt kezdettől fogva cross-platform módon, a legmodernebb tervezési mintákat követve, a felhőre optimalizálva és a modern kor elvárásainak megfelelően nyílt forráskódú módon. És itt most nem arra a “klasszikus Microsoftos” nyílt forráskódra kell gondolni, amikor a rendszer megjelenése után sok hónappal kiteszik a CodePlexre a kipucolt forrásfájlokat, hanem arra az igazi Githubos nyílt forráskódú világra, amiben mindenki küldhet pull requesteket.

Ezekről beszélgettünk a Nyári Akadémián. Akit érdekel a diasor (és benne a demók), megtalálja a Slideshare-en:

 

A teljes platform fejlesztése egyelőre nagyon korai fázisban van, és természetesen még nagyon sok változásra lehet számítani. Aki mégis szívesen ismerkedne vele, itt kezdhet hozzá:

 

Technorati-címkék: ,,,

MVC nézetek fordítása hibamentesen

A Visual Studio és az ASP.NET alapértelmezés szerint nem fordítási időben, hanem futási időben dolgozza fel a MVC nézetekben lévő kódot. Ennek természetesen az az eredménye, hogy ha ott valamilyen hibát vétünk, akkor az csak futási időben fog kiderülni.

Szerencsére van arra lehetőség, hogy rávegyük a Studiot, fordítsa le a nézeteket is a build folyamat részeként. Ehhez nyissuk meg az MVC projekthez tartozó projekt fájlt (.csproj), és írjuk át az MvcBuildViews elem értékét false-ról true-ra:

<MvcBuildViews>true</MvcBuildViews>

Ez meg is oldja a kezdeti problémát, ám gyakran bevezet egy újabbat. Időnként fordításkor vagy a webalkalmazás publikálásakor az alábbi hibaüzenetet kaphatjuk:

It is an error to use a section registered as allowDefinition=’MachineToApplication’ beyond application level.  This error can be caused by a virtual directory not being configured as an application in IIS.

A legborzasztóbb ebben az üzenetben, hogy nem csak az nem derül ki belőle, hogy hol a hiba, hanem az sem, hogy hogyan lehet kijavítani.

A megoldás az obj mappa törlése fordítás előtt, és a hibaüzenet máris megszűnik. Gondoltad volna a hibaüzenet alapján?

Aki nem szeretné mindig kézzel törölgetni ezt a mappát, az persze megteheti a build folyamat első lépéseként is. Szerencsére az MSBuild ad nekünk egy BaseIntermediateOutputPath nevű változót, ami pont az obj mappára mutat, sőt még egy RemoveDir nevű task is van hozzá, így már csak össze kell pakolni őket. Megint nyissuk meg a .csproj fájlt, és vegyünk fel egy RemoveDir elemet a BeforeBuild target-hez:

<Target Name="BeforeBuild">
    <RemoveDir Directories="$(BaseIntermediateOutputPath)" />
</Target>

Akinek esetleg a bin mappával van személyes nézeteltérése, az esetleg használhatja a BaseOutputPath változót.

 

Technorati-címkék: ,,

Átirányítás a bejelentkező oldalra Ajaxnál

A hitelesítés korrekt megvalósításának egyik legproblémásabb része annak megoldása, hogy mi történjen akkor, ha az alkalmazáshoz a szükséges engedélyek nélküli kérések érkeznek. Ez így nagyon bénán hangzik, úgyhogy íme egy példa: a /admin URL-re bejelentkezés nélkül nem érkezhetnek kérések, sőt még azokat a kéréseket is el kell utasítani, amik bejelentkezett felhasználóhoz tartoznak, de a felhasználó nincs az Admins csoportban.

ASP.NET esetén szerencsére a FormsAuthenticationModule modul gondoskodik a probléma megoldásáról. Az OnLeave eseménykezelővel feliratkozik az EndRequest eseményre, és ha a HTTP státusz kód 401, akkor átirányítja a felhasználót a bejelentkező oldalra. Ez egy kiváló szolgáltatás hagyományos kérések esetén, de kifejezetten kellemetlen Ajaxnál.

Az átirányítás hatására ugyanis a kliens nem kapja meg a 401 Unauthorized hibát, hanem egy 302 Redirect fejléc fog visszajönni a szervertől, amit az XMLHttpRequest kliens oldalon az előírásnak megfelelően transzparens módon követ, azaz beküld egy kérést a Location fejlécben megadott útvonalra. Ez az útvonal tipikusan a Login.aspx oldalra mutat, aminek a szerver visszaküldi a HTML kódját, és ezt a HTML kódot fogja megkapni az XHR az Ajax hívás eredményeként 200 OK státusz kóddal. Ember legyen a talpán, aki ezt a helyzetet kliens oldalon értelmesen kezelni tudja.

Sajnos .NET 4.0-ig nem volt más megoldás a probléma kezelésére, mint egy saját HTTP modul beillesztése az ASP.NET csővezetékbe. Az ASP.NET 4.5 azonban bevezetett egy új lehetőséget a HttpResponse.SuppressFormsAuthenticationRedirect tulajdonság formájában, amit true értékre állítva elkerülhető az átirányítás, és helyette az eredeti 401-es hibakódot fogja megkapni a kliens. Mivel ez a tulajdonság a Response része, ezért nem tudjuk globálisan beállítani, hanem minden olyan kérésnél be kell billenteni, ahol el akarjuk kerülni az átirányítást. Ha esetleg minden kérésnél így akarunk eljárni, akkor tehetjük ezt a tulajdonság állítást a global.asax fájlban lévő Application_EndRequest eseménykezelőbe.

Miután a kliens megkapja az egyértelmű hibakódot, JavaScriptből már úgy járhatunk el, ahogy az igényeink megkívánják, például feldobhatunk egy hibaüzenetet vagy egy bejelentkező ablakot. De ez a kód már eddig is megvolt, nem igaz?

 

Technorati-címkék: ,,

Mennyire egyedi a machine key?

Az ASP.NET platform számos kriptográfiai funkciója épül a machine key-re, épp ezért különösen fontos, hogy az egymástól független alkalmazásokhoz egyedi machine key tartozzon. Szerencsére az alapbeállítás szerint látszólag minden alkalmazás egyedi kulcsot kap:

<machineKey validationKey="AutoGenerate,IsolateApps" 
decryptionKey="AutoGenerate,IsolateApps" />

Az AutoGenerate beállítás hatására a platform automatikusan generál kulcsot, az IsolateApps-nak köszönhetően pedig elvileg ezek a kulcsok alkalmazásonként egyediek lesznek.

De nem mindig!

Az ASP.NET valóban generál automatikusan kulcsot, csakhogy nem a validation key-t és a decryption key-t, hanem egy ős kulcsot (hívhatjuk ezt általánosan machine key-nek), amiből azután transzformációkkal áll elő a két kulcs.

A machine key-t  platform a registry-ben tárolja, a következő útvonalon:

HKCU\Software\Microsoft\ASP.NET\4.0.30319.0\AutoGenKeyV4

Érdemes észrevenni, hogy a tárolás a HKEY_CURRENT_USER hive-ban történik, azaz a generált machine key az adott felhasználó profiljához fog tartozni! Ebből logikusan következik, hogy ha az IIS-ben két alkalmazás ugyanannak a felhasználónak a nevében fut, akkor azonos machine key-t fognak használni! Íme egy újabb ok, hogy miért kell elfelejteni a SYSTEM, LOCAL SERVICE és NETWORK SERVICE felhasználókat, és miért kell helyettük önálló ApplicationPoolIdentity-vel futtatni minden egyes alkalmazást. Ebből is látszik, hogy az application pool az alkalmazások izolációjának eszköze.

Az, hogy két alkalmazásnak ugyanaz az ős kulcsa, még nem feltétlenül jelent problémát, hiszen tudjuk, hogy a két valóban használt kulcs transzformációkkal áll elő. Ezt a transzformációt határozza meg az AutoGenerate utáni módosító. Az IsolateApps hatására a transzformációba bekerül a HttpRuntime.AppDomainAppVirtualPath tulajdonság értéke, ami egy website-on belül, két különböző virtuális mappában ülő alkalmazás esetén eltérő lesz, így a két generált kulcs is el fog térni (pont ez a cél).

Ám nem fog eltérni abban az esetben, ha a két alkalmazás ugyanazon az útvonalon van, de eltérő webhelyeken. Például a gyökérben ülő alkalmazásnál ennek a tulajdonságnak az értéke mindkét esetben “/” lesz, azaz az IsolateApps egyáltalán nem teszi meg azt, amit várunk tőle!

Ilyen esetekre került az ASP.NET 4.5 be az IsolateByAppId módosító, ami nem a HttpRuntime.AppDomainAppVirtualPath, hanem AppDomainAppId tulajdonság felhasználásával készíti a két kulcsot. Ennek az értéke valami ilyesmi: 

/LM/W3SVC/3/ROOT 

A közepén a “3” az én esetemben a website azonosítója az IIS-ben, a “ROOT” pedig azt jelenti, hogy a site gyökerében van az alkalmazás.

Összegezve tehát: az alap AutoGenerate,IsolateApps beállítás nem minden esetben eredményez egyedi kulcsot, de ha az alkalmazásokat saját application poolba tesszük és azokat ApplicationPooldentity-vel futtatjuk, valamint az IsolateApps helyett az IsolateByAppId módosítót használjuk, akkor már nem biztosan egyedi kulcsokat fognak kapni az alkalmazásaink.

Ha nem hiszed, legegyszerűbben úgy járhatsz utána, hogy a localtest.me domain segítségével létrehozol két webhelyet az IIS-ben, majd azokon létrehozol két alkalmazást, ami például Milan Negovan kódja segítségével kiírja a kulcsokat.

 

Technorati-címkék: ,,

Több website-ot tartalmazó solution publikálása Azure-ra

Az Azure Websites egyik kiváló szolgáltatása, hogy közvetlenül hozzá lehet kapcsolni forráskód kezelő rendszerhez, így amikor a forráskód frissül, az automatikusan azonnal kikerülhet a felhőbe.

Ezt pofonegyszerű összevarázsolni, csak indítsuk el a New –> Compute –> Web Site –> Custom create varázslót:

multisite-new

Az első lépésben adjunk nevet a webhelynek, és pipáljuk be a Publish from source control jelölőnégyzetet:

multisite-name

Ennek hatására megjelenik a varázsló második lépése, ahol kiválaszthatjuk a forráskódkezelő rendszerünket, például GitHubot:

multisite-where

GitHub esetén egy OAuthos authentikáció következik, majd meg kell adnunk, hogy melyik repository-nak melyik ágát (már nem csak mastert lehet!) kívánjuk erre a webhelyre publikálni:

multisite-repo

A pipára kattintva meg is történik a varázslat, pár másodperc alatt létrejön a webhelyünk és már települ is rá az alkalmazás.

De mi van akkor, ha a forráskód olyan Visual Studio solutiont tartalmaz, amiben több webes projekt vagy több website van, melyik fog települni? Az első.

Azonban mi nem mindig ezt szeretnénk.

Ha az Azure website-ra mindig a solution egyik projektjét kívánjuk telepíteni, akkor létrehozhatunk a repo gyökerében egy .deployment nevű INI fájlt, amiben megadhatjuk a telepítendő projektet:

[config]
project = MySolution/MyWebProject.csproj

Előfordulhat azonban, hogy több Azure website-unk van, és az egyikre a solution egyik webes projektjét, a másikra a másik webes projektjét kívánjuk telepíteni. Ebben az esetben a beállítást nem drótozhatjuk bele a forráskódba, hanem a felhőben kell trükköznünk. Látogassunk el a CONFIGURE oldalon belül az app settings szekcióba, ahol vegyünk fel egy új beállítást Project néven:

multisite-appsettings

Az értékként adjuk meg a .csproj fájlra vagy az ASP.NET website mappájára mutató repo-gyökér relatív elérési utat.

Mentés után a következő deploymentnél már ennek figyelembe vételével fog történni a telepítés.

 

Technorati-címkék: ,,,