如何构建积木式Web应用
生活随笔
收集整理的這篇文章主要介紹了
如何构建积木式Web应用
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
上下文 基本上我們在兒童時代都玩過積木玩具。通過一塊塊的積木,再加上我們的想象力,就可以構造出非常多不同的風格的建筑。那么, 我們可不可以把這種搭積木的方式應用到我們的web應用上呢。問題 web應用通過提供給用戶一整套組件(相當于積木),以及一套已經成型的方案(相當于圖紙)。用戶可以采用類似搭建積木的方式來根據自己的需要制作界面和應用。環境 采用asp.net 1.0或asp.net 1.1預備知識 C#,asp.net的服務器控件編程,asp.net服務器控件生命周期最好具備以下知識 Page Controller,Front Controller解決方案 1.建立目錄結構 為了了解如何采用積木塊式應用,我們首先建立如下的主題目錄結構: 在Sheme目錄下保存所有的主題,每個主題都采用相同的目錄結構。UserControl目錄保存最基本的積木塊(UserControl),Page目錄是積木塊組成的頁面所形成的UserControl,Css目錄保存與積木塊同名的css文件來控制UserControl的界面。 PageTeamplate.ascx是頁面布局框架,根據所請求的頁面不同,在PageTemplate的主體位置載入不同Page目錄下的ascx文件。2.劃分自己的web積木——UserControl 將web應用分成一塊塊的積木,每一塊積木形成一個UserControl,并且每一個UserControl有一個同名的css文件用來控制界面。最基本的積木塊要放在UserControl目錄下,而由最基本積木塊組成的頁面文件放在Page目錄下。 提示:在一般的web應用中,都會有Header,Footer,Login等等這樣的模塊,這些模塊就可以形成UserControl組成web應用的積木。具體積木塊應該根據你的web應用功能來劃分,一般來說我們可以把某個功能就劃分成一個積木。 3.構建載入積木塊的容器——MyPlaceHolder 有了UserControl這些積木塊之后,就需要有能夠自動在應用中載入這些UserControl積木的容器,這就是PlaceHolder。不過,我們需要擴展PlaceHolder的功能達到自動載入UserControl的目的。 public class MyPlaceHolder : PlaceHolder
{
private string userControl; // 要載入的UserControl目錄下的.ascx
private string pageControl; // 要載入的Page目錄下的.ascx
public MyPlaceHolder()
{
userControl = "";
pageControl = "";
}
public string UserControl
{
get
{
return userControl;
}
set
{
userControl = value;
}
}
public string PageControl
{
get
{
return pageControl;
}
set
{
pageControl = value;
}
}
// 當需要載入多個UserControl時,可以直接調用LoadUserControl
// 當只需要載入一個UserControl時,可以調用Clear清除載入過的內容
public void Clear()
{
this.Controls.Clear();
}
// 載入UserControl目錄下的.ascx
// 以及導入對應的css文件
public void LoadUserControl(string UserControl)
{
this.userControl = UserControl;
BasePage page = (BasePage)this.Page;
// 請參考后面的BasePage的代碼
Control control = this.Page.LoadControl(
page.Scheme + "usercontrol/" + userControl + ".ascx");
string css = "css/" + userControl + ".css";
// 對應的css文件
if(File.Exists(this.Page.MapPath(page.Scheme+css)))
{
page.AddCss(page.Scheme + css);
}
this.Controls.Add(control);
}
// 載入Page目錄下的.ascx
// LoadPage與LoadUserControl的區別是兩者載入的.ascx所在的目錄不同
// Page目錄下的.ascx可以看成是一些搭建主體結構的.ascx,其使用MyPlaceHolder
// 來包含最基礎的積木塊.ascx(在UserControl目錄下)
public void LoadPage(string PageControl)
{
this.PageControl = PageControl;
BasePage page = (BasePage)this.Page;
Control control = this.Page.LoadControl(
page.Scheme + "page/" + pageControl + ".ascx");
string css = "css/" + pageControl + ".css";
if(File.Exists(this.Page.MapPath(page.Scheme+css)))
{
page.AddCss(page.Scheme + css);
}
this.Controls.Add(control);
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if(!userControl.Equals(string.Empty))
{
LoadUserControl(userControl);
}
}
}
public class MyPlaceHolder : PlaceHolder
{
private string userControl; // 要載入的UserControl目錄下的.ascx
private string pageControl; // 要載入的Page目錄下的.ascx
public MyPlaceHolder()
{
userControl = "";
pageControl = "";
}
public string UserControl
{
get
{
return userControl;
}
set
{
userControl = value;
}
}
public string PageControl
{
get
{
return pageControl;
}
set
{
pageControl = value;
}
}
// 當需要載入多個UserControl時,可以直接調用LoadUserControl
// 當只需要載入一個UserControl時,可以調用Clear清除載入過的內容
public void Clear()
{
this.Controls.Clear();
}
// 載入UserControl目錄下的.ascx
// 以及導入對應的css文件
public void LoadUserControl(string UserControl)
{
this.userControl = UserControl;
BasePage page = (BasePage)this.Page;
// 請參考后面的BasePage的代碼
Control control = this.Page.LoadControl(
page.Scheme + "usercontrol/" + userControl + ".ascx");
string css = "css/" + userControl + ".css";
// 對應的css文件
if(File.Exists(this.Page.MapPath(page.Scheme+css)))
{
page.AddCss(page.Scheme + css);
}
this.Controls.Add(control);
}
// 載入Page目錄下的.ascx
// LoadPage與LoadUserControl的區別是兩者載入的.ascx所在的目錄不同
// Page目錄下的.ascx可以看成是一些搭建主體結構的.ascx,其使用MyPlaceHolder
// 來包含最基礎的積木塊.ascx(在UserControl目錄下)
public void LoadPage(string PageControl)
{
this.PageControl = PageControl;
BasePage page = (BasePage)this.Page;
Control control = this.Page.LoadControl(
page.Scheme + "page/" + pageControl + ".ascx");
string css = "css/" + pageControl + ".css";
if(File.Exists(this.Page.MapPath(page.Scheme+css)))
{
page.AddCss(page.Scheme + css);
}
this.Controls.Add(control);
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if(!userControl.Equals(string.Empty))
{
LoadUserControl(userControl);
}
}
}
使用方法:
<HomeOffice:MyPlaceHolder
id="Myplaceholder1"
runat="server"
UserControl="Header">
</HomeOffice:MyPlaceHolder>
// 這里的Header是位于UserControl目錄下的Header.ascx 4.構建主要的建筑結構——PageTemplate.ascx PageTemplate其實也是一個UserControl,只不過其功能是用來包含其他的UserControl積木,在PageTemplate里,可以定義頁面的整體布局。比如:Header、Footer在整個頁面中的位置,頁面主體區域的位置等等。 更重要的是,PageTemplate中應該包含Form的定義,這是asp.net所需要的不可缺少的服務器控件。 <%@ Register TagPrefix="HomeOffice"
Namespace="HomeOffice.Web.UI.WebControl"
Assembly = "HomeOffice.Web.UI" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>構建積木式應用程序</title>
<asp:Literal ID="CssHolder" runat="server"></asp:Literal>
<asp:Literal ID="ScriptHolder" Runat="server"></asp:Literal>
<style>
BODY { margin-left : 0px; margin-right : 0px; }
</style>
</HEAD>
<body bgcolor="#e6e6e6">
<form id="Form1" method="post" runat="server"
enctype="multipart/form-data">
<table width="100%" cellpadding="0" cellspacing="0">
<tr>
<td> </td>
<td width="800">
<table width="100%" cellpadding="0" cellspacing="0">
<tr>
<td>
<HomeOffice:MyPlaceHolder id="PlaceHolder1"
runat="server" UserControl="Header">
</HomeOffice:MyPlaceHolder>
</td>
</tr>
<tr>
<td>
<HomeOffice:MyPlaceHolder id="Myplaceholder1"
runat="server" UserControl="MainMenu">
</HomeOffice:MyPlaceHolder>
</td>
</tr>
<tr>
<td style="height:6px;background:#f6f6f6;font-size:1px;
border-top:1px solid white;">
</td>
</tr>
<tr>
<td style="height:4px;background:#e1e1e1;font-size:1px;
border-top:1px solid #e6e6e6; ">
</td>
</tr>
<tr>
<td style="background:white;border-bottom:1px solid #bbbbbb">
<HomeOffice:MyPlaceHolder id="PageBody" runat="server">
</HomeOffice:MyPlaceHolder>
</td>
</tr>
<tr>
<td style="padding-top:20px">
<HomeOffice:MyPlaceHolder id="Myplaceholder2"
runat="server" UserControl="Footer">
</HomeOffice:MyPlaceHolder>
</td>
</tr>
</table>
</td>
<td> </td>
</tr>
</table>
</form>
</body>
</HTML>
在這個PageTemplate中,我們可以看到使用了4個MyPlaceHolder來載入積木塊,除了PageBody這個外,其他的都載入了位于UserControl目錄下最基本的積木塊。而PageBody的作用則是根據http所請求的頁面不同載入Page目錄下不同的頁面。 這些其對應的cs代碼在BasePage中處理。 public class BasePage : Page
{
public string Scheme = "/Scheme/blue/"; // 所采用的主題
public AppSetting Setting;
// 環境配置,在Init中分析,其內容包括解析http請求到正確的Page目錄下的
// 文件,建立當前登陸用戶的信息
public Control focusControl; // 當頁面載入后,首先獲得焦點的控件
private Literal CssHolder; // 要導入的css
private Literal ScriptHolder; // 要導入的script文件
public BasePage()
{
focusControl = null;
}
// 導入css文件引用
public void AddScript(string script)
{
// 進行IsPostBack判斷的原因是
// 防止重復導入
if(!this.IsPostBack)
{
ScriptHolder.Text += string.Format("<script src=\"{0}\"
type=\"text/javascript\"></script>\n", script);
}
}
// 導入script文件引用
public void AddCss(string css)
{
if(!this.IsPostBack)
{
CssHolder.Text += "<link rel=\"stylesheet\"
type=\"text/css\" href=\"" + css + "\">\n";
}
}
// 載入http請求分析后的Page目錄下的所請求的文件
public void LoadPageTemplate()
{
Control control = (Control)this.LoadControl
(this.Scheme+"PageTemplate.ascx");
CssHolder = (Literal)control.FindControl("CssHolder");
ScriptHolder = (Literal)control.FindControl("ScriptHolder");
this.Controls.Add(control);
MyPlaceHolder body = (MyPlaceHolder)
control.FindControl("PageBody");
body.LoadPage(this.Setting.TargetPage);
// 調用MyPlaceHolder的LoadPage方法
// TargetPage記錄了請求的頁面
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
// 分析http請求
Setting = new AppSetting(this.Request.Path);
// 設置用戶信息
if(this.Request.IsAuthenticated)
{
Setting.SetUser(User.Identity.Name);
}
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.LoadPageTemplate();
}
// 當頁面顯示后,初始獲得焦點的控件
protected void SetFocusControl()
{
if(this.focusControl==null) return;
string template = @"<script language='jscript'>
document.all.{0}.focus();</script>";
string script = string.Format(template,
this.focusControl.ClientID);
this.RegisterStartupScript("FocusControl", script);
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
SetFocusControl();
}
// 修復了asp.net 1.1的一個bug
// 沒有這段代碼,LinkButton等某些服務器將無法使用
protected override void Render(HtmlTextWriter writer)
{
StringBuilder stringBuilder = new StringBuilder();
StringWriter stringWriter = new StringWriter(stringBuilder);
HtmlTextWriter htmlWriter = new HtmlTextWriter(stringWriter);
base.Render(htmlWriter);
string html = stringBuilder.ToString();
int start = html.IndexOf("<form name=\"") + 12;
int end = html.IndexOf("\"", start);
string formID = html.Substring(start, end - start);
string replace = formID.Replace(":", "_");
html = html.Replace("document."+formID,"document."+replace);
writer.Write(html);
}
}
BasePage中的一個非常重要的成員變量Setting,其作用是保存分析http請求后的結果。由于采用積木式應用,用戶所請求的aspx文件在硬盤上并不存在,需要把用戶的這種http請求解析成Page目錄下的某個ascx文件,讓BasePage載入。 5. 采用HttpHandler截獲http請求 有了這些基礎性的東西之后,我們就應該使用Front Contrller模式來控制所請求的aspx文件,再把這些請求的文件導向正確的UserControl積木。 .NET framework中的Page繼承了IhttpHandler,因此我們可以對aspx的解析就讓BasePage來處理,這樣BasePage在分析了http請求之后,會載入PageTemplate,然后再根據所請求的頁面載入不同的UserControl積木。 在web.config中進行如下配置,讓httphandler生效:
<httpHandlers>
<add verb="*" path="*.aspx" type="HomeOffice.Web.UI.HttpHandler.MyPageHandlerFactory, HomeOffice.Web.UI"/>
</httpHandlers>
我們在這里不是直接使用了HttpHandler,而是采用了HttpHandlerFactory,不過所使用的代碼也非常簡單,用戶自己可以擴展它的功能。 public class MyPageHandlerFactory : IHttpHandlerFactory
{
public virtual IHttpHandler GetHandler(HttpContext context,
String requestType,
String url,
String pathTranslated)
{
return new BasePage();
}
public virtual void ReleaseHandler(IHttpHandler handler)
{
}
} 為了要正確載入用戶所請求的頁面,需要對其http請求分析,這個功能放在AppSetting中實現,AppSetting還有一個重要功能就是將http請求字符串轉化為頁面中將要使用的參數。比如請求http://localhost/news/1.aspx,將分析出目標頁面為newsdetail.ascx,參數為new_id=1: public class AppSetting
{
public string Url; // request url
public string Site; // request site
public string User; // login name
public string UserName; // display name
public string TargetPage; // target page
public string[] Roles; // user roles in the site
private Hashtable parameter = new Hashtable();
public AppSetting()
{
Site = "default";
User = "*";
UserName = "";
Roles = null;
}
public AppSetting(string url) : this()
{
this.Url = url.ToLower();
AnalysisUrl(this.Url);
}
public object this[string key]
{
get { return parameter[key]; }
set { parameter.Add(key, value); }
}
// 分析用戶所請求的頁面和參數
protected void AnalysisPage(string url)
{
PageInfo[] pages = XmlHomeOffice.Pages();
foreach(PageInfo page in pages)
{
if(Regex.IsMatch(url,page.pattern,RegexOptions.IgnoreCase))
{
this.TargetPage = string.Format(page.target_page,
url.Replace(".aspx","").Split('/'));
if(!page.parameter.Equals(string.Empty))
{
string p = string.Format(page.parameter,
url.Replace(".aspx", "").Split('/'));
string[] ps = p.Split(',');
foreach(string str in ps)
{
string[] item = str.Split('=');
this[item[0]] = item[1];
}
}
return;
}
}
// No one matched, a Exception occur
this.TargetPage = "error";
}
// 分析出站點,類似于blog中的每個站點
protected string AnalysisSite(string url)
{
if(url[0]=='/')
{
url = url.Remove(0, 1);
}
string[] items = url.Split('/');
if(items.Length<1)
{
return Site + "/default.aspx";
}
if(items[0].EndsWith(".aspx"))
{
return Site + "/" + url;
}
string[] reserved_words = XmlHomeOffice.ReservedWords();
foreach(string str in reserved_words)
{
if(items[0].Equals(str.ToLower()))
{
return Site + "/" + url;
}
}
Site = items[0];
return url;
}
public void AnalysisUrl(string url)
{
Url = AnalysisSite(url);
AnalysisPage(Url);
}
public void SetUser(string user)
{
User = user;
UserName = XmlUsers.GetDisplayName(user);
XmlSiteProfile profile = new XmlSiteProfile(Site);
Roles = profile.SiteUserRole(user);
}
public bool HasRole(string role)
{
if(Roles==null) return false;
foreach(string str in Roles)
{
if(str.Equals(role)) return true;
}
return false;
}
}
在分析http請求的頁面時,要用到外部Xml文件記錄的pages映射信息: <pages>
<page pattern="^(\/testboth.aspx)$"
parameter="" target-page="testboth" />
<page pattern="^(\/\w+.aspx)$"
parameter="" target-page="{1}" />
</pages>
6.需要修改的服務器控件 由于采用了HttpHandler來映射新的頁面,因此象HyperLink,Image等這些與相對路徑有關的服務器控件就需要做個小手術來適應這種改變。 7.如何開發其他主題 要進行其他主題積木的開發,只需要開發界面工作即可。后臺代碼文件不需要開發。 Aspx文件前的指示語句為: <%@ Control Language="c#" AutoEventWireup="false"
Inherits="HomeOffice.UserControl.PageTemplate" %> 在這個指示語句里,不需要codehind屬性。其實現代碼已經實現在blue主題中。最好的方式是,將blue主題中的所有ascx的cs文件都獨立到一個UserControlCs目錄中。這樣在所有ascx文件中Inherits=”HomeOffice.UserControlCs.PateTemplate”。流程說明 以某個請求為例,對積木式web應用的流程進行說明 假設用戶請求localhost/default.aspx文件,在xml文件中分析得到default.aspx文件所對 應的處理ascx為Page目錄下的HomePage.ascx。 首先由BasePage載入PageTemplate.ascx,然后根據分析出來的TargetPage載入 HomePage。 具體應用 采用這種方式構建的web應用即將在www.smartyouth.net開通。 其主要功能有:積木式應用,風格多樣化(采用類似于windows主題的方式)。 優點討論 在采用積木式構造www.smartyouth.net過程中,發現了幾個小的優點: 1.可以統一的對應用中的權限進行認證。 實現一個權限認證UserControl,在該UserControl中判斷用戶是否具有某權限,是否符合某種角色,然后將認證的結果保存在BasePage中的IsAuthorized字段中,當認證不通過時顯示某些錯誤字符串。 對于任何需要權限認證的模塊,將該UserControl直接拖進設計器,并且正確的設置權限屬性和角色屬性即可。接著在需要認證的模塊中將某些Button的Enabled狀態設置為BasePage.IsAuthorized即可。 2.統一的Validator 由于asp.net的Validator模型有時候不是非常的方便,采用積木式應用,也可以統一的解決Validator。 實現一個Validator UserControl,在BasePage中用一個對象來記錄錯誤字符串和對應的Control。 在Validator UserControl的PreRender方法中,將記錄了錯誤的對象中所有的內容生成一些Link(當單擊該Link,對應的Control調用focus方法)。 在需要進行檢查的UserControl中,如果發生了錯誤,將錯誤字符串和Control.ClientID添加到錯誤對象中即可。下載 注意:下載的源代碼與示例代碼會存在少少出入,因為示例代碼來自一個更復雜些的 應用。如果需要使用下載代碼中的DataGrid示例,需要安裝SQL Server,并且 建立如下的數據庫,表,存儲過程:
數據庫:test
訪問賬號:sa,sa(可以在web.config中修改)
表:test
字段 test_id(自增),string(nvarchar(50)),number(int)
存儲過程:
testaddnew:(沒有參數)
insert into test(string, number) values(‘’, 0);
testget: (沒有參數)
select * from test
testdelete:(輸入參數:@test_id int)
delete from test where test_id = @teat_id
testupate:(參數:@test_id, @string, @number)
update test set string=@string, number=@number
where test_id=@test_id 采用如下方式訪問示例:(保證是localhost)http://localhost/testboth.aspx
http://localhost/testtextbox.aspx
http://localhost/testdatagrid.aspx
下載地址:http://www.smartyouth.net/scheme.rar
相關知識 使用 Microsoft .NET 的企業解決方案模式http://www.microsoft.com/china/msdn/architecture/patterns/Esp/
.Text Blog源代碼http://scottwater.com/DotText/default.aspx
http://localhost/testtextbox.aspx
http://localhost/testdatagrid.aspx
下載地址:http://www.smartyouth.net/scheme.rar
相關知識 使用 Microsoft .NET 的企業解決方案模式http://www.microsoft.com/china/msdn/architecture/patterns/Esp/
.Text Blog源代碼http://scottwater.com/DotText/default.aspx
轉載于:https://www.cnblogs.com/wzyexf/archive/2006/03/20/353672.html
總結
以上是生活随笔為你收集整理的如何构建积木式Web应用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: GitHub客户端使用
- 下一篇: 应用程序调试技术(更新程度:完毕)送源码