淺談Web Api配合SignalR的跨域支持


  最近接手的一個項目中,涉及到一個簡單的消息模塊,由於之前有簡單了解過SignalR,所以打算嘗試着摸索摸索~!

  首先,通過Nuget管理器添加Microsoft ASP.NET SignalR引用~目前最新版本2.2.0,依賴項目也有點多,什么Microsoft.AspNet.SignalR.JS,Microsoft.AspNet.SignalR.SystemWeb,還有Owin相關的項目,沒法咯,一起統一引用!

  添加啟動設置

 1 [assembly: OwinStartup(typeof(SignalR.Demo.OwinStartup))]
 2 
 3 namespace SignalR.Demo
 4 {
 5     public class OwinStartup
 6     {
 7         public void Configuration(IAppBuilder app)
 8         {
 9             app.MapSignalR();
10         }
11     }
12 }
View Code

  接下來,在原有的Web API項目中Controller里面添加一個支持SignalR的基類~  

 1     /// <summary>
 2     /// SignalR ApiController
 3     /// </summary>
 4     /// <typeparam name="THub"></typeparam>
 5     public abstract class ControllerWithHub<THub> : ApiBase
 6         where THub : IHub
 7     {
 8         private readonly Lazy<IHubContext> _hub = new Lazy<IHubContext>(
 9             () => GlobalHost.ConnectionManager.GetHubContext<THub>()
10             );
11 
12         protected IHubContext Hub
13         {
14             get { return _hub.Value; }
15         }
16     }
View Code

  然后寫了一個類似於Hello World的Hub。

 1     [HubName("message")]
 2     public class MessageHub : Hub
 3     {
 4         public void Hello()
 5         {
 6             Clients.Others.addMessage(string.Format("用戶:{0},進入聊天室!", Context.ConnectionId));
 7             Clients.Caller.addMessage("Welcome!");
 8         }
 9 
10         public override Task OnConnected()
11         {
12             return base.OnConnected();
13         }
14 
15         public override Task OnDisconnected(bool stopCalled)
16         {
17             return base.OnDisconnected(stopCalled);
18         }
19 
20         public override Task OnReconnected()
21         {
22             return base.OnReconnected();
23         }
24     }
View Code

  最后就是API接口啦~

 1     public class MessageController : ControllerWithHub<MessageHub>
 2     {
 3         private static readonly List<string> MessageList = new List<string>
 4         {
 5             "Init Message",
 6             "Second Message"
 7         };
 8 
 9         [HttpGet]
10         [Route("~/message/list")]
11         public List<string> List()
12         {
13             lock (MessageList)
14             {
15                 return MessageList;
16             }
17         }
18 
19         [HttpPost]
20         [Route("~/message/send")]
21         public object Send([FromBody] string message)
22         {
23             var id = "id".Form("");
24             var msg = "message".Form(string.Empty);
25             lock (MessageList)
26             {
27                 MessageList.Add(message);
28                 message = string.Format("id:{2}[{0}]:<br/>{1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), msg,
29                     id);
30                 Hub.Clients.AllExcept(id).addMessage(message);
31                 Hub.Clients.Client(id)
32                     .addMessage(string.Format("我[{0}]:<br/>{1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), msg));
33                 return new {status = "success"};
34             }
35         }
36     }
View Code

  測試接口就算是基本完成啦~

  再來看下客戶端的調用  

 1 <!DOCTYPE html>
 2 <html>
 3 <head lang="en">
 4     <meta charset="UTF-8"/>
 5     <title></title>
 6     <link rel="stylesheet" href="../css/amazeui.min.css"/>
 7     <style>
 8         .msg-list {
 9             min-height: 230px;
10             border: solid 1px #eee;
11             width: 1000px;
12             margin: 10px auto;
13             height: 300px;
14             overflow-y: scroll;
15         }
16     </style>
17 </head>
18 <body>
19 <div class="msg-list">
20 </div>
21 <div class="am-panel" style="width: 1000px;margin: 10px auto;">
22     <textarea class="am-text-primary" style="width: 500px"></textarea>
23     <button class="btn-message am-btn am-btn-primary">Say</button>
24 </div>
25 <script src="../js/jquery.min.js"></script>
26 <script src="../js/jquery.signalR-2.2.0.min.js"></script>
27 <script type="text/javascript" src="http://localhost/signalr/hubs"></script>
28 <script>
29     (function ($) {
30         var messageHub = $.connection.message;
31         $.connection.hub.logging = true;
32         messageHub.client.addMessage = function (msg) {
33             $(".msg-list").append('<div>' + msg + '</div>');
34         };
35         $.connection.hub.start().done(function () {
36             messageHub.server.hello();
37             $(".btn-message").click(function () {
38                 var msg = $(".am-text-primary");
39                 $.ajax({
40                     url: "http://localhost/message/send",
41                     data: {id: $.connection.hub.id, message: msg.val()},
42                     type: 'Post',
43                     success: function () {
44                         msg.val("");
45                     }
46                 });
47             });
48         });
49     })(jQuery);
50 </script>
51 </body>
52 </html>
View Code

  本以為可以很輕松的搞定這個Demo,但是~

  第一回合:

  SignalR完全沒有加載~

  

  路徑問題,小Case啦~

  在頁面JS中手動指定SignalR服務地址

$.connection.hub.url = "http://localhost/signalr";

  但是問題並沒有想我想象那么簡單的解決~

  

  由於之前的接口,我已經很粗暴的加入了跨域支持,怎么還會存在跨域的問題呢~

  配置如下:  

 1   <system.webServer>
 2     <httpProtocol>
 3       <customHeaders>
 4         <add name="Access-Control-Allow-Origin" value="*" />
 5         <add name="Access-Control-Allow-Methods" value="GET,POST" />
 6         <add name="Access-Control-Allow-Headers" value="content-type" />
 7       </customHeaders>
 8     </httpProtocol>
 9     <handlers>
10       <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
11       <remove name="OPTIONSVerbHandler" />
12       <remove name="TRACEVerbHandler" />
13       <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
14     </handlers>
15   </system.webServer>
View Code

  於是乎,查閱了下官方說明~發現SignalR有着自己的跨域組件Microsoft.Owin.Cors,那就加上吧~在Nuget上找到它,並添加到項目中,稍微修改下啟動設置:

 1 [assembly: OwinStartup(typeof(SignalR.Demo.OwinStartup))]
 2 
 3 namespace SignalR.Demo
 4 {
 5     public class OwinStartup
 6     {
 7         public void Configuration(IAppBuilder app)
 8         {
 9             app.Map("/signalr", map =>
10             {
11                 map.UseCors(CorsOptions.AllowAll);
12                 var hubConfiguration = new HubConfiguration
13                 {
14                     EnableJSONP = true
15                 };
16                 map.RunSignalR(hubConfiguration);
17             });
18         }
19     }
20 }
View Code

  但是,問題同樣存在~,這下就糾結了,這是什么個情況!在測試了各種方式之后,問題依舊!於是乎,改用Chrome來看下,一下就找到問題出在哪了。

  PS:Chrome真不愧是程序員的最愛啊!來張Chrome里面的報錯信息:

  

  這下錯誤信息就很清晰了,api返回的Access-Control-Allow-Origin居然是"http://localhost:63342, *",很明顯是SignalR的跨域組件和我之前粗暴的配置文件沖突了~

  那么只有換一種方式了~

  1.注釋掉配置文件中system.service里面的跨域支持相關的配置;

  2.添加Web API的跨域支持組件System.Web.Cors;

  3.在WebApiConfig中開啟跨域支持;

   config.EnableCors(); 

  4.在Controller中配置跨域屬性。  

[EnableCors("*","*","*")]
public class MessageController : ControllerWithHub<MessageHub>

  大功告成啦,再來測試下。

  

  成功跨域!~

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM