Thursday, October 01, 2009 - 文章

挑戰自訂條件流程的彈性極限

在開發強調"彈性"的系統功能時,我偶爾會遇到要由動態產生的複雜運算式決定流程的情境,例如: "( (a + b) > 0 || c > 100 && ( d || e ) )",當運算式參數可預期或是會固定依某些條件變化時,這倒還不算什麼難題;但若是運算式被要求開放使用者或使用元件的開發人員自由設定,就真的不是一般Coding做法能解決的,得寫個Parser解析使用者輸入的運算式再算出結果。而Parser得掌握所有的語法規則,涵蓋各種可能出現的組合,要寫得夠強韌不易出錯實非易事。

相形之下,Javascript裡有eval()函數可用就省事很多,等同於Script Engine本身就提供強大的Parser,可以解析任意Javascript指令並正確執行。

那... 何不直接偷來用?

以下的程式碼(非原創,網路上有許多類似的參考,例如: CodeProject的範例),利用在Mini C# Lab用過的CodeDom即時編譯技術,建了一個JScript物件來執行Javascript eval()函數,將結果以字串形式傳回。

/*
using System.CodeDom.Compiler;
using System.Reflection;
*/ 
public class JSEvaluator
{
    static Type _jseType = null;
    static object _jse = null;
    static JSEvaluator()
    {
        CodeDomProvider provider = CodeDomProvider.CreateProvider("JScript");
        CompilerParameters p = new CompilerParameters();
        p.GenerateInMemory = true;
        CompilerResults r = provider.CompileAssemblyFromSource(p,
@"class JSEvaluator {  
public function Eval(expr : String) : String { return eval(expr); } 
}");
        Assembly asm = r.CompiledAssembly;
        _jseType = asm.GetType("JSEvaluator");
        _jse = Activator.CreateInstance(_jseType);
    }
 
    public static string Eval(string expr) {
        return _jseType.InvokeMember("Eval", BindingFlags.InvokeMethod,
            null, _jse, new object[] { expr }).ToString();
    }
}

這樣子,寫一行JSEvaluator.Eval("( ( 4 + 5 * 18 ) > 12 || 'abc' == 'ABC' && 'aaaa'.indexOf('a') > -1 )")就能得到true/false,再複雜(謎之聲: 這範例分明是在找碴吧?)的Javascipt運算式都可以支援! 如此,系統就可以透過程式動態組裝複雜的條件,或是開放具有Javascript背景的開發人員依特定規則自訂條件,條件邏輯可以變化的空間就相當驚人了。

【資安考量】

至於會不會像SQL Injection一樣,被人在運算式中夾帶惡意內容而衍生風險呢? 依據MSDN上的說明:

The code passed to the eval method is executed in a restricted security context, unless the string "unsafe" is passed as the second parameter. The restricted security context forbids access to system resources, such as the file system, the network, or the user interface. A security exception is generated if the code attempts to access those resources.

依我的解讀,只要不打開"unsafe"參數,遭受入侵破壞的風險應該不會太高。(若有錯請指正)

***提醒*** 允許外界傳入程式碼執行還是會存在一定的風險,請務必酌斟使用,可參考後續文章裡的延伸討論。

百萬俱樂部!

彎彎的一小步,卻是我的一大步...

【黑暗執行緒】從2006年4月掛上計數器以來,在各位鄉親的熱情支持下,歷經三年半的時間,總算累積到100萬點閱人次,終於擠身部落客界的百萬俱樂部!!

基於肥水不落外人田的道理,由我親自踩下第100萬人次,拍照留念準備做為傳家寶。

謎之聲(以下簡稱謎): 你該不會一直按Refresh才搶到第100萬吧?

黑暗執行緒(以下簡稱黑): 放肆!! 吾乃名門正派,豈會用此等投機取巧、令人不齒的狡詐伎倆?

: 嘴砲誰不會呀? 沒圖沒真相! 沒圖沒真相! (敲碗)

: 早知你會來這一套,就以下圖詳解999,997到1,000,000的踩線過程,讓你心服口服。

咦... 這種訪談過程好眼熟,莫非?

是的,為了答謝各位鄉親支持,在此實踐70萬人次時的承諾,真的要送獎品了!!

但先前已說過,我生平最痛恨靠運氣抽到大獎的人,所以這次還是會用比賽的方式決定誰可以把市價2,200的Microsoft VX-7000 WebCam帶回家!

目前的構想會辦一場適合Coding魔人參加的尋寶大賽,由某個線索找到下一關,接著取得新線索再找到下一關(大家有看過國家寶藏吧?),最快達陣的人勝出。詳細的比賽規則過幾天再公告(恕不保證公佈時機,我會在不爆肝的前題下盡快完成),屆時歡迎魔人們來挑戰!!

【成長歷程】

搜尋

Go

<October 2009>
SunMonTueWedThuFriSat
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567
 
RSS
【工商服務】
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


BlogLook Score and Rank

Syndication