实时,异步网页使用jTable, SignalR和ASP。NET MVC
图:不同客户端的实时同步表。 点击这里观看现场演示。 文章概述 介绍使用的工具演示实现 模型视图控制器 遗言和感谢参考历史 介绍 HTTP(即web)工作于请求/应答机制。客户端(浏览器)发出请求(通常一个GET或POST)到服务器,服务器将响应(这可能是一个HTML页面,一个图像,一个JSON数据,等等),发送给客户端,然后客户端和服务器之间的连接被关闭(因此,HTTP是称为无连接协议)。服务器不能向客户端异步发送任何信息或通知(没有请求)。这是web页面/站点与桌面应用程序相比的主要缺点之一,因为桌面应用程序可以打开到服务器的TCP连接并异步获取数据。 作为web开发人员,我们尝试了许多方法来克服这一限制。让我们看看一些已知的技术刷新当前页面或页面的某些部分在服务器上的变化: 定期刷新页面:这显然是最糟糕的方法,因为所有页面都要刷新。不幸的是,它仍然在一些新闻门户网站上使用。定期刷新页面中的某个部分:在此方法中,我们定期向服务器发出AJAX请求,并用传入的数据刷新页面的一部分。这通常是在ASP中使用UpdatePanel和Timer实现的。净Web表单。对该方法的改进是只有在片段数据发生更改时才刷新片段。这种方法也不好,而且不可扩展,因为即使在服务器上没有可用的新数据时,我们也会周期性地发出请求,从而使服务器繁忙。另外,它不是实时通知,因为我们是周期性地而不是连续地发出请求。长轮询:这是SignalR使用的方法。我们向服务器发出请求,但直到服务器上有可用的新数据时才接收响应。因此,如果服务器上没有可用的新信息,客户机和服务器将等待,不执行任何操作,因此服务器不繁忙。HTML5自带WebSockets API。它允许我们在客户端和服务器之间创建持久的连接,因此,服务器和客户端可以异步地互相发送数据。它是一个更高级别的TCP连接。在不久的将来,这将成为web上异步通信的标准方式。但是,到目前为止,并不是每个浏览器都完全实现了它。我读过但还没试过,SignalR也支持WebSockets。所以,你现在就可以使用SignalR,并在将来改变传输层。 因此,长轮询是目前最可接受的服务器到客户端通知方式。它快速且可扩展。 在本文中,我实现了一个在服务器上的客户机之间实时同步的HTML表。你可以使用相同的机制来实现一个聊天系统,一个实时股票监视系统,一个实时监控系统…等等。我在本文中重点介绍了使用SignalR,因为我以前已经写过一篇关于jTable的完整文章。 使用工具 下面是用于实现本文演示的工具列表: SignalR:一个开源、功能强大且易于使用的服务器(ASP.NET)和客户端(JavaScript/jQuery)库,用于服务器和客户端之间的异步通信。您可以通过https://github.com/SignalR获得详细信息。jTable:一个开源jQuery插件,用于创建由我开发的基于AJAX的CRUD表。它有分页、排序、选择、主/子表、自动创建表单等等。在http://jtable.org上获取详细信息。还可以在http://www.codeproject.com/KB/ajax/jTable.aspx上看到我关于使用jTable的文章。 我使用jQuery,因为SignalR和jTable都在使用它。我还使用了ASP。但是你可以使用asp.net MVC 3。当然是网络形式。 示范 在继续阅读本文之前,我建议您查看http://jtable.org/RealTime上的运行演示。在两个或模式不同的浏览器窗口中打开这个页面,并添加/删除/更新表中的一些行。你也可以做一个简单的聊天。 实现 这个演示允许用户添加/删除/更新表中的任何行。此外,用户可以发送聊天消息给所有其他在线用户,如下所示。每个用户都有一个随机生成的惟一用户名(如user-78636)。 首先,我们创建一个新的空ASP。NET MVC 3 web应用程序项目(我将其命名为jTableWithSignalR)。由于SignalR依赖于它,所以我们首先引用Microsoft.Web。使用包管理器(NuGet)的基础设施。SignalR也需要jQuery 1.6。如果您的jQuery版本低于1.6,您还必须更新它。最后,我们可以安装SignalR包: 我们还将在项目中添加jTable插件。您可以从http://jtable.org/Home/Downloads下载它。jTable管理AJAX调用自身的所有插入/更新/删除/列表。我们只是准备ASP。NET MVC操作(或asp.net MVC的页面方法)。净Web表单。看到我的文章)。 模型 正如您在上图中看到的,th中的一条记录e表表示一个学生,其定义如下: 隐藏,收缩,复制Code
public class Student
{
public int StudentId { get; set; } [Required]
public int CityId { get; set; } [Required]
public string Name { get; set; } [Required]
public string EmailAddress { get; set; } [Required]
public string Password { get; set; } // "M" for mail, "F" for female.
[Required]
public string Gender { get; set; } [Required]
public DateTime BirthDate { get; set; } public string About { get; set; } // 0: Unselected, 1: Primary school, 2: High school 3: University
[Required]
public int Education { get; set; } //true: Active, false: Passive
[Required]
public bool IsActive { get; set; } [Required]
public DateTime RecordDate { get; set; } public Student()
{
RecordDate = DateTime.Now;
Password = "123";
About = "";
}
}
它是一个常规的c#类,用作我们在客户端和服务器之间传输的模型。 控制器 SignalR是一个功能强大的框架。这里,我使用了它的Hub类,它允许我们轻松地在服务器和在线客户端之间构建双向通信。SignalR是一个非常容易使用的库。我创建了一个Hub类,服务于客户端: 隐藏,复制Code
public class RealTimeJTableDemoHub : Hub
{
public void SendMessage(string clientName, string message)
{
Clients.GetMessage(clientName, message);
}
}
如您所见,它只定义了一个客户端可以调用的方法:SendMessage。它被用来和其他客户聊天。如你所见,它调用所有客户端的GetMessage方法。其他事件(例如通知客户机将新行插入到表中)是服务器到客户机的调用。让我们看看当客户端删除表中的一行时,服务器上发生了什么: 隐藏,复制Code
[HttpPost]
public JsonResult DeleteStudent(int studentId)
{
try
{
//Delete from database
_repository.StudentRepository.DeleteStudent(studentId); //Inform all connected clients
var clientName = Request["clientName"];
Task.Factory.StartNew(
() =>
{
var clients = Hub.GetClients<RealTimeJTableDemoHub>();
clients.RecordDeleted(clientName, studentId);
}); //Return result to current (caller) client
return Json(new { Result = "OK" });
}
catch (Exception ex)
{
return Json(new { Result = "ERROR", Message = ex.Message });
}
}
当用户删除一行时,jTable会自动调用这个操作(DeleteStudent)(我们将在view部分看到它的配置)。在DeleteStudent操作中,我执行了以下操作: 我根据它的ID (StudentId)从数据库中删除了记录。然后我得到了调用该操作的客户机的名称(我们将在视图中看到,它被作为查询字符串参数发送到URL)。然后我开始了一个新的任务(线程)来发送通知给客户端(当然这不是必需的,我可以发送在同一个线程,但我不想等待调用者客户端)。在该任务中,我获得了对使用Hub的所有客户端的引用。GetClients通用方法。在从Hub派生的类中,您可以直接访问客户机(当客户机调用它时,如RealTimeJTableHub类的SendMessage方法中所示)。但是,为了在任何时候(特别是在这个类之外)获得对客户机的引用,我们使用Hub。GetClients方法。然后我调用所有客户端的RecordDeleted方法来通知记录删除。重要!我们从服务器异步调用客户端的JavaScript方法。那太神奇了!最后,我向jTable返回了一切正常的响应。 让我们看看服务器代码更新行/记录: 隐藏,收缩,复制Code
[HttpPost]
public JsonResult UpdateStudent(Student student)
{
try
{
//Validation
if (!ModelState.IsValid)
{
return Json(new { Result = "ERROR",
Message = "Form is not valid! Please correct it and try again." });
} //Update in the database
_repository.StudentRepository.UpdateStudent(student); //Inform all connected clients
var clientName = Request["clientName"];
Task.Factory.StartNew(
() =>
{
var clients = Hub.GetClients<RealTimeJTableDemoHub>();
clients.RecordUpdated(clientName, student);
}); //Return result to current (caller) client
return Json(new { Result = "OK" });
}
catch (Exception ex)
{
return Json(new { Result = "ERROR", Message = ex.Message });
}
}
它非常类似于删除操作(DeleteStudent)。它更新数据库中的学生记录,通知所有客户机一条记录被更新。最后,将结果返回给jTable。注意,在客户端(JavaScript)和服务器(c#)之间传输Student对象非常简单。 创建动作和从服务器获取第一个学生列表是类似的,可以在源代码中探索。 视图 在视图侧(HTML代码),我们首先包括需要的CSS和JavaScript文件: 隐藏,复制Code
<!-- Include style files -->
<linkhref="@Url.Content("~/Content/themes/redmond/jquery-ui-1.8.16.custom.css")"rel="stylesheet"type="text/css"/>
<linkhref="@Url.Content("~/Scripts/jtable/themes/standard/blue/jtable_blue.css")"rel="stylesheet"type="text/css"/> <!-- Include jQuery -->
<scriptsrc="@Url.Content("~/Scripts/jquery-1.6.4.min.js")"type="text/javascript">
</script>
<scriptsrc="@Url.Content("~/Scripts/jquery-ui-1.8.17.min.js")"type="text/javascript">
</script> <!-- Include jTable -->
<scriptsrc="@Url.Content("~/Scripts/jtable/jquery.jtable.min.js")"type="text/javascript"></script> <!-- Include SignalR -->
<scriptsrc="@Url.Content("~/Scripts/jquery.signalR.min.js")"type="text/javascript"></script>
<scriptsrc="@Url.Content("~/signalr/hubs")"type="text/javascript"></script>
最后一行很重要,因为没有这样的JavaScript文件(Visual Studio可能会为此显示一个警告)。它由SignalR在运行时动态生成。 下面是客户端完整的JavaScript代码: 隐藏,收缩,复制Code
$(document).ready(function () { //ViewBag.ClientName is set to a random name in the Index action.
var myClientName = '@ViewBag.ClientName'; //Initialize jTable
$('#StudentTableContainer').jtable({
title: 'Student List',
actions: {
listAction: '@Url.Action("StudentList")?clientName=' + myClientName,
deleteAction: '@Url.Action("DeleteStudent")?clientName=' + myClientName,
updateAction: '@Url.Action("UpdateStudent")?clientName=' + myClientName,
createAction: '@Url.Action("CreateStudent")?clientName=' + myClientName
},
fields: {
StudentId: {
title: 'Id',
width: '8%',
key: true,
create: false,
edit: false
},
Name: {
title: 'Name',
width: '21%'
},
EmailAddress: {
title: 'Email address',
list: false
},
Password: {
title: 'User Password',
type: 'password',
list: false
},
Gender: {
title: 'Gender',
width: '12%',
options: { 'M': 'Male', 'F': 'Female' }
},
CityId: {
title: 'City',
width: '11%',
options: '@Url.Action("GetCityOptions")'
},
BirthDate: {
title: 'Birth date',
width: '13%',
type: 'date',
displayFormat: 'yy-mm-dd'
},
Education: {
title: 'Education',
list: false,
type: 'radiobutton',
options: { '1': 'Primary school', '2': 'High school', '3': 'University' }
},
About: {
title: 'About this person',
type: 'textarea',
list: false
},
IsActive: {
title: 'Status',
width: '10%',
type: 'checkbox',
values: { 'false': 'Passive', 'true': 'Active' },
defaultValue: 'true'
},
RecordDate: {
title: 'Record date',
width: '15%',
type: 'date',
displayFormat: 'yy-mm-dd',
create: false,
edit: false,
sorting: false
}
}
}); //Load student list from server
$('#StudentTableContainer').jtable('load'); //Create SignalR object to communicate with server
var realTimeHub = $.connection.realTimeJTableDemoHub; //Define a function to get 'record created' events
realTimeHub.RecordCreated = function (clientName, record) {
if (clientName != myClientName) {
$('#StudentTableContainer').jtable('addRecord', {
record: record,
clientOnly: true
});
} writeEvent(clientName + ' has <b>created</b>
a new record with id = ' + record.StudentId, 'event-created');
}; //Define a function to get 'record updated' events
realTimeHub.RecordUpdated = function (clientName, record) {
if (clientName != myClientName) {
$('#StudentTableContainer').jtable('updateRecord', {
record: record,
clientOnly: true
});
} writeEvent(clientName + ' has <b>updated</b>
a new record with id = ' + record.StudentId, 'event-updated');
}; //Define a function to get 'record deleted' events
realTimeHub.RecordDeleted = function (clientName, recordId) {
if (clientName != myClientName) {
$('#StudentTableContainer').jtable('deleteRecord', {
key: recordId,
clientOnly: true
});
} writeEvent(clientName + ' has <b>removed</b>
a record with id = ' + recordId, 'event-deleted');
}; //Define a function to get 'chat messages'
realTimeHub.GetMessage = function (clientName, message) {
writeEvent('<b>' + clientName + '</b> has sent a message:
' + message, 'event-message');
}; //Send message to server when user press enter on the message textbox
$('#Message').keydown(function (e) {
if (e.which == 13) { //Enter
e.preventDefault();
realTimeHub.sendMessage(myClientName, $('#Message').val());
$('#Message').val('');
}
}); // Start the connection to get events
$.connection.hub.start(); //A function to write events to the page
function writeEvent(eventLog, logClass) {
var now = new Date();
var nowStr = now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds();
$('#EventsList').prepend('<li class="' + logClass + '"><b>' +
nowStr + '</b>: ' + eventLog + '.</li>');
}
});
让我们来解释代码的一些部分。 首先,我为所有客户端分配了一个随机名称,用于查看哪个用户进行了更改。然后初始化了jTable。如果您不了解jTable,请参阅我的文章。 要获得对信号通信对象的引用,我们使用$.connection. realtimejtabledemohub。这是由SignalR根据我们的服务器到客户机和客户机到服务器方法动态生成的。请注意,代理类名(is realTimeJTableDemoHub)以小写开始,而c#类名是以大写开始的。 当服务器调用client的方法时(正如我们在controller部分中看到的),SignalR将调用一个JavaScript回调方法。因此,我们可以像上面的代码那样定义这些回调方法。例如,要从服务器获取聊天消息,我们可以定义这样一个方法: 隐藏,复制Code
//Define a function to get 'chat messages'
realTimeHub.GetMessage = function (clientName, message) {
//...
};
要从客户端调用服务器方法,我们可以直接使用相同的代理对象(注意,sendMessage是驼峰式的,不是帕斯卡式的): 隐藏,复制Code
realTimeHub.sendMessage('halil', 'a test message');
最后,我们必须调用start方法来启动与服务器的通信: 隐藏,复制Code
// Start the connection with server
$.connection.hub.start();
这是所有!SignalR和jTable是非常容易使用且功能强大的库。 临终遗言与致谢 在本文中,我介绍了SignalR并将其与jTable一起使用来创建动态web表。SignalR是一个令人惊奇的框架,当我第一次看到它时,它给我留下了深刻的印象。它使得web上的异步服务器-客户端通信变得非常容易。多亏了它的开发者。 参考文献 SignalR主页:https://github.com/SignalR/SignalR jTable主页:http://jtable.org使用jTable与ASP。asp.net MVC: http://www.codeproject.com/KB/ajax/jTable.aspx使用jTable与ASPNET WebForms: http://jtable.org/Tutorials/UsingWithAspNetWebFormsPageMethods Using jTable with PHP: http://jtable.org/GettingStarted 历史 2012年1月17日:首次上映 本文转载于:http://www.diyabc.com/frontweb/news19767.html
实时,异步网页使用jTable, SignalR和ASP。NET MVC的更多相关文章
- SignalR + KnockoutJS + ASP.NET MVC 实现井字游戏
SignalR + KnockoutJS + ASP.NET MVC 实现井字游戏 1.1.1 摘要 今天,我们将使用SignalR + KnockoutJS + ASP.NET MVC实现一个实 ...
- SignalR在ASP.NET MVC中的应用
一.简介 ASP.NET SignalR 是为 ASP.NET 开发人员提供的一个库,可以简化开发人员将实时 Web 功能添加到应用程序的过程.实时 Web 功能是指这样一种功能:当所连接的客户端变得 ...
- SignalR + KnockoutJS + ASP.NET MVC4 实现井字游戏
1.1.1 摘要 今天,我们将使用SignalR + KnockoutJS + ASP.NET MVC实现一个实时HTML5的井字棋游戏. 首先,网络游戏平台一定要让用户登陆进来,所以需要一个登陆模块 ...
- 在ASP.NET MVC里对Web Page网页进行权限控制
我们在ASP.NET MVC开发时,有时候还是得设计ASP.NET的Web Page网页(.aspx和.aspx.cs),来实现一些ASP.NET MVC无法实现的功能,如此篇<Visual S ...
- ASP.NET MVC Jquery Validate 表单验证的多种方式
在我们日常开发过程中,前端的表单验证很重要,如果这块处理不当,会出现很多bug .但是如果处理的好,不仅bug会很少,用户体验也会得到很大的提升.在开发过程中我们可以不借助 JS 库,自己去手写 JS ...
- [转]ASP.NET MVC Jquery Validate 表单验证的多种方式介绍
在我们日常开发过程中,前端的表单验证很重要,如果这块处理不当,会出现很多bug .但是如果处理的好,不仅bug会很少,用户体验也会得到很大的提升.在开发过程中我们可以不借助 JS 库,自己去手写 JS ...
- ASP.NET MVC案例教程(基于ASP.NET MVC beta)——第五篇:MVC整合Ajax
摘要 本文将从完成“输入数据验证”这个功能出发,逐渐展开ASP.NET MVC与Ajax结合的方法.首先,本文将使用ASP.NET MVC提供的同步方式完成数据验证.而后,将分别结合ASP. ...
- asp.net MVC SignalR 与数据库 实时同步显示
asp.net MVC SignalR 与数据库 实时同步显示 错误:未启用当前数据库的 SQL Server Service Broker,因此查询通知不受支持.如果希望使用通知,请为此数据库启用 ...
- C# ASP.NET MVC 之 SignalR 学习 实时数据推送显示 配合 Echarts 推送实时图表
本文主要是我在刚开始学习 SignalR 的技术总结,网上找的学习方法和例子大多只是翻译了官方给的一个例子,并没有给出其他一些经典情况的示例,所以才有了本文总结,我在实现推送简单的数据后,就想到了如何 ...
随机推荐
- 使用Unity的50个建议
关于这些建议 这些建议并不适用于所有的项目 这些建议是基于我与3-20人的小团队项目经验总结出来的 结构.可重复使用性.明晰度都是有价的——团队规模和项目规模决定了是否值得付这个价. 一些建议也许公然 ...
- spring集成shiro,事务失效问题 not eligible for auto-proxying
BeanPostProcessor bean实例化顺序有关,@Configuration会最先实例化,也就是在spring启动完成之前. 导致Configuration中使用的注入,没能在spring ...
- JVM内存区域与垃圾回收
1.JAVA内存区域与内存溢出 1.1.概述 Java中JVM提供了内存管理机制,Java虚拟机在执行Java程序的过程中会把内分分为不同的数据区,如图: 1.2.程序计数器 程序计数器是当前线程所执 ...
- 组件给App全局传值vue-bus的使用
npm安装 npm install vue-bus main.js引入 import VueBus from 'vue-bus' Vue.use(VueBus) 组件 getHouse(e){ thi ...
- Oracle12C配置对外访问
Oracle12C配置对外访问 第一步: 开放端口或者关闭防火墙 第二步: 配置Oracle net manager打开Net manager 修改为共享服务器 第三步: 配置连接数打开Databas ...
- MySql 实现数组根据下标获取对应值逻辑(array[i]逻辑)
在使用sql模拟一段java逻辑开发时碰到有一段逻辑为从字符串数组中根据下标获取对应的值的情况,百度了一番没有发现有类似功能的函数和现成的实现方式,经过调试弄出来了,记录下来,以备参考 //举例:从数 ...
- 面经手册 · 第10篇《扫盲java.util.Collections工具包,学习排序、二分、洗牌、旋转算法》
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 算法是数据结构的灵魂! 好的算法搭配上合适的数据结构,可以让代码功能大大的提升效率. ...
- Cutting Game(POJ 2311)
原题如下: Cutting Game Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 5721 Accepted: 208 ...
- 修改mysql、sqlserver数据库默认用户,不允许为root、sa等
1.mysql cmd进入dos命令,输入mysql -u root -P 1202 -h localhost -p敲回车输入密码 use mysql; 修改用户名root为其他用户 update u ...
- [LeetCode]64. 最小路径和(DP)
题目 给定一个无序的整数数组,找到其中最长上升子序列的长度. 示例: 输入: [10,9,2,5,3,7,101,18] 输出: 4 解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4 ...