Web API从MVC4开始出现,可以服务于Asp.Net下的任何web应用,本文将介绍Web api在单页应用中的使用。什么是单页应用?Single-Page Application最常用的定义:一个最初内容只包含html和JavaScript,后续操作通过Restful风格的web服务传输json数据来响应异步请求的一个web应用。SPA的优势就是少量带宽,平滑体验,劣势就是只用JavaScript这些平滑的操作较难实现,不像MVC应用,我们可以异步form,partview。不用担心,我们有利器:knockoutjs。

一、工程准备

1.新建一个Api工程。

2.创建模型和仓库

Reservation:

  public class Reservation
{
public int ReservationId { get; set; }
public string ClientName { get; set; }
public string Location { get; set; }
}

ReservationRespository:在真实的项目中,仓库都需要有接口和依赖注入,但是这里我们把重点放到后,将这个部分简化。所以也没有用数据库,全部放到内存里面。

 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 = , ClientName = "Adam", Location = "Board Room"},
new Reservation {
ReservationId = , ClientName = "Jacqui", Location = "Lecture Hall"},
new Reservation {
ReservationId = , 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 + ;
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;
}
} }

用来为我们的单页应用提供数据的增删改查。

3.用Nuget添加Juqery,Bootstrap,Knockoutjs。

Install-Package jquery –version 1.10.
Install-Package bootstrap –version 3.0.
Install-Package knockoutjs –version 3.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);
}
}

二、Web Api

这个时候运行访问 /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的方法来进行交互。

三、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

【读书笔记】WebApi 和 SPA(单页应用)--knockout的使用的更多相关文章

  1. 前端 SPA 单页应用数据统计解决方案 (ReactJS / VueJS)

    前端 SPA 单页应用数据统计解决方案 (ReactJS / VueJS) 一.百度统计的代码: UV PV 统计方式可能存在问题 在 SPA 的前端项目中 数据统计,往往就是一个比较麻烦的事情,Re ...

  2. [vue]spa单页开发及vue-router基础

    - 了解spa页面跳转方式:(2种) spa: 单页跳转方式 开发(hash模式): https://www.baidu.com/#2313213 生产(h5利于seo): history.pushS ...

  3. Javascript 与 SPA单页Web富应用

    书单推荐 # <单页Web应用:JavaScript从前端到后端> http://download.csdn.net/detail/epubitbook/8720475 # <MVC ...

  4. 【MySQL 读书笔记】SQL 刷脏页可能造成数据库抖动

    开始今天读书笔记之前我觉得需要回顾一下当我们在更新一条数据的时候做了什么. 因为 WAL 技术的存在,所以当我们执行一条更新语句的时候是先写日志,后写磁盘的.当我们在内存中写入了 redolog 之后 ...

  5. 大熊君学习html5系列之------History API(SPA单页应用的必备)

    一,开篇分析 Hi,大家好!大熊君又和大家见面了,(*^__^*) 嘻嘻……,这系列文章主要是学习Html5相关的知识点,以学习API知识点为入口,由浅入深的引入实例, 让大家一步一步的体会" ...

  6. 基于VUE的SPA单页应用开发-加载性能篇

    1.基于异步数据的vue页面刷新 先看看基于异步数据的vue页面刷新后,都发生了啥- 如图所示: 图1 基于异步数据的vue页面刷新 网络请求图 步骤如下: step1:请求页面: step2:请求页 ...

  7. 前端学习之路之SPA(单页应用)设计原理

    SPA设计 1.设计意义 前后端分离 减轻服务器压力 增强用户体验 Prerender预渲染优化SEO 前后端分离:前端做业务逻辑,后端处理数据和接口,耦合度减少,开发效率提高. 减轻服务器压力:一个 ...

  8. 大熊君学习html5系列之------History API(SPA单页应用的必备------重构完结版)

    一,开篇分析 Hi,大家好!大熊君又和大家见面了,(*^__^*) 嘻嘻……,这系列文章主要是学习Html5相关的知识点,以学习API知识点为入口,由浅入深的引入实例, 让大家一步一步的体会" ...

  9. 《Linux内核设计与实现》读书笔记(十六)- 页高速缓存和页回写

    好久没有更新了... 主要内容: 缓存简介 页高速缓存 页回写 1. 缓存简介 在编程中,缓存是很常见也很有效的一种提高程序性能的机制. linux内核也不例外,为了提高I/O性能,也引入了缓存机制, ...

随机推荐

  1. ReportViewer内存泄漏问题解决方案[上]

    做这个项目有点倒霉,快要验收的时候,发现微软ReportViewer控件的一个bug,导致我们的项目无法正常验收. 问题描叙:用ReportViewer本地模式做的报表,在ASP.NET页面中呈现.在 ...

  2. 沉浸式状态栏_boolean hasTopLine = a.getBoolean(1, false);//AS会在"1"下显示错误红线

    TypedArray a = mContext.obtainStyledAttributes(attrs); boolean hasBottomLine = a.getBoolean(0, false ...

  3. phantomjs和angular-seo-server实现angular单页面seo

    1.下载phantomjs,并配置环境变量为   eg:E:\phantomjs-2.1.1-windows\bin 2.下载angular-seo-server 3.windows下:cmd eg: ...

  4. Python html.parser库学习小结

    分类路径:/Datazen/DataMining/Crawler/   前段时间,一朋友让我做个小脚本,抓一下某C2C商城上竞争对手的销售/价格数据,好让他可以实时调整自己的营销策略.自己之前也有过写 ...

  5. Centos 基础开发环境搭建之Maven私服nexus

    hmaster 安装nexus及启动方式 /usr/local/nexus-2.6.3-01/bin ./nexus status Centos 基础开发环境搭建之Maven私服nexus . 软件  ...

  6. linux 项目环境搭建配置

    经过三天加一上午的努力折腾,本地项目终于跑起来了,linux系统,重装后需要安装基本的php,nginx,mysql.php扩展需要安装curl ,memcache,memcached等.然后就是修改 ...

  7. [转]双数组TRIE树原理

    原文名称: An Efficient Digital Search Algorithm by Using a Double-Array Structure 作者: JUN-ICHI AOE 译文: 使 ...

  8. 数据导出Excel中文乱码

    数据导出到EXCEL提供用户下载,当记录数大于5行时不会出现乱码.但只要不退出Excel,再删除除记录,当记录数小于5行时,导出也不会出现乱码.当然一旦退出Excel再导出就会出现乱码了. 可以试试 ...

  9. virtualbox下面安装ubuntu后外网如何远程ssh访问

    这两天在折腾virtualbox安装linux的事情,想多弄几个节点,装hadoop, 环境如下 两台thinkpad, 一台正常上班用的,win7 一台装的ubuntu kylin 16.04, 上 ...

  10. js创建对象的几种方式

    /** * 顺便重温一下对象的创建方式 * 代码简单说明问题就好 * 概念性的东西这里就不提了,只加上自己简单理解 */ /** * 工厂模式,就是将手动的创建细节封装在一个方法里, * return ...