在某些情境,桌面環境執行的程式(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授權雖然手續較麻煩,但比讓整個應用程式都具有管理者權限安全。

接著,使用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 好威呀!


Comments

# by GATTACA

SignalR也有SelfHost可用啊, ip過濾也可以只限自己127.0.0.1連線, 我現在所有單機版的操作介面都只剩下一個填滿的WebBrowser,啟動後再去讀Html檔案進來; 美工依照原本的WinForm排版Html順便美化, 程式設計師再去填SignalR JavaScript Client與Knockout ViewModel, 如果有用到COM元件,其callback事件處理後透過SignalR更新到Html上,也沒有背景執行緒更新主執行緒WinForm畫面的問題; 雖然是較極端的做法,但是可以擺脫微軟悲情的UI更迭(MFC->WinForm->WPF...), 避免程式設計師將業務邏輯綁死到UI元件上, Html美工又很好找, 可說是利大於弊啊

# by anson

請問一下黑大,我在實作上,出現以下的錯誤訊息,可以幫助我一下嗎?? <Error><Message>No HTTP resource was found that matches the request URI 'http://localhost:32767/Blah/Date'.</Message><MessageDetail>No type was found that matches the controller named 'Blah'.</MessageDetail></Error>

# by Jeffrey

to anson, 由訊息來看,問題出在Web API Self-Host在你的程式找不到名為BlahController的類別,檢查一下專案是否有正確加入BlahController.cs?

# by anson

黑大,我比對之後,程式碼完全一樣,只是可能下載self host的問題,非常的奇怪.........我文章看了好幾次,目前比較明顯的不同,就是我的有app.config,還有packages.config有一點不同.......

# by frank

controller 的 class 需要設成public class 才可以~~

# by Terence

黑大你好,小弟最近在苦惱怎樣測試web api, 上網看過很多方法是使用postman之類的工具,可是我的問題是把POST進去的json先貼在postman上顯然非常困難,那應該怎樣在Visual Studio進行測試呢?

# by Terence

感謝黑大!! 其實我正在煩惱的問題對於web api是如何去寫測試。在網路上看過很多文章,有些說法是直接用postman之類的去做測試,有些人就是使用nunit + Moq,有些人說就是使用integration test才對,那如果今天webapi的規模不大,只是使用了OAuth機制,那其實使用那種策略比較有效率?

# by Jeffrey

to Terence, 做自動測試時,建議將把不想測試的部分By Pass掉(也是需要Mock的原因)。在你的案例中,聽起來OAuth整合不是要測試的焦點。 較正規做法是將各功能切成Interface,以允許抽換不同實作,當測試其他功能,OAuth整合介面可改成永遠傳回驗證成功的版本,不要干擾要測試的功能。或者另一個角度,將Web API的核心邏輯抽取成為獨立DLL,如此用單元測試DLL的特定方法即可驗證正確性,跟OAuth整合部分就留待整合測試再驗證。 以上都需要調整程式架構,如果沒有重構的空間,則小幅修改程式加個是否引用OAuth與否開關也是解法。(但上線時務必要調對設定)

# by Terence

謝謝Jeff大的意見,剛好那個是新的系統,所以才想到用一些新的想法來開發...

# by Ben

感謝分享,試著做了一個sample 有辦法把http變成https嗎? 謝謝

# by Leo

黑大,最近正拜讀您的非同步的相關文章,有個疑問想請教您 目前我已理解若透過IIS託管的服務,會藉由執行緒集區(thread pool)來管理來自用戶端的 HTTP 請求,應用程式若能適時運用非同步模式,就比較不會出現執行緒耗盡的情形並有助於提升回應速度與延展性。但若是self hosting的方式建置Web API等服務時,當用戶端的 HTTP 請求湧入時,還會有類似像緒集區(thread pool)來協助分配執行緒嗎? 網路找不太到這方面的資訊,還懇請黑大解惑,感謝!

# by Jeffrey

to Leo, 現在討論這個議題,我會假設你說的是 ASP.NET Core。ASP.NET Core 的 Self-Hosting 是交由 Kestrel 打理,Kestrel 本身就是一個跨平台的小型 Web Server,並行處理多個 HTTP Request 是基本要求,文件也很強調它的高效能,宣稱 Kestrel 已最佳化,可以有效地處理大量並行連線。 參考:https://learn.microsoft.com/zh-tw/aspnet/core/fundamentals/servers/kestrel?view=aspnetcore-8.0 故不需擔心 Self-Hosting 時無法應付大量請求,但一般實務應用時會透過 Reverse Proxy (如 Nginx、IIS ARP) 接上 Internet,以強化安全防護、負載平衡、URL Rewrite、傳輸壓縮... 等功能。

# by Leo

抱歉資訊沒有提供完整,是 Kestrel 沒錯,謝謝黑大解惑

Post a comment