(三)ASP.NET Web API 2 - Web API與Routing的互動 - 1

不得不說,Routing(路由)是整個ASP.NET Web API中最重要的部分,因為多數人無法很好的轉換平台,也是因為Routing實在有點複雜,而且有很多小地方要注意,程式設計師必須要兼顧Controller與Routing的設計和架構,以免一不小心的小錯誤卻造成大災難,本篇會以簡單易懂的範例來實作Routing,讓大家了解Routing的基礎運作方式。

這次,同樣是要管理應徵者的資料,請依照(二)ASP.NET Web API 2 - 建立第一個Web API一步一步地完成Model的設計,我們在本篇僅會對Controller以及WerbApiConfig.cs撰寫程式,其餘的一切都不會有變動,當然,最後仍會修改index.html網頁,真正對Routing的設計有感覺!

第一步:完成Model的設計後, 我們繼續編輯"CandidatesController.cs",除了原本回傳全部應徵者資料的方法外,我們要在新增三個搜尋應徵者資料的方法,其搜尋關鍵分別是:

  • 姓名
  • ID
  • 姓名以及ID
//以姓名搜尋應徵者資料
public IHttpActionResult GetCandidateByNamea(string Name)
{
    var myCandidate = Candidates.FirstOrDefault((c) => c.Name == Name);
    if (myCandidate == null)
        return NotFound();
    else
        return Ok(myCandidate);
}

//以ID搜尋應徵者資料
public IHttpActionResult GetCandidateById(string Id)
{
    var myCandidate = Candidates.FirstOrDefault((c) => c.Id == Id);
    if (myCandidate == null)
        return NotFound();
    else
        return Ok(myCandidate);
}

//以姓名及ID搜尋應徵者資料
public IHttpActionResult GetCandidateByNameaAndId(string Name, string Id)
{
    var myCandidate = Candidates.FirstOrDefault((c) => c.Name == Name && c.Id == Id);
    if (myCandidate == null)
        return NotFound();
    else
        return Ok(myCandidate);
}

可以發現,新增的這三個方法最大的不同,就是回傳值的型別"IHttpActionResult",由字面上的意思可以知道,IHttpActionResult代表HTTP動作的結果(狀態),所以我們可以在HttpStatusCode這個列舉中找到所有可用於IHttpActionResult的回傳值,最常使用的如下:

# 方法名稱 HTTP Status Code 意義
1 Ok 200 要求成功
2 NoContent 204 已成功處理要求但回應是空白
3 Unauthorized 401 要求的資源需要驗證
4 NotFound 404 要求的資源不存在伺服器上
5 InternalServerError 500 伺服器端發生錯誤

另外,也可以發現,每個方法的參數值與我們以前寫函數的方式一樣,所以,在這個階段,不需要去改變任何的習慣。

第二步:接著我們要來設計Routing,打開App_Start/WebApiConfig.cs,我們要針對每個方法設計一個專屬的Routing,如下:

//路由1
config.Routes.MapHttpRoute(
    name: "GetCandidateByName",
    routeTemplate: "api/{controller}/name/{name}",
    defaults: new { name = RouteParameter.Optional }
);

//路由2
config.Routes.MapHttpRoute(
    name: "GetCandidateById",
    routeTemplate: "api/{controller}/id/{id}",
    defaults: new { id = RouteParameter.Optional }
);

//路由3
config.Routes.MapHttpRoute(
    name: "GetCandidateByNameAndId",
    routeTemplate: "api/{controller}/{name}/{id}",
    defaults: new { name = RouteParameter.Optional, id = RouteParameter.Optional }
);

每個路由的設定都有三個參數,分別為:

  • name:路由的名稱
  • routeTemplate:路由URI範本
  • default:定義參數

在路由1中,先給他一個名稱叫做"GetCandidateByName",在Controller裡面也有一個一樣名稱的方法,這樣的設計只是讓我們比較容易分辨這個Routing到底是做什麼用的、與那些Controller方法有關係,名稱並不絕對一定要與Controller方法相同。

路由URI範本描述了這個Web API對外的URI長什麼樣子,例如"api/{controller}/name/{name}",翻譯一下就成為:"api/控制器名稱/name/參數name",如果套用到瀏覽器的網址上就成為:"http://domain:port/api/控制器名稱/name/參數name",這樣看起來就很簡單了吧!

最後就是定義參數,"RouteParameter.Optional"代表這個參數是可選用的,我們在呼叫Web API時不一定要送出這個參數,這也造就了路由3可以一個路由多個用途的原因!

我們來看看路由3,他的路由範本是"api/{controller}/{name}/{id}",且兩個參數都是可選用的,也就是不一定要傳入,所以可能的URI如下:

  • api/{controller}/{name}/{id}
  • api/{controller}/{name}
  • api/{controller}

有人可能會問:「為什麼routeTemplate可以重複利用,並且以一個routeTemplate對應多個Controller方法,卻還要這樣一對一的去設計呢?」,沒錯,確實是可以這樣做,但若是你的Web API非常的多,到了一個很難管理的程度,你確定你會知道哪個routeTemplate對應到哪幾個Controller方法嗎?當然不知道,尤其那種大雜燴,所以有的時候我們還真的必須針對Controller方法設計專用的routeTemplate,一樣,這並不絕對,還是以規模、架構與經驗有關。

第三步:修改index.html,讓我們可以用姓名、ID來搜尋應徵者的名字

程式碼如下:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
    <title>Candidates</title>
    <meta charset="utf-8" />
    <script>
        var apiurl = 'api/candidates';
        $(document).ready(function () {
            GetAllCandidates();
        });

        function GetAllCandidates() {
            $.ajax({
                url: apiurl,
                success: function (result) {
                    $('#divCandidates').empty();
                    $.each(result, function (key, item) {
                        $('<li>' + item.Name + ', ' + item.Id + ', ' + item.Age + ', ' + item.Email + '</li>').appendTo('#divCandidates');
                    });
                }
            });
        }

        function SearchCandidateByName() {
            var name = $('#candidateName').val();
            $.ajax({
                url: apiurl + '/name/' + name,
                success: function (result) {
                    $('#divCandidates').empty();
                    $('<li>' + result.Name + ', ' + result.Id + ', ' + result.Age + ', ' + result.Email + '</li>').appendTo('#divCandidates');
                },
                error: function (jqXHR, exception) {
                    $('#divCandidates').empty();
                    alert('Nothing');
                }
            });
        }

        function SearchCandidateById() {
            var id = $('#candidateId').val();
            $.ajax({
                url: apiurl + '/id/' + id,
                success: function (result) {
                    $('#divCandidates').empty();
                    $('<li>' + result.Name + ', ' + result.Id + ', ' + result.Age + ', ' + result.Email + '</li>').appendTo('#divCandidates');
                },
                error: function (jqXHR, exception) {
                    $('#divCandidates').empty();
                    alert('Nothing');
                }
            });
        }

        function SearchCandidateByNameAndId() {
            var name = $('#candidateName').val();
            var id = $('#candidateId').val();
            $.ajax({
                url: apiurl + '/' + name+ '/' + id,
                success: function (result) {
                    $('#divCandidates').empty();
                    $('<li>' + result.Name + ', ' + result.Id + ', ' + result.Age + ', ' + result.Email + '</li>').appendTo('#divCandidates');
                },
                error: function (jqXHR, exception) {
                    $('#divCandidates').empty();
                    alert('Nothing');
                }
            });
        }


    </script>
</head>
<body>
    <div>
        <h2>Candidates</h2>
        <p>
            Name:<input id="candidateName" type="text" />, ID:<input id="candidateId" type="text" />
            <input type="button" value="Search by Name" onclick="SearchCandidateByName();" />
            <input type="button" value="Search by Id" onclick="SearchCandidateById();" />
            <input type="button" value="Search by Name and ID" onclick="SearchCandidateByNameAndId();" />
        </p>
        <p id="divCandidates"></p>
    </div>
</body>
</html>

最後,遵照上一篇的往例,附上完整的範例專案檔,以便更全面、直觀的學習撰寫ASP.NET Web API。

範例下載連結:ASP.NET Web API 2 Practics02 - 1