1. 程式人生 > >Web API 強勢入門指南

Web API 強勢入門指南

Web API是一個比較寬泛的概念。這裡我們提到Web API特指ASP.NET Web API。

這篇文章中我們主要介紹Web API的主要功能以及與其他同類型框架的對比,最後通過一些相對複雜的例項展示如何通過Web API構建http服務,同時也展示了Visual Studio構建.net專案的各種強大。

目錄

什麼是 Web API

官方定義如下,強調兩個關鍵點,即可以對接各種客戶端(瀏覽器,移動裝置),構建http服務的框架。

ASP.NET Web API is a framework that makes it easy to build HTTP services that reach a broad range of clients

, including browsers and mobile devices. ASP.NET Web API is an ideal platform for building RESTful applications on the .NET Framework.

Web API在ASP.NET完整框架中地位如下圖,與SignalR一起同為構建Service的框架。Web API負責構建http常規服務,而SingalR主要負責的是構建實時服務,例如股票,聊天室,線上遊戲等實時性要求比較高的服務。

Picture20

為什麼要用 Web API

Web API最重要的是可以構建面向各種客戶端的服務。另外與WCF REST Service不同在於,Web API利用Http協議的各個方面來表達服務(例如 URI/request response header/caching/versioning/content format),因此就省掉很多配置。

Picture2

當你遇到以下這些情況的時候,就可以考慮使用Web API了。

  • 需要Web Service但是不需要SOAP
  • 需要在已有的WCF服務基礎上建立non-soap-based http服務
  • 只想釋出一些簡單的Http服務,不想使用相對複雜的WCF配置
  • 釋出的服務可能會被頻寬受限的裝置訪問
  • 希望使用開源框架,關鍵時候可以自己除錯或者自定義一下框架

功能簡介

Web API的主要功能

1. 支援基於Http verb (GET, POST, PUT, DELETE)的CRUD (create, retrieve, update, delete)操作

    通過不同的http動作表達不同的含義,這樣就不需要暴露多個API來支援這些基本操作。

2. 請求的回覆通過Http Status Code表達不同含義,並且客戶端可以通過Accept header來與伺服器協商格式,例如你希望伺服器返回JSON格式還是XML格式。

3. 請求的回覆格式支援 JSON,XML,並且可以擴充套件新增其他格式。

4. 原生支援OData

5. 支援Self-host或者IIS host。

6. 支援大多數MVC功能,例如Routing/Controller/Action Result/Filter/Model Builder/IOC Container/Dependency Injection。

Web API vs MVC

你可能會覺得Web API 與MVC很類似,他們有哪些不同之處呢?先上圖,這就是他們最大的不同之處。

Picture1

詳細點說他們的區別,

  • MVC主要用來構建網站,既關心資料也關心頁面展示,而Web API只關注資料
  • Web API支援格式協商,客戶端可以通過Accept header通知伺服器期望的格式
  • Web API支援Self Host,MVC目前不支援
  • Web API通過不同的http verb表達不同的動作(CRUD),MVC則通過Action名字表達動作
  • Web API內建於ASP.NET System.Web.Http名稱空間下,MVC位於System.Web.Mvc名稱空間下,因此model binding/filter/routing等功能有所不同
  • 最後,Web API非常適合構建移動客戶端服務

Web API vs WCF

釋出服務在Web API和WCF之間該如何取捨呢?這裡提供些簡單地判斷規則,

  • 如果服務需要支援One Way Messaging/Message Queue/Duplex Communication,選擇WCF
  • 如果服務需要在TCP/Named Pipes/UDP (wcf 4.5),選擇WCF
  • 如果服務需要在http協議上,並且希望利用http協議的各種功能,選擇Web API
  • 如果服務需要被各種客戶端(特別是移動客戶端)呼叫,選擇Web API

Web API 實戰 (Web API + MongoDB + knockoutjs)

ASP.NET網站上有很多簡單的Web API例項,看看貼圖和例項程式碼你就明白怎麼用了。這裡我們通過一個稍微複雜一點的例項來展示下Web API的功能。

涉及技術

在我們的例項裡面用到了:

服務URI Pattern

Action Http verb URI
Get contact list GET /api/contacts
Get filtered contacts GET /api/contacts?$top=2
Get contact by ID GET /api/contacts/id
Create new contact POST /api/contacts
Update a contact PUT /api/contacts/id
Delete a contact DELETE /api/contacts/id

準備工作

1. 下載並安裝Mongo DB,步驟看這裡

2. Mongo DB C# driver下載可以在nuget搜尋mongocsharpdriver。

3. 如果想本地察看資料庫中內容,下載MongoVUE

4. Knockoutjs下載可以在nuget搜尋knockoutjs。

程式碼實現

1. 建立專案

建立MVC4 Web Application

1

在Project Template中選擇Web API

2

然後專案就建立成了,Controllers裡面有一個ValuesController,是自動生成的一個最簡單的Web API Controller。

正如我們前面所說,裡面引用的是System.Web.Http名稱空間。

3

2. 建立model

在model裡面新增Contact類

4

程式碼如下,其中BsonId需要mongocsharpdriver。

1

2

3

4

5

6

7

8

9

public class Contact

{

[BsonId]

public string Id { get; set; }

public string Name { get; set; }

public string Phone { get; set; }

public string Email { get; set; }

public DateTime LastModified { get; set; }

}

我們需要新增mongosharpdriver。

7

8

另外我們需要在Model中新增Repository,Controller通過該類來訪問Mongo DB。

1

2

3

4

5

6

7

public interface IContactRepository {

IEnumerable GetAllContacts();

Contact GetContact(string id);

Contact AddContact(Contact item);

bool RemoveContact(string id);

bool UpdateContact(string id, Contact item);  

}

ContactRepository的完整實現如下,

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

public class ContactRepository : IContactRepository

{

MongoServer _server = null;

MongoDatabase _database = null;

MongoCollection _contacts = null;

public ContactRepository(string connection)

{

if (string.IsNullOrWhiteSpace(connection))

{

}

_server = new MongoClient(connection).GetServer();

_database = _server.GetDatabase("Contacts");

_contacts = _database.GetCollection("contacts");

// Reset database and add some default entries

_contacts.RemoveAll();

for (int index = 1; index < 5; index++)

{

Contact contact1 = new Contact

{

Email = string.Format("test{0}@example.com", index),

Name = string.Format("test{0}", index),

Phone = string.Format("{0}{0}{0} {0}{0}{0} {0}{0}{0}{0}", index)

};

AddContact(contact1);

}

}

public IEnumerable GetAllContacts()

{

return _contacts.FindAll();

}

public Contact GetContact(string id)

{

IMongoQuery query = Query.EQ("_id", id);

return _contacts.Find(query).FirstOrDefault();

}

public Contact AddContact(Contact item)

{

item.Id = ObjectId.GenerateNewId().ToString();

item.LastModified = DateTime.UtcNow;

_contacts.Insert(item);

return item;

}

public bool RemoveContact(string id)

{

IMongoQuery query = Query.EQ("_id", id);

WriteConcernResult result = _contacts.Remove(query);

return result.DocumentsAffected == 1;

}

public bool UpdateContact(string id, Contact item)

{

IMongoQuery query = Query.EQ("_id", id);

item.LastModified = DateTime.UtcNow;

IMongoUpdate update = Update

.Set("Email", item.Email)

.Set("LastModified", DateTime.UtcNow)

.Set("Name", item.Name)

.Set("Phone", item.Phone);

WriteConcernResult result = _contacts.Update(query, update);

return result.UpdatedExisting;

}

}

3. 新增Controller

右鍵Controllers目錄選擇新增Controller

5

選擇Empty API controller,將Controller命名為ContactsController

6

新增如下程式碼,可以看到Controller中的API方法名就是以http verb命名的。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

public class ContactsController : ApiController

{

private static readonly IContactRepository _contacts = new ContactRepository(string.Empty);

public IQueryable Get()

{

return _contacts.GetAllContacts().AsQueryable();

}

public Contact Get(string id)

{

Contact contact = _contacts.GetContact(id);

if (contact == null)

{

throw new HttpResponseException(HttpStatusCode.NotFound);

}

return contact;

}

public Contact Post(Contact value)

{

Contact contact = _contacts.AddContact(value);

return contact;

}

public void Put(string id, Contact value)

{

if (!_contacts.UpdateContact(id, value))

{

throw new HttpResponseException(HttpStatusCode.NotFound);

}

}

public void Delete(string id)

{

if (!_contacts.RemoveContact(id))

{

throw new HttpResponseException(HttpStatusCode.NotFound);

}

}

}

4. 新增View

首先新增Knockoutjs庫,

9

Knockoutjs通過MVVM模式來實現動態html繫結資料,如下圖,其中View-Model是客戶端的javascript object儲存的model資料。

webapi_ef16

先開啟HomeController,裡面新增一個新的Action程式碼如下,因為我們要在MVC中對於ContactsController新增對應的View。

1

2

3

4

5

6

7

public ActionResult Admin()

{

string apiUri = Url.HttpRouteUrl("DefaultApi", new { controller = "contacts", });

ViewBag.ApiUrl = new Uri(Request.Url, apiUri).AbsoluteUri.ToString();

return View();

}

然後右鍵Admin方法,選擇新增View

10

選擇Create strongly-typed view,在model class中選擇Contact類。

11

新增View的完整程式碼,注意view中我們通過js去訪問WebAPI,以及通過動態繫結將資料呈現在網頁上。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

@model WebAPIDemo.Models.Contact

@{

ViewBag.Title = "Admin";

}

@section Scripts {

@Scripts.Render("~/bundles/jqueryval")

<script type="text/javascript" src="@Url.Content("~/Scripts/knockout-2.3.0.js")"></script>

<script type="text/javascript">

function ProductsViewModel() {

var self = this;

self.products = ko.observableArray();

var baseUri = '@ViewBag.ApiUrl';

self.create = function (formElement) {

// If valid, post the serialized form data to the web api

$(formElement).validate();

if ($(formElement).valid()) {

$.post(baseUri, $(formElement).serialize(), null, "json")

.done(function (o) { self.products.push(o); });

}

}

self.update = function (product) {

$.ajax({ type: "PUT", url: baseUri + '/' + product.Id, data: product });

}

self.remove = function (product) {

// First remove from the server, then from the UI

$.ajax({ type: "DELETE", url: baseUri + '/' + product.Id })

.done(function () { self.products.remove(product); });

}

$.getJSON(baseUri, self.products);

}

$(document).ready(function () {

ko.applyBindings(new ProductsViewModel());

})

</script>

}

<h2>Admin</h2>

<div class="content">

<div class="float-left">

<ul id="update-products" data-bind="foreach: products">

<li>

<div>

<div class="item">ID</div> <span data-bind="text: $data.Id"></span>

</div>

<div>

<div class="item">Name</div>

<input type="text" data-bind="value: $data.Name"/>

</div>

<div>

<div class="item">Phone</div>

<input type="text" data-bind="value: $data.Phone"/>

</div>

<div>

<div class="item">Email</div>

<input type="text" data-bind="value: $data.Email"/>

</div>

<div>

<div class="item">Last Modified</div> <span data-bind="text: $data.LastModified"></span>

</div>

<div>

<input type="button" value="Update" data-bind="click: $root.update"/>

<input type="button" value="Delete Item" data-bind="click: $root.remove"/>

</div>

</li>

</ul>

</div>

<div class="float-right">

<h2>Add New Product</h2>

<form id="addProduct" data-bind="submit: create">

@Html.ValidationSummary(true)

<fieldset>

<legend>Contact</legend>

@Html.EditorForModel()

<p>

<input type="submit" value="Save" />

</p>

</fieldset>

</form>

</div>

</div>

接下來在_layout.cshtml中新增一個admin頁面的連結如下

1

2

3

4

5

6

<ul id="menu">

<li>@Html.ActionLink("Home", "Index", "Home", new { area = "" }, null)</li>

<li>@Html.ActionLink("API", "Index", "Help", new { area = "" }, null)</li>

<li>@Html.ActionLink("Admin", "Admin", "Home")</li>

</ul>

5. 測試與除錯

大功告成,直接執行下我們的作品,我們的admin連結也顯示在右上角,

12

Admin頁面的樣子,Contact list是動態載入進來的,可以通過這個頁面做新增,修改,刪除的操作。

13

通過IE network capture來檢視請求內容,

重新載入頁面,可以看到回覆的格式為JSON,

14

JSON內容就是我們mock的一些資料。

image

接下來我們修改,刪除,又添加了一條記錄,可以看到使用了不同的http method。

image

通過前面安裝的mongovue來檢視下DB種的資料,先新增的user也在其中,令我感到欣慰。。。

image