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主要負責的是構建實時服務,例如股票,聊天室,在線游戲等實時性要求比較高的服務。
為什么要用 Web API
Web API最重要的是可以構建面向各種客戶端的服務。另外與WCF REST Service不同在於,Web API利用Http協議的各個方面來表達服務(例如 URI/request response header/caching/versioning/content format),因此就省掉很多配置。
當你遇到以下這些情況的時候,就可以考慮使用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很類似,他們有哪些不同之處呢?先上圖,這就是他們最大的不同之處。
詳細點說他們的區別,
- 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的功能。
涉及技術
在我們的實例里面用到了:
- Mongo DB數據庫保存數據 (NoSQL, Document Store,跨平台,跨語言)
- Web API提供數據服務
- MVC作數據展示
- Knockoutjs動態綁定客戶端數據,這里有一個簡單的介紹
服務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
在Project Template中選擇Web API
然后項目就創建成了,Controllers里面有一個ValuesController,是自動生成的一個最簡單的Web API Controller。
正如我們前面所說,里面引用的是System.Web.Http命名空間。
2. 創建model
在model里面添加Contact類
代碼如下,其中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。
另外我們需要在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
選擇Empty API controller,將Controller命名為ContactsController
添加如下代碼,可以看到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庫,
Knockoutjs通過MVVM模式來實現動態html綁定數據,如下圖,其中View-Model是客戶端的javascript object保存的model數據。
先打開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
選擇Create strongly-typed view,在model class中選擇Contact類。
添加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鏈接也顯示在右上角,
Admin頁面的樣子,Contact list是動態加載進來的,可以通過這個頁面做添加,修改,刪除的操作。
通過IE network capture來查看請求內容,
重新加載頁面,可以看到回復的格式為JSON,
JSON內容就是我們mock的一些數據。
接下來我們修改,刪除,又添加了一條記錄,可以看到使用了不同的http method。
通過前面安裝的mongovue來查看下DB種的數據,先添加的user也在其中,令我感到欣慰。。。
其實還有兩個有趣的實例,不過文章一寫就長了,不好意思耽誤大家時間,只好先放放,以后再寫。