查看: 1847|回复: 0

[ASP.NET教程] ASP.NET MVC4异步聊天室的示例代码

发表于 2018-1-3 08:00:01

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

类图:

Domain层

IChatRoom.cs

  1. using System;
  2. using System.Collections.Generic;
  3. namespace MvcAsyncChat.Domain
  4. {
  5. public interface IChatRoom
  6. {
  7. void AddMessage(string message);
  8. void AddParticipant(string name);
  9. void GetMessages(
  10. DateTime since,
  11. Action<IEnumerable<string>, DateTime> callback);
  12. void RemoveParticipant(string name);
  13. }
  14. }
复制代码

IMessageRepo.cs

  1. using System;
  2. using System.Collections.Generic;
  3. namespace MvcAsyncChat.Domain
  4. {
  5. public interface IMessageRepo
  6. {
  7. DateTime Add(string message);
  8. IEnumerable<string> GetSince(DateTime since);
  9. }
  10. }
复制代码

ICallbackQueue.cs

  1. using System;
  2. using System.Collections.Generic;
  3. namespace MvcAsyncChat.Domain
  4. {
  5. public interface ICallbackQueue
  6. {
  7. void Enqueue(Action<IEnumerable<string>, DateTime> callback);
  8. IEnumerable<Action<IEnumerable<string>, DateTime>> DequeueAll();
  9. IEnumerable<Action<IEnumerable<string>, DateTime>> DequeueExpired(DateTime expiry);
  10. }
  11. }
复制代码

ChatRoom.cs

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading;
  5. using MvcAsyncChat.Svcs;
  6. namespace MvcAsyncChat.Domain
  7. {
  8. public class ChatRoom : IChatRoom
  9. {
  10. readonly ICallbackQueue callbackQueue;
  11. readonly IDateTimeSvc dateTimeSvc;
  12. readonly IMessageRepo messageRepo;
  13. public ChatRoom(
  14. ICallbackQueue callbackQueue,
  15. IDateTimeSvc dateTimeSvc,
  16. IMessageRepo messageRepo)
  17. {
  18. this.callbackQueue = callbackQueue;
  19. this.dateTimeSvc = dateTimeSvc;
  20. this.messageRepo = messageRepo;
  21. }
  22. public void AddMessage(string message)
  23. {
  24. var timestamp = messageRepo.Add(message);
  25. foreach (var callback in callbackQueue.DequeueAll())
  26. callback(new[] { message }, timestamp);
  27. }
  28. public void AddParticipant(string name)
  29. {
  30. AddMessage(string.Format("{0} 已进入房间.", name));
  31. }
  32. public void GetMessages(
  33. DateTime since,
  34. Action<IEnumerable<string>, DateTime> callback)
  35. {
  36. var messages = messageRepo.GetSince(since);
  37. if (messages.Count() > 0)
  38. callback(messages, since);
  39. else
  40. callbackQueue.Enqueue(callback);
  41. }
  42. public void RemoveParticipant(string name)
  43. {
  44. AddMessage(string.Format("{0} left the room.", name));
  45. }
  46. }
  47. }
复制代码

InMemMessageRepo.cs

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. namespace MvcAsyncChat.Domain
  5. {
  6. public class InMemMessageRepo : IMessageRepo
  7. {
  8. public InMemMessageRepo()
  9. {
  10. Messages = new List<Tuple<string, DateTime>>();
  11. }
  12. public IList<Tuple<string, DateTime>> Messages { get; private set; }
  13. public DateTime Add(string message)
  14. {
  15. var timestamp = DateTime.UtcNow;
  16. Messages.Add(new Tuple<string, DateTime>(message, timestamp));
  17. return timestamp;
  18. }
  19. public IEnumerable<string> GetSince(DateTime since)
  20. {
  21. return Messages
  22. .Where(x => x.Item2 > since)
  23. .Select(x => x.Item1);
  24. }
  25. }
  26. }
复制代码

CallbackQueue.cs

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. namespace MvcAsyncChat.Domain
  5. {
  6. public class CallbackQueue : ICallbackQueue
  7. {
  8. public CallbackQueue()
  9. {
  10. Callbacks = new Queue<Tuple<Action<IEnumerable<string>, DateTime>, DateTime>>();
  11. }
  12. public Queue<Tuple<Action<IEnumerable<string>, DateTime>, DateTime>> Callbacks { get; private set; }
  13. public void Enqueue(Action<IEnumerable<string>, DateTime> callback)
  14. {
  15. Callbacks.Enqueue(new Tuple<Action<IEnumerable<string>, DateTime>, DateTime>(callback, DateTime.UtcNow));
  16. }
  17. public IEnumerable<Action<IEnumerable<string>, DateTime>> DequeueAll()
  18. {
  19. while (Callbacks.Count > 0)
  20. yield return Callbacks.Dequeue().Item1;
  21. }
  22. public IEnumerable<Action<IEnumerable<string>, DateTime>> DequeueExpired(DateTime expiry)
  23. {
  24. if (Callbacks.Count == 0)
  25. yield break;
  26. var oldest = Callbacks.Peek();
  27. while (Callbacks.Count > 0 && oldest.Item2 <= expiry)
  28. {
  29. yield return Callbacks.Dequeue().Item1;
  30. if (Callbacks.Count > 0)
  31. oldest = Callbacks.Peek();
  32. }
  33. }
  34. }
  35. }
复制代码

RequestModels文件夹实体类

EnterRequest.cs

  1. using System;
  2. using System.ComponentModel;
  3. using System.ComponentModel.DataAnnotations;
  4. namespace MvcAsyncChat.RequestModels
  5. {
  6. public class EnterRequest
  7. {
  8. [DisplayName("名称")]
  9. [Required, StringLength(16), RegularExpression(@"^[A-Za-z0-9_\ -]+$", ErrorMessage="A name must be alpha-numeric.")]
  10. public string Name { get; set; }
  11. }
  12. }
复制代码

GetMessagesRequest.cs

  1. using System;
  2. namespace MvcAsyncChat.RequestModels
  3. {
  4. public class GetMessagesRequest
  5. {
  6. public string since { get; set; }
  7. }
  8. }
复制代码

SayRequest.cs

  1. using System;
  2. using System.ComponentModel;
  3. using System.ComponentModel.DataAnnotations;
  4. namespace MvcAsyncChat.RequestModels
  5. {
  6. public class SayRequest
  7. {
  8. [Required, StringLength(1024), DataType(DataType.MultilineText)]
  9. public string Text { get; set; }
  10. }
  11. }
复制代码

ResponseModels文件夹实体类

GetMessagesResponse.cs

  1. using System;
  2. using System.Collections.Generic;
  3. namespace MvcAsyncChat.ResponseModels
  4. {
  5. public class GetMessagesResponse
  6. {
  7. public string error { get; set; }
  8. public IEnumerable<string> messages { get; set; }
  9. public string since { get; set; }
  10. }
  11. }
复制代码

SayResponse.cs

  1. using System;
  2. namespace MvcAsyncChat.ResponseModels
  3. {
  4. public class SayResponse
  5. {
  6. public string error { get; set; }
  7. }
  8. }
复制代码

ChatController.cs

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5. using System.Web.Mvc;
  6. using System.Web.Mvc.Async;
  7. using MvcAsyncChat.Domain;
  8. using MvcAsyncChat.RequestModels;
  9. using MvcAsyncChat.ResponseModels;
  10. using MvcAsyncChat.Svcs;
  11. namespace MvcAsyncChat.Controllers
  12. {
  13. public class ChatController : AsyncController
  14. {
  15. readonly IAuthSvc authSvc;
  16. readonly IChatRoom chatRoom;
  17. readonly IDateTimeSvc dateTimeSvc;
  18. public ChatController(
  19. IAuthSvc authSvc,
  20. IChatRoom chatRoom,
  21. IDateTimeSvc dateTimeSvc)
  22. {
  23. this.authSvc = authSvc;
  24. this.chatRoom = chatRoom;
  25. this.dateTimeSvc = dateTimeSvc;
  26. }
  27. [ActionName("enter"), HttpGet]
  28. public ActionResult ShowEnterForm()
  29. {
  30. if (User.Identity.IsAuthenticated)
  31. return RedirectToRoute(RouteName.Room);
  32. return View();
  33. }
  34. [ActionName("enter"), HttpPost]
  35. public ActionResult EnterRoom(EnterRequest enterRequest)
  36. {
  37. if (!ModelState.IsValid)
  38. return View(enterRequest);
  39. authSvc.Authenticate(enterRequest.Name);
  40. chatRoom.AddParticipant(enterRequest.Name);
  41. return RedirectToRoute(RouteName.Room);
  42. }
  43. [ActionName("room"), HttpGet, Authorize]
  44. public ActionResult ShowRoom()
  45. {
  46. return View();
  47. }
  48. [ActionName("leave"), HttpGet, Authorize]
  49. public ActionResult LeaveRoom()
  50. {
  51. authSvc.Unauthenticate();
  52. chatRoom.RemoveParticipant(User.Identity.Name);
  53. return RedirectToRoute(RouteName.Enter);
  54. }
  55. [HttpPost, Authorize]
  56. public ActionResult Say(SayRequest sayRequest)
  57. {
  58. if (!ModelState.IsValid)
  59. return Json(new SayResponse() { error = "该请求无效." });
  60. chatRoom.AddMessage(User.Identity.Name+" 说:"+sayRequest.Text);
  61. return Json(new SayResponse());
  62. }
  63. [ActionName("messages"), HttpPost, Authorize]
  64. public void GetMessagesAsync(GetMessagesRequest getMessagesRequest)
  65. {
  66. AsyncManager.OutstandingOperations.Increment();
  67. if (!ModelState.IsValid)
  68. {
  69. AsyncManager.Parameters["error"] = "The messages request was invalid.";
  70. AsyncManager.Parameters["since"] = null;
  71. AsyncManager.Parameters["messages"] = null;
  72. AsyncManager.OutstandingOperations.Decrement();
  73. return;
  74. }
  75. var since = dateTimeSvc.GetCurrentDateTimeAsUtc();
  76. if (!string.IsNullOrEmpty(getMessagesRequest.since))
  77. since = DateTime.Parse(getMessagesRequest.since).ToUniversalTime();
  78. chatRoom.GetMessages(since, (newMessages, timestamp) =>
  79. {
  80. AsyncManager.Parameters["error"] = null;
  81. AsyncManager.Parameters["since"] = timestamp;
  82. AsyncManager.Parameters["messages"] = newMessages;
  83. AsyncManager.OutstandingOperations.Decrement();
  84. });
  85. }
  86. public ActionResult GetMessagesCompleted(
  87. string error,
  88. DateTime? since,
  89. IEnumerable<string> messages)
  90. {
  91. if (!string.IsNullOrWhiteSpace(error))
  92. return Json(new GetMessagesResponse() { error = error });
  93. var data = new GetMessagesResponse();
  94. data.since = since.Value.ToString("o");
  95. data.messages = messages;
  96. return Json(data);
  97. }
  98. }
  99. }
复制代码

room.js

  1. var since = "",
  2. errorCount = 0,
  3. MAX_ERRORS = 6;
  4. function addMessage(message, type) {
  5. $("#messagesSection > td").append("<div class='" + (type || "") + "'>" + message + "</div>")
  6. }
  7. function showError(error) {
  8. addMessage(error.toString(), "error");
  9. }
  10. function onSayFailed(XMLHttpRequest, textStatus, errorThrown) {
  11. showError("An unanticipated error occured during the say request: " + textStatus + "; " + errorThrown);
  12. }
  13. function onSay(data) {
  14. if (data.error) {
  15. showError("An error occurred while trying to say your message: " + data.error);
  16. return;
  17. }
  18. }
  19. function setSayHandler() {
  20. $("#Text").keypress(function (e) {
  21. if (e.keyCode == 13) {
  22. $("#sayForm").submit();
  23. $("#Text").val("");
  24. return false;
  25. }
  26. });
  27. }
  28. function retryGetMessages() {
  29. if (++errorCount > MAX_ERRORS) {
  30. showError("There have been too many errors. Please leave the chat room and re-enter.");
  31. }
  32. else {
  33. setTimeout(function () {
  34. getMessages();
  35. }, Math.pow(2, errorCount) * 1000);
  36. }
  37. }
  38. function onMessagesFailed(XMLHttpRequest, textStatus, errorThrown) {
  39. showError("An unanticipated error occured during the messages request: " + textStatus + "; " + errorThrown);
  40. retryGetMessages();
  41. }
  42. function onMessages(data, textStatus, XMLHttpRequest) {
  43. if (data.error) {
  44. showError("An error occurred while trying to get messages: " + data.error);
  45. retryGetMessages();
  46. return;
  47. }
  48. errorCount = 0;
  49. since = data.since;
  50. for (var n = 0; n < data.messages.length; n++)
  51. addMessage(data.messages[n]);
  52. setTimeout(function () {
  53. getMessages();
  54. }, 0);
  55. }
  56. function getMessages() {
  57. $.ajax({
  58. cache: false,
  59. type: "POST",
  60. dataType: "json",
  61. url: "/messages",
  62. data: { since: since },
  63. error: onMessagesFailed,
  64. success: onMessages,
  65. timeout: 100000
  66. });
  67. }
复制代码

Chat视图文件夹

Enter.cshtml

  1. @model MvcAsyncChat.RequestModels.EnterRequest
  2. @{
  3. View.Title = "Enter";
  4. Layout = "~/Views/Shared/_Layout.cshtml";
  5. }
  6. @section Head {}
  7. <tr id="enterSection">
  8. <td>
  9. <h2>[MVC聊天]是使用ASP.NET MVC 3的异步聊天室
  10. <table>
  11. <tr>
  12. <td class="form-container">
  13. <fieldset>
  14. <legend>进入聊天室</legend>
  15. @using(Html.BeginForm()) {
  16. @Html.EditorForModel()
  17. <input type="submit" value="Enter" />
  18. }
  19. </fieldset>
  20. </td>
  21. </tr>
  22. </table>
  23. </td>
  24. </tr>
  25. @section PostScript {
  26. <script>
  27. $(document).ready(function() {
  28. $("#Name").focus();
  29. });
  30. </script>
  31. }
复制代码

Room.cshtml

  1. @using MvcAsyncChat;
  2. @using MvcAsyncChat.RequestModels;
  3. @model SayRequest
  4. @{
  5. View.Title = "Room";
  6. Layout = "~/Views/Shared/_Layout.cshtml";
  7. }
  8. @section Head {
  9. <script src="@Url.Content("~/Scripts/room.js")"></script>
  10. }
  11. <tr id="messagesSection">
  12. <td></td>
  13. </tr>
  14. <tr id="actionsSection">
  15. <td>
  16. <label for="actionsList">操作:</label>
  17. <ul id="actionsList">
  18. <li>@Html.RouteLink("离开房间", RouteName.Leave)</li>
  19. </ul>
  20. @using (Ajax.BeginForm("say", new { }, new AjaxOptions() {
  21. OnFailure = "onSayFailed",
  22. OnSuccess = "onSay",
  23. HttpMethod = "POST", }, new { id = "sayForm"})) {
  24. @Html.EditorForModel()
  25. }
  26. </td>
  27. </tr>
  28. @section PostScript {
  29. <script>
  30. $(document).ready(function() {
  31. $("#Text").attr("placeholder", "你说:");
  32. $("#Text").focus();
  33. setSayHandler();
  34. getMessages();
  35. });
  36. </script>
  37. }
复制代码

运行结果如图:

这里写图片描述

这里写图片描述

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持程序员之家。



回复

使用道具 举报