BETA
Aby się zalogować, najpiew wybierz portal.
Aby się zarejestrować, najpiew wybierz portal.
Podaj słowa kluczowe
Słowa kluczowe muszą mieć co najmniej 3 sąsiadujące znaki alfanumeryczne
Pole zawiera niedozwolone znaki

Baza wiedzy











Przyjazne adresy URL w ASP.NET

25-01-2005 14:22 | User 79341
Artykuł o tym, jak skonstruować moduł http służący do obsługi przyjaznych użytkownikowi oraz łatwych do indeksowania przez wyszukiwarki adresów URL.

Główna idea

Często chcąc podać znajomej osobie, mniej doświadczonej w technologiach internetowych, adres do strony określonego produktu w serwisie producenta mamy niemały dylemat – jest on skomplikowany i nieczytelny. Przykłady mamy dostępne na każdym kroku – chociażby „http://www.nazwafirmy.com/consumer/catalog/catalog.jsp?fhquery=fh_secondid%3Dhr8040_01_pl_consumer%26fh_location%3D%2F%2Fconsumer%2Fpl_PL%2Fcategories%3Ccatalog_pl_consumer%2Fcategories%3Chousehold_products_gr_pl_consumer%2Fcategories%3Chop_householdaccessories_ca_pl_consumer&productId=HR8040_01_PL_CONSUMER&active%2FCategory=HOUSEHOLD_PRODUCTS_GR_PL_CONSUMER&fredhopperpage=detail.jsp&catalog%2FType=CONSUMER” czy też „http://www.adreswortalu.pl/Default.aspx?Page=Forum/GroupList”.

Wiadomo – użytkownik przeglądający stronę rzadko spogląda w pasek adresowy, gdyż na co dzień nie potrzebuje znajomości konkretnej strony. Ale jak już spojrzy i postanowi wysłać komuś ten adres, czy też do zapamiętać, to ma nie lada problem – czy jeżeli go nada jakimkolwiek kanałem komunikacji, to czy czytnik odbiorcy go nie zniekształci (na przykład poprzez „złamanie” czyli podział na dwie osobne linijki gdyż nie zmieścił się w jednej, czy też poprzez ucięcie parametru w skutek nie prawidłowej interpretacji znaków) ?

Od jakiegoś czasu możemy zauważyć iż coraz więcej witryn posiada adresy w formie znacznie czytelniejszej – przyjazne dla odbiorcy. Pierwszy z brzegu przykład : http://www.firma.com/product/1531/ - czy taki adres nie jest przyjemniejszy niż http://www.firma.com/product.aspx?id=1531 ? Zapewniam iż w przypadku bardziej skomplikowanych parametrów da się uzyskać naprawdę ciekawe efekty.

A co więcej – tak stworzone adresy będą poprawnie indeksowane przez wyszukiwarki. Powód jest prosty – nie odwołujemy się wielokrotnie do tego samego pliku tylko z innym numerem id, ale każdy adres wygląda jakby miał osobny katalog.

Co ciekawe – duże portale posiadające bogatą treść już dawno postanowiły wykorzystać większość możliwości, z których część przedstawię w artykule. W poniższym artykule chciałbym przedstawić moduł, którego zadaniem będzie zmiana adresu żądanego pliku, posługując się przy tym skonfigurowanymi schematami wykorzystującymi wyrażenia regularne.

 

 

Gdzie zacząć – czyli czym jest moduł http

Jakiś czas temu na łamach Codeguru.pl mieliśmy przyjemność przeczytać artykuł Adama Wardyńskiego na temat http handlers w asp.net, dziś powiemy sobie o czymś co ma miejsce jeszcze wcześniej – zanim proces dojdzie do http handlers – moduły http (ang. http modules).

Gdy żądanie http o stronę asp.net przychodzi od klienta do procesu serwera iis, poprzez bibliotekę aspnet_isapi.dll jest kierowane do całego silnika asp. Przetwarzane żądanie przez silnik asp przechodzi przez wszystkie moduły http, ale na końcu trafia do jednego, zarejestrowanego dla danego pliku,  http handlers. Tak więc moduły http dają nam dość duże możliwości – możemy w locie zmienić niemalże całe żądanie klienta – korzystając przy tym z wielu modułów, które w zależności od potrzeb mogą być albo włączone, albo nie dla danej aplikacji.

 

Ważnym elementem podczas pisania modułu http jest określenie w którym momencie cyklu życia obsługi żądania http wywołać naszą akcję. Poniższy schemat obrazuje kolejność wykonywania zdarzeń podczas obsługi pojedynczej strony asp.net. Do sekcji aspx.page dochodzi żądanie od klienta (ang. Request), po drodze odpowiednio przetrawione przez moduły http, po czym wpada do odpowiedniego handlera obsługującego aspx.page, a następnie powracają dane do klienta (ang. Response), oczywiście znów przechodząc przez każdy załadowany moduł.

 

 

Na rysunku zauważyć możemy dwa bloki o zaokrąglonych rogach – BeginRequest oraz EndRequest – są to dwa zdarzenia które informują moduł o, odpowiednio, rozpoczęciu obsługi żądania, oraz o zakończeniu obsługi żądania.

Dwa bloki ciemniejsze na schemacie są interesujące jeżeli używamy jakiejkolwiek formy uwierzytelnienia tożsamości użytkownika (jako iż moduł nasz chce zmienić adres URL). Musimy się zastanowić nad następującymi przypadkami reakcji modułu na zdarzenia :

- BeginRequest – dotyczy uwierzytelniania Windows; polega na sprawdzeniu uprawnień do plików; gdy system Windows otrzyma prośbę uwierzytelnienia do jakiegoś dziwnego pliku, to oczywistym jest iż próba się nie powiedzie, zatem musimy adres przepisać na prawidłowy przed uwierzytelnianiem

- AuthenticateRequest – nie można zastosować z uwierzytelnianiem Windows (powód powyżej); dotyczy między innymi uwierzytelniania Forms; jeżeli użytkownik nie może zostać uwierzytelniony, to musi zostać przekierowany do strony logowania – a co się stanie jeżeli przepiszemy adres przed logowaniem ? Bingo. Użytkownik otrzyma prawdziwy adres zamiast naszego przyjaznego.

 

Natomiast jeżeli nie stosujemy uwierzytelnienia to najlepiej pozostać przy zdarzeniu BeginRequest.

 

Zasiądźmy do Visual Studio

 

Stworzenie odpowiedniego modułu rozpoczniemy od implementacji dwóch metod interfejsu IHttpModule – Init() oraz Dispose(), które to musi implementować każdy moduł HTTP. Metoda Dispose() jest wywoływana gdy obsługa żądania się zakończyła i wynik jest zwracany do IIS’a. W metodzie Init() dodamy obsługę naszej funkcji przepisującej adres do reakcji na zdarzenie BeginRequest :

 

[C#,UrlRewite.cs]

 

public void Init(HttpApplication Appl)

{

Appl.BeginRequest += new EventHandler(UrlRewrite_BeginRequest);

}

 

Oto funkcja przepisująca adres – można tu zauważyć elementy pobierane z pliku konfiguracyjnego – między innymi chodzi o zestawy reguł którymi będziemy modyfikować adresy (o konfiguracji w dalszej części artykułu) :

 

[C#,UrlRewrite.cs]

 

public void UrlRewrite_BeginRequest(object sender, System.EventArgs args)

{

      int i;

      // nadawca zdarzenia jest aplikacja

      System.Web.HttpApplication Appl=(System.Web.HttpApplication)sender;

      string new_address = Appl.Request.Path;

      string originalPath = Appl.Request.Path;

      // odczytujemy konfiguracje z Web.config

      UrlRewriteConfig.RulesCollection rules = UrlRewriteConfig.GetConfig().Rules;

      // przechodzimy przez kolekcje zasad

for(i = 0; i < rules.Count; i++)

      {

            Regex regularExpression = new Regex("^" + rules[i].fromUrl + "$", RegexOptions.IgnoreCase);

            // sprawdzamy czy wystapilo trafienie

            if (regularExpression.IsMatch(originalPath))

            {

                  // zastepujemy adres

                  new_address = regularExpression.Replace(originalPath, rules[i].toUrl);

                  break;

            }

      }

                 

      // jezeli sa jeszcze jakies parametry w QueryString to je dodamy do adresu

      string query = String.Empty;

      if (Appl.Context.Request.QueryString.Count > 0)

      {

            if (new_address.IndexOf('?') != -1)

            new_address += "&" + Appl.Context.Request.QueryString.ToString();

            else

            new_address += "?" + Appl.Context.Request.QueryString.ToString();

      }

 

      // jezeli adres ma parametry to rozbijmy na dwa lancuchy

      string tmp = new_address;

      if (tmp.IndexOf('?') > 0)

      {

            query = tmp.Substring(tmp.IndexOf('?') + 1);

            new_address = tmp.Substring(0, tmp.IndexOf('?'));

      }

 

      // i już ostateczne przepisanie adresu

      // metoda zgodna z .net framework 1.1

      Appl.Context.RewritePath(new_address, String.Empty, query);

 

// w przypadku .net framework 1.0 :

      //if (query.Length > 0) new_address += "?"+query;

      //Appl.Context.RewritePath(new_address);

      }

}

 

Jak widzimy – korzysta ona z poszczególnych reguł przepisywania adresów – par <fromUrl>adres żądania</fromUrl><toUrl>adres którym zastąpimy</toUrl>. Po pierwszym trafieniu w odpowiednią regułę nie kontynuuje już sprawdzania – oczywiście można zastosować kompleksowe przetwarzanie na przykład - jeżeli trafiłeś w regułę to zastosuj i sprawdź wszystkie od początku,  ale w tym module będziemy używać jednoznacznych wyrażeń – łatwiej będzie się zorientować w przypadku wielu reguł.

 

I jeszcze jedna ciekawa uwaga odnośnie metody przepisującej adres – RewritePath. .NET Framework 1.1 obsługuje wersję w której podajemy czysty adres, oraz parametry osobno. Jednakże jeżeli użyłem metody w której podaje się od razu cały adres z parametrami to otrzymałem trudną do rozgryzienia zagadkę – mianowicie jeżeli włączyłem śledzenie wykonywania aplikacji (Trace) to wszystko działało jak powinno, ale gdy wyłączyłem śledzenie to moduł przestawał działać. Do dziś nie wiem czym to jest  spowodowane, ale chciałbym przestrzec programistów którzy się za to wezmą – gdyż mogą mieć taki sam problem do rozwiązania.

 

Konfiguracja aplikacji

Oczywiście jak konfiguracja to i plik Web.config. A jak plik Web.config, to też XML. A jak XML to (de)serializacja. Jednym z podejść dostęp do konfiguracji zawartej w pliku Web.config polega na implementacji metody Create interfejsu IConfigurationSectionHandler, oraz dodaniu odpowiedniego wpisy mówiącego o tym, gdzie szukać tej klasy (o wpisie w następnym akapicie).

Konfiguracja naszego modułu ma postać :

 

[XML, Web.config]

 

<UrlRewrite>

<Rules>

            <Rule>

                        <fromUrl>adres żądania</fromUrl>

                        <toUrl>adres docelowy</toUrl>

            </Rule>

           

</Rules>

</UrlRewrite>

 

Tworzymy klasę UrlRewriteConfig, a w niej serializowalne elementy o nazwach odpowiednich do użytych w konfiguracji XML :

 

[C#,Config.cs]

 

[Serializable()]

[XmlRoot("UrlRewrite")]

public class UrlRewriteConfig

{

      private RulesCollection rules;

      public static UrlRewriteConfig GetConfig()

      {

      return (UrlRewriteConfig) ConfigurationSettings.GetConfig("UrlRewrite");

      }

 

      public RulesCollection Rules

      {

            get { return rules; }

            set { rules = value; }

      }

 

      [Serializable()]

      public class RulesCollection : CollectionBase

      {

            public virtual void Add(Rule r) { this.InnerList.Add(r); }

            public Rule this[int index] {

                  get { return (Rule) this.InnerList[index]; }

                  set { this.InnerList[index] = value; }

            }

      }

 

      [Serializable()]

      public class Rule

      {

            private string From, To;

            public string fromUrl

            {

                  get { return From; }

                  set { From = value; }

            }

            public string toUrl

            {

                  get { return To; }

                  set { To = value; }

            }

      }

}

 

Metoda GetConfig zwraca nam obiekt klasy UrlRewriteConfig, z którego metodą obsługi zdarzenia BeginRequest pobieramy kolekcję reguł. Zgrabnie pominę temat (de)serializacji XML, gdyż był on już wielokrotnie omawiany– na przykład w artykule James’a Johnson’a pt. „.NET XML Serialization - a settings class”.

 

Uruchamiamy nasz moduł

Aby nasz moduł zaczął wykonywać swoje zadania musimy go zarejestrować jako moduł – czyli powiedzieć silnikowi asp, iż jest tu taki i czeka na zadania. Mamy dwa sposoby na zrobienie tego – indywidualnie dla aplikacji, lub globalnie dla całego serwera aplikacji.

 

a) Web.config

Jest to prostsza metoda – lokalna – dotyczy pojedynczej aplikacji. Polega na umieszczeniu pliku biblioteki, o której mowa, UrlRewrite.dll w katalogu bin naszej aplikacji, po czym dodanie sekcji rejestrujących moduł oraz jego konfigurację, oraz samą sekcję konfiguracji modułu w pliku Web.config :

 

[XML, Web.config]

 

<configuration>

<!-- rejestracja obsługi sekcji konfiguracji -->

<configSections>

      <section name="UrlRewrite" type="UrlRewrite.UrlRewriteConfigHandler, UrlRewrite" />

</configSections>

<!-- sekcja konfiguracji -->

<UrlRewrite>

      <Rules>

</Rules>

</UrlRewrite>

<!-- rejestracja modułu -->

<system.web>

     

      <httpModules>

            <add name="UrlRewrite" type="UrlRewrite.UrlRewrite, UrlRewrite" />

      </httpModules>

     

</system.web>

</configuration>

 

b) machine.config

Przyznam, iż konfiguracja globalna – dla całego serwera aplikacji – jest trochę bardziej uwikłana. A to głównie ze względu na konieczność rejestracji w Global Assembly Cache (GAC) oraz podawanie „strong name” w konfiguracji.

 

Jak podpisać plik i stworzyć dla niego „strong name” możemy poczytać w artykule Jacka Miś. Jeżeli mamy już podpisany plik wrzucamy go do GAC przy pomocy narzędzia gacutil.exe zawartego w .NET Framework SDK :

 

gacutil.exe /i UrlRewrite.dll

 

Jeżeli operacja się powiedzie możemy podejrzeć wpis, który zaraz będzie nam potrzebny :

 

gacutil.exe /l UrlRewrite

 

Otrzymamy (wpis dla pliku który dołączyłem w załączniku artykułu może się różnić numerem wersji) , np.:

 

UrlRewrite, Version=1.0.1829.2387, Culture=neutral, PublicKeyToken=5b0076ada897c828, Custom=null

 

Teraz możemy przystąpić do modyfikacji pliku machine.config (pamiętaj o zrobieniu kopii tego pliku przed rozpoczęciem edycji ! Jest w nim zawarta globalna konfiguracja, która jest dziedziczona przez wszystkie aplikacje (o ile nie nadpisze się jej w Web.config)) – dodajmy następujące wpisy :

 

[XML, machine.config]

 

<configuration>

      <configSections>

      <section name="UrlRewrite" type="UrlRewrite. UrlRewriteConfigHandler, UrlRewrite, Version=1.0.1829.2387, Culture=neutral, PublicKeyToken=5b0076ada897c828" />

</configSections>

      <system.web>

            <compilation>

           

                  <assemblies>

                 

                  <add assembly="UrlRewrite, Version=1.0.1829.2387, Culture=neutral, PublicKeyToken=5b0076ada897c828" />

                  </assemblies>

            </compilation>

            <httpModules>

                 

                  <add name="UrlRewrite" type="UrlRewrite.UrlRewrite, UrlRewrite, Version=1.0.1829.2387, Culture=neutral, PublicKeyToken=5b0076ada897c828" />

            </httpModules>

      </system.web>

</configuration>

 

Od tej pory możemy w pliku Web.config używać tylko sekcji konfiguracyjnej <UrlRewrite>.

 

Przykład 1

Załóżmy, że mamy sklep z różnymi produktami, gdzie każdy produkt ma swój numer identyfikacyjny. Wówczas adres URL do opisu produktu, normalnie wyglądał by tak :

http://sklep.localhost/produkt.aspx?id=123

zrobimy, żeby wyglądał o tak  :

http://sklep.localhost/123/opis.aspx

 

Oto reguła którą musimy podać w tym celu :

 

[XML,Web.config]

 

<UrlRewrite>

      <Rules>

            <Rule>

                  <fromUrl>/(\d{1,5})/opis\.aspx</fromUrl>

                  <toUrl>/produkt.aspx?id=$1</toUrl>

            <Rule>

</Rules>

</UrlRewrite>

 

Należy pamiętać, iż fromUrl jest wyrażeniem regularnym, a więc obowiązują zasady ich dotyczące. Parametry, które wyłuskaliśmy z adresu żądania, możemy wstawić do przepisanego adresu przy pomocy symboli ${nr} – jeszcze raz przypominam – wyrażenia regularne.

 

Jeszcze jedna ważna uwaga – w zapytaniu do serwera zastosowałem odwołanie do pliku .aspx, którego wprawdzie nie ma, ale aby IIS użył silnika asp.net potrzebne jest odpowiednie rozszerzenie. Bez rozszerzenia zapytanie skierowane pod adres http://sklep.localhost/123/ zwróci nam błąd 404 – brak takiego zasobu.

Oczywiście można to obejść – poprzez kierowanie wszelkiego typu zapytań do silnika asp.net, ale zapewne będzie miało to swoje konsekwencje w wydajności serwowania stron.

Aby kierować każde zapytanie poprzez silnik asp.net należy przy pomocy Inetmgr (Menedżer IIS) dla wybranej aplikacji przejść do Właściwości, następnie Katalog i przycisk „Konfiguracja…”. Następnie wstawić wpis odnoszący się do aspnet_isapi.dll w polu „Mapowania zastosowań symboli wieloznacznych” z odznaczoną opcją „Sprawdź czy plik istnieje”. Należy zwrócić uwagę na to, iż wówczas musimy pamiętać między innymi o obsłudze domyślnej strony (Default.aspx) – IIS nam tego nie załatwi. Możemy się posłużyć prostym wpisem typu :

 

[XML,Web.config]

 

<Rule>

      <fromUrl>/katalog/</fromUrl>

      <toUrl>/katalog/Default.aspx</toUrl>

</Rule>

 

Przykład 2

Pewnym rozwiązaniem problemu przedstawionego powyżej jest utworzenie odpowiednich katalogów, w których zawarte będą puste pliki Default.aspx. Wówczas możemy utworzyć reguły konwertujące zapytania do aplikacji kalendarza typu http://localhost/kalendarz/2005/01/01/ :

 

[XML,Web.config]

 

<Rules>

            <Rule>

                        <fromUrl>/kalendarz/(\d{4})/Default.aspx</fromUrl>

                        <toUrl>/rok.aspx?rok=$1</toUrl>

            </Rule>

<Rule>

                        <fromUrl>/kalendarz/(\d{4})/(\d{2})/Default.aspx</fromUrl>

                        <toUrl>/miesiąc.aspx?rok=$1&mies=$2</toUrl>

</Rule>

<Rule>

                        <fromUrl>/kalendarz/(\d{4})/(\d{2})/(\d{2})/Default.aspx</fromUrl>

                        <toUrl>/dzien.aspx?rok=$1&mies=$2&dzien=$3</toUrl>

</Rule>

</Rules>

 

W tym przypadku musimy utworzyć katalog 2005, w nim 12 katalogów reprezentujących miesiące, a w każdym z nich odpowiednią ilość katalogów reprezentujących dni.

Wada tego rozwiązania jest oczywista – tworzenie dużej ilości prawie  pustych (tylko Default.aspx) katalogów.

 

Problem – metoda post

Dodatkowym problemem przepisywania adresów są formularze tworzone po stronie serwera – chodzi o to, iż nie możemy im zmienić adresu docelowego (atrybut action elementu form) – a co za tym idzie zamiast do naszego przyjaznego adresu będzie się odwoływał do adresu zasobu którego może nawet nie być. Szybkim obejściem problemu jest krótki skrypt JavaScript – ustawienie adresu na pusty, co spowoduje, że przeglądarka odwoła się do aktualnego adresu :

 

[HTML]

 

<formrunat=”server”>

</form>

<script language=”JavaScript”>

if(document.layers)  

{

document.layers['Content'].document.forms[0].action = ””;

}

else // DOM

{

document.forms[0].action = ””;

}

</script>

 

Na zakończenie

Podałem dwa proste przykłady reguł - jeżeli podczas czytania artykułu, lub też w pracy z podobnym modułem utworzyliście inne ciekawe reguły przekształceń adresów nie wahajcie się wpisywać w komentarzu do artykułu.

 

Do artykułu załączone są dwa pliki – w jednym kod i dll modułu, a w drugim przykłady wykorzystania – zarówno dwa powyższe jak i dodatkowo bardziej atrakcyjny – prostą implementację forum dyskusyjnego opartą o XML. Do działania forum potrzebuje ustawienia w IIS kierowania wszystkich żądań do silnika ASP.NET  (wskazówki powyżej) oraz uprawnień do zapisu/modyfikacji plików xml w katalogu „dane” (w zależności od konfiguracji – domyślnie prawa te powinien posiadać użytkownik ASPNET). W forum zastosowano cztery reguły – przejście do odpowiedniego działu (np. http://localhost/forum/Polityka/), rozpoczęcie nowego tematu (np. http://localhost/forum/Polityka/NowyTemat), przegląd tematu (np. http://localhost/forum/Polityka/123) oraz odpowiedź w temacie (np. http://localhost/forum/Polityka/123/Odpowiedz).

 

Oczywiście stworzenie takiego modułu było tylko pretekstem do zastanowienia się nad takimi rozwiązaniami, gdyż odpowiednie narzędzia (wprawdzie jest ich niewiele dla IIS’a) istnieją. Jednym z nich jest RemapUrl dostępny w IIS6 Resource Kit Tools. Ważnym czynnikiem jest tu również bezpieczeństwo – poprzez stosowanie takich narzędzi możemy zapewnić aby do naszej aplikacji docierały tylko poprawne żądania.

Załączniki:

Podobne artykuły

Komentarze 6

gizm07803
gizm07803
0 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
Artykuł ciekawy, ale nie wycxzerpujący..  ja mam jeszcze jedno pytanie: widać to w onecie  - http://sport.onet.pl/1044088,wiadomosci.html. no właśnie - co zrobić żeby rozszerzenie .html było traktowane jako .aspx, bo szcczerze mówiąc lepiej to wygląda, ukrywa się wykorzystywaną technologię server-side i podobno search-boty lepiej indeksują strony z rozszerzeniem .html
User 79543
User 79543
30 pkt.
Poczatkujacy
21-01-2010
oceń pozytywnie 0

Szkoda tylko, że ostatnio pisałem moduł do Rewrite'ingu posiłkując się materiałami dostępnymi na sieci ;) i dostrzegam niesamowite wręcz zbieżności w kodzie i komentarzach.... Poza tym trick z javascriptem jest ... średni - znacznie bardziej mi się podoba rozwiązanie z subclassowaniem form-a.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/urlrewriting.asp

Porównajcie sami ;). Autor zadał sobie niesamowity trud posklejania kodu z elegancko napsianej aplikacji w mały syf. Moje gratulacje.

User 79341
User 79341
39 pkt.
Poczatkujacy
21-01-2010
oceń pozytywnie 0

Odnosnie Onet'u - na przyklad prosta regula typu :

<fromUrl>([0-9]{7,10}),wiadomosci.html</fromUrl>

<toUrl>wiadomosci.aspx?id=$1</toUrl>

Tylko ze w przypadku takiego serwisu dochodzi tam jeszcze np. cache'owanie w postaci statycznej, wiec co sie kryje za tym zapytaniem to wiedza tylko tworcy.

gizm07803
gizm07803
0 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
niestey rozszerzenie .html nie dzała  w podanej regule. po prostu taka strona nie zostaje znaleziona... gdy mnieniam na .aspx to wszystko chodzi... może trzeba coś w IISie ustawić? W Apache'u w pliku konfiguracyjnym trzeba bylo takie coś napisac...
User 79341
User 79341
39 pkt.
Poczatkujacy
21-01-2010
oceń pozytywnie 0

No tak - rozszerzenie html domyslnie nie jest kierowane przez ASP.NET. W artykule podalem sposob na kierowanie wszystkich zapytan do ASP.NET, jednakze mozna tez wybiorczo. Poza tym moj komentarz zostal pozbawiony backslashy () w paru miejscach, wiec prosze o zwrocenie uwagi na to.

glad01
glad01
0 pkt.
Nowicjusz
13-08-2013
oceń pozytywnie 0
Mam problem. Skorzystałem z przykładu i działa ale bardzo wolno ładuje strony. I nie jest to wina srodowiska bo inne aplikacje działają normalnie. W czym jest problem ? Jak to poprawić ? Proszę o komentarz.
pkt.

Zaloguj się lub Zarejestruj się aby wykonać tę czynność.