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