首页 > 代码库 > SignalR 和跨域问题 (ASP.NET)

SignalR 和跨域问题 (ASP.NET)

SignalR 有三块,

Server:
  Hub Server
  Custom Message Sender Service (Sender Proxy)

Client:
  Message Sender
  Message Receiver

技术分享

1. Hub Server & Sender Proxy Service
Hub Server 和 Custom Message Sender Service 在一个app中,“Custom Message Sender Service” 通过api直接控制Hub Server行为。(connection filtering, status, deliver message, etc )

2. 跨域
Sender, Receiver 和 Hub Server 可能都不在同一个域里。 会有跨域访问的问题。

3. 发消息的客户端
可以有两种方式发消息给Hub Server
  3.1 通过hubproxy.server.func(), 走的是signalR的persistent connection. (这种情形下一般只发一次消息,persistent connection占用了资源,浪费。 当然也可以建立完释放,persistent connection -> send message -> close persistent connection)。 这种模式下,跨域由SignalR Hub框架控制。
  3.2 通过ajax调用 "Custom Message Sender Service (Proxy)", 由Proxy调用Hub的服务。 这种结构更干净,发送者和SignalR connection没有关系, 走单独Http请求。 跨域由 "Custom Message Sender Service (Proxy)", 一般由服务端暴露服务(MVC/WebAPI). 但是MVC和WebApi的跨域管理不太一样。(见“MVC和WebApi的跨域管理”)
  3.3 有一种情况可以考虑用signalR的persistent connection发消息。 就是发消息的人,同时又是收消息的人。 那么一个persistent connection可以同时用来收发消息。

4. 收消息的客户端
这个只有一种,“持久连接,保持监听”。
  4.1 SignalR保持连接的方式由协议本身定义,由服务器和浏览器支持。 WebSocket / iFrame / longPooling。 具体可google
  4.2 WebSocket的效率要比其他高很多,比较reliable,比如disconnect, 其他方式可能不能直接反应客户端关闭的动作,需要等待超时发生触发disconnect. 代码和排错上有时比较费解。
  4.3 【Hub Server服务端】微软的WebApp PaaS上, by default, "WebSocket"不enable。要去管理界面把它打开。

5. Hub Server
  5.1 Owin中map /signalr 作为SignalR routing的开始
     (https://docs.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api/hubs-api-guide-server#tracing If you are adding SignalR functionality to an ASP.NET MVC application, make sure that the SignalR route is added before the other routes. For more information, see Tutorial: Getting Started with SignalR 2 and MVC 5.)
  5.2 跨域访问由Owin框架自己处理, 如果再由IIS web.config来控制CORS, 会产生冲突。
  5.3 Hub Server中的方法,只是个签名,具体实现不重要(无需写实现,除非客户端用hub.server.func()发消息)。 主要作用
    5.3.1 生成客户端代理类。
    5.3.2 让代理类绑定本地方法,以被服务端调用

 6. Sender Message Proxy
  6.1 如果用MVC做,有个很大的问题就是,跨域。 
    6.1.1 如果浏览器是IE, 问题不是很大。 貌似preflight request 被省略了。 request 被直接提交到MVC层, 可以自定义Action属性, 加入”Access-Control-Allow-Origin“。
    6.1.2 如果浏览器是Chrome, "preflight request"不会被忽略. http option verb会发过来。 这个时候MVC就有问题了, 无论从owin还是application_beginRequest,都无法获得这个request,无法修改http头,使得CORS通过。 (猜想是不是MVC的routing,所在的层次太高,获得不了这种request)
    6.1.3 对于simple CORS, IE 和 Chrome (Chrome 无须preflight), 如果用Get/Post传数据,由于不走preflight, 自定义Action属性修改http header是可行的。 但是如果用ajax, 有content type header的话, 那就必须要走preflight.

7. IIS setting, web.config 
  实际使用当中,如果使用MVC 和 SignalR, 客户端用ajax post json (content type = application / json), 那么需要考虑直接使用直接从服务器设置应用CORS。 这个时候SignalR的CORS需要被取消(//map.UseCors(CorsOptions.AllowAll) )

    <add name="Access-Control-Allow-Origin" value="https://localhost:44300" />
    <add name="Access-Control-Allow-Headers" value="http://www.mamicode.com/Content-Type" />
    <add name="Access-Control-Allow-Credentials" value="http://www.mamicode.com/true" />

 

CORS 介绍 

  一篇很好的文章介绍CORS:https://staticapps.org/articles/cross-domain-requests-with-cors/

  注意Simple CORS / Complex CORS 的区别

 

常用代码

var heartBeat = GlobalHost.DependencyResolver.Resolve<ITransportHeartbeat>();
var connectionAlive = heartBeat.GetConnections().FirstOrDefault(c=>c.ConnectionId == connection.ConnectionId);
if (connectionAlive.IsAlive)
{//Do whatever...}

 ======================================================

IHubContext context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
IHubContext context = GlobalHost.ConnectionManager.GetHubContext(“MyHub”);

======================================================

public class EnableCorsAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.RequestContext.HttpContext.Response.AddHeader("Access-Control-Allow-Origin", "*");
        base.OnActionExecuting(filterContext);
    }
}

 

SignalR 和跨域问题 (ASP.NET)