題目有些大,但文中談到的問題很小;看似表揚C#,實際不是。
這個小問題來自這樣的應用情境——以HTTP POST的方式調用第三方API,第三方API不支援JSON傳參,只能通過URL query string方式傳參(a=1&b=2)。
假設API的地址是http://www.cnblogs.com/api/say,需要傳遞的參數是username與words,只支援HTTP POST調用。
另外,加一個約束條件——不允許用字串拼接,比如:"username="+username+"&words="+words;
jQuery中的調用範例程式碼
var postData = { username: 'test', words: 'hello world'};$.ajax({ url: 'http://www.cnblogs.com/api/say', data: postData, type: 'post', });
在上面的Javascript代碼執行時,jQuery會自動將js對象postData轉換為Url query string的形式(username=test&words=hello+world),並自動進行Url encode。
PHP中的調用範例程式碼
$url = 'http://www.cnblogs.com/api/say';$data = array('username' => 'test', 'words' => 'hello world');$data = http_build_query($data); $ch = curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_POST, 1);curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);curl_setopt($ch, CURLOPT_POSTFIELDS, $data); $response = curl_exec($ch);curl_close($ch);
PHP內建的http_build_query()函數能自動將數群組轉換為Url query string的形式(username=test&words=hello+world),並自動進行Url encode。
C#中的調用範例程式碼
1、在.NET Framework 4.0中的實現(HttpWebRequest+匿名型別+反射+LINQ)
參數通過匿名型別(Anonymous Type)進行定義:
var postData = new{ username = "test", words = "hello world"};
.NET Framework 4.0的類庫中沒有提供直接將“匿名型別執行個體”轉換為“Url查詢參數”的API,只能藉助“反射+LINQ”自己實現。實現代碼如下:
static void Main(string[] args){ var url = "http://www.cnblogs.com/api/say"; var postData = new { username = "test", words = "hello world" }; var webRequest = WebRequest.Create(url) as HttpWebRequest; webRequest.Method = "post"; webRequest.ContentType = "application/x-www-form-urlencoded"; var queryString = string.Join("&", from p in postData.GetType().GetProperties() select p.Name + "=" + HttpUtility.UrlEncode(p.GetValue(postData, null).ToString())); using (var sw = new StreamWriter(webRequest.GetRequestStream())) { sw.Write(queryString); } using (var response = webRequest.GetResponse()) { using (var sr = new StreamReader(response.GetResponseStream())) { Console.WriteLine(sr.ReadToEnd()); } }}
2、在.NET Framework 4.5中的實現(HttpClient+FormUrlEncodedContent)
.NET Framework 4.5考慮到了這個應用情境,提供了FormUrlEncodedContent,但它不支援匿名型別(Anonymous Type),只支援字典(Dictionary)。參數需要這樣定義:
var postData = new Dictionary<string, string>{ { "username", "test" }, { "words", "hello world" }};
完整實現代碼如下(需要引用System.Net.Http):
static void Main(string[] args){ var url = "http://www.cnblogs.com/api/say"; var postData = new Dictionary<string, string> { { "username", "test" }, { "words", "hello world" } }; var urlEncodedContent = new FormUrlEncodedContent(postData); var httpClient = new HttpClient(); var result = httpClient.PostAsync(url, urlEncodedContent).Result.Content.ReadAsStringAsync().Result; Console.WriteLine(result);}
.NET Framework 4.5中的實現還算簡單,但是FormUrlEncodedContent只支援Dictionary,考慮還是不周到。
感想
.NET因互連網而生,而通過URL query string傳參的需求在互連網應用中普通存在,但.NET從4.5才開始考慮這個應用情境,實在有點說不過去。
多數開發互連網應用多年的.NET開發人員都有多年拼接字串的經曆,但是.NET也沒考慮到這個情境,比如雙引號問題(字串不支援單引號內直接包含雙引號)。即使拼接字串,也沒有Javascript與PHP中操作方便。
.NET功能強大、設計領先,但是對互連網應用情境缺少細緻入微的考慮,在用.NET開發互連網應用時經常有殺雞用牛刀的感覺。幸虧牛刀上有非常舒服的刀柄(Visual Studio),才吸引了如些多的開發人員。如果互連網應用是未來,即使刀柄再舒服,用牛刀殺雞的感覺畢竟不好,牛刀自身的改變才是解決之道。