V této čtyř dílné sérii budu postupně popisovat postup vytvoření jednoduché ASP.NET aplikace pro umožnění přístupu k souborům pro přihlášené uživatele. Nepůjde jen o aplikaci samotnou, ale hlavně si ukážeme některé záležitosti dnešních ASP.NET WebForms aplikací. Některé řešené věci budou určitě využitelné i v jiných aplikacích. Také je možné, že některé tyto záležitosti mnozí z vás znáte (nebo je řešíte trochu jinak), ale věřím, že někteří si některé novinky z této série odnesou.
Zadání aplikace
Popíšeme si co od naší vzorové aplikace očekáváme.
Jedná se o aplikaci, která bude hlídat a zajišťovat přistup k souborů v určené složce. Po vstupu do aplikace bude po uživateli nejprve na přihlašovací stránce požadováno přihlášení. Po úspěšném přihlášení uživatele bude povolen přístup ke hlavní stránce aplikace, která nabídne seznam souborů ze složky a umožní jejich stahování.
Abychom nemuseli uživatele, kteří budou mít přístup k souborům někde spravovat a udržovat (například v databázi), budeme v příkladu uživatele ověřovat proti Windows účtům. K tomu zde s výhodou využijeme technologii WIF (podle postupu z příspěvku Windows Identity Foundation: Náhrada za Forms autentizaci).
Z obecných požadavků na aplikaci ještě doplním:
- Použité technologie .NET 4.0, ASP.NET WebForms, IIS 7/7.5
- Aplikace bude používat URL Routing a URL budou bez přípon .aspx.
- Aplikace bude přizpůsobena pro přístup z Internetu (budeme implementovat několik málo SEO principů).
- Aplikace bude pro design používat jednoduché HTML/CSS a kde to bude možné tak bez nutnosti Javascriptu tj. bez jquery-ui a podobných Frameworků. (V aplikaci bude na jednom místě Javascript použit).
- Případné chyby v aplikaci budeme logovat a uživateli zobrazíme pouze informaci, že došlo k neočekávané chybě.
Kompletní zdrojové soubory celé aplikace jsou k dispozici zde. Na některé soubory z tohoto archivu, které by neměli cenu celé popisovat, se budu v textu této série pouze odkazovat.
Vytvoření projektu
Začneme vytvořením nového projektu ve Visual Studiu (2010 nebo 2012). Zvolíme .NET Framework 4.0 a šablonu ASP.NET Empty Web Application, aplikaci nazveme například FileAccessWeb.
Pozn.: Volíme šablonu ASP.NET Empty Web Application ze dvou důvodů. Jednak je pro pochopení funkčnosti jednodušší konkrétní věci postupně přidávat, a jednak se mi v poslední době výchozí šablona Web Application zdá již poměrně moc obsáhlá věcmi, které většinou nepotřebuji, nebo dělám jinak.
Ve vlastnostech projektu dále nastavíme spouštění z lokálního IIS (7 / 7.5), případně IISExpress. (V případě IIS můžeme použít výchozí ASP.NET v4.0 pool).
Hlavní stránka Master Page
Vytvoříme hlavní stránku Master Page s názvem Site.Master. A v HTML vytvoříme hlavní layout celé aplikace.
<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.master.cs" Inherits="FileAccessWeb.Site" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head" runat="server">
<meta http-equiv="X-UA-Compatible" content="IE=9" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="author" content="All: IMP spol. s r. o." />
<meta name="copyright" content="(C) 2012 IMP spol. s r. o." />
<link rel="shortcut icon" href="favicon.ico" />
<link rel="stylesheet" type="text/css" media="screen" href="css/Site.css" />
<link rel="stylesheet" type="text/css" media="screen" href="css/StyleSheet.css" />
<asp:ContentPlaceHolder ID="HeadContent" runat="server">
</asp:ContentPlaceHolder>
</head>
<body>
<form id="form" runat="server">
<div id="header">
<div id="main" class="clearfix">
<div id="header_inner">
<table id="headertable" cellpadding="0" cellspacing="0">
<tr>
<td>
<h1><asp:Label ID="PageTitleLabel" runat="server" ViewStateMode="Disabled"></asp:Label></h1>
</td>
<td id="header_right">
<asp:LoginView ID="loginView" runat="server" ViewStateMode="Disabled">
<LoggedInTemplate>
<asp:Label ID="UserNameLabel" runat="server"></asp:Label><br />
<asp:LoginStatus ID="loginStatus" LogoutText="Odhlásit" CssClass="signout" runat="server" OnLoggingOut="loginStatus_LoggingOut"/>
</LoggedInTemplate>
</asp:LoginView>
</td>
</tr>
</table>
</div>
<div id="container">
<asp:ContentPlaceHolder ID="MainContent" runat="server" />
</div>
</div>
</div>
<div id="footer">
<div id="footer_inner" class="clearfix">
<div id="cleft">
Vytvořil: <a href="http://www.imp.cz" target="_blank" title="Přejít na IMP / www.imp.cz">© 2012 IMP spol. s r.o.</a>
</div>
<div id="cright">
<a href="http://blog.imp.cz" title="" target="_blank">http://blog.imp.cz</a>
</div>
</div>
</div>
</form>
</body>
</html>
V hlavičce jsou nastaveny dva CSS soubory. Zatím budeme ale potřebovat pouze Site.css. Vytvoříme tedy podadresář Css a Site.css do adresáře zkopírujte (naleznete ho v archivu celé aplikace, nebo si ho můžete prohlédnou zde). Do souboru Site.css jsem kromě stylů použité ve stránce Site.Master umístil i další základní styly aplikace. Při tvorbě webové aplikace se dnes totiž nemůžeme spoléhat na výchozí hodnoty elementů CSS, protože každý prohlížeč je má jiné. Musíme tedy vždy nastavit i těmto elementům jako html, body, form, a, h1, h2, h3, h4, h5 základní styly jako margin, padding, font-size, line-height atd. Neuvedení těchto stylů základních elementů je mnohdy důvod velkých rozdílů v zobrazování stránek v různých prohlížečích.
Pozn.: Pokud často testujete zobrazení stránky ve více prohlížečích, doporučuji se také podívat na projekt http://multi-browser.com.
Za zmínku stojí také nastavení http-equiv meta tagu X-UA-Compatible. Ten určuje compatibility level pro IE prohlížeče. Jedná se o to, že v některých prostředí, konkrétně intranetové prostředí s doménou, je u IE ve výchozím nastavením automaticky zapínán compatibility view mód a tím je stránka zobrazena leckdy špatně (je dokonce ignorován i nastavený DOCTYPE). Popravdě nechápu smysl této funkce, když jsem to viděl poprvé, tak jsme nemohl dlouho přijít na důvod, proč je stránka po nasazení na produkční prostředí nejednou zobrazována jinak. Každopádně nastavením X-UA-Compatible se IE compatibility view mód potlačí (zároveň se skryje i tlačítko, které ho umožňuje zapnout ručně). Více o volbě v IE zde a o nastavení meta tagu zde.
Do projektu dále také z archivu celé aplikace přidáme další odkazované soubory jako favicon.ico a Images/headerside.png (odkazovaný ve stylu #header).
Layout stránky je rozdělen na části header, main a footer. V hlavičce je vlevo vypisován titulek jednotlivých stránek a doprava je umístěn kontrol LoginView , který bude zobrazovat jméno přihlášeného uživatele a odkaz na odhlášení. Do code behind Master page pro toto doplníme potřebný kód
(kód loginStatus_LoggingOut doplníme později):
public partial class Site : System.Web.UI.MasterPage
{
protected override void OnPreRender(EventArgs e)
{
PageTitleLabel.Text = Page.Title;
}
protected void loginStatus_LoggingOut(object sender, LoginCancelEventArgs e)
{
}
}
Abychom mohli layout vyzkoušet, vytvoříme novou prázdnou stránku, volbou Web Form using Master Page a vybereme naší Site.Master, stránku pojmenujeme Default.aspx. Do ní nám zatím stačí doplnit Title jak v hlavičce, tak i v definici Page, odkud se kódem v Master page vypisuje.
<%@ Page Title="File Access Web" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="FileAccessWeb.Default" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
<title>File Access Web</title>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
</asp:Content>
Stejným postupem ještě založíme jednu stránku s názvem Login.aspx a opět vyplníme titulek například na “File Access Web – Login”.
Routing
Poslední věcí co v této části provedeme bude zavedení routingu na stránky aplikace. To provedeme v souboru Global.asax, který do projektu vytvoříme. V Application_Start provedeme registraci výchozí (prázdné) adresy a adresy Login.
Soubor bude vypadat takto:
public class Global : System.Web.HttpApplication
{
#region event handlers
protected void Application_Start(object sender, EventArgs e)
{
//Register routes
RouteTable.Routes.MapPageRoute("Default", "", "~/Default.aspx");
RouteTable.Routes.MapPageRoute("Login", "Login", "~/Login.aspx");
}
protected void Application_BeginRequest(Object sender, EventArgs e)
{
if (HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath == "~/" && !HttpContext.Current.Request.Path.EndsWith("/"))
{
Response.RedirectToRoute("");
return;
}
if (HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.Equals("~/default.aspx", StringComparison.OrdinalIgnoreCase))
{
Response.RedirectToRoute("");
return;
}
}
#endregion
}
Ještě si vysvětlíme přidaný kód v Application_BeginRequest, ten se nám postará o to, že pokud jdeme na web adresou stránky default.aspx (http://FileAccessWeb/Default.aspx), tak provedeme redirect výchozí routou a tím skončíme na adrese http://FileAccessWeb/. Horní část kódu ještě ošetřuje přístup adresou http://FileAccessWeb (bez lomítka), přejde opět na http://FileAccessWeb/.
Abychom stránky nemohli vyvolávat pomoci adresy s jejich fyzickém umístěním (například http://FileAccessWeb/Login.aspx), zablokujeme přístup na soubory podle přípony .aspx. Provedeme to nastavením ve Web.config souboru, do kterého přidáme následující sekci system.webServer:
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<handlers>
<add name="AspxBlockHandler" path="*.aspx" verb="*" type="IMP.Web.NotFoundHandler" />
</handlers>
<defaultDocument>
<files>
<clear />
<add value="Default" />
</files>
</defaultDocument>
</system.webServer>
Důležitá je zde podsekce handlers , ve které zaregistrujeme cestu *.aspx. Pro ní budeme volat náš handler NotFoundHandler, ten bude pouze vracet StatusCode 404 (stránka nenalezena). Tuto třídu handleru vložíme do projektu.
public class NotFoundHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.StatusCode = 404;
context.Response.StatusDescription = "Not Found";
}
public bool IsReusable
{
get { return true; }
}
}
Nyní nám tedy v aplikaci fungují dvě stránky – výchozí přes URL http://FileAccessWeb/ a také adresou http://FileAccessWeb/Default.aspx, kdy dojde k přesměrování na /. Druhá stránka pro přihlášení je přístupná na adrese http://FileAccessWeb/Login a adresa na fyzický soubor login.aspx nám vrací požadovanou chybu
404 - Not Found.
Příště budeme pokračovat zavedením mechanizmu pro autentizaci uživatelů.