網頁開發人員應對 URL 的大小寫「有感覺」!

我以前混 Linux 的時候就對檔名的大小寫很注意,因為一個英文字母的大小寫搞錯就會都找不到網頁,之後雖然改混 Windows 也沒丟掉之前的習慣,雖然 NTFS 檔案系統或 IIS 在處理 URL 時預設不會特別區分大小寫,打錯大小寫依然找的到網頁,但不管是網址還是檔名,我對大小寫還是很龜毛,看到別人亂取檔名或 URL 大小寫與檔案大小寫不同時也多少也會唸一下,我還曾經被譏笑太無聊,不過今天我就要證明我的堅持是對的!

Windows 領域的開發人員大多對大小寫「沒感覺」或覺得「沒差」,但如果你打算建構一個超高流量的網站,就必須注意任何微小細節,包括 URL 的大小寫也不例外。

我們都知道透過 Browser Cache 機制可有效加速網頁下載速度,被快取過的 URL 會直接從本機取得網路資源 ( 圖片, JavaScript, CSS, … ),我之前也寫過【簡易克服 CSS 被瀏覽器快取(Cache)的問題】文章可有效克服 用戶端快取 (Client-side Cache) 的問題,但各位可能不會想到當 URL 完全不變僅將大小寫修改後,就可以避免該 URL 不被用戶端快取。

舉個簡單的例子證明,例如我的部落格有張圖片有設定用戶端快取,網址是:

http://blog.miniasp.com/themes/TheWillWillWeb/images/img06.jpg

我總共抓三次此圖片:

  1. 當用戶端第一次取得該圖片時,由於尚未快取,所以會抓取完成的內容 ( HTTP 200 )
  2. 由於該圖片已經快取至瀏覽器,所以第二次再要求一次該圖片,瀏覽器就不會再次下載,直接取得本機快取的內容!
  3. 第三次我刻意修改圖片的副檔名,由於瀏覽器會視為完全不同的網址,因此會再次從 Server 下載圖片!

請參照以下圖示:

依據這個特性,在規劃大型網站時就必須注意,如果你已經將網站回應速度與下載速度都最佳化,結果卻因為網頁 HTML 內的圖片、CSS、JavaScript 檔案的大小寫問題而導致內容重複下載,一來佔用頻寬、二來也拉長網頁下載的時間,或許這超級小技巧對大部分情況來說無關痛癢,但專業來自對細節的堅持,當網站需要這些小細節來支援時,就很好用了!

其實我今天講的這個小技巧並沒有說「好」與「壞」,而是希望你「有感覺」而已,當你想有效提升全站的網頁效能,你應該注意所有 URL 都能注意使用統一的大小寫;如果你想讓該網頁立即重整,避免部分檔案被用戶端快取且不想加上奇怪的尾巴的話,就可以稍稍修改檔名大小寫,讓使用者就算開啟網頁原始碼察看也完全沒感覺。

  

此文章由 will 發表於 2009/11/30 下午 12:44:35

永久連結 | 評論 (2) | 此文章的RSSRSS comment feed |

分類: Tips | Web

標籤: ,

收藏:

善用 Response.IsClientConnected 避免不必要的資源浪費

你在使用網站時不知道有沒有一種經驗是當你按下某一個連結或一個表單按鈕時,突然發現你按錯了,然後你會立即按下 ESC 鍵或按下瀏覽器的停止按鈕中斷網頁執行?我以前就曾經遇到過好幾次這種狀況,而且有些網頁程式執行時間非常長且非常消耗系統資源,例如:匯出幾十萬筆資料、對大量資料進行彙總運算、從伺服器端執行一個長時間的 Web Service 查詢、…等等,雖然使用者很「直覺」的中斷執行,但是伺服器端的程式卻還是會在背景乖乖的把所有的工作做完才會罷手。

我在開發時期只要遇到這種情況,通常都不太願意等程式執行完,而是直接把 w3wp.exe 給手動砍掉,讓 IIS 自動重新啟動應用程式集區,但是網站上線後可能是客戶自己遇到這種情況,而客戶只會抱怨說為什麼網站變很慢而已,為了提升客戶滿意度,這點小技巧的確可以視情況用在專案上。

Response.IsClientConnected 屬性是一個布林值(bool),用來判斷 Client 端是否已經斷線,所以當你要執行十分消耗資源的程式前都做檢查一下,就可以應付這類惱人但不常見的需求。

if(!Response.IsClientConnected) Response.End();

單純執行 Response.End() 會讓之前已經儲存在緩衝區的內容輸出到瀏覽器,如果連這些內容都不要回應的話,可以再加上 Response.Clear() 清除緩衝區的資料(反正使用者本來就看不到)。

if(!Response.IsClientConnected) {
Response.Clear();
Response.End();
}

如果你不想停止執行程式,而是多處理一些其他工作的話,也可以參考以下範例:

if (Response.IsClientConnected)
{
Server.Transfer("SomeOtherPage.aspx");
}
else
{
DoSomeThing();
Response.End();
}

相關連結

  

此文章由 will 發表於 2009/11/29 下午 06:15:12

永久連結 | 評論 (1) | 此文章的RSSRSS comment feed |

分類: ASP.NET

標籤: ,

收藏:

設定 Cookie 時可善用 HttpOnly 特性減低網站安全風險(XSS)

Cookie hijacking 是個很常見的 XSS 攻擊手法,大多是利用網站既有的 XSS 漏洞並透過 JavaScript 取得 documnet.cookie 資料,而 documnet.cookie 就包含所有你在該網頁所有可用的 Cookie 資料,但若你的網站程式在設定 Cookie 的時候有特別加上 HttpOnly 屬性,就可以進一步避免該頁的 Cookie 被 JavaScript 存取,也可保護使用者的 Cookie 不會偷走。

以下是 ASP.NET 的程式範例:

// Create an HttpOnly cookie.
HttpCookie myHttpOnlyCookie = new HttpCookie("LastVisit", DateTime.Now.ToString());

// Setting the HttpOnly value to true, makes
// this cookie accessible only to ASP.NET.

myHttpOnlyCookie.HttpOnly = true;
myHttpOnlyCookie.Name = "MyHttpOnlyCookie";
Response.AppendCookie(myHttpOnlyCookie);

由於 HttpOnly 是 W3C 的標準配備,所以不止 ASP.NET 可以運用這個技巧,其他程式語言要利用 HttpOnly 時只要在 Set-Cookie 的 Header 最後面加上 ; HttpOnly 即可套用完成,如下圖示:

image

所以你的網站在使用 Cookie 時如果確定該 Cookie 不會或不需要被 Browser 端的 JavaScript 使用,建議都加上 HttpOnly 屬性,以免當真的有一天你的網站不小心出現了 XSS 弱點時不會傷及無辜用戶。

雖然套用 HttpOnly 屬性可以有效防堵 Cookie 被劫走(Hijacking),但這並不代表使用 HttpOnly 就是安全的!因為如果你的網站還是有 XSS 風險,還是很有機會讓駭客利用 XHR 代客操作,所以就算駭客不取得 Cookie 也可以達成攻擊的手段。

另外,由於 Cookie 一樣會透過 HTTP 在網路上傳輸,被半路攔截的機會還是有的,建議使用 SSL 保護網路封包的傳輸,避免資料遭攔截。

我的 解釋 Cookie 的特性 文章有解釋 Cookie 的特性,不甚了解的人建議可閱讀一番,Cookie 基本上有兩種類型:Persistent Cookie 與 Session Cookie,而 HttpOnly 可套用在任何 Cookie 類型上,唯一的差別僅在於可不可以由 JavaScript, Silverlight 或 Flash 等前端程式存取而已!

相關連結

  

此文章由 will 發表於 2009/11/26 下午 12:13:48

永久連結 | 評論 (0) | 此文章的RSSRSS comment feed |

分類: Web | JavaScript | ASP.NET | Security

標籤: , , ,

收藏:

提升 SQL SERVER 執行效率的小技巧:為 SQL 快取暖身

SQL Server 的效能調校是一門很大的學問,先不論 T-SQL 寫的好與壞如何衝擊效能,我們先假設 T-SQL 已經全部都調到最佳狀態(或是根本無法調整),在這種情境下如何再提升執行效能呢,答案還是非常多,例如:調整磁碟效能、加記憶體、….等,但我今天想特別強調的是 快取 (Caching)。今天我來分享一個非常實用且簡單的伎倆,讓你的應用程式一瞬間即可加快查詢速度。

SQL Server 使用三種不同的檔案儲存你的資料:

  1. MDF ( 主要資料檔 ):包括表格、索引、以及資料
  2. NDF ( 次要資料檔 ):跟 MDF 一樣,只是放在不同的檔案群組而已
  3. LDF ( 交易記錄檔 ):儲存所有資料檔的歷史變更紀錄

SQL Server 寫入資料時 (INSERT, UPDATE, DELETE),至少會先確保資料有寫入交易記錄檔才會進行下一步動作,而寫入交易記錄檔這一段幾乎是無法最佳化的,考驗的就是你的磁碟 IO 的效能,由於交易記錄檔的寫入幾乎是「循序的」,所以磁碟 IO 的效能數據 (IOps) 要看的是 循序寫入(Sequential WRITE) 的速度,所以一般都建議使用獨立的硬碟來放置交易記錄檔,以確保資料寫入的效能。

SQL Server 讀取資料時 (SELECT),就完全依賴 資料檔 (MDF, NDF) 的讀取速度,由於資料寫入資料檔時通常不是循序的,所以資料通常非常亂且透過內部索引指標指來指去的,所以 SQL Server 在讀取資料時幾乎都是「隨機存取」,所以磁碟 IO 的效能數據 (IOps) 要看的是 隨機讀取 (Random READ) 的速度,數據越高就代表讀取效能就越好。

由於 SQL Server 讀取資料檔的效能較差,所以會盡可能的使用記憶體快取所有從資料檔讀出來的資料,以加速資料查詢的速度,一般來說 SQL Server 伺服器的記憶體的確是多多益善 (但 SQL Express 有記憶體使用限制),而且就單純的資料查詢來講,所有已經被載入到記憶體中的資料,SQL Server 都不會再去資料檔抓取資料,此時的 SQL Server 讀取速度最快,所以我們就打算用這個特性來優化資料庫的查詢效能。

所以當 SQL Server 主機重開機或是網站剛啟動且資料庫尚未被讀取任何資料時,此時的 SQL Server 資料庫的查詢效能最差,這個時候我們就可以透過 預儲程序(Stored Prodedure) 搭配 SELECT COUNT(*) 將所有你想要預先快取的資料表讀取並載入 SQL 記憶體快取 中。

當你的資料庫不太大且記憶體大到用不完的情況下,很適合執行以下 T-SQL 將特定資料庫中的所有資料表快取:

DECLARE @obj sysname , @sch sysname
DECLARE @owner varchar(100)
DECLARE @sql varchar(1000) 

DECLARE tblcur INSENSITIVE CURSOR FOR
    SELECT schema_name(uid) as schema_name, object_name(id) as obj_name 
    FROM sysobjects o WHERE OBJECTPROPERTY(o.id, N'IsUserTable') = 1 

OPEN tblcur 

WHILE 1 = 1
BEGIN
    FETCH tblcur INTO @sch, @obj
    IF @@fetch_status <0 BREAK 

    SET @sql = 'SELECT COUNT(*) FROM [' + @sch + '].[' + @obj + ']'
    EXEC(@sql)
END 

DEALLOCATE tblcur

如果你的 SQL Server 資料非常多,不建議一開始就將所有資料快取,因為如果記憶體不夠大,後來讀取的資料會把之前已經快取過的清除,如果快取到不常用的歷史資料表,反而踢掉常用的資料表時,這樣就沒有什麼效果了,所以建議的方式是將最常用的資料表或篩選特定條件下的資料快取起來就好。

如果你想查詢每一個資料表到底用了多少空間,可以利用 sp_spaceused 查詢各表格所使用的磁碟空間:

DECLARE @obj sysname , @sch sysname
DECLARE @owner varchar(100)
DECLARE @sql varchar(1000) 

DECLARE tblcur INSENSITIVE CURSOR FOR
    SELECT schema_name(uid) as schema_name, object_name(id) as obj_name 
    FROM sysobjects o WHERE OBJECTPROPERTY(o.id, N'IsUserTable') = 1 

OPEN tblcur 

WHILE 1 = 1
BEGIN
    FETCH tblcur INTO @sch, @obj
    IF @@fetch_status <0 BREAK 

    SET @sql = 'EXEC sp_spaceused N''[' + @sch + '].[' + @obj + ']'''
    EXEC(@sql)
END 

DEALLOCATE tblcur

利用 sp_spaceused 查詢各表格所使用的磁碟空間

若使用 Management Studio 也可使用內建的報表功能直接查詢出【依資料表的磁碟使用量】,這個介面就漂亮多啦!

使用 Management Studio 也可使用內建的報表功能直接查詢出【依資料表的磁碟使用量】

使用 Management Studio 也可使用內建的報表功能直接查詢出【依資料表的磁碟使用量】

透過這個技巧就是當資料尚未完全讀取到這些常用表格時預先讀入 SQL Server 的快取記憶體中,以提升這些常用表格的查詢效能,簡單、有效!

  

此文章由 will 發表於 2009/11/25 下午 11:41:21

永久連結 | 評論 (3) | 此文章的RSSRSS comment feed |

分類: SQL Server | Tips

標籤: ,

收藏:

PHP 如何排序二維陣列 [第1維:陣列,第2維:關聯式陣列]

已經好久沒寫 PHP 了,今天為了維護一個五年前開發的案子,客戶要求將一個畫面的資料進行排序,但這種二維陣列(第一層一般陣列第二層關聯式陣列)的排序之前好像沒做過(有做過也該忘了),想了一些時間終於寫出來,特別寫起來備忘一下。

首先,我原始的二維陣列用 print_r 印出來的內容如下:

Array
(
[0] => Array
(
[0] => 11111
[inventory] => 11111
[1] => 222222
[id] => 222222
[2] => LE000
[type_id] => LE000
)

[1] => Array
(
[0] => 12
[inventory] => 12
[1] => 232
[id] => 232
[2] => TK000
[type_id] => TK000
)

[2] => Array
(
[0] => 40
[inventory] => 40
[1] => 123
[id] => 123
[2] => LE001
[type_id] => LE001
)

[3] => Array
(
[0] => 5
[inventory] => 5
[1] => 331
[id] => 331
[2] => TK002
[type_id] => TK002
)

[4] => Array
(
[0] => 5
[inventory] => 5
[1] => 23
[id] => 23
[2] => LE055
[type_id] => LE055
)

[5] => Array
(
[0] => 21
[inventory] => 21
[1] => 312
[id] => 312
[2] => LE067
[type_id] => LE067
)
)

由此可見我的第一維陣列只是 一般陣列,第二維陣列是 關聯式陣列(Associated Array),而排序的需求是依據第二為陣列的 type_id 的值進行排序。

我到 Sorting Arrays 查到一個 uasort 似乎蠻適合我這個排序需求的,因此寫了一個簡單的比對函式進行陣列排序,程式碼如下:

function sort_by_type_id($a, $b)
{
if($a['type_id'] == $b['type_id']) return 0;
return ($a['type_id'] > $b['type_id']) ? 1 : -1;
}

如果我的陣列名稱為 $data 的話,套用排序的語法如下:

uasort($data, 'sort_by_type_id');

然而,排序好之後的結果雖然第一維陣列的「排列順序」改變了,但是 index 卻沒變!這是因為 PHP 把我的陣列視為關聯式陣列來看待,並不像一般程式語言的陣列有按照陣列的索引子進行排列,由此可見我真的很久沒寫 PHP 了  :-P

排序後的結果如下:

Array
(
[0] => Array
(
[0] => 11111
[inventory] => 11111
[1] => 222222
[id] => 222222
[2] => LE000
[type_id] => LE000
)

[2] => Array
(
[0] => 40
[inventory] => 40
[1] => 123
[id] => 123
[2] => LE001
[type_id] => LE001
)

[4] => Array
(
[0] => 5
[inventory] => 5
[1] => 23
[id] => 23
[2] => LE055
[type_id] => LE055
)

[5] => Array
(
[0] => 21
[inventory] => 21
[1] => 312
[id] => 312
[2] => LE067
[type_id] => LE067
)

[1] => Array
(
[0] => 12
[inventory] => 12
[1] => 232
[id] => 232
[2] => TK000
[type_id] => TK000
)

[3] => Array
(
[0] => 5
[inventory] => 5
[1] => 331
[id] => 331
[2] => TK002
[type_id] => TK002
)
)

這種狀況造成我在 Smarty Template 中的迴圈依然以尚未排序前的順序顯示內容。

最後,我轉個小彎,利用 array_values 函式僅取回第一維陣列的 value 部分,所取得的新陣列的索引子就是按照我要的順序排列了!

$data = array_values($data);

最後,得到大澤木小白鐵的訂正,原來只要用 usort 就可以解決這個問題,不需要再用 array_values 函式僅取回第一維陣列的 value 部分,這才是正解! ^_^

usort($data, 'sort_by_type_id');

 

相關連結

  

此文章由 will 發表於 2009/11/24 下午 11:42:13

永久連結 | 評論 (0) | 此文章的RSSRSS comment feed |

分類: PHP

標籤: , ,

收藏: