ASP 002, Programowanie


Spis treści:

  1. Wstęp

  2. Wzorzec projektowy - definicja

  3. Wzorzec Budowniczy - teoria

  4. Wzorzec Budowniczy - praktyka - tworzenie interfejsu użytkownika w ASP.NET

  5. Przykłady

  6. Podsumowanie

  7. Bibliografia

 

Wstęp:

Projektując graficzny interfejs użytkownika (tzw. GUI) dla aplikacji desktopowej lub internetowej często tworzymy go w oparciu o zakładki, dzieląc dostępne w nim funkcje na części pogrupowane według określonych kategorii. Dzięki temu powstała aplikacja jest przyjazna dla użytkownika, gdyż poszczególne zakładki jej interfejsu zawierają logicznie, powiązane ze sobą elementy. Tworząc taki interfejs można posłużyć się wzorcem projektowym Budowniczy. Umożliwi nam on stworzenie uniwersalnego rozwiązania składającego się z usystematyzowanego i łatwego do analizy zbioru klas upraszczając w ten sposób programiście późniejszy proces modyfikacji i rozszerzania funkcjonalności takiego GUI.

Na początek krótko zdefiniuję pojęcie wzorca projektowego, napiszę kto po raz pierwszy go wprowadził, następnie przejdę do  omówienia wzorca Budowniczego, a w dalszej części artykułu przedstawię praktyczne przykłady zastosowania omówionego wzorca.

 

Wzorzec projektowy - definicja:

Wzorzec projektowy to uniwersalne, sprawdzone w praktyce rozwiązanie często pojawiających się, powtarzalnych problemów projektowych. Termin ten jako pierwszy wprowadził Christopher Alexander. Nie pisał on o architekturze oprogramowania, ale o wzorcach projektowania budynków. W swoich pracach stwierdza: „Każdy wzorzec określa problem, który wielokrotnie występuje w naszym środowisku, a następnie opisuje zasadniczą część rozwiązania tego problemu w taki sposób, by można było użyć tego rozwiązania milion razy, ale nigdy dwa razy tak samo”. Jego słowa są prawdziwe również w odniesieniu do projektowania obiektowego. W tym wypadku wzorcem projektowym jest „opis komunikujących się ze sobą obiektów i klas, który przerabia się w celu rozwiązania ogólnego problemu projektowego w danym kontekście”.

 

Wzorzec Budowniczy - teoria:

Wzorzec Budowniczy jest stosowany w celu oddzielenia procesu konstruowania skomplikowanych obiektów od ich reprezentacji. Dzięki temu mechanizm konstruowania może tworzyć różne reprezentacje bez modyfikowania konstruowanych obiektów.

 

Wzorca używaj, gdy:

 

Struktura:

 

0x01 graphic

 

Klasy uczestniczące:

 

Opis współpracy:

  1. Klient (programista) tworzy obiekt Kierownik, przekazując do jego konstruktora obiekt BudowniczyKonkretny.

  2. Klient (programista) wywołuje metodę Konstruuj należącą do klasy Kierownik.

  3. Kierownik wydaje polecenie budowy części obiektu Budowniczemu.

  4. BudowniczyKonkretny konstruuje poszczególne części produktu i łączy je.

  5. Klient (programista) otrzymuje gotowy produkt.

 

Konsekwencje zastosowania:

 

Wzorzec budowniczy - praktyka - tworzenie interfejsu użytkownika w ASP.NET

 

ASP.NET zakłada budowanie aplikacji według określonego schematu polegającego na oddzieleniu warstwy prezentacji (wyglądu) zawartej w plikach *.aspx (strony WWW) lub *.ascx (kontrolki użytkownika)od warstwy logicznej zawartej w plikach *.cs. Podejście takie sprawdza się jednak głównie przy tworzeniu prostych i nierozbudowanych aplikacji. W przypadku kiedy pracujemy nad skomplikowanym projektem, takie podejście nie jest optymalną metodą. Pełną kontrolę nad tworzeniem aplikacji otrzymamy wówczas, kiedy będziemy projektować interfejs użytkownika oraz pozostałe elementy aplikacji zgodnie z zasadami programowania obiektowego (zorientowanie na obiekty). Wszystkie elementy aplikacji dzielimy na funkcjonalne części, które definiujemy w osobnych klasach. Tak zorganizowany kod aplikacji jest bardzo czytelny. Warunkiem tego jest jednak stosowanie odpowiednich nazw klas, które będą wiązać się z określonym zakresem funkcjonalności definiowanym w danej klasie. Każda taka klasa zawiera w sobie kod odpowiedzialny za wygląd oraz logikę pewnego fragmentu interfejsu użytkownika (panelu), definiując w ten sposób jego zakres odpowiedzialności. Takie rozwiązanie ma jeszcze jedną zaletę. Umożliwia zastosowanie wzorca Budowniczego w celu stworzenia złożonego interfejsu użytkownika zawierającego zakładki, umożliwiającego wybranie interesującego widoku. Każda zakładka powiązana jest z jednym panelem, który aktywuje się po jej kliknięciu. Odpowiednie zastosowanie wzorca Budowniczego umożliwia szybkie wprowadzanie zmian w wyglądzie budowanej w ten sposób aplikacji. Przykładowo programista może dodawać nieograniczoną liczbę nowych paneli oraz je usuwać. Taka funkcjonalność możliwa jest jedynie wtedy kiedy poszczególne panele są definiowane w osobnych klasach implementujących wspólny interfejs. Odpowiedzialność za tworzenie obiektu złożonego (zbioru paneli) przyjmuje klasa „Kierownik”. Wykorzystuje ona parametry przekazane w konstruktorze (m.in. tablica elementów implementujących interfejs abstrakcyjny) do budowy poszczególnych widoków interfejsu użytkownika i umieszcza je w kontrolce MultiView, pozwalającej na przełączanie się pomiędzy nimi (metoda SetActiveView). Każdy widok budowany jest z dwóch elementów. Pierwszym z nich jest zbiór zakładek, drugim natomiast panel zawierający pozostałe elementy wchodzące w skład interfejsu użytkownika (tekst, grafika, przyciski, listy wyboru, pola tekstowe itp.). Zakładki wyświetlane są w jednej z dwóch form:

Każdy z linków powiązany jest z określonym widokiem, dzięki czemu jego kliknięcie powoduje wyświetlenie określonej zawartości.

 

Przykłady:

Poniższe fragmenty kodów źródłowych są częścią stworzonej przeze mnie aplikacji będącej systemem zarządzania treścią (ang. ContentManagementSystem) dla przykładowej witryny internetowej. Fragmenty kodów źródłowych dotyczą wyłącznie warstwy prezentacji (GUI) w/w aplikacji.

 

Przykład 1:

Pierwszym przykładem będzie widok panelu odpowiedzialnego za wyświetlanie artykułu na stronie WWW. Panel ten składa się z 3 zakładek: Treść, Załączniki oraz Komentarze.

Funkcje „Kierownika” odgrywa tutaj klasa: DetailsPanel. To obiekt tej klasy decyduje o tym jak będzie wyglądał panel zawierający szczegóły dotyczące wybranego artykułu. Wykorzystano tutaj kontrolkę MultiView, która służy do definiowania różnych widoków strony wyświetlanych w zależności od potrzeby (np. po kliknięciu wybranego linku). Klasa DetailsPanel zawiera konstruktor ogólny, który wywoływany jest z parametrem będącym tablicą elementów typu IDetailsPart, czyli obiektów klas implementujących ten interfejs. Wywołując konstruktor klasy DetailsPanel podajemy jako parametr tablicę obiektów z których będzie składał się panel artykułów. Drugi konstruktor występujący w tej klasie wykorzystano do przywrócenia ostatnio wybranego widoku w przypadku przeładowania strony np. po dodaniu nowego komentarza. W tym celu dodano do konstruktora dodatkowo parametr typu int: activeView. Zmiana widoku następuje po kliknięciu na jeden ze stworzonych odnośników (LinkButton).

 

W klasie DetailsPanel znajduje się również metoda:   private void CreateView(string[] viewsNames, int activeView) , która odpowiada za zautomatyzowanie procesu tworzenia poszczególnych widoków oraz zbioru linków potrzebnych do nawigowania po nich. Poniższy listing przedstawia kod źródłowy klasy DetailsPanel.

 

[Kod C#]

public class DetailsPanel

    {

        private MultiView multiView = null;

 

        private View[] viewParts = null;

        private IDetailsPanelPart[] articlePanelParts = null;

 

        public MultiView MultiView

        {

            get { return multiView; }

        }

 

        public DetailsPanel(IDetailsPanelPart[] articlePanelParts)

        {

            multiView = newMultiView();

            multiView.ActiveViewIndex = 0;

 

            string[] viewsNames = newstring[articlePanelParts.Length];

            for (int i = 0; i < articlePanelParts.Length; i++)

            {

                viewsNames[i] = articlePanelParts[i].PartName;

            }

 

            this.articlePanelParts = newIDetailsPanelPart[articlePanelParts.Length];

            viewParts = newView[articlePanelParts.Length];

            for (int i = 0; i < articlePanelParts.Length; i++)

            {

                this.articlePanelParts[i] = articlePanelParts[i];

                CreateView(viewsNames, i);

 

                //ustaw numer widoku komentarzy

                if (articlePanelParts[i] isArticleComments) (articlePanelParts[i] asArticleComments).CommentViewIndex = i;

                //ustaw numer widoku załączników

                if (articlePanelParts[i] isArticleMedia) (articlePanelParts[i] asArticleMedia).MediaViewIndex = i;

            }

        }

 

        public DetailsPanel(IDetailsPanelPart[] articlePanelParts, int activeView)

        {

            multiView = newMultiView();

            multiView.ActiveViewIndex = activeView;

 

            string[] viewsNames = newstring[articlePanelParts.Length];

            for (int i = 0; i < articlePanelParts.Length; i++)

            {

                viewsNames[i] = articlePanelParts[i].PartName;

            }

 

            this.articlePanelParts = newIDetailsPanelPart[articlePanelParts.Length];

            viewParts = newView[articlePanelParts.Length];

            for (int i = 0; i < articlePanelParts.Length; i++)

            {

                this.articlePanelParts[i] = articlePanelParts[i];

                CreateView(viewsNames, i);

            }

        }

 

        private void CreateView(string[] viewsNames, int activeView)

        {

            Panel panel = newPanel();

            panel.CssClass = "ArticleViewContainer";

 

            for (int i = 0; i < viewsNames.Length; i++)

            {

                if (i == activeView)

                {

                    Label label = newLabel();

                    label.Text = viewsNames[i];

                    label.CssClass = "ArticleViewItemActive";

                    panel.Controls.Add(label);

                }

                else

                {

                     LinkButton linkButton = newLinkButton();

                    linkButton.Text = viewsNames[i];

                    linkButton.CommandName = i.ToString();

                    linkButton.Command += newCommandEventHandler(linkButton_Command);

                    linkButton.CssClass = "ArticleViewItemInactive";

                    panel.Controls.Add(linkButton);

                }

            }

 

            viewParts[activeView] = newView();

            multiView.Views.Add(viewParts[activeView]);

 

            viewParts[activeView].Controls.Add(panel);

            viewParts[activeView].Controls.Add(articlePanelParts[activeView].PanelMain);

        }

 

        private void linkButton_Command(object sender, CommandEventArgs e)

        {

            multiView.SetActiveView(viewParts[int.Parse(e.CommandName)]);

            //jeśli przełączono widok i parametr sesji o nazwie view istnieje to go usuń

            if (HttpContext.Current.Session["view"] != null) HttpContext.Current.Session.Remove("view");

        }

     }

 

Za definicje poszczególnych części interfejsu użytkownika (GUI) odpowiedzialne są klasy: ArticleContents (Treść), ArticleMedia (Załączniki) oraz ArticleComments (Komentarze). Wszystkie te klasy implementują wspólny interfejs IDetailsPanelPart. Poniższy rysunek prezentuje strukturę klas występującą w opisanym rozwiązaniu.

0x01 graphic

W celu dodania do witryny panelu zawierającego widok składający się z określonych zakładek należy dodać w ciele metody:   protected void Page_Load(object sender, EventArgs e) następujący fragment kodu:

[Kod C#]

DetailsPanel articleParts = newDetailsPanel(newIDetailsPanelPart[] { newArticleContents("Treść", article), newArticleMedia("Załączniki", article, controlParams), newArticleComments("Komentarze", article, controlParams) });

panel.Controls.Add(articleParts.MultiView);

Powyższy kod tworzy obiekt klasy DetailsPanel przy użyciu konstruktora przyjmującego jako parametr tablicę obiektów implementujących interfejs IDetailsPanelPart. Tablicę tą inicjujemy odpowiednimi obiektami w zależności od tego z jakich elementów ma składać się produkt końcowy. W przedstawionym powyżej przykładzie panel Artykuły będzie zawierał trzy zakładki o nazwach: Treść (obiekt klasy ArticleContents), Załączniki (obiekt klasy ArticleMedia) oraz Komentarze (obiekt klasy ArticleComments).

Następnie należy dodać do zbioru kontrolek (this.Controls) obiekt klasy MultiView zawierający zbiór widoków artykułu.

 

Przykład 2:

Tworzenie graficznego interfejsu użytkownika w oparciu o wzorzec Budowniczy wykorzystałem również w edytorze mapy Google. Interfejs tego edytora składa się z czterech zakładek. Są to w kolejności: Opis, Ustawienia, Markery oraz Polilinie.

Rolę „Kierownika” odgrywa tutaj klasa MapEditor. Zasada działania jest podobna do opisanej w przykładzie pierwszym. Kod źródłowy klasy MapEditor został przedstawiony na poniższym listingu.

[Kod C#]

public class MapEditor

    {

        private Panel panelMain = null;

       

        private MultiView multiView = null;

        private View[] viewParts = null;

        private IEditorPart[] editorsParts = null;

 

        public Panel PanelMain

        {

            get { return panelMain; }

        }

 

        public MapEditor(IEditorPart[] editorsParts)

        {

            panelMain = newPanel();

 

            …

 

            UpdatePanel updatePanel = newUpdatePanel();

            panelMain.Controls.Add(updatePanel);

 

            multiView = newMultiView();

            multiView.ActiveViewIndex = 0;

            updatePanel.ContentTemplateContainer.Controls.Add(multiView);

 

            string[] viewsNames = newstring[editorsParts.Length];

            for (int i = 0; i < editorsParts.Length; i++)

            {

                viewsNames[i] = editorsParts[i].EditorName;

            }

 

            this.editorsParts = newIEditorPart[editorsParts.Length];

            viewParts = newView[editorsParts.Length];

            for (int i = 0; i < editorsParts.Length; i++)

            {

                this.editorsParts[i] = editorsParts[i];

                CreateView(viewsNames, i);

            }

 

            …

        }

 

        private void CreateView(string[] viewsNames, int activeView)

        {

            Panel panel = newPanel();

            panel.CssClass = "TabContainer";

 

            for (int i = 0; i < viewsNames.Length; i++)

            {

                if (i == activeView)

                {

                    Label label = newLabel();

                    label.Text = viewsNames[i];

                    label.CssClass = "TabItemActive";

                    panel.Controls.Add(label);

                }

                else

                {

                    LinkButton linkButton = newLinkButton();

                    linkButton.ID = activeView.ToString() + i.ToString();

                    linkButton.Text = viewsNames[i];

                    linkButton.CommandName = i.ToString();

                    linkButton.Command += newCommandEventHandler(linkButton_Command);

                    linkButton.CssClass = "TabItemInactive";

                    panel.Controls.Add(linkButton);

                }

            }

 

            viewParts[activeView] = newView();

            multiView.Views.Add(viewParts[activeView]);

 

            viewParts[activeView].Controls.Add(panel);

            viewParts[activeView].Controls.Add(editorsParts[activeView].PanelMain);

        }

 

        private void linkButton_Command(object sender, CommandEventArgs e)

        {

            multiView.SetActiveView(viewParts[int.Parse(e.CommandName)]);

        }

    }

Poszczególne zakładki są reprezentowane przez obiekty następujących klas: DescriptionEditor, SettingsEditor, MarkersEditor, Polilines Editor. Struktura klas komunikujących się ze sobą w celu realizacji funkcji edytora zastała przedstawiona na poniższym diagramie.

0x01 graphic

Podobnie jak miało to miejsce w przykładzie pierwszym, w celu dodania do strony panelu zawierającego edytor wewnątrz metody   protected void Page_Load(object sender, EventArgs e) należy wstawić następujący fragment kodu:

[Kod C#]

//wyświetl edytor mapy

MapEditor mapEditor = newMapEditor(newIEditorPart[] { newDescriptionEditor("Opis", filePath), newSettingsEditor("Ustawienia", filePath, newExtendedSettingsEditor()), newMarkersEditor("Markery", filePath), newPolylinesEditor("Polilinie", filePath) });

this .Controls.Add(mapEditor.PanelMain);

Kod ten jest odpowiedzialny za stworzenie obiektu klasy MapEditor wykorzystując jej jedyny konstruktor przyjmujący parametr będący tablicą obiektów klas implementujących interfejs IEditorPart. W powyższym przykładzie inicjujemy tablicę obiektami klas: DescriptionEditor, SettingsEditor, PolylinesEditor, MarkersEditor).

Następnie należy dodać do zbioru kontrolek (this.Controls) obiekt klasy Panel zawierający edytor mapy.

 

Przykład 3:

Zmodyfikowaną wersję wzorca Budowniczego wykorzystałem przy tworzeniu rozwiązania służącego do automatycznego budowania menu (jedno lub kilkupoziomowego). Menu takie można następnie umieścić w dowolnym miejscu witryny Web.

Modyfikacja wzorca budowniczy polega na pominięciu definiowania interfejsu, ponieważ wszystkie elementy menu są obiektami jednego typu (klasa NavigationItem). Główną klasą (tzw. „Kierownikiem”) odpowiedzialną za tworzenie struktury menu jest NavigationPanel. Klasa ta zawiera jeden konstruktor ogólny posiadający następujący zbiór parametrów:

 

Klasa NavigationPanel zawiera również metodę public NavigationPanel(NavigationItem[] navigationItems, NavigationPanel parentNavigationPanel, string paramName) odpowiedzialną za tworzenie elementów menu. Poniższy listing przedstawia implementację klasy NavigationPanel.

[Kod C#]

public class NavigationPanel

    {

        private Panel panelMain = null;

 

        string paramName = null;

        string value = null;

        string currentUrlParameters = null;

 

        public Panel PanelMain

        {

            get { return panelMain; }

        }

 

        public string CssClass

        {

            get { return panelMain.CssClass; }

            set { panelMain.CssClass = value; }

        }

 

        public string ParamName

        {

            get { return paramName; }

        }

 

        public string Value

        {

            get { return value; }

        }

 

        public string CurrentUrlParameters

        {

            get { return currentUrlParameters; }

        }

 

        public NavigationPanel(NavigationItem[] navigationItems, NavigationPanel parentNavigationPanel, string paramName)

        {

            panelMain = newPanel();

 

            this.paramName = paramName;

            value = HttpContext.Current.Request.Params[paramName];

 

            if (parentNavigationPanel == null)

            {

                CreateNavigationPanel(navigationItems, "", paramName, (value == null) ? 0 : int.Parse(value));

            }

            else if (HttpContext.Current.Request.Params[parentNavigationPanel.paramName] != null)

            {

                currentUrlParameters = parentNavigationPanel.currentUrlParameters + parentNavigationPanel.paramName + "=" + parentNavigationPanel.value + "&";

                CreateNavigationPanel(navigationItems, currentUrlParameters, paramName, (value == null) ? 0 : int.Parse(value));

            }

        }

 

        private void CreateNavigationPanel(NavigationItem[] navigationItems, string currentUrlParameters, string paramName, int activeLinkParamValue)

        {

            for (int i = 0; i < navigationItems.Length; i++)

            {

                if (navigationItems[i].ParamValue == activeLinkParamValue)

                {

                    Label label = newLabel();

                    label.CssClass = "NavigationItemActive";

                    label.Text = navigationItems[i].ItemName;

                    panelMain.Controls.Add(label);

                }

                else

                {

                    HyperLink linkCategory = newHyperLink();

                    linkCategory.CssClass = "NavigationItemInactive";

                    linkCategory.Text = navigationItems[i].ItemName;

                    linkCategory.NavigateUrl = HttpContext.Current.Request.FilePath + "?" + currentUrlParameters + paramName + "=" + navigationItems[i].ParamValue;

                    panelMain.Controls.Add(linkCategory);

                }

            }

        }

    }

Każdy element menu jest obiektem klasy NavigationItem. Na poniższym diagramie przedstawione zostały wszystkie klasy uczestniczące w tworzeniu menu.

0x01 graphic

Zdefiniowanie i dodanie do witryny przykładowego menu wiąże się z umieszczeniem wewnątrz metody :  protected void Page_Load(object sender, EventArgs e) następującego fragment kodu :

[Kod C#]

string navigationParamName = "nav";

//menu

NavigationPanel navigationPanel = newNavigationPanel(newNavigationItem[] { newNavigationItem("Strona główna", 0), newNavigationItem("Artykuły 1", 1), newNavigationItem("Artykuły 2", 2), newNavigationItem("Artykuły 3", 3), newNavigationItem("Artykuły 4", 4), newNavigationItem("GoogleMapsAPI", 5) }, null, navigationParamName);

navigationPanel.CssClass = "Demonstration_Menu";

form1.Controls.Add(navigationPanel.PanelMain);

Powyższy kod tworzy nowy obiekt klasy NavigationPanel przy pomocy konstruktora ogólnego. Pierwszym parametrem konstruktora jest tablica obiektów klasy NavigationItem. Przykład pokazuje w jaki sposób utworzyć menu składające się z następujących pozycji: Strona główna, Artykuły 1, Artykuły 2, Artykuły 3, Artykuły 4 oraz GoogleMapsAPI. Każdej pozycji należy ponadto przypisać numer, który będzie jednoznacznie identyfikował ten element (liczba typu int).

Kolejny parametr przyjmuje wartość null, ponieważ tworzone menu jest elementem głównym (stanowi pierwszy poziom). Ostatni parametr to nazwa parametru URL niezbędna do określenia, który element menu został wybrany przez użytkownika serwisu Web.

 

Podsumowanie

Wzorzec Budowniczy zakłada stworzenie jednej głównej klasy (tzw. „Kierownika”). Obiekt tej klasy decyduje o tym jak będzie wyglądał produkt ostateczny. Programista wywołuje jedynie konstruktor klasy „Kierownik” z odpowiednimi parametrami tj. tablicą obiektów klas dziedziczących po wspólnym interfejsie. Obiekty te reprezentują poszczególne części graficznego interfejsu użytkownika (zakładki). Każda taka część GUI musi zostać zdefiniowana jako osobna klasa implementująca określony interfejs. W klasie tej definiujemy wygląd zakładki oraz operacje możliwe do zrealizowania (wykonywane w przypadku zarejestrowania zdarzenia np. kliknięcie przycisku itp.)

W części teoretycznej opisano metodę Konstruuj, znajdującą się w klasie „Kierownik”, dzięki której można tworzyć obiekt złożone. W przykładach praktycznych metoda ta została pominięta, aby uprościć tworzenie obiektów (w celu stworzenia obiektu złożonego wystarczy tylko wywołać konstruktor klasy „Kierownik”).

 

Zastosowanie wzorca budowniczy w projekcie GUI zorientowanego na zakładki daje nam możliwość elastycznego wprowadzania zmian w wyglądzie poszczególnych zakładek. Możemy również modyfikować wygląd całego panelu: np. usuwać wybrane zakładki, dodawać nowe, zmieniać kolejność wyświetlania. W tym celu modyfikujemy jedynie fragment kodu tworzący obiekt klasy „Kierownik” (modyfikujemy wywołanie konstruktora klasy „Kierownik”).

Ponadto nic nie stoi na przeszkodzie, aby zmodyfikować istniejące zakładki definiując nową klasę obiektów dziedziczącą po wybranej zakładce (zdefiniowaną w klasie bazowej).

 

Portal Internetowy w ASP.NET 2.0 - z czym to się je?

 

Spis treści:

1. Wstęp

2. Model w ASP .NET 2.0 oparty na obiektach Provider

3. Uwierzytelnienie użytkowników

4. Wykorzystanie Web Parts

5. Wykorzystanie wiedzy w praktyce - Portal konferencji IAEF2006

6. Podsumowanie

 

 

1. Wstęp

Czym właściwie portal różni się od zwykłego serwisu WWW? Portal jest pewnego rodzaju witryną umożliwiającą użytkownikom dostęp do różnorodnych serwisów informacyjnych. Portal w odróżnieniu od serwisu WWW daje możliwość personalizacji konta użytkownika. Po zalogowaniu, przy użyciu unikalnego hasła i identyfikatora, użytkownik może skonfigurować zakres danych wyświetlanych w poszczególnych działach portalu. Ustawienia są następnie zapisywane na serwerze aplikacyjnym i dostępne przy każdym zalogowaniu użytkownika. W ten sposób powstają indywidualne profile do prezentacji zasobów portalu.

Na rynku istnieje wiele firm, które udostępniają narzędzi do tworzenia portali internetowych. Dużymi sukcesami w tej dziedzinie może poszczycić się firma IBM, która promuje rozwiązania portalowe dla serwera aplikacyjnego WebSphere. Wprowadziła ona pojęcie portletu, czyli atomowego obiektu w pełni „samo-funkcjonalnego”. Portal informacyjny jest wówczas niczym innym jak kontenerem portletów i odpowiedzialny jest za ich wzajemne rozmieszczenie i komunikowanie się. Językiem programowania jest Java, a aplikacje portalowe pisane są zgodnie ze specyfikacją J2EE (Java Platform Enterprise Edition).

Firma Microsoft pozostawała trochę z tyłu konkurencyjnych rozwiązań. W Windows Server 2003 pojawiły się wprawdzie serwisy Windows SharePoint Services, które dostarczały podstawowej funkcjonalności dla rozwoju portali internetowych, takiej jak uwierzytelnianie, zarządzanie dokumentami i kontentem. Wprowadzono pojęcie Web Parts, określające `byty webowe', które odpowiedzialne były za przeprowadzenie operacji niezbędnych do kustomizacji i personalizacji portalu.

W ASP .NET 2.0 pojawiły się kontrolki typu Web Parts, które są podobne do serwisów oferowanych przez Windows SharePoint Services, pod względem serializacji, magazynowania danych oraz wydobywania spersonalizowanych ustawień użytkownika. Rozwiązania wdrożone w ASP .NET 2.0 są o tyle lepsze, że nie są ściśle powiązane z SQL Server lub Active Directory.

Celem artykułu jest opisanie podstawowych praktyk podczas projektowania portalu internetowego w środowisku Visual Studio 2005 przy wykorzystaniu ASP .NET 2.0 Framework. Ponieważ każdy portal internetowy powinien dać użytkownikowi możliwość posiadania własnego konta, we wstępie poruszę temat podstawowych kontrolek z działu uwierzytelnienia i zarządzania użytkownikami. Następnie przedstawię narzędzia Web Parts, które pozwolą każdemu użytkownikowi personalizować swoje konto (dokładniej - swój kontent). W ostatniej części publikacji opiszę praktyczne zastosowanie zawartej w artykule wiedzy podczas tworzenia portalu dla konferencji IEAF 2006. Wspomnę również o problemach, na jakie można natknąć się przy tworzeniu i dystrybuowaniu aplikacji webowych. Problem praw autorskich uniemożliwił mi opublikowanie kompletnego rozwiązania, dlatego zbudowałem uproszczoną wersję portalu, aby pokazać portal w akcji. Przedstawione rozwiązanie nie jest kompletne w 100%, nie wykorzystuje obiektów Resources, atrybutów globalization, tekst zaszyty jest czasami w kodzie a kontrolki nie są często profesjonalnie/tematycznie nazwane. Portal ten, symuluje jednak zachowanie prawdziwego portalu.

Mam nadzieję, że po lekturze tego artykułu programista zdobędzie niezbędną wiedzę do tworzenia portali internetowych w środowisku ASP .NET 2.0.

 

2. Model oparty na obiektach Provider

Zanim przejdę do omawiania poszczególnych kontrolek opowiem o modelu, na którym opiera się serwis uwierzytelniania i zarządzania użytkownikami portalu. W skład modelu wchodzą obiekty: Membership, MembershipProvider i bazy (magazyny) danych.

 

0x01 graphic

Rys.  1 Model oparty o obiekty typu Provider w ASP .NET

 

Obiekt Membership

Obiekt Membership pozwala programiście jak najmniejszym nakładem sił i kodu zrealizować podstawowe zadanie uwierzytelnienia użytkowników portalu i zarządzania ich danymi. Membership opiera swoje działanie na abstrakcyjnym obiekcie MembershipProvider, który jest w pełni konfigurowalny przez programistę.

Z obiektu MembershipProvidera nie korzysta się jawnie, a jedynie poprzez wywołanie statycznych metod obiektu Membership. Do najważniejszych metod klasy Membership należą (opis pobieżny, dokładniejszy znaleźć można na stronach internetowych podanych w bibliografii):

 

CreateUser (..)

                Tworzy nowego użytkownika

DeleteUser (..)

                Usuwa użytkownika.

FindUserByName (..)

                Zwraca użytkownika (obiekt MembershipUser) o danym identyfikatorze.

GetUser (..)

                Zwraca aktualnie zalogowanego użytkownika

ValidateUser (..)

                Sprawdza czy użytkownik istnieje w bazie danych.

 

Obiekt MembershipProvider

Domyślnym obiektem MembershipProvider w nowo tworzonej aplikacji webowej jest SqlMembershipProvider, który jest nazwany AspNetMembershipProvider. Korzysta on z bazy danych Express SQL Server zainstalowanej lokalnie na stacji roboczej, gdzie powstaje aplikacja. Jego konfiguracja zawarta jest w pliku machine.config w katalogu \WINDOWWS\Microsoft.NET\Framework.\v.2.0.xxx\. Warto zaznaczyć, że w środowisku VS 2005, podczas tworzenia nowej aplikacji webowej, produkt Microsoftu automatycznie tworzy w katalogu App_Data (nowego projektu) bazę danych o nazwie aspnetdb.mdf. Z tej właśnie bazy korzysta domyślny MembershipProvider i w zupełności wystarczy ona do uwierzytelniania małego lub średniej wielkości portalu (pod względem ilości jego użytkowników).

ASP .NET 2.0 daje użytkownikowi możliwość stworzenia własnego obiektu MembershipProvider (CustomProvider), tak aby magazynem danych użytkowników była baza Oracle, plik XML, Active Directory, Web Service.

W celu zdefiniowania SqlMembershipProvidera, który korzysta z jawnie wyspecyfikowanej bazy danych, wystarczy zmodyfikować odpowiednie sekcje pliku web.config aplikacji. Domyślne ustawienia MembershipProvidera w machine.config wskazują na obiekt AspNetSqlProvider:

 

Kod1

[Plik web.config]

<configuration>

      <system.web>

      …

      <membership defaultProvider=”AspNetSqlProvider” />

      <providers>

            <definicja1/>

            <definicja2/>

      </providers>

      …

      </system.web>

</configuration>

 

Obiekt MembershipProvider posiada wiele atrybutów, których wartość programista może zmieniać. Od nich zależy zachowanie części uwierzytelniającej portalu oraz odpowiednich kontrolek, które korzystają z obiektu.

Atrybuty konfiguracyjne:

applicationName:

Określa nazwę aplikacji, która korzysta z magazynu danych obiektu MembershipProvider. Jeśli chcemy, aby kilka aplikacji korzystało z tego samego magazynu wystarczy wyspecyfikować tę samą nazwę atrybutu. Domyślna wartość to „/”.

connectionStringName:

Określa, w jaki sposób SqlMembershipProvider współpracuje z magazynem danych. Specyfikuje ścieżkę oraz sposób dostępu do magazynu.

enablePasswordRetrieval

Określa, czy po dodaniu nowego użytkownika do bazy danych, będzie można wydobyć jego hasło (np. do celów przesłania go do użytkownika emailem). Jeśli hasło zostało zapisane w formacie Hashed (atrybut konfiguracyjny PasswordFormat) nie ma możliwości wydobycia go z magazynu danych. Jeśli użytkownik zapisze hasło w formacie Clear lub Encrypted istnieje możliwość pobrania zapisanego w bazie danych hasła. Niestety trzeba wziąć pod uwagę aspekty bezpieczeństwa takiego rozwiązania.

enablePasswordReset

Określa czy możliwa będzie zmiana hasła użytkowników.

requiresQuestionAndAnswer

Określa, czy użytkownik podczas rejestracji przy użyciu kontrolki rejestracyjnej będzie musiał wprowadzić, dla potrzeb bezpieczeństwa, prywatne pytanie i odpowiedź. Jeśli ma wartość „true”, użytkownik, który zapomni hasła, żeby je otrzymać, będzie musiał podać poprawną odpowiedź na pytanie wprowadzone w procesie rejestracji.

requiresUniqueEmail

Określa, czy emaile podawane przez użytkowników muszą być unikalne.

passwordFormat

Określa, w jakim formacie hasło użytkowników będzie przechowywane w bazie danych. Dopuszczalne wartości to Clear, Hashed, Encrypted.

 

Poniżej pokazuję, w jaki sposób można skonfigurować obiekt MembershipProvider, aby spełnił następujące wymagania:

  1. Baza danych to MS SQL Server w wersji Express. Nazwa bazy - mojabaza.

  2. Proces rejestracji nowego użytkownika ma być przeprowadzony bez konieczności podania pytania i odpowiedzi kontrolnych.

  3. Ma istnieć możliwość wydobycia z bazy danych hasła użytkownika bez możliwości jego zmiany.

  4. Hasła powinny być bezpiecznie przechowywane w bazie danych.

Jeżeli, modyfikacja ustawień obiektu MembershipProvider, poprzez zmianę pliku web.config, wydaje się zbyt skomplikowana, możliwe jest zmienianie ich za pomocą konsoli w przeglądarce. Służy do tego ASP .NET Configuration Tool, gdzie można w przyjaznym dla użytkownika GUI, bardziej intuicyjnie, dokonać modyfikacji ustawień dla aplikacji internetowych. ASP .NET Configuration Tool umożliwia dodatkowo tworzenie / usuwanie użytkowników aplikacji, jak również przypisywanie im praw dostępu do odpowiednich katalogów, wchodzących w skład aplikacji. Można również tworzyć role dla użytkowników. Jeżeli korzystamy z VS 2005, aby uruchomić konsolę administracyjną, wystarczy wybrać z menu WebSite -> ASP .NET Configuration. Konsolę konfiguracyjną przedstawia rysunek Rys. 2.

 

Kod2

[Plik web.config]

<connectionString>

      <remove name=”MojaBD”/>

      <add name=”MojaBD” connectionString=”data source=.\SQLEXPRESS; 

            Integrated Security=SSPI; 

            AttachDBFilename=|DataDirectory|mojabaza.mdf; 

            User Instance=true” providerName=”System.Data.SqlClient”/>

</connectionString>

<system.web>

      <membership defaultProvider="MySqlProvider" 

            userIsOnlineTimeWindow="20">

      <providers>

        <remove name="MySqlProvider" />

        <add name="MySqlProvider"

          type="System.Web.Security.SqlMembershipProvider"

          connectionStringName="MojaBD"

          enablePasswordRetrieval="true"

          enablePasswordReset="false"

          requiresQuestionAndAnswer="true"

          passwordFormat="Encrypted"

          applicationName="/" />

      </providers>

    </membership>

  </system.web>

</configuration>

Jeżeli, modyfikacja ustawień obiektu MembershipProvider, poprzez zmianę pliku web.config, wydaje się zbyt skomplikowana, możliwe jest zmienianie ich za pomocą konsoli w przeglądarce. Służy do tego ASP .NET Configuration Tool, gdzie można w przyjaznym dla użytkownika GUI, bardziej intuicyjnie, dokonać modyfikacji ustawień dla aplikacji internetowych. ASP .NET Configuration Tool umożliwia dodatkowo tworzenie / usuwanie użytkowników aplikacji, jak również przypisywanie im praw dostępu do odpowiednich katalogów, wchodzących w skład aplikacji. Można również tworzyć role dla użytkowników. Jeżeli korzystamy z VS 2005, aby uruchomić konsolę administracyjną, wystarczy wybrać z menu WebSite -> ASP .NET Configuration. Konsolę konfiguracyjną przedstawia rysunek Rys. 2.

 

0x01 graphic

Rys.  2 Konsola Administracyjna

 

Podsumowując, obiekt MembershipProvider oraz obiekt Membership, który udostępnia API, pozwalają m.in. na:

 

3. Uwierzytelnienie użytkowników

Zapewne nie raz zdarzyło się wam tworzyć dla potrzeb aplikacji webowych kontrolki do Logowania i Rejestrowania użytkowników. Zarejestrowany użytkownik zobowiązany był do podania swojego unikalnego identyfikatora i hasła, programista natomiast generował skrót hasła za pomocą np. funkcji skrótu MD5 i porównywał z już istniejącym w bazie danych. Jeśli skróty były takie same, użytkownik pomyślnie przechodził proces autoryzacji i mógł korzystać z aplikacji. Podobny scenariusz biznesowy realizowały setki kontrolek używanych przez innych programistów, którzy dostarczali podobnej funkcjonalności w swoich aplikacjach. ASP .NET 2.0 wychodzi naprzeciw potrzebom programisty i dostarcza kontrolek, które są samoistnymi obiektami o określonej funkcjonalności. Autoryzacja użytkownika, rejestracja użytkownika, przypominanie hasła zarejestrowanemu użytkownikowi i inne funkcje zostały dostarczone wraz z nowym Frameworkiem. Należy podkreślić, że wszystkie kontrolki z działu Login korzystają z modelu opartego na obiektach Provider. Wynika z tego, iż zachowanie kontrolek jest konfigurowalne poprzez atrybuty obiektu MembershipProvider w pliku web.config.

Forms Authentication, to klasa, która pozwala na autoryzację użytkownika poprzez walidację jego identyfikatora i hasła, bez wykorzystania mechanizmów Windows Authantication. Gdy użytkownik przejdzie pomyślnie logowanie, jego obiekt jest przechowywany w obiekcie Cookie i dostępny przy każdym zapytaniu HTTP do serwera. Z wartości przechowywanych w obiekcie Cookie korzystają kontrolki z działu Login.

Aby uaktywnić autoryzację Forms Authantication wystarczy dodać do pliku web.config odpowiedni wpis, podobny do tego poniżej:

 

[Plik Web.config]

<system.web>

  <authentication mode="Forms">

    <forms loginUrl="login.aspx" />

  </authentication>

  <authorization>

    <deny user="?" />

  </authorization>

</system.web>

 

Atrybut authantization specyfikuje sposób autoryzacji, zaś authorization, informuje o braku dostępu użytkownikom nieupoważnionym. W pokazanym przykładnie blokowany jest dostęp nieuwierzytelnionych, czyli niezalogowanych użytkowników. Strona, gdzie przeprowadzana jest operacja logowania to login.aspx. Każdy nieautoryzowany dostęp do portalu spowoduje przekierowanie użytkownika do strony loginUrl.

 

Kontrolka Login

Kontrolka Login dostarcza niezbędnej funkcjonalności do przeprowadzenia operacji autoryzacji użytkownika. Autoryzacja użytkownika opiera się o Forms Authantication, a domyślnym obiektem MemebershipProvider, z którego korzysta kontrolka jest oznaczony jako defaultProvider w pliku web.config. Użytkownik może jednak określić dowolny inny (zdefiniowany w pliku web.config) obiekt MembershipProvider poprzez zmianę odpowiedniego atrybutu kontrolki.

Atrybut RemmberMe pozwala dodatkowo na przechowywanie danych użytkownika w obiekcie Cookie, dzięki czemu nie musi on podczas każdego logowania wpisywać jawnie swoich danych identyfikacyjnych.

Kontrolka posiada także wbudowane walidatory, żeby wymusić podanie niezbędnych danych przez użytkownika. Rysunek Rys.3 pokazuje standardową kontrolkę Login. Więcej informacji o kontrolce można znaleźć na stronie podanej w bibliografii.

0x01 graphic

Rys 3. Kontorlka Login

Kontrolka CreateUserWizard

Kontrolka CreateUserWizard dostarcza niezbędnej funkcjonalności do przeprowadzenia rejestracji nowego użytkownika portalu. Nowy użytkownik jest zobligowany do podania swojego identyfikatora, hasła i adresu email oraz pytania bezpieczeństwa, które jest wykorzystywane w sytuacji, gdy użytkownik zapomni swojego hasła i poprosi o przypomnienie go. Jeśli proces rejestracji przebiegnie pomyślnie, użytkownik zostanie powiadomiony o tym poprzez wiadomość email, wysłaną na podany adres. Wiadomość wysyłana jest za pomocą obiektu MailDefinition, który jest konfigurowalnym atrybutem kontrolki. Wysyłanie wiadomości email wymaga ponadto ustawienia odpowiedniego serwera SMTP w pliku web.config lub poprzez konsolę administracyjną. Przykładowe ustawienia są podane poniżej:

 

<configuration>

<system.net>

<mailSettings>

            <smtp from="adres_email">

            <network host="nazwa_serwera" password="haslo"

userName="nazwa_uzytkownika" />

            </smtp>

      </mailSettings>

</system.net>

</configuration>

 

Treść wysyłanej wiadomości email jest określana przez użytkownika. Istnieje możliwość ustalenia w statyczny sposób standardowej treści wiadomości w oddzielnym pliku (np. registrationMail.txt) i dołączenia go jako część Body w wysyłanej wiadomości. W pliku można posługiwać się specjalnymi oznaczeniami <%Password%> i <%UserName%> w celu przesłania identyfikatora i hasła nowemu użytkownikowi.

 

Kod4

[Plik registrationMail.txt]

Dear Mr./Ms.

You have successfully registered to the XXX portal.

Your registration data are:

username: <%UserName%>

password: <%Password%>.

Regards

 

Treść wiadomości można ustalić dynamicznie w funkcji obsługującej zdarzenie SendingMail.

Jeśli programista nie chce wysyłać żadnej wiadomości do użytkownika podczas rejestracji, wystarczy, że atrybut MailDafinition kontrolki pozostawi pusty.

Dodatkowo, należy pamiętać, żeby obsłużyć zdarzenie SendingMailError:

 

Kod5

protected void CreateUserWizard1_SendMailError(object sender, SendMailErrorEventArgs e)

    {

        //byl blad, ale uzytkownik nie musi o tym wiedziec

        e.Handled = true;

    }

 

Uwaga!

 

Niestety zachowanie kontrolki nie jest do końca deterministyczne. Być może powodem są ograniczenia/konfiguracja serwerów SMTP, za pomocą, których wysyłane są wiadomości email. Wiadomości zbyt długich nie udało się nigdy wysłać. Dlatego należy zadbać o to, aby treść przesyłana była jak najkrótsza, np. 3-5 linii. Utrudnienie to można łatwo obejść poprzez wykorzystanie w funkcji SendingMail obiektu MailDefinition i stworzenie własnego obiektu Mail, który można wysłać zamiast wiadomości email kontrolki.

 

Zachowanie kontrolki jest konfigurowalne poprzez obiekt MembershipProvider, z którego korzysta kontrolka. Przykładowo, jeśli użyjemy ustawień z Kod2 zachowanie kontrolki ulegnie zmianie. Wymusi ona podawanie unikalnego adresu email przez użytkowników i nie wyświetli pytania bezpieczeństwa podczas operacji rejestracji na portalu. Pokazano to na rysunku Rys. 4.

 

 0x01 graphic

[a. Standardowa kontrolka]

 

 0x01 graphic

[b. Kontrolka korzystająca z obiektu MySqlProvider]

Rys.  4 Kontrolki CreateUserWizard

 

Więcej informacji o kontrolce można znaleźć na stronie, której adres został podany w bibliografii.

 

Kontrolka PasswordRecovery

Kontrolka PasswordRecovery dostarcza niezbędnej funkcjonalności do przeprowadzenia operacji przypomnienia użytkownikowi hasła, w razie, gdy go zapomni. Kontrolka wykorzystuje obiekt MailDefinition do wysłania emaila. Korzystanie z niej wymaga skonfigurowania serwera SMTP, o czym była mowa przy okazji omawiania kontrolki CreateUserWizard. Teoretycznie kontrolka powinna korzystać z ustawień serwera SMTP w pliku web.config, jednak koniecznie okazuje się jawne wyspecyfikowanie pola From (adresu email) w jej obiekcie MailDefinition, zgodnego z tym z pliku web.config. Tylko wówczas wiadomość email zostanie wysłana.

Żeby kontrolka mogła wydobyć hasło użytkownika z bazy danych użytkowników i przesłać je użytkownikowi, w ustawieniach obiektu MembershipProvider niezbędne jest ustawienie wartości pola enablePasswordRetrieval na wartość true i zaznaczenie pola passwordFromat jako Clear lub Encrypted (nie jako Hashed). Wysyłanie w tekście jawnym hasła użytkownika z bazy danych niesie ze sobą komplikacje związane z bezpieczeństwem. Można zaimplementować trochę bezpieczniejszy scenariusz, poprzez nadawanie użytkownikowi losowo wygenerowanego hasła i zmuszanie do jego zmiany. Taki scenariusz nie wymaga ograniczeń, co do formatu przechowywanego hasła, a jedynie ustawienia w obiekcie MembershipProvider pola enablePasswordReset na wartość true.

Podobnie jak w przypadku kontrolki CreateUserWizard można zdefiniować plik tekstowy, który ma zostać dołączony w części Body wiadomości. Należy uważać na rozmiar przesyłanego emaila.

 0x01 graphic

Rys.  5 Kontrolka PasswordRecovery

 

Kontrolka ChangePassword

Kontrolka ChangePassword dostarcza niezbędnej funkcjonalności do przeprowadzenia operacji zmiany hasła. Pomyślna zmiana hasła powoduje wysłanie wiadomości email do użytkownika (za pomocą obiektu MailDefinition), którego treść jest definiowana przez programistę. Aby możliwa była zmiana hasła użytkownika, niezbędne jest ustawienie w obiekcie MembershipProvider wartości pola passwordReset na true.

Zachowanie kontrolki i jej pola konfiguracyjne są bardzo podobne do tych kontrolki PasswordRecovery.

 

0x01 graphic

Rys.  6 Kontorlka ChangePassword

 

Kontrolka LoginView

Kontrolka LoginView jest kontenerem kontrolek, który pozwala na wyświetlenie dowolnej zawartości z podziałem na role przydzielone użytkownikom. Standardowo kontrolka posiada dwa stany, dla dostępu anonimowego użytkownika - <AnonymousTemplate> i zalogowanego użytkownika - <LoggedInTemplate>. Programista może zdefiniować dodatkowe stany <ContentTemplate>, np. dla użytkowników (zalogowanych), którzy mają nadana rolę admin. Dla każdego ze zdefiniowanych stanów programista może określić dowolny kontent. Kontrolka jest bardzo przydatna. Można ją wykorzystać do wyświetlenia na jednej stronie różnej zawartości, w zależności od przydzielonych użytkownikom ról. Poniżej przedstawiony jest kod kontrolki, która posiada 3 zdefiniowane stany - dla dostępu anonimowego, zalogowanego użytkownika oraz dla użytkownika z przydzieloną rolą o nazwie admin:

 

Kod 6

<asp:LoginView ID=”MyLoginView” Runat=”Server

   <RoleGroups>

            <asp:RoleGroup Roles=”admin”>

                  <ContentTemplate>

                        Treść dla administratorów

                  </ContentTemplate>

            </asp:RoleGroup>

      </RoleGroups>

            <LoggedInTemplate>

            Treść dla normalnych użytkowników

      </LoggedInTemplate>

      <AnonymousTemplate>

            Treść dla dostępu anonimowego

      </AnonymousTemplate>

</asp:LoginView>

 

Kontrolka LoginName

Jedyny zadaniem kontrolki jest wyświetlanie nazwy użytkownika, czyli jego identyfikatora po pomyślnym zalogowaniu. Wykorzystywana jest do wyświetlenia komunikatu, podobnego do: Witaj, J.Kowalski.

 

Kontrolka LoginStatus

Kontrolka pozwala na wyświetlenie konfigurowalnych przez użytkownika pól tekstowych  Login/Logout (wartości domyślne) w zależności od tego, czy użytkownik jest zalogowany na portalu czy nie. Informacje są pobierane z obiektu Cookie. Jeśli użytkownik nie jest zalogowany, kontrolka wyświetla napis Login i po jej kliknięciu przekierowuje użytkownika do strony logowania. Strona logowania jest wyspecyfikowana w atrybucie loginUrl sekcji authentication pliku web.config i nie jest podawana jako atrybut kontrolki:

 

Kod 7

[Plik web.config]

<authentication mode="Forms">

<forms name=".ASPXAUTH" loginUrl="~/Authorization/Login.aspx"/>

</authentication>

Jeśli użytkownik jest zalogowany, kontrolka wyświetla się jako Logout (domyślnie) i po jej kliknięciu usuwany jest odpowiedni obiekt identyfikacyjny z Cookie, a użytkownik jest przekierowywany na stronę podaną w atrybucie LogoutPageUrl kontrolki.

Podsumowując, ASP .NET 2.0 Framework dostarcza w pełni funkcjonalnych rozwiązań dla przeprowadzenia scenariusza uwierzytelniania użytkowników i zarządzania nimi. Służą do tego kontrolki z działu Login. Pozwalają one m.in. na:

§         Autoryzację użytkownika.

§         Rejestrowanie nowego użytkownika.

§         Dostarczenie hasła, w przypadku, gdy użytkownik go zapomniał.

§         Zmianę hasła.

§         Kontrolę kontentu na stronie w zależności od roli przypisanej użytkownikowi.

§         Łatwe i spójne kontrolowanie zachowania za pomocą obiektu MembershipProvider.

Kontrolki z działu Login są „dobrze” napisane i przetestowane przez programistów firmy Microsoft, dlatego warto z nich korzystać.

 

3. Wykorzystanie Web Parts

Już wcześniej wspomniałem, że tym, co rozróżnia portal i serwis internetowy, są możliwości personalizacji kont użytkowników portalu. Każdy użytkownik może na własne potrzeby określić swój profil, a jego ustawienia zostaną zapisane na serwerze i będą dostępne przy każdorazowym korzystaniu z portalu. Taką funkcjonalność można uzyskać wykorzystując kontrolki Web Parts. Na rysunku Rys. 7 przedstawiony jest model strony wykorzystujący obiekty Web Parts.

 0x01 graphic

Rys.  7 Strona z obiektami WebParts

 

Przykłady, którymi będę się w tej części posługiwał pochodzą z projektu portalu dla konferencji IAEF 2006, której kod został dołączony do artykułu.

 

Kontrolka WebPartManager

Kontrolka jest odpowiedzialna za zarządzanie i kontrolę zachowaniem kontrolek WebPartZone, EditorZone i CatalogZone (będzie o nich szczegółowo mowa później) i nadzorowanie wszystkimi zdarzeniami, które zachodzą na stronie oraz stanem aplikacji (tryb pracy portalu). To ona odpowiada za kustomizację oraz personalizację stron portalu.

Na stronie, wykorzystującej obiekty Web Parts, musi znajdować się dokładnie jedna instancja obiektu WebPartManager. WebPartManager musi zostać zdefiniowany zanim jakakolwiek inna kontrolka z działu Web Parts zostanie zdefiniowana.

Żeby lepiej zarządzać wyglądem i rozmieszczeniem kontrolek na stronie zaleca się stosowanie prostych tablic HTML i umieszczanie w nich kontrolek typu WebPartZone, EditorZone i CatalogZone.

 

Tryby pracy Portalu

Zanim przejdę do omawiania obiektów Web Parts, powiem kilka słów o sposobie funkcjonowania strony wykorzystującej obiekt WebPartManager.

WebPartManager kontroluje zachowanie każdej kontrolki strony korzystającej z obiektów WebPart. Jest odpowiedzialny za zależności i powiązania pomiędzy obiektami Web Parts, wchodzącymi w skład odpowiednich obszarów WebPartZone. WebPartManager udostępnia API do zmiany trybu wyświetlania danej strony. Programista może zmieniać stan strony, wybierając jeden z trybów pracy: Browse, Design, Edit i Catalog. Poniższy kod zmienia tryb pracy strony, wykorzystującej obiekt MyPartManager, na Design:

 

Kod 8

MyPartManager.DisplayMode = WebPartManager.DesignDisplayMode

 

W trybie Browse użytkownik nie może edytować strony ani zmieniać położenia kontrolek WebPart. Jest to możliwe dopiero w trybie Design. W trybie tym istnieje możliwość wykonania akcji Drag and Drop na obiektach WebPart, wchodzących w skład strony, a także spersonalizowanie swojego profilu. Indywidualne ustawienia użytkownika zostaną automatycznie zapamiętane na serwerze. W trybie Edit użytkownik ma te same możliwości, co w trybie Design, z tym wyjątkiem, że może edytować kontrolki - zmieniać ich atrybuty, poprzez udostępniony interfejs. Udostępnianiem interfejsu zajmują się kontrolki wchodzące w skład obszaru EditorZone. Tryb Catalog umożliwia użytkownikowi dodawania lub usuwanie kontrolek typu WebPart do/z poszczególnych obszarów WebPartZone. Listę kontrolek, które można dodać / usunąć programista udostępnia użytkownikowi w obszarze CatalogZone, przy użyciu odpowiednich kontrolek z działu CatalogParts. Ten tryb również posiada właściwości trybu Design.

WebPartManager pozwala też na kontrolę kontekstu strony i zmianę pomiędzy User Scope i Shared Scope. W przypadku User Scope (ustawienia domyślne) wszelkie zmiany wykonywane na stronie nie są widoczne przez innych użytkowników (personalization). Tymczasem w kontekście Shared Scope, jeśli użytkownik wprowadzi zmiany, zostaną one rozpropagowane do profili pozostałych użytkowników (customization). Atuty kontekstu Shared Scope polegają np. na możliwości kontroli przez administratora zachowania strony we wszystkich profilach użytkowników.

 

Kontrolka WebPartZone

Kontrolka jest kontenerem dla innych kontrolek typu WebPart (klasa dziedzicząca po WebPart). Funkcję kontrolki WebPart może spełniać również dowolna instancja klasy, która dziedziczy po UserControl. WebPartZone może zawierać wiele kontrolek WebPart, które występują na niej odpowiednio poindeksowane. Jeśli WebPartManager na danej stronie pracuje w trybie innym niż Browse, istnieje możliwość zmiany położenia kontrolek w kontekście obszaru WebPartZone.

Poniższy rysunek przestawia przykładową kontrolkę WebPartZone w skład, której wchodzą dwie kontrolki WebPart - Final_Reg_Control oraz Calendar_Control.

 0x01 graphic

Rys.  8 Przykład wykorzystania kontrolki WebPartZone

 

Kontrolki WebPart mogą zostać dodane do obiektu WebPartZone statycznie lub dynamicznie poprzez wywołanie odpowiedniej metody obiektu WebPartManager. Ponadto, wykorzystując kontrolkę CatalogPart użytkownik może wybrać z listy rozwijanej obiekt WebPart i dodać go do odpowiedniej WebPartZone w indywidualnym profilu na portalu.

 

Kontrolka CatalogZone

Kontrolka jest widoczna i może być wykorzystana tylko wówczas, gdy WebPartManager jest w trybie Catalog. Kontrolka służy jako kontener dla kontrolek CatalogPart oraz DeclarativeCatalogPart.

Kontrolka PageCatalogPart, sprawdza, które kontrolki, w ramach wszystkich obszarów WebPartZone na stronie zostały usunięte i wyświetla je na dynamicznie tworzonej liście. W ten sposób użytkownik może w dowolnej chwili dodać je z powrotem do wybranego obszaru WebPartZone. Raz dodana kontrolka znika z listy w PageCatalogZone, do czasu, aż znowu nie zostanie usunięta ze strony.

Kontrolka DeclarativeCatalogZone nie wyświetla listy wszystkich możliwych do dodania kontrolek na stronie, a tylko te, które zostały dla niej zdefiniowane - stąd jej nazwa. Rysunek poniżej przedstawia kontrolkę CatalogZone wraz z DeclarativeCatalogZone, która zawiera kontrolkę AbstractsList. Można ją dodać do jednego z 4 obszarów WebPartZone, na które podzielona jest strona. W VS 2005, aby dodać wcześniej zdefiniowaną kontrolkę należy na niej wykonać operację Drag and Drop i przenieść z Project Explorera do kontrolki CatalogZone.

 0x01 graphic

Rys.  9 Kontrolka CatalogZone (tryb Ctalog)

 

Usuwanie / dodawanie kontrolek podlega zasadom Share Scope i User Scope, o czym była mowa w części „Tryby Pracy Portalu”.

 

Kontrolka EditorZone

Kontrolka EditorZone jest widoczna tylko, jeśli WebPartManager pracuje w trybie Edit. Wówczas istnieje możliwość edycji kontrolek, które znajdują się w obszarach WebPartZone:

 0x01 graphic

Rys.  10 Edycja kontorlek WebPart w ramach obszaru WebPartZone

 

Kontrolka EditorZone pełni rolę kontenera dla kontrolek typu EditPart - AppearanceEditorPart, PropertyGridEditorPart, LayoutEditorPart, BehaviorEditorPart. Najbardziej istotne dla realizacji projektu portalu konferencyjnego okazały się dwie pierwsze.

AppearanceEditorPart pozwala na zmianę wyglądu danej kontrolki WebPartZone poprzez konfigurowalne ustawienie kilku atrybutów obszaru. Poniżej zamieszczam rysunek kontrolki, gdzie widoczne są atrybuty obszaru, które użytkownik może zmieniać:

 

 0x01 graphic

Rys.  11 Kontorlka EditorZone wraz z AppearanceEditorPart

Ważną kontrolką z punku widzenia właściwości personalizacji portalu jest kontrolka PropertyGridEditorPart. Kontrolki, posiadające atrybuty, do których dostęp zdefiniowany jest jako WeBrowsable (będzie o tym mowa w dalszej części artykułu), pozwalają użytkownikowi na ich kontrolę. Na rysunku Rys. 12 pokazano obszar WebPartZone3, na którym wyświetlana jest kontrolka WebPart AbstractsList. Kontrolka ta posiada atrybut AbstractListProperty, oznaczony przez WebBrowsable. Dlatego podczas jej edycji, w kontrolce PropertyGridEditorPart użytkownik może dowolnie kontrolować jego wartość:

 0x01 graphic

Rys.  12 Atrybut kontrolki WebPartZone  konfigurowalny  w PropertyGrid

 

Komunikacja pomiędzy kontrolkami WebPartZone

Kontrolki WebPartZone, nazywane też obszarami, są bytami autonomicznymi. Nasuwa się pytanie, w jaki sposób zrealizować komunikacje pomiędzy nimi (w ramach jednej strony)? W celu przepływu danych pomiędzy dwoma kontrolkami WebPartZone należy skorzystać z obiektu WebPartConnection. W rzeczywistości jednak to nie obszary WebPartZone się komunikują, ale kontrolki WebPart, które do nich należą. Połączenia między kontrolkami można zrealizować dynamicznie lub statycznie. Atuty statycznego połączenia to prostota w implementacji oraz realizacji schematu master-details pomiędzy dwoma kontrolkami. Na tym rodzaju połączenia skupię uwagę.

Zachowaniem strony zarządza obiekt WebPartManager i to on posiada atrybut StaticConnections, gdzie definiować można obiekty dla komunikacji pomiędzy kontrolkami WebPart. Na rysunku poniżej przedstawiam, w jaki sposób można w VS 2005 zrealizować komunikację, edytując atrybut StaticConnection obiektu WebPartManager i dodając połączenia:

0x01 graphic

Rys.  13 Definiowanie połączenia pomiędzy kontrolkami WebPart

 

Przykład z rysunku pokazuje przesyłanie ID użytkownika z kontrolki UserShortList1 (master) do kontrolki PreRegView1 (details), gdzie zostaną wyświetlone szczegółowe dane użytkownika. KontrolkaUserShortList1 składa się z obiektu GridView, który wyświetla listę użytkownikowi rozpoznawanych po swoim unikalnym identyfikatorze ID.

Obiekt komunikacji Conn1 tworzy przepływ danych pomiędzy kontrolką PreRegView1 oraz UserShortList1, dla realizacji schematu master-details. Na rysunku Rys. 13 można zauważyć, że kontrolka UserShortList1 dostarcza danych (Provider), zaś PreRegView1 je konsumuje (Consumer).

W celu dostarczenia informacji o ID użytkownika do kontrolki PreregView1, kontrolka UserShortList1 powinna implementować publiczny interfejs, np. ISelectedUser, który definiuje publiczną właściwość (property) SelectedUserID:

Kod 9

[ISelectedUser.cs]

public interface ISelectedUser

{

        string SelectedUserID{get;}

}

 

Kod 10

[UsersShortList.ascx.cs]

 

public partial class UsersShortList : System.Web.UI.UserControl,ISelectedUser

{

//interfejs ISelectedUser

public string SelectedUserID

{

        get

        {

            if (this.Users.SelectedRow != null)

            {

                GridViewRow row = this.Users.SelectedRow;

                return

Membership.GetUser( Membership.GetUserNameByEmail(

row.Cells[1].Text)).ProviderUserKey.ToString();

            }

            return null;

        }

    }

}

 

UserShortList1 poprzez właściwość SelectedUserId zwraca ID aktualnie zaznaczonego na liście użytkownika. Z właściwości tej skorzysta kontrolka PreRegView1.

Aby WebPartManager mógł zrealizować komunikację pomiędzy dwoma kontrolkami określonymi jako Provider i Consumer, wymaga zdefiniowania w tych nich metod z atrybutami ConnectionProvider, dla Providera, i ConnectionConsumer dla Consumera. Metoda Providera musi zwracać publiczny interfejs. Nazwy metod są dowolne.

 

Kod 11

[PreRegView.ascx.cs]

 

[ConnectionConsumer("SelectedUser_desc1", "SelectedUser")]

    public ISelectedUser user = null;

    public void SetSelectedUser(ISelectedUser user)

    {

        this.user = user;

       }

Kod 12

[UsersShortList.ascx.cs]

 

[ConnectionProvider("SelectedUser_desc2", "SelectedUser")]

    public ISelectedUser GetSelectedUser()

    {

        return this;

    }

 

Pierwszy parametr atrybutu metod jest dowolnym „user-friendly” opisem ConnectionProvidera/ConnectionConusmera, który pojawia się w kontrolce ConnectionZone (dokładniejsze informacje o kontrolce można znaleźć pod adresem podanym w bibliografii), drugi zaś jest jego identyfikatorem. Nazwa tego identyfikatora interpretowana jest później jako ConsumerConnectionPointID dla Conusmera i odpowiednio jako ProviderConnectionPointID dla Providera (patrz Rys. 13). Za pomocą tych identyfikatorów WebPartManager wie, między którymi kontrolkami ma zrealizować połączenie.

 

Połączenie między kontrolkami jest realizowane w taki sposób, żeby podczas wywołania metody OnPreRender w kontrolce Consumera można było korzystać z pobranych od Providera danych. Przy każdym pobraniu strony, WebPartManager sprawdza wszystkie zdefiniowane przez programistę połączenia w kolekcji StaticConnections. Dla każdego połączenia wywołuje metodę oznaczoną jako ConnectionProvider, by pobrać referencję do interfejsu, a następnie przekazuje tę referencję do oznaczonej jako ConnectionConsumer metody Consumera. Consumer może wówczas korzystać z metod dostarczonych w interfejsie.

 

Poniższa metoda wykorzystuje ID użytkownika z interfejsu user i wywołuje dla niego funkcję na bazie danych:

 

Kod 13

[PreRegView.ascx.cs]

 

public int userId;

protected override void OnPreRender(EventArgs e)

{

        base.OnPreRender(e);

        if (user != null && user.SelectedUserID != null)

        {

            userId = user.SelectedUserID;

            this.doGetPreRegUserDetails(user.SelectedUserID);

            ...

        }

    }

 

Personalizacja kontrolek

 

Portal pozwala na personalizację konta użytkownika, tworzenie indywidualnych profili. Zmiany wprowadzone przez użytkownika w jego koncie powinny zostać przechowane do następnej sesji. ASP .NET 2.0 dostarcza bardzo prostych mechanizmów do zapewnienia tej funkcjonalności. Indywidualne ustawienia mogą dotyczyć rozmieszczenia kontrolek na stronie, usuwania bądź dodawania nowych lub zmiany odpowiednich (edytowalnych) atrybutów poszczególnych kontrolek.

 

Aby użytkownik mógł zmieniać na własne potrzeby atrybuty kontrolek, tak żeby zmiany były przechowane na serwerze, atrybuty te muszą być odpowiednio zdefiniowane. Personalizacji tak naprawdę podlegają właściwości kontrolek, czyli property, zdefiniowane jako Personalizable. Atrybut ten mówi wyłącznie, że stan właściwości ma zostać przechowany na serwerze. Zazwyczaj atrybut Personalizable występuje w parze z WebBrowsable. Ten ostatni „znakuje” właściwość kontrolki, po to, aby była konfigurowalna podczas pracy w trybie Edit i korzystaniu z EditorZone zawierającą PropertyGridEditorPart (patrz. rozdział „Kontrolka EditorZone”). PropertyGridEditorPart ma tę właściwość, że podczas edycji danej kontrolki WebPart, wyszukuje wszystke jej właściwości (property) oznaczone jako WebBrowsable i wyświetla je, tak, aby użytkownik mógł je skonfigurować. Dla przykładu scenariusza z Rys. 12, kod ma postać:

 

Kod 14

[AbstractsList.ascx.cs]

    private String myAttr="default";

    /// </summary>

    [Personalizable, WebBrowsable]

    public string AbstractListProperty

    {

        get { return myAttr; }

        set { myAttr = value; }

}

 

Personalizacja pozwala na permanentne zapisanie stanu strony na serwerze. Przydatny wtedy  mechanizm resetowania tych ustawień. Można tego dokonać przez wywołanie odpowiedniej metody klasy PersonalizationAdministration:

PersonalizationAdministration.ResetUserState dla User Scope lub PersonalizationAdministration.ResetUserState dla Shared Scope.

 

Podsumowując, kontrolki z działu Web Parts wniosły do platformy ASP .NET bardzo ważną i przydatną funkcjonalność. Pozwalają m.in. na:

§         Personalizację kontrolek w indywidualnych profilach użytkowników.

§         Kustomizację kontrolek jednocześnie we wszystkich profilach użytkowników.

§         Wydzielenie samoistnych `bytów', komponentów strony, które są w pełni funkcjonalne.

§         Komunikacje pomiędzy samoistnymi `bytami' na stronie w sposób bardzo elastyczny.

§         Realizację podstawowych cech portalu internetowego w prosty, spójny sposób.

 

5. Wykorzystanie wiedzy w praktyce - Portal Konferencji IAEF2006

 

Pierwsza część artykułu opisywała niezbędne minimum teoretyczne, które należy zrozumieć, żeby rozpocząć programowanie portali internetowych. Wszelkie brakujące informacje i wyjaśnienia można znaleźć na stronach MSDN (adres w bibliografii).

 

Ostatnia część artykułu poświęcona jest zastosowaniu wiedzy w praktyce na przykładzie Portalu Konferencji IAEF 2006. W przedstawianym rozwiązaniu są pewne braki (musiałem poczynić znaczne uproszczenia w stosunku do oryginalnego portalu), ale ich uzupełnienie nie powinny sprawić większych kłopotów.

 

Wymagania funkcjonalne portalu konferencyjnego

 

Portal konferencyjny ma za zadanie umożliwienie rejestrowania się użytkownikom na konferencję IAEF2006.

 

0x01 graphic

Rys.  14 Strona startowa portalu

 

Rejestracja składa się z dwóch etapów - Preliminary Registration oraz Final Registration. Pierwszy etap wymaga podania swoich danych osobowych i akceptacji administratora (osoby, która organizuje konferencję). W drugim etapie, użytkownik podaje dokładniejsze dane dotyczące przyjazdu, rezerwacji hotelu i przesyła abstrakt na serwer.

 

Administrator tymczasem, musi mieć dostęp do listy wszystkich użytkowników i ich danych wprowadzonych i zapisanych na serwerze oraz możliwość akceptacji zgłoszenia Preliminary Registration. Następnie musi mieć dostęp do wszystkich abstraktów zapisanych na serwerze przez użytkowników, w celu weryfikacji ich programu przed konferencją.

 

Przed przystąpieniem do projektowania zostały sformułowane następujące wymagania dla aplikacji (są one uproszczone w stosunku do oryginalnych):

§         Dostęp przez Internet do aplikacji.

§         Istnienie odrębnych kont użytkowników, do których dostęp następuje poprzez logowanie (identyfikator, hasło).

§         Rejestracja użytkownika, rozumiana jako zdefiniowanie nowego użytkownika portalu, musi zakończyć się wysłaniem emaila do użytkownika z jego identyfikatorem i hasłem. Rejestracja nie wymaga podania pytania bezpieczeństwa.

§         Administrator musi być notyfikowany emailem o rejestracji nowego użytkownika (rejestracja rozumiana jako zdefiniowanie nowego użytkownika portalu). Treść wiadomości jest jasno określona i różni się od tej przesyłanej do użytkownika.

§         W części Preliminary Registration użytkownik musi podać kraj, z którego pochodzi. Lista krajów jest pobierana z zewnętrznego serwisu (Web Service) i nie powinna być uaktualniana (zakładam, że raz pobrana lista nie zmieni się w przeciągu 1 roku).

§         Użytkownik może do woli zmieniać dane Preliminary Registration, dopóki jego zgłoszenie nie zostanie zaakceptowane przez administratora.

§         Po zaakceptowaniu zgłoszenia użytkownik musi podać dane Final Registration i przesłać abstrakt na serwer.

§         Abstrakt może być przesłany w formacie .zip,.pdf,.txt,.doc, a jego rozmiar nie może przekroczyć 8 MB.

§         Użytkownik może zapisywać nowe wersje abstraktu, ale na serwerze musi znajdować się dokładnie jeden plik (abstrakt). Po wgraniu nowej wersji stara jest kasowana. Wszystkie abstrakty są umieszczone w jednym katalogu, dlatego ważne jest zmodyfikowanie nazwy plików tak, aby zawierały datę przesłania abstraktu oraz email osoby, która go wysłała.

§         Użytkownik musi mieć dostęp do danych, które podał podczas obu etapów rejestracji.

§         Administrator musi mieć dostęp do danych wszystkich użytkowników, którzy podali dane dla Preliminary Registration lub Final Registration.

§         Administrator musi mieć możliwość łatwego akceptowania zgłoszeń użytkowników.

§         Administrator musi mieć dostęp do listy abstraktów przechowywanych na serwerze i móc je pobrać i zapamiętać lokalnie.

§         Może istnieć wielu administratorów portalu. Każde konto administratora musi być personalizowane.

 

Model bazy danych dla portalu konferencji IAEF2006

 

Dla celów artykułu korzystam z bazy danych SQL Express, a nie ORACLE, jak w wersji oryginalnej.

 

Podczas rejestracji nowego użytkownika portalu jego dane identyfikacyjne są automatycznie zapisywane do tabeli aspnet_Users przez kontrolkę CreateUserWizard. W tym samym momencie jest wywoływana procedura bazy danych, która najważniejsze dane użytkownika zapisuje do tabeli conf_Users. Aby była spójność danych conf_Users posiada taki sam klucz główny jak aspnet_Users.

 

Każdy użytkownik może posiadać dokładnie jeden wpis w tabeli conf_PreRegistration i jeden wpis w conf_FinalRegistration. Relacje z tymi tabelami są realizowane poprzez klucze obce. Zachęcam do samodzielnej analizy rysunku Rys. 14 w celu zrozumienia zależności w bazie danych.

 

0x01 graphic
 

Rys.  15 Model bazy danych

 

Poniżej znajduje się opis najciekawszych elementów portalu, o których nie było mowy w teoretycznej części artykułu.

 

Pobieranie abstraktów i zapamiętywanie ich na serwerze.

 

Każdy użytkownik, którego zgłoszenie Preliminary Registration zostało zaakceptowane przez administratora jest zobowiązany do przesłania mu swojego abstraktu. Aby obciążyć skrzynki mailowe administratorów, wszystkie abstrakty są gromadzone na serwerze a administrator może je pobrać.

 

Do celów zapamiętywania abstraktów na serwerze, korzystam z dostarczonej w ASP .NET 2.0 kontrolki FileUpload. Ważne jest, aby folder, do którego zapisywane są abstrakty, miał nadane prawa do zapisu i odczytu (dla administratorów). Do nazwy każdego z abstraktów dołączana jest informacja z datą dodania abstraktu oraz emailem użytkownika. Dzięki temu administrator może rozróżnić abstrakty, które są nazwane w ten sam sposób (abstrakty są zapamiętywane w jednym folderze).

 

Żeby ograniczyć rozmiar pliku, który może być pobierany na serwer do 8 MB wystarczy w pliku web.config dodać wpis:

 

<httpRuntime maxRequestLength="8196" />

 

Do kontroli typu akceptowanych plików, trzeba dopisać parę linii kodu:

 

Kod 15

if (FileUpload.HasFile)

{

string fileExt = System.IO.Path.GetExtension(FileUpload.FileName).ToLower();

           

if (fileExt == ".txt" || fileExt == ".pdf" || fileExt == ".doc" || fileExt == ".zip")

{

   try

   {

//zapisz plik...

   }

   else

   {

      this.UploadStatus.Text = "Only .txt,.pdf,.doc and .zip files allowed!";

    }

 }

 

Szyfrowanie pliku web.config

 

Do celów bezpieczeństwa zalecam szyfrowanie danych w pliku web.config, gdyż zawiera on dane poufne (chociażby konto emailowe, z którego wysyłane są automatyczne wiadomości z portalu do użytkowników). Żeby zaszyfrować odpowiednie sekcje pliku web.config dla aplikacji, która znajduje się w wirtualnym katalogu XXX, należy skorzystać z narzędzia aspnet_regiis i z linii poleceń wywołać następujące polecenie (przykład dotyczy sekcji connectionStrings):

 

aspnet_regiis -pe "connectionStrings" -app "/XXX" -prov "DataProtectionConfigurationProvider"

 

Narzędzie aspnet_regiis znajduje się w katalogu %windir%\Microsoft.Net\Framework\v.2.xxx.

 

W celu odszyfrowania pliku, należy skorzystać z innego polecenia: 

                 

                aspnet_regiis -pd "connectionStrings" -app "/XXX".

 

Klucz do szyfrowania i odszyfrowania jest przechowywany lokalnie na serwerze, a przechowywaniem go zajmuje się ASP .NET Framework. Programista nie musi się o niego martwić. Każda próba odszyfrowania pliku na innym komputerze zakończy się niepowodzeniem.

 

Korzystanie z Web Service'u z listą istniejących krajów

 

W projekcie realizowanym na potrzeby artykułu nie optymalizowałem użycia Web Service'u. Przy każdym załadowaniu strony z kontrolką przechowującą listę krajów, korzystam z serwisu „country” i pobieram z niego informacje. Docelowo w aplikacji, powinno się zapisać lokalnie listę krajów, np. w pliku i z niej korzystać. Można również w odpowiedniej sekcji pliku Global.asax, wczytać listę krajów raz, gdy pierwszy użytkownik jej zażąda i przechowywać ją w jakimś globalnym obiekcie.

 

 0x01 graphic

Rys.  16 Lista krajów pobranych z Web Serwice'u

 

Dla osób, które nie miały do czynienia z serwisami typu Web Service, powiem tylko, w jaki sposób się z nich korzysta. Portal konferencyjny używa serwisu, który znajduje się pod adresem http://www.webservicex.net/country.asmx. Żeby dodać Web Service do projektu i korzystać z jego metod lokalnie, należy kliknąć prawym przyciskiem na Add References w projekcie i wybrać Add Web Reference. W okienku specyfikuje się adres serwisu i… gotowe.

 

Prawa dostępu do katalogów portalu

 

Dla celów bezpieczeństwa i kontroli autoryzowanego dostępu do aplikacji, zostały nadane różne prawa dostępu do odpowiednich katalogów aplikacji. Przykładowo do katalogu register, mają dostęp anonimowi użytkownicy. Do katalogu Account, tylko uwierzytelnieni. Zaś do katalogu Admin, tylko uwierzytelnieni, posiadający nadaną rolę admin. O sekcjach authorization i authantication była mowa przy okazji omawiania kontrolek do uwierzytelniania.

 

Uruchamianie aplikacji portalowej na komputerze

 

Dostarczoną z artykułem aplikację portalu konferencyjnego można uruchomić bezpośrednio w VS 2005 jako aplikację działająca na „pseudo serwerze” dostarczonym z VS 2005. Więcej trudności sprawi uruchomienie portalu tak, aby był publicznie dostępny, czyli uruchomienie go na serwerze IIS. Poniżej opisuję czynności, niezbędne do osiągnięcia tego celu:

1.       Stworzyć nowy projekt New Web Site lokalnie na maszynie, np. http://localhost/portal. Katalogiem wirtualnym, jest portal.

2.       Skopiować dostarczone z artykułem pliki i wrzucić je do katalogu, który jest mapowany na katalog wirtualny nowoutworzonego projektu.

3.       Żeby uaktywnić tryb Debug aplikacji, prawym przyciskiem kliknąć na My Computer i wybrać Manage. Wybrać Service and Applications->Internet Inforation Services -> Web Sites -> Default Web Sites i wyedytować Properties katalogu /portal. W zakładce Directory Security kliknąć Edit i zaznaczyć Integrated Windosws Authentication. Zapamiętać zmiany.

4.        (Uwaga! Istnieją inne sposoby uruchamiania istniejącej aplikacji, jednak sposób podany powyżej jest najmniej zawodny zwłaszcza, jeśli chodzi o dostęp do bazy danych aspnetdb.mdf).

5.       Aplikacja dostarczana jest z kontem administratora (użytkownik z rolą admin), którego dane to: 

                Username: alamakota

                Password: alamakota.

Można z niego korzystać w celach testowych. Jeśli trzeba zdefiniować innego administratora, najprościej jest skorzystać z konsoli administracyjnej ASP .NET Configuration i opcji Create User.

6.       Należy skonfigurować serwer SMTP do wysyłania wiadomości email do użytkowników portalu (była o tym mowa wcześniej), gdyż dostarczony plik web.config posiada zaślepki.

7.       W pliku web.config zmienić ścieżkę uploadFolder, a folderowi nadać prawa Write i Read.

 

Zachęcam do zainstalowania aplikacji i do samodzielnej analizy kodu. Warto zarejestrować się jako nowy użytkownik i „pobawić” się dodawaniem i edycją danych. Później zachęcałbym także do wgłębnej analizy kodu odpowiedzialnej za zarządzanie kontami administratorów, gdyż tam zawarta jest prawdziwa wiedza dotycząca portalu internetowego.

  

6. Podsumowanie

 

ASP .NET 2.0 dostarcza potężnych mechanizmów do tworzenia portali internetowych. W artykule opowiedziałem o tym, co należy przyswoić, żeby przeprowadzić najprostsze scenariusze występujące na portalu.

Jeszcze raz zachęcam do analizy załączonej aplikacji i zapoznania się z informacjami uzupełniającymi zawartymi w bibliografii. Wiedza, którą starałem się podzielić, stanowi bardzo małą część tej, która pozwoli tworzyć w pełni profesjonalne portale internetowe.

W kolejnym artykule postaram się opowiedzieć o bardziej zaawansowanych mechanizmach, wykorzystywanych w ASP .NET 2.0 podczas tworzenia portali webowych.

 



Wyszukiwarka