閱讀427 返回首頁    go 阿裏雲 go 技術社區[雲棲]


asp.net的MVC編程、MV編程以及URL重寫

 前一段時間做一個網站項目,使用win2003+.net2.0開發;在學習了一些.net的相關知識後,並考慮到此項目需要多人合作,以及架構清晰、URL重寫等優點,決定用MVC方式開發。但微軟的.net MVC框架據說要下半年才出正式版,而且還需要.net3.5,其他的MVC框架又不熟悉,估計也需要一段時間學習。由於開發時間比較緊,我們開發小組中也沒有一個對.net及.net MVC框架非常熟悉的人,所以又想轉回使用傳統的.net編程方式開發。

  在兩難之際,我想也許可以在項目需求出來前,自己試著寫一個MVC架構出來,因為以前曾用PHP和JSP寫過類似的MVC架構,而Web的運行環境和編程語言,相通的地方還是很多的,於是就有了下麵這個.net的MVC架構。

  一、MVC編程:

  針對用戶的瀏覽器來說,網站可以看作一個實體、一個接口,其接收瀏覽器的請求,並將相應的信息返回給瀏覽器;因此,網站程序完全可以用一個程序來完成,而實際上也確實如此,IIS、apache等web服務器本身就是一個程序,而運行其中的asp、aspx、php、jsp、html等等的單個頁麵,隻不過是幫助web服務器來實現一定功能而已。

  由此可以引申出:我們完全可以用一個aspx頁麵來處理針對網站的所有動態請求。

  而這個頁麵,我們就把它起名為index.aspx吧。

  在apache、tomcat等web服務器,都有相應的技術,將網站符合一定規則的所有http請求,都轉向一個程序頁麵(如index.jsp或index.php)來處理。而IIS在IIS7(前麵提到,我們的網站服務器是windows2003,IIS版本為6.0)出來以前,隻能借助於第三方組件實現。這其中比較有名的2個是ISAPI_Rewrite(Full版收費,Lite版免費)和IonicIsapiRewriter(免費),而對於我們這個MVC架構來說,ISAPI_Rewrite Lite版(這裏有ISAPI_Rewrite Lite版的下載,我們使用的是ISAPI_Rewrite3_0047_Lite.msi)就足夠了,由它來控製請求到我們的index.aspx。

  首先需要確定一下網站的目錄結構。為了使圖片、css文件、js文件、html文件等免於ISAPI_Rewrite處理,需在網站根目錄建立一個單獨的mvc目錄,其中存放MVC架構需要的aspx文件,從而網站的目錄結構如下:

       ROOT
  |--App_Code
  |--DotNetMVC
  |--DAL
  |--UserDA.cs
  |--Model
  |--User.cs
  |--Util
  |--ControllerUtil.cs
  |--DBUtil.cs
  |--Bin
  |--log4net.dll
  |--css
  |--images
  |--js
  |--mvc
  |--application
  |--controllers
  |-default.aspx
  |-user.aspx
  |--views
  |--default
  |-index_view.aspx
  |--share
  |--user
  |-home_view.aspx
  |-list_view.aspx
  |--cache
  |--log
  |-index.aspx
  |-Default.aspx
  |-Global.asax
  |-web.config

  上麵的結構中,css、images、js放什麼文件分別存放站點用的樣式文件、圖片文件、javascript腳本文件;Bin存放公共組件(目前有一個日誌組件log4net.dll,其配置信息在web.config配置文件中),App_Code存放自定義的公共類,這是.net2.0規定的,mvc目錄的作用上麵說了,不再贅述。

  下麵說說App_Code和mvc下的子目錄構成:

  App_Code->Util目錄,存放一些實用工具類,目前有2個,DBUtil.cs是SQLServer數據庫處理工具類,而ControllerUtil.cs則是我們這個MVC架構比較核心的URL路由工具類,其中ParseUri方法解析URI返回控製器、控製分支、參數等,LinkTo方法產生需要的網站鏈接URL(具體代碼參見DotNetMVC示例網站代碼)。大家還可以在其中添加一些需要的功能,如上傳、Email、字符串、分頁、圖形處理等。

  而App_Code->DAL和App_Code->Model目錄中的內容一起組成MVC架構中M層邏輯,其中Model目錄的僅僅是數據庫表的數據抽象類(這些類可以自己寫,也可以通過工具結合數據庫表自動生成),DAL目錄中的類則通過訪問數據庫返回數據抽象類的對象實例或實例列表,供控製層調用。

  mvc根目錄下的index.aspx文件,如上所述是MVC架構的入口文件。

  mvc->cache和mvc->log目錄分別存放緩存文件和日誌文件,如果需要的話。

  mvc->application目錄中的controllers目錄和views目錄分別是控製器文件和視圖文件,即MVC架構中的C和V層,其中一個控製器對應一個aspx文件(如user.aspx),並在views目錄中有一個視圖子目錄對應(如views->user),其中存放多個此控製器可能用到的視圖文件(如home_view.aspx和list_view.aspx等);views->share目錄中,則是一些視圖文件可能用到的公共文件,如頭尾包含頁、母板頁等。

  目錄結構介紹完了,下麵介紹單個的程序文件:

  前麵曾經提到,要把所有針對本站點的動態請求,都統一到index.aspx這個文件來集中處理,因此需要借助ISAPI_Rewrite,在ISAPI_Rewrite Lite的配置文件httpd.conf(位於ISAPI_Rewrite Lite的安裝目錄)中增加下麵一條記錄即可:

  RewriteRule /mvc/(.*)$ /mvc/index.aspx/?idu=$1 [NC,NE]

  這條規則是將針對/mvc這個URI及其後的所有訪問,都導向到index.aspx這個文件來處理,並將URI路徑作為idu參數的值傳給index.aspx。

  下麵看看index.aspx文件的內容:

<%@ Page Language="C#" %>
<%@ import namespace="System.Threading" %>
<%
     string controller;
     string method;
     string[] parameters;
     string uri_string = Request.QueryString["idu"];
     DotNetMVC.Util.ControllerUtil.ParseUri(uri_string, out controller, out method, out parameters);

     Context.Items.Add("method", method);
     Context.Items.Add("parameters", parameters);
    
     string ctlpage = "system/application/controllers/" + controller + ".aspx";
     try
     {
         Server.Transfer(ctlpage, true);
     }
     catch (HttpException e)
     {
         log4net.LogManager.GetLogger("LogFile").Error("/r/n客戶機IP:" + Request.UserHostAddress + "/r/n錯誤地址:" + Request.Url, e);
     }
%>

  上麵代碼的關鍵是,通過對idu值的解析,取得controller、method、parameters這3個參數;其中controller對應於一個控製器文件,method對應控製器中的一個控製分支,parameters則是參數的字符串數組(參數順序自己把握),其對應URI如下:

  /mvc/controller/method/parameter1/parameter2/parameterN

  目前這個MVC架構的URI,都是一個控製器,後跟一個分支,再後跟多個參數,如果沒有controller、method、parameters,則默認調用default控製器的index分支,如果沒有method、parameters,則默認調用本控製器的index分支;如果以上不滿足要求,大家可以自己更改ControllerUtil類的ParseUri方法。Server.Transfer方法的第2個參數為true,是確保request、cookie、session等變量能夠傳遞到控製器程序中。

  下麵通過default.aspx這個控製器(可以一個功能或一個模塊作為一個控製器)說明一下控製器的流程:

<%@ Page Language="C#" %>
<%@ import namespace="DotNetMVC.Util" %>
<%
string method = (string)Context.Items["method"];
string[] parameters = (string[])Context.Items["parameters"];
try
{
     if (method == "index")
     {
         Context.Items.Add("loginurl", ControllerUtil.LinkTo("user", "login", ""));
         Context.Items.Add("userlisturl", ControllerUtil.LinkTo("user", "list", ""));
         Server.Execute("application/views/default/index_view.aspx");
     }
}
catch (HttpException e)
{
     log4net.LogManager.GetLogger("LogFile").Error("/r/n客戶機IP:" + Request.UserHostAddress + "/r/n錯誤地址:" + Request.Url, e);
}
%>

  上麵代碼中,首先通過Context獲取index.aspx中得到的method、parameters參數,然後根據method值的不同,走不同的處理分支(這裏可以調用M層獲取數據)產生數據並保存在Context(目前僅找到用Context來傳遞數據到視圖)中,再調用不同的視圖;這裏隻有一個默認index分支,去調用首頁視圖index_view.aspx。

  最後看看views->default目錄下index_view.aspx這個視圖文件內容:

<%@ Page Language="C#" EnableViewStateMac="false"%>
<%
     string loginurl = (string)Context.Items["loginurl"];
     string userlisturl = (string)Context.Items["userlisturl"];
%>
<!-- #include -->
<center>
<form name=frm1 method=post action=<%=loginurl %>>
昵稱:<input type=text name=nick size=20>&nbsp;&nbsp;
密碼:<input type=password name=pass size=20>
<input type=submit value=登陸> &nbsp;<a href=<%=userlisturl %>>去用戶列表</a>
</form>
</center>
<!-- #include -->

  這個視圖文件中的代碼則通過Context接收控製器傳過來的數據並顯示它。

  至此,整個MVC的運轉流程就完成了;其他更複雜的一些操作,大家可以參照DotNetMVC示例網站代碼,相信根據這個示例,就可以直接建立並快速開發自己的MVC網站了,並有URL重寫功能;在其中的user控製器及視圖中,大家可以看到這種編程有一個好處,那就是可以充分利用visual studio的代碼智能感知功能。

  不過,有一點需要大家注意,因為需要經過ISAPI_Rewrite過濾器與Transfer、Execute處理,所以會有一些性能損失。根據流程的複雜程度,其損失所占比重會有所不同:越複雜的流程,其損失所占比重越小,越簡單的流程,其損失所占比重越大;所以大家在用這套流程開發網站時,應充分注意這一點,並適當進行程序優化,或者幹脆別用它了。

  二、MV編程:

  大家在看上麵的DotNetMVC示例網站代碼的時候,肯定有一個感覺:在控製層產生數據,要賦給Context,視圖層再從Context中取出數據來顯示,似乎多此一舉;所以引出下麵的MV編程(可能會喪失一些靈活性和可維護性,但開發一般的網站應該足夠了)。

  我們隻需要把DotNetMVC示例網站代碼中的代碼簡單做一下修改就可以了。

  首先,修改index.aspx如下:

<%@ Page Language="C#" %>
<%@ import namespace="System.Threading" %>
<%
     string controller;
     string method;
     string[] parameters;
     string uri_string = Request.QueryString["idu"];
     DotNetMVC.Util.ControllerUtil.ParseUri(uri_string, out controller, out method, out parameters);

     Context.Items.Add("method", method);
     Context.Items.Add("parameters", parameters);

     string ctlpage = "application/views/" + controller + "/" + method + ".aspx";
     try
     {
         //Server.Transfer(ctlpage, true);
         Server.Execute(ctlpage);
     }
     catch (HttpException e)
     {
         log4net.LogManager.GetLogger("LogFile").Error("/r/n客戶機IP:" + Request.UserHostAddress + "/r/n錯誤地址:" + Request.Url, e);
     }
%>

  這裏直接用controller和method組合出視圖路徑,並用Server.Execute去執行它(因為Server.Transfer總是拋出System.Threading.ThreadAbortException異常,雖然不影響運行,這似乎是asp.net的一個小問題)。

  然後刪除控製器目錄的內容,並將控製器頁麵中的代碼轉移到視圖中即可,具體請參看DotNetMV示例網站代碼。這樣,既減少了編程複雜度,又增加的開發效率和運行性能,同時URL重寫和代碼智能感知等優點仍然得以保留。

  附:

  下載DotNetMVC示例網站代碼和DotNetMV示例網站代碼到本地後,請注意示例網站代碼是一個完整的網站架構,大家可以直接在VS中建立一個新網站(VS2005和VS2008),清空VS自動產生的內容,然後將代碼拷入即可,根據實際修改web.config文件;運行調試前,請先安裝ISAPI_RewriteLite,並修改其配置文件;調試時,請注意修改站點的“啟動選項”使用本地IIS,因為VS中的DevelopWebServer無法加載ISAPI_Rewrite過濾器。

  另外,測試數據表建立腳本給出如下:

CREATE TABLE [dbo].[users](
     [id] [int] IDENTITY(1,1) NOT NULL,
     [nick] [varchar](50) COLLATE Chinese_PRC_CI_AS NULL,
     [pass] [varchar](20) COLLATE Chinese_PRC_CI_AS NULL,
     [reg_date] [datetime] NULL CONSTRAINT [DF_test_inputdate]   DEFAULT (getdate())
) ON [PRIMARY]

最後更新:2017-04-02 00:06:30

  上一篇:go 幾個重要搜索引擎的網站提交地址
  下一篇:go 用ASP.NET加密Cookie數據