[Web API 2]Auto Generate Web API Proxy Objects with C#

[Web API 2]Auto Generate Web API Proxy Objects with C#

前言

ASP.NET Web API 因為是純的 HTTP service ,所以它不像原本的 web service 有 wsdl 或 end-point 來提供 meta-data 的資訊,自然在使用上就比較不像在 Client 端要使用 web service 只需要直接 Add Web Reference ,就可以在 Client 的環境自動產生對應 web service 的 C# proxy class 來使用。

而 WCF 其實也有考慮到供 client 使用的 meta-data ,讓呼叫端可以快速地根據這些 meta-data 來建立自己的 remote proxy objects ,這也是為什麼在 Visual Studio 中加入服務參考後,一樣會在 client 端產生對應的 proxy objects。

那 ASP.NET Web API 怎麼辦?既然 ASP.NET Web API 的輕量化跟架構的可擴充性能符合這麼多的使用場景,當迅速地導入或將原本的 web service 升級成 Web API 之後,會不會因為 Client 端需要自己定義自己的 proxy object 而導致生產力下降,最後反而因為 client 端的呼叫或開發,而捨 Web API 不用呢?

當然,這樣常見的需求,一定有人已經幫忙寫好套件了,這篇文章就簡單介紹一下 WebApiProxy 這個 NuGet package ,透過它在 Web API server 上增加 message handler 以及運用 Web API 2 中的 ApiExplorer ,會額外建立一個 /api/proxies 的 end-point 來提供 Web API 的 meta-data ,因此就可以簡單快速地自動產生 javascript proxy objects 與 C# proxy objects 。

因為產生 javascript proxy 的文章比較多,所以這篇文章就只先簡介 C# proxy objects 的部分。

 

步驟1:建立 Web API 2.0 站台,新增 OrdersController 並於 Help Page 顯示 API Document

首先建立一個 Web API 2.0 的站台,並新增一個空白的 API Conroller: OrdersController ,程式碼如下:

namespace WebApiForCsharpProxy.Controllers
{
    /// <summary>
    /// 訂單相關處理
    /// </summary>
    public class OrdersController : ApiController
    {
        private List<Order> _orders;

        public OrdersController()
        {
            this._orders = new List<Order>
            {
                new Order {Id = 1, Amount = 100, CustomerId=10},
                new Order {Id = 2, Amount = 200, CustomerId=20},
            };
        }

        /// <summary>
        /// 取得所有訂單
        /// </summary>
        /// <returns>所有訂單</returns>
        public IEnumerable<Order> GetAllOrders()
        {
            return this._orders;
        }

        /// <summary>
        /// 根據訂單金額條件取得訂單
        /// </summary>
        /// <param name="lowBound">訂單金額下限</param>
        /// <param name="upBound">訂單金額上限</param>
        /// <returns>符合條件的訂單</returns>
        public IEnumerable<Order> GetOrdersByAmount(int lowBound, int upBound)
        {
            var orders = this._orders.Where(x => x.Amount >= lowBound && x.Amount <= upBound);

            return orders;
        }
    }
}

接下來執行一下網站,並點到 Help Page ,可以看到上面已經有 Orders 的服務了,如下圖所示:

image

接著,先將 API document 顯示在 Help Page 上,先在網站的屬性頁,「建置」的頁籤中,將 document 產生在 App_Data 的 folder 中,如下圖所示:

image

接著按下「Ctrl+,」找到「HelpPageConfig.cs」,在 Register() 中取消第一行的註解,並將路徑指到剛剛建置會產生的 XML 檔案,如下所示:

image

重新建置後,可以看到 Help Page 上有 Controller 相關的說明了。如下所示:

image

image

 

步驟2:透過 NuGet 參考 WebApiProxy

接著就是要想辦法在 Web API 站台自動產生可以取得 meta-data 的 end-point 供 client 端使用。透過 NuGet 安裝 WebApiProxy ,如下:

PM> Install-Package WebApiProxy

或是透過 IDE 搜尋「WebApiProxy」安裝,如下所示:

image

安裝完成後重新建置,在網站上 URL 輸入:「/api/proxies」,就可以看到 WebApi Proxy 自動產生的 javascript proxy objects ,如下所示:

image

這樣就代表 package 安裝成功且執行無誤。

站台上網頁只要 include 這個 uri ,就可以直接在 javascript 對 Web API 進行呼叫。
<script src="/api/proxies" type="text/javascript"></script>

 

步驟3:新增一個 Console Project 當 Client 端,並透過 NuGet 加入參考 WebApiProxy.CSharp

在方案中加入一個新的 Console Project 用來呼叫 Web API。建立完成之後,透過 NuGet 加入 WebApiProxy.CSharp 的 Package 。

PM> Install-Package WebApiProxy.CSharp

或是透過 IDE 安裝 package,如下:

image

打開 WebApiProxy.config 檔,把剛剛站台的 /api/proxies 加入設定。如下圖所示:

image

清除方案,重新建置一下,如果沒有錯誤,代表偵測得到 Web API proxies 的 end-point 。

接下來有個動作很重要,因為是在建置時動態產生 C# proxy 的物件,所以要先把 Console project 卸載(unload),再加入一次,才能在 console project 中 intellisence 直接看到對應的 proxy object 。

卸載再載入 Console project 之後,就可以直接使用對應 Web API 所動態產生的 proxy 類別了。首先新建一個 InvokeOrderApi() 的方法,而這個 package 有加入使用 proxy object 的 code snippet ,只要輸入「webapiproxy」,按下 tab 就可以了。如下所示:

image

產生的 code snippet 如下:

image

將 ApiClient 改成 OrdersClient ,並加入相關的 namespace 參考:

image

Intellisense 也支援 Proxy 物件上的方法與 document ,如下所示:

方法說明:

image

參數說明:

image

接著修改取得 response 的部分,GetOrdersByAmount() 回傳的是 IEnumerable<Order> ,這時 Model 的物件也已經自動產生好了,只需要引入 namespace ,如下所示:

image

ReadAsAsync<T>() 記得要加入 System.Net.Http 的 namespace ,因為是擴充方法。

接著完成 Console project 的程式,如下:

    internal class Program
    {
        private static void Main(string[] args)
        {
            InvokeOrderApi();
            Console.Read();
        }

        private static async void InvokeOrderApi()
        {
            using (var client = new OrdersClient())
            {
                var response = await client.GetOrdersByAmountAsync(50, 150);
                response.EnsureSuccessStatusCode();
                var orders = await response.Content.ReadAsAsync<IEnumerable<Order>>();

                foreach (var order in orders)
                {
                    Console.WriteLine("order id:{0}, amount:{1}", order.Id, order.Amount);
                }
            }
        }
    }

看一下執行的過程:

image

結果:

image

改變一下條件, Amount 範圍為 50 到 250 ,則結果如下:

image

image

 

結論

常用 .NET 寫 Web Service 的朋友,應該都有個感觸就是:用了 Web API 之後就回不去了。而如果 Web API 越來越被廣泛使用,那麼 Client 端該如何快速地針對呼叫 Web API 進行開發,則會是決定生產力的因素之一。

透過這篇文章,簡單介紹這個不錯用的 NuGet Package ,希望對已經使用 Web API 2.0 的朋友,可以在適用的場景下,增加生產力,把心力關注在 context 的商業邏輯與需求,要呼叫遠端的 Web API 只要直接操作 Proxy 物件即可。

Sample code 下載:OneDrive 位置

補充

直接用Get去呼叫 /api/proxies 時,拿到的是 javascript 使用的 proxy objects。

image

 

但如果 request header 加上

X-Proxy-Type : metadata

那拿到的就會是純粹的 meta-data,如下所示:

image

 

Reference

  1. GeekFest #2 - Web API Proxies with WebApiProxy
  2. github: WebApi C# Proxy Generator
  3. Introducing WebApiProxy: Providing JavaScript & C# proxies with Intellisense including documentation for ASP.NET Web API

對敏捷開發有興趣的朋友,可以參考我的粉絲專頁:91敏捷開發之路

若需要聯絡我,可以透過粉絲專頁私訊或是側欄的關於我。