2007年8月21日 星期二

HttpWebRequest 錯誤訊息 "The server committed a protocol violation..." 的解法
MSN SpaceGoogle DocGoogle Blog
Chui-Wen Chiu
2007.08.22

今天收到一個錯誤訊息"The server committed a protocol violation. Section=ResponseHeader Detail=CR must be followed by LF", Google 一下有相當多這方面的資料。我就不一一細列,這個錯誤主要是因為 Web Server 回傳的 HTTP 訊息結尾只有 n,而 .NET 實作的 WebRequest 預設情況下,必須符合 RFC 855 實作,也就是結尾必須包含 rn。所以,當 .NET 的 WebRequest 收到這個 HTTP 訊息時,就會出現錯誤。

解法兩個,一個就是讓 Web Server 丟出的 HTTP 訊息符合 RFC 855,另一個就是是讓 WebRequest 睜一隻眼閉一隻眼,亦即將 WebRequest 的 UseUnsafeHeaderParsing 屬性設為 true,這個屬性在 MSDN 文件上得描述如下:

當這個屬性設定為 false 時,會在 HTTP 剖析期間執行下列驗證:

  • 在行結尾程式碼中使用 CRLF;不允許單獨使用 CR 或 LF。

  • 標頭名稱中不應該有空格。

  • 如果有多個狀態列,會將所有額外狀態列視為不正確的標頭名稱/值組。

  • 除了狀態碼外,狀態列還必須有狀態描述。

  • 標頭名稱中不能有非 ASCII 字元。無論這個屬性設定為 truefalse,都會執行這個驗證。

當發生通訊協定違規時,會擲回 WebException 例外狀況,並將狀態設定為 ServerProtocolViolation。如果 UseUnsafeHeaderParsing 屬性設定為 true,則會忽略驗證錯誤。

將這個屬性設定為 true 具有安全性含義,因此只有在需要與伺服器的回溯相容性 (Backward Compatibility) 時,才應該執行此動作。


需要注意的是這個屬性到 .NET 2.0 才新增的,且文件中也提到除非為了 Web Server 回溯相容,否則還是設定為 false。另外,如果要全面套用到每一個使用到 WebRequest 的地方,在 app.config 加上下面的設定:

<system.net>
<settings>
<httpWebRequest useUnsafeHeaderParsing="true" />
</settings>
</system.net>


2007年8月19日 星期日

(C#) 取得最新的 Twitter 訊息
MSN SpaceGoogle DocGoogle Blog
Chui-Wen Chiu
2007.08.20

測試環境
1. Windows XP Pro
2. Visual Studio 2005

TWessenger[1] 是一套抓取 twitter 最新訊息來更新 MSN 狀態的 Messenger Add-In,主要技術只有兩個,第一個是透過 Windows Live Messenger Add-In API 更新 MSN 狀態文字,第二個是抓取 ATOM 取得最新訊息。本文主要描述抓取最新 Twitter 訊息的方式,首先要取得一個 twitter id,然後可代換成 ATOM URL 如:http://twitter.com/statuses/user_timeline/1713501.atom,其中 1713501 是我個人的 twitter ID,你可以代換成任何 ID。*.atom 的內容類似如下:
<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
<title>Twitter / Chui-Wen Chiu</title>
<id>tag:twitter.com:Statuses</id>
<link type="text/html" href="http://twitter.com/chui_wen_chiu" rel="alternate"/>
<subtitle>Twitter updates from Chui-Wen Chiu.</subtitle>
<entry>
<title>Chui-Wen Chiu: ebXML&#26159;&#19968;&#20491;&#38283;&#25918;&#24615;&#30340;&#38651;&#23376;&#21830;&#21209;&#24314;&#27083;&#27161;&#28310; (http://www.ebxml.org.tw/big5/index.jsp)</title>
<content type="html">Chui-Wen Chiu: ebXML&#26159;&#19968;&#20491;&#38283;&#25918;&#24615;&#30340;&#38651;&#23376;&#21830;&#21209;&#24314;&#27083;&#27161;&#28310; (http://www.ebxml.org.tw/big5/index.jsp)</content>
<id>tag:twitter.com,2007-08-19T17:51:32+00:00:http://twitter.com/chui_wen_chiu/statuses/214515122</id>
<published>2007-08-19T17:51:32+00:00</published>
<updated>2007-08-19T17:51:32+00:00</updated>
<link type="text/html" href="http://twitter.com/chui_wen_chiu/statuses/214515122" rel="alternate"/>
</entry>
<entry>
<title>Chui-Wen Chiu: &#21152;&#24555;OpenOffice&#22519;&#34892;&#36895;&#24230;&#30340;&#26041;&#27861; (http://blog.xuite.net/emisjerry/tech/13078174)</title>
<content type="html">Chui-Wen Chiu: &#21152;&#24555;OpenOffice&#22519;&#34892;&#36895;&#24230;&#30340;&#26041;&#27861; (http://blog.xuite.net/emisjerry/tech/13078174)</content>
<id>tag:twitter.com,2007-08-19T17:35:47+00:00:http://twitter.com/chui_wen_chiu/statuses/214495672</id>
<published>2007-08-19T17:35:47+00:00</published>
<updated>2007-08-19T17:35:47+00:00</updated>
<link type="text/html" href="http://twitter.com/chui_wen_chiu/statuses/214495672" rel="alternate"/>
</entry>
...略


接著透過 .NET 的 XmlTextReader 以 SAX 方式循序讀取 ATOM,直到搜尋 <content> 標籤,其標籤內容即為 twitter 內容,因為 ATOM 是從新到就排序,所以第一筆極為最新消息,因此,整個完成的程序如下:

using System;
using System.Xml;

namespace ConsoleApplication1 {
class Program {
static void Main(string[] args) {
// Twitter ATOM URL
string url = "http://twitter.com/statuses/user_timeline/1713501.atom";
String statusMsg = String.Empty;
String username = String.Empty;
using (XmlTextReader xmlReader = new XmlTextReader(url)) {
while (xmlReader.Read()) {
if (xmlReader.Name == "content") {
// 取得 twitter 內容
String tmp = xmlReader.ReadString();

// 內容切割
String[] contents = tmp.Split(new char[] { ':' }, 2);
username = contents[0];
statusMsg = contents[1].Trim();

break;
}
}
}
Console.WriteLine("Username: {0}", username);
Console.WriteLine("Message: {0}", statusMsg);
}
}
}


執行結果

參考資料
[1] TWessenger

2007年8月15日 星期三

(Javascript) Lazy Function Definition
MSN SpaceGoogle DocGoogle Blog
Chui-Wen Chiu
2007.08.16

Peter[1] 提出一種用於函數式編程的設計模式稱為"Lazy Function Definition"。首天他提出一個問題:


"撰寫一個 foo 函數,並回傳第一次呼叫 foo 的時間。"


針對這個問題,他提出下面四種不同的解法:

解法#1: 傳統解法-全域變數

var t; // 紀錄第一次呼叫 foo 的時間
function foo() {
if (t) { // 假如已經有存放時間,則直接回傳
return t;
}

t = new Date(); // 紀錄第一次呼叫時間
return t;
}

上述解法有兩個問題:

1. 需要一個額外的全域變數 t,該變數有可能在呼叫 foo 時被修改

2. 程式碼在執行階段無法達到最佳效率。因為每次呼叫都需要進行條件評估。雖然上述範例的條件式損耗很廉價,但是真實情況的條件式可能是多層次的 if-else-else-... 結構。

解法 #2: Module Pattern

利用 Cornford and Crockford 所提出的 Module Pattern[2] 可以解決解法#1 的第一個問題,透過 closure 技法將全域變數封裝成只有 foo 內的程式可存取,如下:

var foo = (function() {
var t; // 封裝在 foo 內的變數
return function() {
if (t) {
// 假如已經有存放時間,則直接回傳
return t;
}
t = new Date();
// 紀錄第一次呼叫時間
return t;
}
})();

這種解法仍有解法#1的第2個問題存在,仍需重複進行條件式評估。


解法 #3: 函數即物件-使用物件屬性保存狀態

function foo() {
if (foo.t) {
// 假如已經有存放時間,則直接回傳
return foo.t;
}
foo.t = new Date();
// 紀錄第一次呼叫時間
return foo.t;
}

這個解法比 Module Pattern 更為簡潔,直接透過物件屬性存放資料,而不需要在產生一個額外的 Function Object。所以也能避免全域變數的問題。但這種解法仍有解法#1的第2個問題存在,仍需重複進行條件式評估。

解法 #4: Lazy Function Definition

var foo = function() {
var t = new Date();
foo = function() { // 改寫原先的函數定義
return t; // Closure t
};
return foo(); // 套用新的 foo 函數回傳值
};

當 foo 第一次被呼叫時,會先實體化一個 Date 物件並重新指定將 Date 物件 Closure 的 foo 函數,透過重新的指定可以改寫原來的函數定義,因為 Date 物件已經被 Closure,所以會一直存在。透過此一解法可以避免解法#1的兩個問題。


因此上述原來的 foo 函數定義可以想像成初始化內部 foo。簡單的說,第一次呼叫 foo 的函數定義如上程式,但之後的程式碼定義則是如下:
function foo() {
return t; // Closure t
};


上述可透過下面的程式碼進行測試:

var foo = function() {
var t = new Date();
alert(1);
foo = function() {
return t;
};
alert(2)
return foo();
};
alert( foo() ); // 出現 1, 2, 日期
alert( foo() ); // 出現 日期


應用:判斷不同瀏覽器的頁面捲動

var getScrollY = function() {

if (typeof window.pageYOffset == 'number') {

getScrollY = function() {
return window.pageYOffset;
};

} else if ((typeof document.compatMode == 'string') &&
(document.compatMode.indexOf('CSS') >= 0) &&
(document.documentElement) &&
(typeof document.documentElement.scrollTop == 'number')) {

getScrollY = function() {
return document.documentElement.scrollTop;
};

} else if ((document.body) &&
(typeof document.body.scrollTop == 'number')) {

getScrollY = function() {
return document.body.scrollTop;
}

} else {

getScrollY = function() {
return NaN;
};

}

return getScrollY();
}

透過 "Lazy Function Definition" 只需要在第一次花費條件式評估,對於重複呼叫的函數特別能夠改善效率。
補充

Firefox/Safari/Opera 的 getter methods[3] 能夠在屬性上模擬 "lazy definition" 如下:

this.__defineGetter__("foo", function() {
var t = new Date();
this.__defineGetter__("foo", function() {
return t;
});
return t;
});

// To the user, foo appears as a plain old
// non-function valued property of the global object.
console.log(this.foo);
setTimeout(function(){console.log(this.foo);}, 3000);

參考資料
[1] Peter, "Lazy Function Definition Pattern"
[2] Eric Miraglia, "A JavaScript Module Pattern"
[3] "Core JavaScript 1.5 Guide:Creating New Objects:Defining Getters and Setters"

2007年8月8日 星期三

C++ 使用 JScript 和 VBScript
Live SpaceGoogle DocGoogle Blog
Chui-Wen Chiu
2007.08.08

測試環境
1. Windows XP Pro SP2
2. Visual Studio 2005

範例程式下載

一般 C++ 會使用 Python 作為 Script 引擎,但本文將說明透過 Windows 內建的 Script 引擎,讓 C++ 可以使用 VBScript 或 JScript 引擎。一切的核心是由 Microsoft Scripting ActiveX (MSSCR.OCX)元件所提供,由於是 COM 元件,所以,其他支援 COM 的語言也可透過類似的方法達到。

以下是 C++ 使用 JScript 的簡單範例
#import "msscript.ocx" no_namespace
#include <comutil.h>
#include <tchar.h>

const TCHAR JSCRIPT[] = _T("JScript");
const int LANGUAGE_NAME_LEN = 40;

int main(int argc, _TCHAR* argv[]){
CoInitialize(NULL);

// 建立 COM 元件
IScriptControlPtr m_pScript;
HRESULT hr = m_pScript.CreateInstance(__uuidof(ScriptControl));
_com_util::CheckError( hr );

// 設定 Script Engine
TCHAR m_szLanguage[LANGUAGE_NAME_LEN+1];
_tcscpy(m_szLanguage, JSCRIPT);
m_pScript->PutAllowUI( VARIANT_FALSE );
m_pScript->PutLanguage( _bstr_t(m_szLanguage ) );

// 宣告一個新函數
_bstr_t strCode = _T("function calc(a, b){ return a+b; }");
m_pScript->AddCode( strCode );

// 參數設定
SAFEARRAY* saParameters;
saParameters = SafeArrayCreateVector(VT_VARIANT, 0, 2);
_variant_t var;

long lIndices = 0;
var = 33;
SafeArrayPutElement(saParameters, &lIndices, (void*)&var);

lIndices = 1;
var = 44;
SafeArrayPutElement(saParameters, &lIndices, (void*)&var);

// 呼叫函數 calc(33, 44) = 77
VARIANT varRet = m_pScript->Run("calc", &saParameters);
LPCTSTR result = (LPCTSTR)_bstr_t(varRet);

// 顯示結果
MessageBox(0, result, _T("Result"), 0);

m_pScript = NULL;

CoUninitialize();
return 0;
}

只要將上述程式稍作修改,即可改用 VBScript 引擎

#import "msscript.ocx" no_namespace
#include <comutil.h>
#include <tchar.h>

const TCHAR VBSCRIPT[] = _T("VBScript");
const int LANGUAGE_NAME_LEN = 40;

int main(int argc, _TCHAR* argv[]){
CoInitialize(NULL);

// 建立 COM 元件
IScriptControlPtr m_pScript;
HRESULT hr = m_pScript.CreateInstance(__uuidof(ScriptControl));
_com_util::CheckError( hr );

// 設定 Script Engine
TCHAR m_szLanguage[LANGUAGE_NAME_LEN+1];
_tcscpy(m_szLanguage, VBSCRIPT);
m_pScript->PutAllowUI( VARIANT_FALSE );
m_pScript->PutLanguage( _bstr_t(m_szLanguage ) );

// 宣告一個新函數
_bstr_t strCode = _T("Function calc(a,b) calc = CInt(a)+CInt(b) End Function");
m_pScript->AddCode( strCode );

// 參數設定
SAFEARRAY* saParameters;
saParameters = SafeArrayCreateVector(VT_VARIANT, 0, 2);
_variant_t var;

long lIndices = 0;
var = 33;
SafeArrayPutElement(saParameters, &lIndices, (void*)&var);

lIndices = 1;
var = 44;
SafeArrayPutElement(saParameters, &lIndices, (void*)&var);

// 呼叫函數 calc(33, 44) = 77
VARIANT varRet = m_pScript->Run("calc", &saParameters);
LPCTSTR result = (LPCTSTR)_bstr_t(varRet);

// 顯示結果
MessageBox(0, result, _T("Result"), 0);

m_pScript = NULL;

CoUninitialize();
return 0;
}

上述的片段用來了解 Script 引擎是相當好,可是實務上太過於瑣碎不容易維護。所以,[1] 提供兩個好用輔助類別:CScriptObject 和 CSafeArrayHelper。下面使用這兩個類別改寫上述 JScript 的範例。

#include <tchar.h>
#include <windows.h>

#include "ScriptObject.h"
#include "SafeArrayHelper.h"


int main(int argc, _TCHAR* argv[]){
CoInitialize(NULL);

CScriptObject* m_ScriptObj = new CScriptObject();

// 設定 Script Engine
m_ScriptObj->SetLanguage( _T("JScript") );

// 宣告一個新函數
m_ScriptObj->AddScript( _T("function calc(a, b){ return a+b; }") );

// 參數設定
CSafeArrayHelper sfHelper;
_variant_t var;
sfHelper.Create(VT_VARIANT, 1, 0, 2);
var = 33;
sfHelper.PutElement(0, (void*)&var);

var = 44;
sfHelper.PutElement(1, (void*)&var);

LPSAFEARRAY sa = sfHelper.GetArray();
_variant_t varRet;

// 呼叫函數
m_ScriptObj->RunProcedure(_T("calc"), &sa, &varRet);
LPCTSTR result = (LPCTSTR)_bstr_t(varRet);

// 顯示結果
MessageBox(0, result, _T("Result"), 0);

delete m_ScriptObj;

CoUninitialize();
}

上述三個範例的執行結果都是如下:

參考資料
[1] Ernest Laurentin, "Adding VBScript and JScript support in your C++ applications"


2007年8月1日 星期三

Ext.js 釋出 1.1 版
Live SpaceGoogle DocGoogle Blog
Chui-Wen Chiu
2007.08.02

本次更新包含獨立運作版本的 Ext,一個輕量級的 HTML 編輯器、新的 Ext.Ajax 應用類別,另外也強化 DateField 和 DatePicker 元件。

  • 獨立運作版本的 Ext
    • 可選擇性的引入外部函數庫,如:jQuery, YUI, prototype
    • 有效降低佈署時的檔案大小
    • 使用時只需要引入
      • ext-base.js
      • ext-all.js
  • 輕量級 HTML 編輯器


  • 強化版 DateField
    • 新增解析使用者輸入的日期字串
    • 新增年、月選擇方式
  • Ext.Ajax
    • 1.0 版的 Ajax 與 YUI 綁在一起,1.1 版已經切割成可獨立運作的 Ajax 類別。

下載網址
1.1 版線上 API 文件

參考資料
[1] http://extjs.com/blog/2007/08/01/ext-v11-released/


YUI 釋出 2.3 版

Live SpaceGoogle DocGoogle Blog
Chui-Wen Chiu
2007.08.02

YUI 這次主要新增下面六大元件

下載網址

參考資料
[1] YUI 2.3 Released: Rich Text Editor, Components, and Themes
[2] YUI 2.3.0: Six New Components and a Prettier Face


搜尋此網誌