Dziś wpis o tym jak w kontrolowany sposób popsuć ASP.NET tak by osiągnąć pewne cele, które generalnie byłyby nieosiągalne stosując się do zaleceń Microsoftu odnośnie wykorzystania ich własnej platformy. Będzie słów kilka o workaroundach do różnych ograniczeń z przykładowym wykorzystaniem.

Wszyscy na pewno wiedzą o tym, że ASP.NET pozwala na wygenerowanie tylko i wyłącznie jednego taga typu form na stronie internetowej, co ma swoje wyjaśnienie w samej logice działania serwera ASP.NET, postbackami, view statem, itd. Próba obejścia tego w normalny sposób poskutkuje wygenerowaniem odpowiedniego błędu i zakończeniem procesu renderowania strony. Jednakowoż jest możliwe stworzenie strony z wieloma formularzami i to ponadto w taki sposób by całość działała w sposób przewidywalny i stabilny. I dziś będzie właśnie o tym workaroundzie i wszystkim co należy wiedzieć, zanim zechce się skorzystać z tej “profanacji” (dla ludzi z Microsoftu to prawdopodobnie jedyne określenie jakie by zastosowali wobec takiego pomysłu).

Po co takie obejście i na czym polega?

To pytanie jest całkiem zasadne i oto kilka argumentów, które w naszym projekcie były względnie kluczowe:

  • chęć ograniczenia wielkości strony
  • chęć zminimalizowania czasu ładowania strony
  • podział logiki strony na części

Na ogół są to ostatnie aspekty jakie będą rozważane przy tworzeniu strony internetowej, jednak czasami bywają kluczowe. W szczególności gdy tworzony jest projekt strony dostępnej dla szerokiego grona, który bez optymalizacji na dzień dobry generuje 600KB samego HTML.

W takiej sytuacji najlepiej sprawuje się podział strony (w szczególności gdy są ku temu warunki, tj. obecność tabów i zawartości widocznej dopiero po jakiejś akcji użytkownika) na mniejsze strony i ładowanie “dynamicznej” zawartości poprzez żądania AJAX-owe. Idealnie w tym celu nada się funkcja z jQuery:

$("#content-div").load("SomePage.aspx");

albo wykorzystanie kontrolki TabStrip z kolekcji jQuery UI i skonfigurowanie jej tak by używała żądań AJAX-owych.

Każda z podstron może być albo statyczna albo dynamiczna (czyli mogą to być zwykłe strony ASP.NET). W przypadku stron ASP.NET zachowujemy całą funkcjonalność tej technologii, tylko należy pamiętać o tym by ładować czystą stronę bez żadnych Master page (pierwszym tagiem w dynamicznie ładowanym dokumencie powinien być form).

Rezultaty

A więc mamy w rezultacie wygenerowaną strukturę podobną do tej zamieszczonej na obrazku powyżej. Czerwonym obramowaniem wyróżnione są formularze. A wszystkie przyciski z założenia mają jakiś postback i przypisane zdarzenie na kliknięcie. Opiszę teraz co ASP.NET jak się zachowa mając do obsłużenia żądania z takiej stronki.

Po pierwsze: formularze w tabach (czyli te ładowane dynamicznie) będą działały bez zarzutu.

Po drugie: zdarzenia umiejscowione na głównym formularzu nie będą w ogóle działać. A raczej będą, tylko spowodują wywołanie zdarzenia na pierwszym załadowanym dynamicznie formularzu (czyli tym z pierwszego taba). Czemu – trudno powiedzieć ;]

Po trzecie: Wyróżniony na niebiesko obszar z zawartością zawierającą się w głównym formularzu, ale wygenerowany w sposób nie-dynamiczny oraz po zawartości dynamicznej będzie działał tylko i wyłącznie jeśli nie używamy przeglądarki Internet Explorer. To chyba największa niespodzianka w całym tym rozwiązaniu. Każda normalna przeglądarka zachowuje się w sposób powiedzmy przewidywalny w tym momencie, za to jeśli chcielibyśmy uruchomić zdarzenie podpięte pod Button 3 w IE (w każdej wersji), to dostaniemy błąd ViewStateException z mało pomocną treścią:

Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that <machineKey> configuration specifies the same validationKey and validation algorithm. AutoGenerate cannot be used in a cluster.  

Z bliżej nieokreślonego powodu Internet Explorer przy każdym submicie scala wszystkie wartości ViewState występujące na stronie i taki łączny ViewState wysyła do serwera, co z naturalnych powodów się nie powiedzie (m.in. dlatego, że zostały scalone przecinkiem, a tego serwer nie będzie w stanie zdekodować przy pomocy standardowego Base64). Ot, taka ciekawostka ;]

Co dalej?

Jedyne co musimy tak na prawdę zrobić, to niebieską część formularza zamienić na nowy formularz ładowany dynamicznie podobnie jak poprzednie kontrolki w tabach (przynajmniej tak było w moim przypadku i było najłatwiejszym rozwiązaniem). Za to jeśli chodzi o ten przycisk w nagłówku strony, to tutaj będzie najbardziej problematyczne miejsce. Nie zawsze da się bowiem zamienić tę problematyczną część kodu html zawartością ładowaną przez AJAX. W moim przypadku (w którym też do poprawienia były tylko dwa ImageButtony) zamieniłem je na zwykłe linki prowadzące do konkretnej strony z odpowiednimi parametrami w QueryStringu. Zamiast wykonywać funkcjonalność w funkcji, ta funkcjonalność została przeniesiona do PageLoada na tej stronce – i to w sumie wszystko.

Należy więc zdecydowanie uważać na sytuację, gdy mamy całkiem dużo zdarzeń na masterze, które są także rozproszone i trudno przenoszalne w inne miejsce.

Wyjątkowo dziś bez żadnych referencji, bo i żadnych w sumie nie było :)

A tak jeszcze słowo na niedzielę: nie używajcie ASP.NET jako podstawy do wykonania front-endu dla tworzonego portalu. To praktycznie w każdym aspekcie jest zły pomysł ;]