Web API從MVC4開始出現,能夠服務於Asp.Net下的任何web應用,本文將介紹Web api在單頁應用中的使用。什麼是單頁應用?Single-Page Application最經常使用的定義:一個最初內容只包含html和JavaScript,後續操做經過Restful風格的web服務傳輸json數據來響應異步請求的一個web應用。SPA的優點就是少許帶寬,平滑體驗,劣勢就是隻用JavaScript這些平滑的操做較難實現,不像MVC應用,咱們能夠異步form,partview。不用擔憂,咱們有利器:knockoutjs。css
1、工程準備html
1.新建一個Api工程。前端
2.建立模型和倉庫jquery
Reservation: 程序員
public class Reservation { public int ReservationId { get; set; } public string ClientName { get; set; } public string Location { get; set; } }
ReservationRespository:在真實的項目中,倉庫都須要有接口和依賴注入,可是這裏咱們把重點放到後,將這個部分簡化。因此也沒有用數據庫,所有放到內存裏面。web
public class ReservationRespository { private static ReservationRespository repo = new ReservationRespository(); public static ReservationRespository Current { get { return repo; } } private List<Reservation> data = new List<Reservation> { new Reservation { ReservationId = 1, ClientName = "Adam", Location = "Board Room"}, new Reservation { ReservationId = 2, ClientName = "Jacqui", Location = "Lecture Hall"}, new Reservation { ReservationId = 3, ClientName = "Russell", Location = "Meeting Room 1"}, }; public IEnumerable<Reservation> GetAll() { return data; } public Reservation Get(int id) { return data.Where(r => r.ReservationId == id).FirstOrDefault(); } public Reservation Add(Reservation item) { item.ReservationId = data.Count + 1; data.Add(item); return item; } public void Remove(int id) { Reservation item = Get(id); if (item != null) { data.Remove(item); } } public bool Update(Reservation item) { Reservation storedItem = Get(item.ReservationId); if (storedItem != null) { storedItem.ClientName = item.ClientName; storedItem.Location = item.Location; return true; } else { return false; } } }
用來爲咱們的單頁應用提供數據的增刪改查。ajax
3.用Nuget添加Juqery,Bootstrap,Knockoutjs。chrome
Install-Package jquery –version 1.10.2 Install-Package bootstrap –version 3.0.0 Install-Package knockoutjs –version 3.0.0
4.建立Api控制器。數據庫
public class WebController : ApiController { private ReservationRespository repo = ReservationRespository.Current; public IEnumerable<Reservation> GetAllReservations() { return repo.GetAll(); } public Reservation GetReservation(int id) { return repo.Get(id); } [HttpPost] public Reservation PostReservation(Reservation item) { return repo.Add(item); } [HttpPut] public bool PutReservation(Reservation item) { return repo.Update(item); } public void DeleteReservation(int id) { repo.Remove(id); } }
2、Web Api express
這個時候運行訪問 /api/web ,chrome下是xml數據,而ie下是json數據,這是由於Web api會根據請求中的http header 的接收類型來返回客戶端「喜歡」的格式。
Web api的路由和普通的mvc路由略有不一樣,它是能夠根據請求的操做類型(get,post,delete)以及參數來匹配對應的方法。
public static void Register(HttpConfiguration config) { // Web API 配置和服務 // Web API 路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); }
若是你仍是想使用api/{controller}/{action}/{id}這樣的方式,加個action就好
config.Routes.MapHttpRoute( name: "ActionApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional } );
WebApi的一些基本原理和操做你們能夠移步 小牛之路 webapi 這裏我就再也不贅述了。 該文也詳細的介紹了使用ajax和Ajax.BeginForm的方法來進行交互。
3、Knockout
SPA將更多的任務移到了瀏覽器上,這樣就須要保存應用的狀態,須要能夠更新的數據模型,一系列用戶能夠經過UI元素觸發的邏輯操做。這樣就意味着須要一個微型的MVC模式。而微軟爲此提供的類庫就是Knockout,準確的說,Knockout是MVVM模式,下面就用它完成一個簡單的應用。
1.在Shared文件夾中新增_Layout.cshtml,並引用相關類庫。
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> <link href="~/Content/bootstrap.css" rel="stylesheet" /> <link href="~/Content/bootstrap.min.css" rel="stylesheet" /> <script src="~/Scripts/jquery-1.10.2.min.js"></script> <script src="~/Scripts/bootstrap.min.js"></script> <script src="~/Scripts/knockout-3.0.0.js"></script> </head> <body> @RenderSection("Body") </body> @RenderSection("Scripts",false) </html>
2.添加HomeController。添加Index視圖。
public class HomeController : Controller { public ViewResult Index() { return View(); } }
控制器不須要其餘的視圖和邏輯處理,由於這些都交給瀏覽器去處理了。
Knockout的感受和WPF很像,簡單的說,就是定義好模型和事件,綁定到元素上面就好了。咱們先實現全部數據的讀取和刪除。
1)定義模型
var model = { reservations: ko.observableArray() };
ko.observableArray()至關因而集合.若是是單個模型,用ko.observable("")。例如:name: ko.observable("")。而reservations至關因而model的一個屬性。
2)定義異步方法。
function sendAjaxRequest(httpMethod, callback, url) { $.ajax("/api/web" + (url ? "/" + url : ""), { type: httpMethod, success: callback }); }
繼而定義一個獲取數據和刪除數據的方法
function getAllItems() { sendAjaxRequest("GET", function (data) { model.reservations.removeAll(); for (var i = 0; i < data.length; i++) { model.reservations.push(data[i]);//一個個添加進來 } }); } function removeItem(item) { sendAjaxRequest("DELETE", function () { getAllItems(); }, item.ReservationId); }
這兩個方法只和數據相關,不用咱們去關心dom的處理。
3)應用綁定。
$(document).ready(function () { getAllItems(); ko.applyBindings(model); });
所有腳本:
@section Scripts{ <script> var model = { reservations: ko.observableArray() }; function sendAjaxRequest(httpMethod, callback, url) { $.ajax("/api/web" + (url ? "/" + url : ""), { type: httpMethod, success: callback }); } function getAllItems() { sendAjaxRequest("GET", function(data) { model.reservations.removeAll(); for (var i = 0; i < data.length; i++) { model.reservations.push(data[i]); } }); } function removeItem(item) { sendAjaxRequest("DELETE", function () { getAllItems(); }, item.ReservationId); } $(document).ready(function () { getAllItems(); ko.applyBindings(model); }); </script> }
4)綁定到元素
綁定的表達式是
data-bind="type: expression"
綁定到一個集合:
<tbody data-bind="foreach: model.reservations">
綁定到對象的屬性
<td data-bind="text: ReservationId"></td>
綁定到一個事件
<button class="btn btn-xs btn-primary" data-bind="click: removeItem">Remove</button>
那所有的html的以下:
@section Body { <div id="summary" class="section panel panel-primary"> @*@Html.Partial("Summary", Model)*@ <div class="panel-body"> <table class="table table-striped table-condensed"> <thead> <tr><th>ID</th><th>Name</th><th>Location</th><th></th></tr> </thead> <tbody data-bind="foreach:model.reservations"> <tr> <td data-bind="text:ReservationId"></td> <td data-bind="text:ClientName"></td> <td data-bind="text:Location"></td> <td> <button class="btn btn-xs btn-primary" data-bind="click:removeItem"> Remove </button> </td> </tr> </tbody> </table> </div> </div> }
運行起來:
點擊Remove立刻刪除。平滑的異步體驗。可是咱們看removeitem方法就有點奇怪,是刪除以後又調用了一次getAllitems()來更新數據,那麼咱們也能夠直接更新model
... function removeItem(item) { sendAjaxRequest("DELETE", function () { for (var i = 0; i < model.reservations().length; i++) { if (model.reservations()[i].ReservationId == item.ReservationId) { model.reservations.remove(model.reservations()[i]); break; } } }, item.ReservationId); } ...
但上面的KO語法有點奇怪,model.reservations()[i].ReservationId,調用集合中的某個對象時,要像函數同樣調用。KO試圖去維持標準的JavaScript語法,但還有些奇怪的地方,初次使用有些困惑,那也只能說,你會很快習慣的。
固然還有同窗有疑問了。這和@Razor的方式有什麼不一樣?主要有三個不一樣。
1.模型數據再也不包含在html元素中了。換句話說就是在html加載以後,瀏覽器才使用異步請求更新數據。用F12打開,看不到任何數據。只有綁定的表達式。
2.當視圖被渲染的時候,數據在瀏覽器端獲得處理,而不是在服務器上。
換作是Razor語法,上面的語句就是這樣:
<tbody> @foreach (var item in Model) { <tr> <td>@item.ReservationId</td> <td>@item.ClientName</td> <td>@item.Location</td> <td> @Html.ActionLink("Remove","Remove",new{id=item.ReservationId},new{@class="btn btn-xs btn-primary"}) </td> </tr> } </tbody>
這都是在服務器端由Razor引擎渲染。
3.綁定的數據是「活」的,意味着數據模型的改變會立刻反應到有foreach和text綁定的內容中,F12,進入Console執行:model.reservations.pop() 取出一條數據,咱們發現數據立刻更新了。
因此Web api只須要提供基本的增刪改查就好了。並且在前端也讓程序員少寫了不少dom操做,而dom操做是腳本最爲繁瑣和容易出錯的地方。接下來繼續完善這個demo。咱們加入編輯部分。修改model以下
var model = { reservations: ko.observableArray(), editor: { name: ko.observable(""), location: ko.observable("") } };
增長了一個editor,包含name和Location兩個屬性。
修改sendAjaxRequest 方法 增長傳輸的數據對象。
function sendAjaxRequest(httpMethod, callback, url, reqData) { $.ajax("/api/web" + (url ? "/" + url : ""), { type: httpMethod, success: callback, data: reqData }); }
增長一個編輯的方法:
function handleEditorClick() { sendAjaxRequest("POST", function (newItem) { model.reservations.push(newItem); }, null, { ClientName: model.editor.name, Location: model.editor.location }); }
這個方法的意思是,當提交的時候,用post方式將editor的name和Location傳遞給api,web api會根據post方式自動匹配到PostReservation方法(MVC的模型綁定會自動講這兩個值生成一個Reservation對象)。而後將返回的Reservation對象添加到model中。這個時候頁面就會更新。
html:
<div id="editor" class="section panel panel-primary"> <div class="panel-heading"> Create Reservation </div> <div class="panel-body"> <div class="form-group"> <label>Client Name</label> <input class="form-control" data-bind="value: model.editor.name" /> </div> <div class="form-group"> <label>Location</label> <input class="form-control" data-bind="value: model.editor.location" /> </div> <button class="btn btn-primary" data-bind="click: handleEditorClick">Save</button> </div> </div>
而後運行,咱們添加數據以後,立刻更新。相對於傳統的方法,咱們須要獲取dom中的數據,而後提交到後臺,而後獲得返回的數據更新table。
進而咱們能夠控制元素的顯示。修改model。添加displaysummary。初始化爲一個bool值。
var model = {
reservations: ko.observableArray(),
editor: {
name: ko.observable(""),//至關於兩個變量。
location: ko.observable(""),
},
displaySummary: ko.observable(true)
};
添加隱藏方法,修改handleCreateClick
function handleCreateClick() { model.displaySummary(false); } function handleEditorClick() { sendAjaxRequest("POST", function (newItem) { model.reservations.push(newItem); model.displaySummary(true); }, null, { ClientName: model.editor.name, Location: model.editor.location }); }
綁定到元素: 這裏用到了if表達式。
<div id="summary" class="section panel panel-primary" data-bind="if: model.displaySummary"> <div class="panel-heading">Reservation Summary</div> ... </div>
運行,就能夠自在切換了。
小結:以上只是Knockout的簡單介紹,可是和Web api配合起來確實感受不錯。更多Knockout的api請參考官網。knockoutjs 。 但願本文對你有幫助。
demo 下載: SPA
參考書籍:Apress Pro Asp.Net MVC5 此書在個人讀書羣裏面有下載,q羣:452450927。歡迎愛讀書,愛分享的朋友加入。
參考文章:小牛之路 web api