.Net Framework 4.5與Visual Studio 11 - WCF Web API (3)

by vivid 15. 二月 2012 01:14

.NET Magazine國際中文電子雜誌
者:許薰尹
稿:張智凱
文章編號:N120212102
出刊日期:2012/02/15

本文延續《.Net Framework 4.5與Visual Studio 11 - WCF Web API (2)》一文的內容來探討WCF Web API提供的新功能,利用Visual Studio 11 Developer Preview開發工具來開發服務程式碼,讓你更容易設計支援REST類型的WCF服務。由於本文撰寫時使用的工具是Visual Studio 11 Developer Preview,而WCF Web API 則是Preview 6版,因此本文探討的內容在正式版上市時可能不適用。

延續前一篇文章《.Net Framework 4.5與Visual Studio 11 - WCF Web API (2)》的情境,介紹WCF Web API Test Client的使用方式,以及開發上的其它注意事項。本文範例使用ASP.NET來裝載服務,服務程式如下:

public class EmployeeService
    {
        static EmployeeRepository Store = new EmployeeRepository ( );
        [WebInvoke ( UriTemplate = "/employees" , Method = "POST" )]
        public System.Net.Http.HttpResponseMessage<Employee>
         CreateEmployee ( HttpRequestMessage<Employee> request )
        {
            HttpResponseMessage<Employee> response = null;

            Employee emp = ( Employee ) request.Content.ReadAsAsync ( ).Result;

            if ( emp == null )
            {
                response = new HttpResponseMessage<Employee> (
                    HttpStatusCode.BadRequest );
            }
            else
            {

                Store.Add ( emp );
                response = new HttpResponseMessage<Employee> (
                    emp , HttpStatusCode.Created );

                response.Headers.Location = new Uri (
                    request.RequestUri , emp.EmployeeID.ToString ( ) );
            }
            return response;

       }

        [WebInvoke ( UriTemplate = "/employees/{id}" , Method = "PUT" )]
        public Employee UpdateEmployee ( Employee emp )
        {

            var toUpdate = Store.GetEmployeeById ( emp.EmployeeID );
            if ( toUpdate == null )
            {
                throw new HttpResponseException ( HttpStatusCode.NotFound );
            }

            toUpdate.EmployeeName = emp.EmployeeName;
            return toUpdate;

        }

        [WebGet ( UriTemplate = "/employees" )]
        public IQueryable<Employee> GetEmployees ( )
        {
            return Store.GetEmployees ( ).AsQueryable ( );
        }

        [WebGet ( UriTemplate = "/employees/{id}" )]
        public Employee GetEmployeeByID ( int id )
        {
            Employee emps = Store.GetEmployeeById ( id );
            if ( emps == null )
            {
                throw new HttpResponseException ( HttpStatusCode.NotFound );
            }
            return emps;

        }

  [WebGet ( UriTemplate = "/employees?id={filter}" )]
        public List<Employee> GetEmployeesWithFilter (int filter )
        {
            return Store.GetEmployees ( ).FindAll ( emp => emp.EmployeeID >= filter );
        }

        [WebInvoke ( UriTemplate = "/employees/{id}" , Method = "DELETE" )]
        public Employee DeleteEmployee ( int id )
        {
            Employee emp = Store.GetEmployeeById ( id );
            if ( emp == null )
            {

                throw new HttpResponseException ( HttpStatusCode.NotFound );
            }
            Store.Delete ( emp );
            return emp;

        }

    }


WCF Web API Test Client – 提交HTTP請求

前文提及WCF Web API提供一個用戶端測試的工具程式,可以用來測試您的WCF Web API,利用此工具可以提交HTTP請求,並且檢視HTTP回應訊息。當您的服務透過HttpServiceHostFactory物件啟用了WCF Web API Test Client功能 (啟用步驟請參考《.Net Framework 4.5與Visual Studio 11 - WCF Web API (2)》一文),

WCF Web API Test Client是Web介面,當你按F5執行網站,或是執行自行撰寫用來裝載WCF服務的裝載程式,你可以開啟瀏覽器,輸入服務的基底位址+「/test」,例如基底位址為「http://localhost:4462/api」,則可以使用以下URL:

http://localhost:4462/api/test/

便可以看到以下的測試頁面,請參考圖1所示:

clip_image002

圖 1:WCF Web API Test Client的測試頁面。

左上角的「Resources」區塊中會表列服務的URI位置,當你點選服務的超連結,右方「Request」區塊會自動顯示此URI位置,請參考圖2所示。

clip_image004

圖 2:「Request」區塊會自動顯示此URI位置。

這時您可以按下畫面中的「Send」按鈕,發出HTTP請求,執行的結果將會顯示在右下方的「Response」區塊,預設叫用EmployeeService類別GetEmployees方法的執行結果是一份XML,請參考圖3所示。

clip_image006

圖 3:測試執行呼叫使用的Http Request與執行後的Http Response。

而WCF Web API Test Client的左下方則是一個呼叫的歷史記錄,您可以點選歷史記錄區上的任意超連結來檢視先前的呼叫測試動作,請參考圖4所示:

clip_image008

圖 4:歷史記錄區。

WCF Web API Test Client –參數傳遞

若使用的URI包含參數的資料,例如點選了左上角的「Resources」區塊中「{id}」,此時右上方的「Request」區塊中會自動顯示以下URI:

http://localhost:4462/api/employees/{id}」,您必須手動輸入將{id}參數代換成正確的參數值,請參考圖5所示:

clip_image010

圖 5:手動輸入將{id}參數代換成正確的參數值。

WCF Web API Test Client –訊息內文

若要測試HTTP POST以便叫用EmployeeService類別CreateEmployee方法來新增資料,可以在WCF Web API Test Client的「Request」區塊,將動詞設定為「POST」,在輸入資料的過程中,像是輸入Request、URI、Body…等區塊,WCF Web API Test Client提供的自動完成功能(auto – completion ) 會自動在頁面上顯示一個下拉式清單,協助您完成輸入的動作,當輸入的資料有問題,不符合需求時,頁面上就會顯示一個黃色的驚探號 (!) 來提醒您,請參考圖6所示。

clip_image012

圖 6:WCF Web API Test Client提供的自動完成功能(auto – completion )。

因為要新增一個員工資料,我們可以使用JSON格式,將要新增的Employee物件序列化完的結果填入Body區塊中,例如選取「Body」區塊下的「JSON」頁,此時,Headers區段中會自動將Content-Type設定為「application/json」:

Accept:*/*

Content-Type:application/json

Content-Length:45

然後再輸入以下JSON資料後,按「Send」按鈕送出HTTP請求:

{ "EmployeeID" : 5 , "EmployeeName" : "lucy" }

底下是執行HTTP POST新增資料的結果,請參考圖7所示:

clip_image014

圖 7:執行HTTP POST新增資料的結果。

當然,我們也可以直接在「Body」區塊使用XML來新增資料,延續前面的例子,點選「Body」區段下「XML」頁,WCF Web API Test Client會自動將HTTP Headers的Content-Type設定為「Content-Type:text/xml」,而Body區塊也會自動顯示底下的XML:

<?xml version="1.0"?>
<Employee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <EmployeeID>5</EmployeeID>
  <EmployeeName>lucy</EmployeeName>
</Employee>

若XML與JSON格式還無法滿足需求,還有一個RAW頁,可以用來支援其它資料格式,請參考圖8所示。

clip_image016

圖 8:支援XML、JSON與RAW資料格式。

WCF Web API Test Client –客製化錯誤

若服務執行發生問題,可以很容易地利用WCF Web API將HTTP狀態碼包裝在HttpResponseException物件,利用HttpResponse傳回用戶端。不過WCF Web API也允許您提供客製化的動作來處理錯誤及回傳錯誤訊息。

若要客製化錯誤,您可以撰寫一個類別,繼承HttpErrorHandler,並改寫OnTryProvideResponse方法,參考底下範例CustomErrorHandler類別在服務執行發生例外錯誤時,若HTTP的狀態碼是 404 (NotFound),程式便回傳一個HttpResponseMessage訊息,顯示自訂的錯誤資訊:

public class CustomErrorHandler : HttpErrorHandler
  {
      protected override bool OnTryProvideResponse ( Exception exception , ref HttpResponseMessage message )
      {
          HttpResponseException exp = exception as HttpResponseException;


          if ( exp != null && exp.Response.StatusCode == HttpStatusCode.NotFound )
              {
                  message = new HttpResponseMessage ( HttpStatusCode.NotFound );
                  message.Content = new StringContent ( "自訂錯誤 : 找不到物件 , 訊息: " + exception.Message );
                  return true;
              }
        
          return false;
      }
  }

最後您還需要利用HttpConfiguration物件來啟用自訂的錯誤處理類別,修改Application_Start方法,建立HttpConfiguration物件後,將CustomErrorHandler加到ErrorHandlers屬性:

protected void Application_Start ( )
     {

         var config = new HttpConfiguration ( ) { EnableTestClient = true };
         config.ErrorHandlers = ( handlers , endpoint , descriptions ) =>
         {
             handlers.Add ( new CustomErrorHandler ( ) );
         };
 

         _factory = new HttpServiceHostFactory ( ) { Configuration = config };
         RouteTable.Routes.Add ( new ServiceRoute ( "api" , _factory , typeof ( EmployeeService ) ) );

     }

接著您可以利用WCF Web API Test Client來測試自訂的錯誤處理程式碼,例如利用HTTP DELETE動詞,以及URI:「http://localhost:4462/api/employees/8」來刪除8號員工資料,由於當下並沒有此筆員工資料,執行時就會產生錯誤,參考WCF Web API Test Client執行的結果,請參考圖9所示:

clip_image018

圖 9:自訂的錯誤處理程式。

在UriTemplate使用查詢字串

WCF Web API的UriTemplate中可以定義查詢字串,以對應到服務方法的參數,例如為服務定義一個GetEmployeesWithFilter方法,此方法需要一個名為filter的傳入參數:

[WebGet ( UriTemplate = "/employees?id={filter}" )]
      public List<Employee> GetEmployeesWithFilter (int filter )
      {
          return Store.GetEmployees ( ).FindAll ( emp => emp.EmployeeID >= filter );
      }

定義UriTemplate時,便可以將參數套用在查詢字串上 ,GetEmployeesWithFilter方法會將員工編號大於、等於filter值的員工資料回傳。在WCF Web Test Client輸入以下URI進行測試:

http://localhost:4462/api/employees?id=2

參考執行結果,請參考圖10所示:

clip_image020

圖 10:使用查詢字串設定資料篩選條件。

oData支援

WCF Web API內建支援Open Data Protocol (OData),可利用查詢字串設定參數來指明資料回傳時的排序方式,也可以直接透過它來篩選資料,並能夠指定回傳的資料格式(回傳XML(ATOM),或JSON格式),這樣你就不必像上一個小節一樣,需要寫程式碼來設定篩選條件。

若服務要支援oData,只要讓服務的方法回傳IQueryable 型別即可,例如改寫服務的GetEmployees程式碼,使其回傳IQueryable<Employee>:

[WebGet ( UriTemplate = "/employees" )]
   public IQueryable<Employee> GetEmployees ( )
   {
       return Store.GetEmployees ( ).AsQueryable ( );
   }

WCF Web API內建以下查詢選項:

  • $filter
  • $orderby
  • $skip
  • $top

以下URI查詢EmployeeID大於2的資料出來:

http://localhost:4462/api/employees?$filter=EmployeeID gt 2

此範例的執行結果請參考圖11所示:

clip_image022

圖 11:使用$filter篩選資料。

以下URI查詢所有Employees資料,並根據EmployeeID降冪排序:

http://localhost:4462/api/employees?$orderby=EmployeeID desc

此範例的執行結果請參考圖12所示:

clip_image024

圖 12:使用$orderby進行資料排序。

以下URI查詢所有Employees資料,利用$skip跳過第一筆,利用$top取回第一筆資料,以此例而言便是取出2號員工資料:

http://localhost:4462/api/employees?$skip=1&$top=1

此範例的執行結果請參考圖13所示:

clip_image026

圖 13:使用$skip與$top取回資料。

回傳oData Feed

oData支援兩種媒體格式,分別為:application/atom+xml與application/json。若要讓服務回傳oData Feed格式,需要事先啟用。利用HttpConfiguration類別的Formatters屬性,新增一個ODataMediaTypeFormatter物件:

var config = new HttpConfiguration ( ) { EnableTestClient = true };
config.Formatters.Insert ( 0 , new ODataMediaTypeFormatter ( ) );

啟用之後服務便會回傳OData Feed格式的資料,執行結果請參考圖14所示:

clip_image028

圖 14:回傳OData Feed格式的資料。

自訂驗證處理程式

為了確保服務程式能夠正確地執行,其中有一很重要的因素,應該避免使用者輸入錯誤的資料,因此您可以適當地在服務程式碼中加入驗證資料的邏輯。

例如底下的服務範例UpdateEmployee方法收到用戶端傳入的Employee物件時,驗證Employee物件不可為null、EmployeeName屬性,以及EmployeeID屬性不可以為負數,若輸入的資料不符需求服務將回傳400號狀態碼,代表BadRequest:

[WebInvoke ( UriTemplate = "/employees/{id}" , Method = "PUT" )]
        public Employee UpdateEmployee ( Employee emp )
        {
            if ( emp == null || emp.EmployeeName==null || emp.EmployeeID <=0)
            {
                throw new HttpResponseException ( HttpStatusCode.BadRequest );
            }

            var toUpdate = Store.GetEmployeeById ( emp.EmployeeID );
            if ( toUpdate == null )
            {
                throw new HttpResponseException ( HttpStatusCode.NotFound );
            }
            toUpdate.EmployeeName = emp.EmployeeName;
            return toUpdate;
        }


若服務端的許多方法都傳入Employee物件當參數的話,你就必需在服務中的每個方法重複撰寫這一段驗證的程式碼。另外一個較好的解決方案便是撰寫自訂的驗證處理程式,稱之為作業處理程式(Operation Handler)。

作業處理程式(Operation Handler)負責將HttpRequestMessage訊息轉換成服務方法中帶的參數,以及將方法的回傳值和輸出參數轉換成HttpResponseMessage。

舉例來說,以下範例宣告一個MyValidationHandler類別,繼承HttpOperationHandler< T, TOutput >,改寫OnHandle方法,在方法中撰寫Employee參數物件的驗證邏輯,若通過驗證則回傳一個Employee物件 ,若違反驗證規則便回應BadRequest:

public class MyValidationHandler : HttpOperationHandler<Employee , Employee>
    {
        public MyValidationHandler ( string outputParameterName )
            : base ( outputParameterName )
        {
        }

        protected override Employee OnHandle ( Employee input )
        {
            if ( input == null || input.EmployeeName == null || input.EmployeeID <= 0 )
            {
                var resp = new HttpResponseMessage ( System.Net.HttpStatusCode.BadRequest )
                {
                    Content = new StringContent ( "資料錯誤!" )
                };
                throw new HttpResponseException ( resp );
            }
            return input;
        }

     
    }


接下來在Application_Start方法中,加入MyValidationHandler:

protected void Application_Start ( )
      {

          var config = new HttpConfiguration ( ) { EnableTestClient = true };

          config.RequestHandlers = ( handlers , endpoint , operation ) =>
          {

              var param = operation.InputParameters.FirstOrDefault ( p => p.ParameterType == typeof ( Employee ) );
              if ( param != null )
              {
                  handlers.Add ( new MyValidationHandler ( param.Name ) );
              }
          };


          _factory = new HttpServiceHostFactory ( ) { Configuration = config };
          RouteTable.Routes.Add ( new ServiceRoute ( "api" , _factory , typeof ( EmployeeService ) ) );

      }

HttpConfiguration 類別的RequestHandlers屬性是一個delegate型別,在服務啟動執行時會被呼叫。我們檢查HttpOperationDescription物件,看看呼叫服務的Operation時是否有帶Input參數,且參數的型別是Employee型別,若是,則套用自訂的MyValidationHandler類別進行驗證。

開啟瀏覽器進行測試,若送出Employee物件進行修改而資料有誤時,WCF Web API Test Client便會顯示400號錯誤,執行結果請參考圖15所示:

clip_image030

圖 15:自訂驗證處理程式產生的400號錯誤。

Tags:

.NET Magazine國際中文電子雜誌 | Visual Studio | WCF Web API | 許薰尹Vivid Hsu

新增評論




  Country flag
biuquote
  • 評論
  • 線上預覽
Loading






NET Magazine國際中文電子雜誌

NET Magazine國際中文電子版雜誌,由恆逸資訊創立於2000,自發刊日起迄今已發行超過500篇.NET相關技術文章,擁有超過40000名註冊讀者群。NET Magazine國際中文電子版雜誌希望藉於電子雜誌與NET Developer達到共同學習與技術新知分享,歡迎每一位對.NET 技術有興趣的朋友們多多支持本雜誌,讓作者群們可以有持續性的動力繼續爬文。<請加入免費訂閱>

月分類Month List