ASP.NET MVC4异步聊天室的示例代码

时间:2022-09-14 12:14:04

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

类图:

ASP.NET MVC4异步聊天室的示例代码

Domain层

IChatRoom.cs

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

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

ICallbackQueue.cs

?
1
2
3
4
5
6
7
8
9
10
11
12
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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

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

SayRequest.cs

?
1
2
3
4
5
6
7
8
9
10
11
12
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

?
1
2
3
4
5
6
7
8
9
10
11
12
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

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

ChatController.cs

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@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>
}

运行结果如图:

ASP.NET MVC4异步聊天室的示例代码

ASP.NET MVC4异步聊天室的示例代码

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

原文链接:http://blog.csdn.net/wulex/article/details/78169106?utm_source=tuicool&utm_medium=referral