视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
ASP.NET MVC4异步聊天室的示例代码
2020-11-27 22:35:18 责编:小采
文档


本文介绍了ASP.NET MVC4异步聊天室的示例代码,分享给大家,具体如下:

类图:

Domain层

IChatRoom.cs

using System;
using System.Collections.Generic;

namespace MvcAsyncChat.Domain
{
 public interface IChatRoom
 {
 void AddMessage(string message);
 void AddParticipant(string name);
 void GetMessages(
 DateTime since, 
 Action<IEnumerable<string>, DateTime> callback);
 void RemoveParticipant(string name);
 }
}
 

IMessageRepo.cs

using System;
using System.Collections.Generic;

namespace MvcAsyncChat.Domain
{
 public interface IMessageRepo
 {
 DateTime Add(string message);
 IEnumerable<string> GetSince(DateTime since);
 }
}

ICallbackQueue.cs

using System;
using System.Collections.Generic;

namespace MvcAsyncChat.Domain
{
 public interface ICallbackQueue
 {
 void Enqueue(Action<IEnumerable<string>, DateTime> callback);
 IEnumerable<Action<IEnumerable<string>, DateTime>> DequeueAll();
 IEnumerable<Action<IEnumerable<string>, DateTime>> DequeueExpired(DateTime expiry);
 }
}

ChatRoom.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using MvcAsyncChat.Svcs;

namespace MvcAsyncChat.Domain
{
 public class ChatRoom : IChatRoom
 {
 readonly ICallbackQueue callbackQueue;
 readonly IDateTimeSvc dateTimeSvc;
 readonly IMessageRepo messageRepo;

 public ChatRoom(
 ICallbackQueue callbackQueue,
 IDateTimeSvc dateTimeSvc,
 IMessageRepo messageRepo)
 {
 this.callbackQueue = callbackQueue;
 this.dateTimeSvc = dateTimeSvc;
 this.messageRepo = messageRepo;
 }

 public void AddMessage(string message)
 {
 var timestamp = messageRepo.Add(message);

 foreach (var callback in callbackQueue.DequeueAll())
 callback(new[] { message }, timestamp);
 }

 public void AddParticipant(string name)
 {
 AddMessage(string.Format("{0} 已进入房间.", name));
 }

 public void GetMessages(
 DateTime since,
 Action<IEnumerable<string>, DateTime> callback)
 {
 var messages = messageRepo.GetSince(since);

 if (messages.Count() > 0)
 callback(messages, since);
 else
 callbackQueue.Enqueue(callback);
 }

 public void RemoveParticipant(string name)
 {
 AddMessage(string.Format("{0} left the room.", name));
 }
 }
}

InMemMessageRepo.cs

using System;
using System.Collections.Generic;
using System.Linq;

namespace MvcAsyncChat.Domain
{
 public class InMemMessageRepo : IMessageRepo
 {
 public InMemMessageRepo()
 {
 Messages = new List<Tuple<string, DateTime>>();
 }

 public IList<Tuple<string, DateTime>> Messages { get; private set; }

 public DateTime Add(string message)
 {
 var timestamp = DateTime.UtcNow;

 Messages.Add(new Tuple<string, DateTime>(message, timestamp));

 return timestamp;
 }

 public IEnumerable<string> GetSince(DateTime since)
 {
 return Messages
 .Where(x => x.Item2 > since)
 .Select(x => x.Item1);
 }
 }
}

CallbackQueue.cs

using System;
using System.Collections.Generic;
using System.Linq;

namespace MvcAsyncChat.Domain
{
 public class CallbackQueue : ICallbackQueue
 {
 public CallbackQueue()
 {
 Callbacks = new Queue<Tuple<Action<IEnumerable<string>, DateTime>, DateTime>>();
 }

 public Queue<Tuple<Action<IEnumerable<string>, DateTime>, DateTime>> Callbacks { get; private set; }

 public void Enqueue(Action<IEnumerable<string>, DateTime> callback)
 {
 Callbacks.Enqueue(new Tuple<Action<IEnumerable<string>, DateTime>, DateTime>(callback, DateTime.UtcNow));
 }

 public IEnumerable<Action<IEnumerable<string>, DateTime>> DequeueAll()
 {
 while (Callbacks.Count > 0)
 yield return Callbacks.Dequeue().Item1;
 }

 public IEnumerable<Action<IEnumerable<string>, DateTime>> DequeueExpired(DateTime expiry)
 {
 if (Callbacks.Count == 0)
 yield break;

 var oldest = Callbacks.Peek();
 while (Callbacks.Count > 0 && oldest.Item2 <= expiry)
 {
 yield return Callbacks.Dequeue().Item1;

 if (Callbacks.Count > 0)
 oldest = Callbacks.Peek();
 }
 }
 }
}

RequestModels文件夹实体类

EnterRequest.cs

using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace MvcAsyncChat.RequestModels
{
 public class EnterRequest
 {
 [DisplayName("名称")]
 [Required, StringLength(16), RegularExpression(@"^[A-Za-z0-9_\ -]+$", ErrorMessage="A name must be alpha-numeric.")]
 public string Name { get; set; }
 }
}

GetMessagesRequest.cs

using System;

namespace MvcAsyncChat.RequestModels
{
 public class GetMessagesRequest
 {
 public string since { get; set; }
 }
}

SayRequest.cs

using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace MvcAsyncChat.RequestModels
{
 public class SayRequest
 {
 [Required, StringLength(1024), DataType(DataType.MultilineText)]
 public string Text { get; set; }
 }
}

ResponseModels文件夹实体类

GetMessagesResponse.cs

using System;
using System.Collections.Generic;

namespace MvcAsyncChat.ResponseModels
{
 public class GetMessagesResponse
 {
 public string error { get; set; }
 public IEnumerable<string> messages { get; set; }
 public string since { get; set; }
 }
}

SayResponse.cs

using System;

namespace MvcAsyncChat.ResponseModels
{
 public class SayResponse
 {
 public string error { get; set; }
 }
}

ChatController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Async;
using MvcAsyncChat.Domain;
using MvcAsyncChat.RequestModels;
using MvcAsyncChat.ResponseModels;
using MvcAsyncChat.Svcs;

namespace MvcAsyncChat.Controllers
{
 public class ChatController : AsyncController
 {
 readonly IAuthSvc authSvc;
 readonly IChatRoom chatRoom;
 readonly IDateTimeSvc dateTimeSvc;

 public ChatController(
 IAuthSvc authSvc,
 IChatRoom chatRoom,
 IDateTimeSvc dateTimeSvc)
 {
 this.authSvc = authSvc;
 this.chatRoom = chatRoom;
 this.dateTimeSvc = dateTimeSvc;
 }

 [ActionName("enter"), HttpGet]
 public ActionResult ShowEnterForm()
 {
 if (User.Identity.IsAuthenticated)
 return RedirectToRoute(RouteName.Room);

 return View();
 }

 [ActionName("enter"), HttpPost]
 public ActionResult EnterRoom(EnterRequest enterRequest)
 {
 if (!ModelState.IsValid)
 return View(enterRequest);

 authSvc.Authenticate(enterRequest.Name);
 chatRoom.AddParticipant(enterRequest.Name);

 return RedirectToRoute(RouteName.Room);
 }

 [ActionName("room"), HttpGet, Authorize]
 public ActionResult ShowRoom()
 {
 return View();
 }

 [ActionName("leave"), HttpGet, Authorize]
 public ActionResult LeaveRoom()
 {
 authSvc.Unauthenticate();
 chatRoom.RemoveParticipant(User.Identity.Name);

 return RedirectToRoute(RouteName.Enter);
 }

 [HttpPost, Authorize]
 public ActionResult Say(SayRequest sayRequest)
 {
 if (!ModelState.IsValid)
 return Json(new SayResponse() { error = "该请求无效." });

 chatRoom.AddMessage(User.Identity.Name+" 说:"+sayRequest.Text);

 return Json(new SayResponse());
 }

 [ActionName("messages"), HttpPost, Authorize]
 public void GetMessagesAsync(GetMessagesRequest getMessagesRequest)
 {
 AsyncManager.OutstandingOperations.Increment();

 if (!ModelState.IsValid)
 {
 AsyncManager.Parameters["error"] = "The messages request was invalid.";
 AsyncManager.Parameters["since"] = null;
 AsyncManager.Parameters["messages"] = null;
 AsyncManager.OutstandingOperations.Decrement();
 return;
 }

 var since = dateTimeSvc.GetCurrentDateTimeAsUtc();
 if (!string.IsNullOrEmpty(getMessagesRequest.since))
 since = DateTime.Parse(getMessagesRequest.since).ToUniversalTime();

 chatRoom.GetMessages(since, (newMessages, timestamp) => 
 {
 AsyncManager.Parameters["error"] = null;
 AsyncManager.Parameters["since"] = timestamp;
 AsyncManager.Parameters["messages"] = newMessages;
 AsyncManager.OutstandingOperations.Decrement();
 });
 }

 public ActionResult GetMessagesCompleted(
 string error, 
 DateTime? since, 
 IEnumerable<string> messages)
 {
 if (!string.IsNullOrWhiteSpace(error))
 return Json(new GetMessagesResponse() { error = error });

 var data = new GetMessagesResponse();
 data.since = since.Value.ToString("o");
 data.messages = messages;

 return Json(data);
 }
 }
}

room.js

var since = "",
 errorCount = 0,
 MAX_ERRORS = 6;

function addMessage(message, type) {
 $("#messagesSection > td").append("<div class='" + (type || "") + "'>" + message + "</div>")
}

function showError(error) {
 addMessage(error.toString(), "error");
}

function onSayFailed(XMLHttpRequest, textStatus, errorThrown) {
 showError("An unanticipated error occured during the say request: " + textStatus + "; " + errorThrown);
}

function onSay(data) {
 if (data.error) {
 showError("An error occurred while trying to say your message: " + data.error);
 return;
 }
}

function setSayHandler() {
 $("#Text").keypress(function (e) {
 if (e.keyCode == 13) {
 $("#sayForm").submit();
 $("#Text").val("");
 return false;
 }
 });
}

function retryGetMessages() {
 if (++errorCount > MAX_ERRORS) {
 showError("There have been too many errors. Please leave the chat room and re-enter.");
 }
 else {
 setTimeout(function () {
 getMessages();
 }, Math.pow(2, errorCount) * 1000);
 }
}

function onMessagesFailed(XMLHttpRequest, textStatus, errorThrown) {
 showError("An unanticipated error occured during the messages request: " + textStatus + "; " + errorThrown);
 retryGetMessages();
}

function onMessages(data, textStatus, XMLHttpRequest) {
 if (data.error) {
 showError("An error occurred while trying to get messages: " + data.error);
 retryGetMessages();
 return;
 }

 errorCount = 0;
 since = data.since;

 for (var n = 0; n < data.messages.length; n++)
 addMessage(data.messages[n]);

 setTimeout(function () {
 getMessages();
 }, 0);
}

function getMessages() {
 $.ajax({
 cache: false,
 type: "POST",
 dataType: "json",
 url: "/messages",
 data: { since: since },
 error: onMessagesFailed,
 success: onMessages,
 timeout: 100000
 });
}

Chat视图文件夹

Enter.cshtml

@model MvcAsyncChat.RequestModels.EnterRequest

@{
 View.Title = "Enter";
 Layout = "~/Views/Shared/_Layout.cshtml";
}

@section Head {}

<tr id="enterSection">
 <td>
 <h2>[MVC聊天]是使用ASP.NET MVC 3的异步聊天室
 <table>
 <tr>
 <td class="form-container">
 <fieldset>
 <legend>进入聊天室</legend>
 @using(Html.BeginForm()) {
 @Html.EditorForModel()
 <input type="submit" value="Enter" />
 }
 </fieldset>
 </td>
 </tr>
 </table>
 </td>
</tr>

@section PostScript {
 <script>
 $(document).ready(function() {
 $("#Name").focus(); 
 });
 </script>
}

Room.cshtml

@using MvcAsyncChat;
@using MvcAsyncChat.RequestModels;
@model SayRequest

@{
 View.Title = "Room";
 Layout = "~/Views/Shared/_Layout.cshtml";
}

@section Head {
 <script src="@Url.Content("~/Scripts/room.js")"></script>
}

<tr id="messagesSection">
 <td></td>
</tr>
<tr id="actionsSection">
 <td>

 <label for="actionsList">操作:</label>
 <ul id="actionsList">
 <li>@Html.RouteLink("离开房间", RouteName.Leave)</li>
 </ul>
 @using (Ajax.BeginForm("say", new { }, new AjaxOptions() { 
 OnFailure = "onSayFailed", 
 OnSuccess = "onSay", 
 HttpMethod = "POST", }, new { id = "sayForm"})) {
 @Html.EditorForModel()
 }
 </td>
</tr>

@section PostScript {
 <script>
 $(document).ready(function() {
 $("#Text").attr("placeholder", "你说:");
 $("#Text").focus();
 setSayHandler();
 getMessages();
 });
 </script>
}

运行结果如图:

下载本文
显示全文
专题