基本上計算日期的差距可以在後端的程式裡或是在資料庫中就可以解決,
但有些時候資料只會在前端出現而未後送到Server Side,或是說不想增加資料庫或是Server Side的負載,
所以就會將這種單純的顯示日期時間差距的計算工作給放到前端去做,
這樣做其實也是有好處的,好處就是讓Client Side去做這類瑣碎的計算,
因為不牽涉到太複雜的邏輯計算,也算是分擔後端的壓力。
所以接下來就會介紹三種方式來達成在Client Side去計算日期時間差距的功能。
基本上計算日期的差距可以在後端的程式裡或是在資料庫中就可以解決,
但有些時候資料只會在前端出現而未後送到Server Side,或是說不想增加資料庫或是Server Side的負載,
所以就會將這種單純的顯示日期時間差距的計算工作給放到前端去做,
這樣做其實也是有好處的,好處就是讓Client Side去做這類瑣碎的計算,
因為不牽涉到太複雜的邏輯計算,也算是分擔後端的壓力。
所以接下來就會介紹三種方式來達成在Client Side去計算日期時間差距的功能。
上一篇「ASP.NET MVC, JSON資料的日期轉換與格式化 1」說明了後端序列化的JSON日期資料要如何轉換以及格式化,
然而我們在後端所用的序列化JSON是直接使用ASP.NET MVC的預設內部方法,也就是用JavascriptSerializer來做序列化,
而之前有好幾篇文章也有說明到各種的在後端序列化的方式,
所以這一篇我們就來看看各種的序列化方式的JSON日期轉換與格式化。
前面有好幾篇文章是說明在後端的程式中要如何去對物件資料序列化為JSON,
而物件資料序列化為JSON之後就是要傳給前端去使用,然而JSON資料卻會有個小小的問題存在,
JSON規格中沒有對日期時間的型別去做定義,在 .NET 環境下的日期時間資料在序列化之後卻會有個特別的表示方法,
讓使用者知道某個欄位的資料是個日期型別,
如果是直接已JsonResult的Json方法做資料的序列化,則日期型別的資料會是以下格式:
\/Date(1316747376403)\/
如果是用Json.NET的JsonConvert.SerializeObject()來序列化,則日期型別的資料會是以下格式:
\/Date(1316747376403+0800)
\/
除非是在後端的程式中就已經對日期型別的資料去做處理,不然傳到前端的日期資料都還是要再做個處理。
如果使用TagBuilder的地方是在ASP.NET MVC2的網站專案下,是可以直接使用TagBuilder,
而要是將有使用到TagBuilder的類別、Helper的程式放在另外的類別庫專案,也只需要去加入System.Web.MVC的參考,
在ASP.NET MVC 2 裡,TagBuilder的命名空間是「System.Web.Mvc」而組件是「System.Web.Mvc.dll」
但是到了ASP.NET MVC 3 後就有了改變。
接續「ASP.NET MVC + JSON 自定義JsonResult 1」的內容,
上一篇文章的最後有說,Json.NET所提供的 JsonNetResult類別並不是很適合拿來使用,
而在「使用Entity Framework 將物件轉為JSON時遇到循環參考錯誤 3」裡面,我們也建立了兩個JavaScriptConverter類別,
所以我們就自己來建立一個自己定義的JsonResult,並且使用之前所建立EFJavaScriptSerializer,
這個 EFJavaScriptSerializer 可以分別依據狀況使用 EFSimpleJavaScriptConverter 或 EFJavaScriptConverter…
在「使用Entity Framework 將物件轉為JSON時遇到循環參考錯誤 1」裡面有提到,
如果把EF取出的物件不做任何處理然後在JsonResult中直接使用 Json()方法傳回,會出現「循環參考」的錯誤,
而文章中有提到說可以使用Json.NET所提供的 JsonConvert.SerializeObject()方法來回傳Json資料,
但是必須要使用ActionResult的類別來回傳,而且為了讓Content-Type正確,也需要指定Content-Type,如以下的程式:
public ActionResult Product(int? id)
{
if (!id.HasValue)
{
Dictionary<string, string> jo = new Dictionary<string, string>();
jo.Add("Msg", "請輸入產品ID編號.");
return Content(JsonConvert.SerializeObject(jo), "application/json");
}
else
{
ProductService service = new ProductService();
var product = service.Single(id.Value);
return Content(JsonConvert.SerializeObject(product), "application/json");
}
}
使用了Json.NET來做為序列化JSON資料的工具,而Json.NET的官網上面也有提供了一個JsonNetResult的類別,
這樣一來在Action就不用再多一個動作去指定使用JsonConvert.Serialize() 方法來序列化資料,
另外也不用使用回傳類別ActionResult,也不用再去指定Content-Type。
接續「使用Entity Framework 將物件轉為JSON時遇到循環參考錯誤 2」
上一篇是說明如何去修改Edmx的屬性「消極式載入已啟用」設定
以及在不修改Edmx的屬性「消極式載入已啟用」的情況下於程式中將LazyLoadingEnable設定為false,
透過這些的修改方式讓物件於序列化為JSON時不去包含或是不載入關連的物件資料。
而這一篇就來看看使用自定義的JavaScriptConverter類別,在不修改上述兩項設定的情形下,
如何做到只序列化物件本身的資料以及序列化物件本身以及其關連的物件資料。
接續「使用Entity Framework 將物件轉為JSON時遇到循環參考錯誤 1」
public ActionResult Product(int? id)
{
if (!id.HasValue)
{
Dictionary<string, string> jo = new Dictionary<string, string>();
jo.Add("Msg", "請輸入產品ID編號.");
return Content(JsonConvert.SerializeObject(jo), "application/json");
}
else
{
ProductService service = new ProductService();
var product = service.Single(id.Value);
var result = new
{
ProductID = product.ProductID,
ProductName = product.ProductName,
SupplierID = product.SupplierID,
CategoryID = product.CategoryID,
CategoryName = product.Category.CategoryName,
QuantityPerUnit = product.QuantityPerUnit,
UnitPrice = product.UnitPrice,
UnitsInStock = product.UnitsInStock,
UnitsOnOrder = product.UnitsOnOrder,
ReorderLevel = product.ReorderLevel
};
return Content(JsonConvert.SerializeObject(result), "application/json");
}
}
上面的程式是把Product物件取出後,再去指定要序列化為JSON時所要用的欄位,
但如果每次要對物件做序列化時都要這樣手動去指定也算是一件蠻累人的事情,
尤其是欄位多的時候以及一個物件會在多個地方去做序列化為JSON的操作時,一再重複這樣的事情就會覺得煩。
在ASP.NET MVC的Controller中會經常使用到JSON,通常都是前後端的資料處理,
後端產生物件資料後再轉為JSON傳到前端做AJAX的處理,而後端也會接收前端所傳來的JSON資料,
而ASP.NET MVC的Controller有很多的傳回類別,其中一個有關JSON的就是「JsonResult」,
JsonResult類別
http://msdn.microsoft.com/zh-tw/library/system.web.mvc.jsonresult.aspx
表示用來將JSON格式之內容傳送至回應的類別。(繼承自抽象類別ActionResult)
如果要經由Json(object, JsonRequestBehavior.AllowGet)的方法去傳回JsonResult,結構簡單的物件是不會發生問題的,
例如:
public JsonResult Product(int? id)
{
if (!id.HasValue)
{
Dictionary<string, string> jo = new Dictionary<string, string>();
jo.Add("Msg", "請輸入產品ID編號.");
return Json(jo, JsonRequestBehavior.AllowGet);
}
else
{
ProductService service = new ProductService();
var product = service.Single(id.Value);
var result = new
{
ID = product.ProductID,
Name = product.ProductName,
CategoryID = product.CategoryID
};
return Json(result, JsonRequestBehavior.AllowGet);
}
}
結果:
但如果我們所取得的物件資料不會再去做物件欄位的選擇,而是直接將整個物件放到Json(object)方法中,
而Model的資料模型是使用ASO.NET Entity Framework,各個資料表都有設定關連時,這個時候就很容易出現以下的錯誤:
是的,出現了循環參考的錯誤!怎解呢?
因為有人問起,再加上自己以往沒有做過,所以就來試試看,其實這一題只是將以往的作法給稍微改變就可以了,
以往很多人使用Web Service的方式都是直接在Web專案中加入服務參考,
但是這樣會有幾個問題就是:
如果這個Web Service會在多個專案中去使用到的話,那麼每個專案都要做同樣的動作…
如果使用Web Service還會再去封裝一些方法,每個專案去加入服務參考後還要再去複製貼上已經做好的方法,這種複製貼上不是正確的寫程式方式…
所以接下來就看看這練習題要如何做。
透過Mail target將Log訊息寄發到指定的Email中
在之前介紹的ELMAH文章中「ASP.NET MVC + ELMAH 監控並記錄你的網站錯誤資訊 3」有介紹到,當系統發生了 Exception 狀況時,就會寄發通知信件到指定的 Email 中,雖然說我們可以在某些特定的 Exception 發生時在去寄發通知(詳見「ELMAH - 自訂錯誤通知郵件的設定(於特定Exception)」),但是只依據特定的 Exception 種類來通知,光是設定就很累人,因為Exception的種類有很多,並非我們可以完全掌握住。
最好的方式就是在程式之中,於捕捉 Exception 的程式碼去使用 NLog 記錄Exception訊息,並且做好 Exception 的Log 分級,然後在 NLog.Config 中再去做設定,當 Log 於什麼樣的等級發生時就去使用 Mail target 寄發通知給特定人員的 Email 信箱中。
接下來就會說明如何設定並透過Mail Target 將Log訊息寄發給特定人員。
雖然NLog提供的Layout Renderers已經相當多種了,
而且如果要記錄ASP.NET Request ServerVariable的資料,可以使用:${aspnet-request : serverVariable=String} ,
但是Request.ServerVariable有相當多種,除非你相當有耐心,不然一個一個變數去加入NLog.Config中也是一件累人的事情,
所以如果可以像ELMAH一般,預設就是把所有的Request.ServerVariable給記錄下來,
我們可以擴展自定義的Layout Renderer來記錄這所有的Request.ServerVariable資料。
在NLog中可以透過${aspnet-request}的layout來取得用戶端所送出的資料,包含有:
cookie, serverVariable, queryString, item, form
參考連結:NLog Documentation - Aspnet-request layout renderer
而其中最常用到的是serverVariable,這是用來取得Web伺服器的環境變數,而伺服器環境變數有相當多,
如以下所示:
ALL_HTTP:傳送到客戶端的所有HTTP header資料
ALL_RAW:傳送到客戶端的所有資料(以raw的資料格式)
APPL_MD_PATH:傳回伺服端的metabase路徑
APPL_PHYSICAL_PATH:將APPL_MD_PATH轉換成為實際的路徑傳回
AUTH_PASSWORD:傳回客戶端使用者在確認對話框中所輸入的密碼
AUTH_TYPE:傳回客戶端認證的方法
AUTH_USER:傳回客戶端確認在確認對話框中所輸入的使用者名稱
CONTENT_LENGTH:傳回content的資料長度
CONTENT_TYPE:傳回客戶端文件傳送的型態,如GET或POST等...
GATEWAY_INTERFACE:傳回伺服端的CGI版本
HTTP_:傳回使用者自建的HTTP Header資料
LOCAL_ADDR:傳回伺服端電腦的IP位址
LOGON_USER:傳回登錄Windows NT的使用者資訊
PATH_INFO:取得目前網頁的虛擬路徑
PATH_TRANSLATED: 目前執行的ASP程式,位於伺服端的真實路徑
QUERY_STRING:傳回在HTTP://後以?所傳遞的參數資料
REMOTE_ADDR:遠端主機的IP位址
REMOTE_HOST:遠端主機的名稱
REMOTE_USER:遠端的使用者名稱
REQUEST_METHOD:傳回HTTP的請求方式,如GET或POST等...
SCRIPT_NAME:被執行的ASP檔案完整的虛擬路徑
SERVER_NAME:傳回網頁伺服端的電腦名稱,DNS或IP位址
SERVER_PORT:伺服端HTTP的埠(Port)
SERVER_PORT_SECURE:傳回客戶端是否指定安全的埠,是則為1,否為0
SERVER_PROTOCOL:取得HTTP的版本
SERVER_SOFTWARE:取得網頁伺服器的名稱與版本
URL:取得目前網頁虛擬路徑的儲存位址
參考連結:MSDN - IIS Server Variables
然而我在NLog.Config中去指定取得一個serverVariable,指定取得目前網頁的網址:
<parameter name="@url" layout="${aspnet-request:serverVariable=url}" />
所出現的錯誤:
嘗試建立型別 'Test.Web.Controllers.HomeController' 的控制器時發生錯誤。請確認此控制器是否有無參數公用建構函式。
再來看看ELMAH所記錄到的詳細錯誤資訊:
type="System.ArgumentException"message="LayoutRenderer cannot be found: 'aspnet-request'"source="NLog"detail="System.InvalidOperationException: 嘗試建立型別 'Test.Web.Controllers.HomeController' 的控制器時發生錯誤。請確認此控制器是否有無參數公用建構函式。
看來這是NLog設定發生問題而出現的錯誤…
在上一篇文章「使用NLog - Advanced .NET Logging (1)」裡面,我們是把Log資料給存放到文字檔中,
文章範例程式中的NLog.Config,有兩個target type是使用了File而另一個是使用EventLog,
其實NLog的taget有預設提供了多達25種,但不見得每個都有機會去使用到,
而且你還可以依據NLog提供的API,讓你可以建立你自己想要的target,
除了已經介紹的File與EventLog外,最常用的也是最多人想要使用的就是「Database」。
以下就將會介紹如何修改target設定,把Log資料存放到資料庫中。
在前面的幾篇文章已經介紹過如何在網站中使用 ELMAH 來記錄與追蹤各種的訊息與錯誤,ELMAH 已經可以達到Logging 的功能要求,但是如果需要對各種記錄去做分級處理的話,使用 ELMAH 就不是那麼方便,一般而言,網站中最好除了使用 ELMAH 外,最好再使用另一套 Logging 的套件,ELMAH 是用來作為日誌記錄,主要工作是捕捉 UnHandler Exception(未處理的異常),所以有處理的(try…catch..)異常就無法捕捉。
NLog就是讓我們可以在程式裡去處理異常時,給予不同的等級後,NLog再依據使用者所定義各等級的不同處理方式做後續的操作,看是要Email通知還是記錄在系統的Event Log還是多種方式傳送訊息等等,一且都可以讓使用者去依照需求而定義。
NLog的功能相當齊全,設定也相當簡單、容易上手,所以接下來將會介紹如何在網站專案中安裝與設定NLog。
因為開發的專案是一個有關會計系統的網站系統,所以頁面上的輸入欄位(TextBox)會相當的多,
而使用者輸入數字後通常不會再去抽出左手去按下Tab鍵讓輸入框的focus移到下一個,
通常使用者都是左手控制傳票或是指向要輸入數字的地方,而右手就是在鍵盤右方的數字鍵那邊一直輸入,
當輸入完一個欄位後就是直接按下數字鍵的Enter鍵,讓輸入框的focus移到下一個。
其實這一點都不是很難,而且網路上也有人針對此功能做出了相關的jQuery Plugin套件,
這裡就做個介紹與使用的方式。
如果開發的專案式屬於會計系統之類的,常常會有個基本的功能要求,
那就是TextBox所輸入的金額數字必須要能夠轉換為千分位,
所謂千分位就是每三位數就要用「,」逗號分隔,我想這應該每個人都知道的基本常識,
於是在開發專案的時候就參考了幾個網路上的Script之後,就寫了幾個function用來處理千分的需求。
其實glimpse可以結合ELMAH,讓ELMAH所記錄到的錯誤訊息於glimpse中顯示,在系統的登入認證後,只要啟用glimpse就可以去看ELMAH的紀錄資料,不必再另外進入ELMAH,接下來介紹如何透過NuGet安裝Elmah plugin for Glimpse以及部分的修改設定。
請記得,你的網站必須示已經安裝了 ELMAH 以及 Glimpse…
在「ASP.NET MVC + ELMAH 監控並記錄你的網站錯誤資訊 3」這篇文章中介紹了ELMAH寄發郵件通知,
也說明了如何不去寄發特定Http StatuCode錯誤的信件通知,
但是在Web.Config的elmah Section的errorMail設定中,已經把寄發信件通知的Subject給統一處理了,
這樣一來就無法分辨寄來的錯誤通知的差異性,
因為有些錯誤是必須要能夠設定重要性,而且也需要在信件主旨中可以馬上辨識以快速處理,
所以接下來就是說明一下對於特定的Exception要如何去設定個別的見信重要性與信件主旨。
上篇文章「ASP.NET MVC 使用Glimpse監測網站的一舉一動 1」,說明了什麼是Glimpse以及如何透過NuGet安裝,另外也簡短的說明Glimpse的內容與如何使用Trace功能,然而與ELMAH所遇到的問題是一樣的,那就是預設安裝後,都是可以匿名瀏覽,對於網站的安全性來說是個相當大的威脅,尤其是Glimpse的Config,會把Web.Config的所有資訊都完整呈現,所以這一篇文章就要來說明如何讓Glimpse在登入後才可以使用。
之前有介紹一個可以即時監測網戰後端每個處理的處理時間的監測套件「MiniProfiler」,如果說要連帶要監測前後端的每一像細節的話,那麼MiniProfiler可能就沒有辦法,而且MiniProfiler是無法遠端做監測的動作,MiniProfiler只能夠監測本地端的動作,所以MiniProfier比較適合開發期間使用。
而在開發ASP.NET WebFrom的時後,如果想要追蹤每個頁面的執行狀況與效能的話,其實是可以使用「Trace」功能,如此一來就可以在每個頁面的下方顯示頁面執行時的完整詳細資訊,包括前端傳送的Http Request、所有的Session、Cookie等,對於開發時期來說,這些資訊可以幫助我們在除錯時候可以掌握確切的資訊。
然而系統上線之後,Trace功能勢必要關閉,而單靠ELMAH記錄錯誤訊息也無法完全掌握,因為有的時候執行正常並不表示功能正常,例如執行時間過慢、路徑錯誤等,所以接下來就來介紹「Glimpse」,除了具有Trace的功能外,也可以結合Forms Authentication的登入認證,讓我們在系統上線之後,一樣可以即時讓開發人員追蹤頁面執行的各項資訊。
前面幾篇文章有關 ELMAH 的文章都是把錯誤訊息記錄在「~/App_Data/Elmah.Errors」下,然後一個錯誤就是一個 XML 檔案,這樣做的原因是可以避免因為資料庫無法連線時而造成錯誤訊息無法記錄的意外,而本篇文章將會說明如何將 ELMAH 的錯誤訊息的儲存媒體改為使用 SQL Server。
前兩篇有關於ELMAH的文章中介紹了了如何安裝以及如何做權限設定的修改,這一篇就來說明一下如何讓ELMAH在記錄錯誤也同時發出Email通知特定的人員,畢竟有錯誤發生時,如果可以即時的將訊息通知給特定人員(WebMaster or PM or Developer),對於錯誤狀況的掌握會更加迅速(對於開發人員來說,比客戶早一點知道總是比較好吧)。
上一篇文章「ASP.NET MVC + ELMAH 監控並記錄你的網站錯誤資訊 1」有遇到一個問題,那就是原先ELMAH所預設的設定會讓任何人都可以看到ELMAH記錄,對於網路的安全性就有極大的危險,因為錯誤記錄都記錄了許多的資訊,除了客戶資訊外也包含了Server的環境資訊,甚至於錯誤的程式碼位置'、輸入的機密資料等,怎麼可以讓任何人說進來就進來呢?
所以有些安全性的設定還是要記得去修改、去設定,免得讓有心人竊取資料或是毀損資料。
網站上線或是還在開發、測試階段時對於難以捉摸的錯誤總是讓開發人員頭痛不已,如果是在開發環境裡,還容易抓到錯誤,一旦上了測試機或是正式機之後,錯誤的發生就無法立即掌握,尤其是當客戶在測試的時候往往遇到錯誤就只會通知開發人員說「你的程式有 BUG!」,但是要問客戶說這些錯誤是怎麼發生的時候,客戶通常是說不清楚,甚至有的客戶明明操作錯誤或是輸入錯誤的資料而引發錯誤,但也不會老實跟開發人員說出實情,而如果可以任何時候都監控網站並且當錯誤發生的時候將當下的資訊給記錄下來的話,對於開發人員甚至是對於系統的功能完整、安全防護、減少 BUG 數等等都是一件好事。
網路上有很多種監控以及記錄的工具,然而眾多的工具、套件中最容易上手的應該就是 ELMAH 了,而且最重要的是,ELMAH 有提供 DashBoard 可以查看記錄的訊息,另外也可以透過遠端的方式去查看記錄,如此一來就可以更加容易掌握網站的一舉一動了(安全機制還是要先做好),就讓我們來看看如何在你的 ASP.NET MVC 3 網站中使用ELMAH 來監控並記錄錯誤訊息。
有時候jQuery寫久了,只要在執行的時候沒有出現錯誤或是執行結果正確,往往都會覺得這麼寫是OK的,
然而就像寫C#程式一樣,有時候有些寫法會帶來更好的效能或是更能夠發揮語言的特性,
但是通常都要到Code Review時或是出現效能問題時才會去注意到然後去改善,
前端語言因為缺乏所謂的編譯檢查,所以只能等到去執行時才能夠檢查出來,
而且還可能有更多位察覺的地方隱藏其中,如果有個工具可以在必要的時候時時提供改善建議不是很好嗎?
其實一直很想寫下這一篇,但是又發現到其實很多人都會用Firefox + Firebug 的組核對前端網頁進行偵錯,
應該說,從事網頁設計、開發的從業人員都應該會用才對,
但是… 在我目前工作的公司裡,還真的有很多人不用、不會用、甚至是不知道有這樣的工具。
更有些人對於前端語言的偵錯方式還是使用那一套 alert() 大法,
就是在程式之中要偵錯的地方就是狂下 alert(),把要顯示的訊息給alert出來,
這方法不能說不好,但是偵錯的時候就去改變原始碼,然後修改完再去一一的把alert給移除,
萬一遺漏了一些的話,那麼就會看到上線的網頁會有些地方就出現不該出現的alert訊息視窗啦,使用者就認為這是BUG!
Firebug是個相當優異的前端偵錯工具,而且還有很多可以掛載在上面的套件,整合在Firebug之中,讓偵錯更加方便,
所以本篇不會是一篇教你如何使用Firebug來偵錯的Step教學,而是一篇介紹如何應用Firebug與Firebug外掛讓偵錯更加便利。
會介紹以下幾個Firebug的外掛工具: