Google Code Prettify

不用IIS也能執行ASP.NET Web API

在某些情境,桌面環境執行的程式(Console、Windows Form、WPF… 等)也需要提供API管道供外界呼叫,例如: 先前提到的Word轉PDF服務、ERP UI接受外部(如Excel VBA)匯入資料... 等等。
設計API管道時有不少選擇: DDE、Anonymous Pipe/Named Pipe、Socket... 都可行。對轉行寫桌面程式的ASP.NET開發者來說,還有一個溫馨的好選擇 -- 在桌面程式專案裡寫ASP.NET Web API吧!!
是的,即使沒有IIS,ASP.NET Web API也能照跑不誤,在Windows Form、WPF可以繼續用同一招打天下,對跨界寫桌面程式的ASP.NET開發人員,實在是一大福音。
以下使用Console Application專案做個簡單示範。建好新專案後,透過NuGet Packages Manager尋找self host,可以找到"Microsoft ASP.NET Web API Self Host"套件,二話不說立刻安裝。
ASP.NET Web API Self Host由多個組件構成,但不用擔心,NuGet會自動一一下載安裝好。
安裝完成後,我們要在主程式中加幾行程式,啟動一個小小的Http Server。
第一步
要先透過HttpSelfHostConfiguratio宣告提供Web API的URL。由於向Windows註冊特定的TCP Port需要權限,有兩種做法: 以管理者身分執行Visual Studio及應用程式,或是透過 netsh http add urlacl url=http://+:port_number/ user=machine\username指令授權。依"永遠只授與足以執行的最小權限"的資安原則,用netsh授權雖然手續較麻煩,但比讓整個應用程式都具有管理者權限安全。

註:
開放授權 netsh http add urlacl url=http://+:32767/ user=machine\username

移除權限 netsh http delete urlacl url=http://+:32767/
接著,使用Routes.MapHttpRoute()指定MVC必備的路由設定,就可使用這組設定值宣告一個HttpSelfHostServer並啟動。由於會動用到網路資源,建議使用using HttpSelfHostServer的寫法,確保結束時會透過Dispose()釋放相關資源。
加上一段迴圈,直到使用者輸入exit才結束HttpSelfHostServer。在這段期間,HttpSelfHostServer便能接收HTTP請求,找到適當的Controller提供服務。
排版顯示純文字
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Http;
using System.Web.Http.SelfHost;
 
namespace SelfHostWebApi
{
    class Program
    {
        static void Main(string[] args)
        {
            //指定聆聽的URL
            var config = new HttpSelfHostConfiguration("http://localhost:32767");
 
//注意: 在Vista, Win7/8,預設需以管理者權限執行才能繫結到指定URL,否則要透過以下指令授權
//開放授權 netsh http add urlacl url=http://+:32767/ user=machine\username
//移除權限 netsh http delete urlacl url=http://+:32767/
 
            //設定路由
              config.Routes.MapHttpRoute("API", "{controller}/{action}/{id}", 
                                        new { id = RouteParameter.Optional });
            //設定Self-Host Server,由於會使用到網路資源,用using確保會Dispose()加以釋放
            using (var httpServer = new HttpSelfHostServer(config))
            {
                //OpenAsync()屬非同步呼叫,加上Wait()則等待開啟完成才往下執行
                   httpServer.OpenAsync().Wait();
                Console.WriteLine("Web API host started...");
                //輸入exit按Enter結束httpServer
                string line = null;
                do
                {
                    line = Console.ReadLine();
                }
                while (line != "exit");
                //結束連線
                   httpServer.CloseAsync().Wait();
            }
            
        }
    }
}
Console Application專案沒有Models、Controllers、Views資料夾,要如何加入Web API Controller讓人有些茫然,此時讓我們回歸ASP.NET MVC的"Convension over Configuration"(以慣例取代設定)原則: 在專案中新增一個名為BlahController的類別並繼承ApiController,Self Host自然會依著類別名稱認出它,並在有人呼叫http:// localhost:32767/Blah時派它上場。
為了測試,我宣告了一個很沒營養的Date方法傳回日期字串,標註[HttpGet]是為方便用瀏覽器輸入URL就能直接看結果(否則預設只接受POST,需要寫JavaScript才能測試)。
排版顯示純文字
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Http;
 
namespace SelfHostWebApi
{
    public class BlahController : ApiController
    {
        [HttpGet]
        public string Date()
        {
            return DateTime.Today.ToString("yyyy/MM/dd");
        }
    }
}
實際執行結果如下:
不會寫Socket、不懂Named Pipe,居然也能寫出具有API整合功能的桌面程式~ 衝著這點,讓我們一起呼喊: ASP.NET Web API 好威呀!


資料來源: http://blog.darkthread.net/post-2013-06-04-self-host-web-api.aspx

產生JSON字串的幾種方式整理(2)

4. 物件序列化
這個要先知道輸出的json字串長什麼樣子
貼到http://json2csharp.com/去產生類別程式碼
image
然後

01protected void Page_Load(object sender, EventArgs e)
02{
03    if (!IsPostBack)//Get Method
04    {
05        //準備資料
06        string collapse_key = "score_update";
07        int time_to_live = 108;
08        bool delay_while_idle = true;
09        string score = "4x8";
10        string time = "15:16.2342";
11        List<string> registration_ids = new List<string>() { "4","8","15","16","23","42"};
12 
13          
14        //建立物件,塞資料
15        RootObject root = new RootObject();
16        root.collapse_key = collapse_key;
17        root.time_to_live = time_to_live;
18        root.delay_while_idle = delay_while_idle;
19        Data data = new Data();
20        root.data = data;
21        root.data.score = score;
22        root.data.time = time;
23        root.registration_ids = registration_ids;
24        //物件序列化
25        string strJson = JsonConvert.SerializeObject(root, Formatting.Indented);
26        //輸出結果
27        Response.Write(strJson);
28    }
29}
或使用物件初始化方式塞值
01protected void Page_Load(object sender, EventArgs e)
02{
03    if (!IsPostBack)//Get Method
04    {
05        //準備資料
06        string collapse_key = "score_update";
07        int time_to_live = 108;
08        bool delay_while_idle = true;
09        string score = "4x8";
10        string time = "15:16.2342";
11        List<string> registration_ids = new List<string>() { "4","8","15","16","23","42"};
12 
13          
14        //建立物件,塞資料
15        RootObject root = new RootObject()
16        {
17            collapse_key = collapse_key,
18            time_to_live = time_to_live,
19            delay_while_idle = delay_while_idle,
20            data = new Data()
21            {
22               score = score,
23               time=time
24            },
25            registration_ids = registration_ids
26        };
27         
28        //物件序列化
29        string strJson = JsonConvert.SerializeObject(root, Formatting.Indented);
30        //輸出結果
31        Response.Write(strJson);
32    }
33}
使用物件初始化方式塞值看起來直覺多了
不過為了物件序列化還要特地宣告類別,這…倒不如使用Linq吧

5.用Linq+匿名物件寫法 直接組JSON
這招在Json.net的官方文件有範例(用JObject.FromObject的那個):http://james.newtonking.com/projects/json/help/html/CreatingLINQtoJSON.htm
但只有範例,沒寫為什麼Linq要那樣寫,誰看得懂阿XD
要用Linq直接組JSON
大概把握幾點:
Json Object:Json字串用大括號{}表示,Linq也是用大括號{}表示
Json Object的Name(Key、Property):Json字串在兩個雙引號””裡寫一個英文單字,Linq就直接寫英文單字即可
Json Array:Json字串用中括號[]表示,Linq就用from o in XXX select o,這種回傳IEnumerable的寫法(如果JSON字串是物件陣列的話就用from o in XXX select new {欄位1=YYY}這種形式)
Json Value:Linq就看Json字串填什麼值就跟著填什麼值
01protected void Page_Load(object sender, EventArgs e)
02{
03    if (!IsPostBack)//Get Method
04    {
05        //準備資料
06        List<string> registration_ids = new List<string>() { "4""8""15""16""23""42" };
07 
08        //用Linq直接組
09        var result = new
10                        {
11                            collapse_key = "score_update",
12                            time_to_live = 108,
13                            delay_while_idle = true,
14                            data = new{
15                                score ="4x8",
16                                time = "15:16.2342"
17                            },
18                            registration_ids = from s in registration_ids
19                                                        select s
20 
21                        };
22 
23        //序列化為JSON字串並輸出結果
24        Response.Write(JsonConvert.SerializeObject(result));
25    }
26}
請看圖示解說↓
image
由於Linq支援DataTable的查詢,所以再難的JSON格式都組得出來,不用再跑for迴圈寫一堆Code,開發速度大幅提昇
以下截自實務上的一段Code(有做了一點修改)
01DataTable dt = new DataTable();//撈出一張資料表的所有數據(這個表類似北風資料庫的員工資料表,有階層關係)
02DataTable dtDetail = new DataTable();//上一張表的一對多明細表
03var firstLevel = dt.Select("pid is NULL");//第一層數據
04var result = new
05{
06      Info = new
07    {
08        Level1 = from a in firstLevel
09                 join b in dt.AsEnumerable() on a.Field<int>("id") equals b.Field<int?>("pid") into secondLevel
10                 select new
11                 {
12                     Title =  a.Field<string>("Title"),
13                     Level2 = secondLevel.Select(c => new
14                     {
15                         Title =  c.Field<string>("Title"),
16                         Photos = from s in secondLevel
17                                       join uu in dtDetail.AsEnumerable() on s.Field<int>("id") equals uu.Field<int>("id")
18                                       where s.Field<int>("id") == c.Field<int>("id")
19                                select new
20                                {
21                                    PhotoID = uu.Field<string>("PhotoID"),
22                                    PhotoTitle = uu.Field<string>("PhotoTitle")
23                                }
24 
25                     })
26                 }
27    }
28};
※不過要注意對於寫的人是寫很快,後人維護會有閱讀困難的可能


最後附上一張比較圖
image