SingnalR 从开发到生产部署闭坑指南
前天倒腾了一份[SignalR在react/go技术栈的实践], 步骤和思路大部分是外围框架的应用, 今天趁热打铁, 给一个我总结的SignalR避坑指南。
1.SignalR 默认协商
不管是.NET客户端还是JavaScript客户端,构建连接时都存在一个默认配置:SkipNegotiation=fasle,负负得正就等于要求协商,这个默认配置的完整含义是 建立SignalR连接时,客户端要求协商传输方式。
对应产生下图:

小技巧:如果你确定你的网络环境能稳定的走websocket传输, 为了快速建立实时通信,可跳过协商请求(设置SkipNegotiation=true), 毕竟每次刷新页面,react组价都会重新加载,重新协商再传输 费时费力。
const connection = new HubConnectionBuilder()
.withUrl(process.env.REACT_APP_APIBASEURL+"realtime", {
skipNegotiation: true,
transport: HttpTransportType.WebSockets
})
.withAutomaticReconnect()
.withHubProtocol(new JsonHubProtocol())
.configureLogging(LogLevel.Information)
.build();
注意: SkipNegotiation=true,仅限于客户端的传输方式指定为 websocket, 其他方式均会报错。
2.SignalR 传输协商是fetch请求
跟ajax一样,fetch请求也是浏览器脚本的一种,所以很明显也会涉及跨域,标准的CORS方案依然对其有效。
http://localhost:9598/realtime/negotiate?negotiateVersion=1
Post请求
有自定义的请求头 X-Requested-With, X-Signalr-User-Agent
很明显,这又会触发预检Option请求

故你还需要在使用 CORS Middleware时允许这几个自定义请求头。
// 下面是Go github.com/rs/cors package 支持CORS的代码
c := cors.New(cors.Options{
// AllowedOrigins: []string{"http://localhost:3000","http://rosenbridge.17usoft.com"},
AllowOriginFunc: func(origin string) bool {
return true
},
AllowedMethods: []string{"POST", "GET", "OPTIONS", "PUT", "DELETE"}, // 下面要加上signalr传输协商要用到的自定义请求头
AllowedHeaders: []string{"Content-Type", "x-requested-with", "x-signalr-user-agent"},
AllowCredentials: true,
Debug: cfg.Log.Debug,
})
3. websocket也有同源限制
ws://localhost:9598/realtime?id=aoSD_WZhqbRfPyXVTYsHig==
WebSocket也有同源限制,但是标准的CORS对其无效,因为CORS解决是HTTP脚本请求的跨域问题,WebSocket说到底不算http协议。
浏览器依旧会为我们携带Origin标头,所以服务端需要验证这些标头,确保只允许来自预期来源的WebSocket。
// 以下是.NET Core 针对websocket同源限制做出的跨域策略
var webSocketOptions = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(120),
};
webSocketOptions.AllowedOrigins.Add("https://client.com");
webSocketOptions.AllowedOrigins.Add("https://www.client.com");
app.UseWebSockets(webSocketOptions);
btw, 我使用的GO SignalR库不支持WebSocket跨域, 我提了一个PR, 已经成功合并,兴奋,这是我首次向开源项目提MR且获得通过的项目。
不仅signalr协商请求需要 CORS, 真正传输的websocket协议也有跨域一说,我使用了一个库,没有做到cors,我提了一个mr,成功合并。
4. 部署生产后,需要nginx支持
按照默认配置,一般会先协商,再使用websocket传输。
部署到生产之后,协商后优先使用WebSocket模式, 但是传输失败了, 自动降级为服务器发送事件SSE模式,传输成功。

浏览器开发者工具看不出啥端倪, 使用Fiddler抓包发现 400 状态码

网上搜索了一下,可能是生产的nginx不识别websocket标头。
在nginx配置里面添加如下配置就可以了
location / {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
关注本公众号的5000+筒靴们应该都知道,本号一直不遗余力的输出原创技术、职场心得,内容说不上什么耳目一新、醍醐灌顶,但号主的技能点一直在进化,本次建立了一个[码甲哥高质量交流群],希望能和童鞋面对面成长(真诚脸图片)。

SingnalR 从开发到生产部署闭坑指南的更多相关文章
- RabbitMQ生产部署指南
像RabbitMQ这样的数据服务通常有许多可调参数.一些配置对开发有很大的意义,但并不适合生产,本指南旨在为此提供帮助 虚拟主机 例如,在单租户环境中,当您的RabbitMQ集群专门为生产中的单个系统 ...
- Linux 桌面玩家指南:18. 使用 Docker 隔离自己的开发环境和部署环境
特别说明:要在我的随笔后写评论的小伙伴们请注意了,我的博客开启了 MathJax 数学公式支持,MathJax 使用$标记数学公式的开始和结束.如果某条评论中出现了两个$,MathJax 会将两个$之 ...
- 2016/5/6 thinkphp ①框架 ② 框架项目部署 ③MVC模式 ④控制器访问及路由解析 ⑤开发和生产模式 ⑥控制器和对应方法创建 ⑦视图模板文件创建 ⑧url地址大小写设置 ⑨空操作空控制器 ⑩项目分组
真实项目开发步骤: 多人同时开发项目,协作开发项目.分工合理.效率有提高(代码风格不一样.分工不好) 测试阶段 上线运行 对项目进行维护.修改.升级(单个人维护项目,十分困难,代码风格不一样) 项目稳 ...
- 一个gulp用于开发与生产的示例
gulp是一款流行的前端构建工具,可以帮我们完成许多工作:监听文件修改.刷新浏览器.编译Less/Scss.压缩代码.添加md5.合并文件等.gulp的配置和使用特别简单,学习gulp过程中顺便写了一 ...
- webpack开发与生产环境配置
前言 作者去年就开始使用webpack, 最早的接触就来自于vue-cli.那个时候工作重点主要也是 vue 的使用,对webpack的配置是知之甚少,期间有问题也是询问大牛 @吕大豹.顺便说一句,对 ...
- kettle-数据源配置化-开发、生产采用不同配置
数据etl常用工具kettle. 1.说明: kettle-数据源配置化:是指kettle的数据源连接信息全部或者部分从配置文件中读取(如果是数据库的资源库,那么资源库也可以配置化). 2.优点: 1 ...
- spring boot--日志、开发和生产环境切换、自定义配置(环境变量)
Spring Boot日志常用配置: # 日志输出的地址:Spring Boot默认并没有进行文件输出,只在控制台中进行了打印 logging.file=/home/zhou # 日志级别 debug ...
- 免费开源《OdooERP系统部署架构指南》试读:第一章 Odoo架构概述
文/开源智造联合创始人老杨 本文来自<OdooERP系统部署架构指南>的试读章节.书籍尚未出版,请勿转载.欢迎您反馈阅读意见. 从web浏览器到PostgreSQL,多层与其他层交互以处理 ...
- Laravel本地环境搭建:Homestead开发环境的部署
Laravel框架在php开发过程是不断进行优化的,当然也包括了本地环境的开发,下面我们就来具体看看laravel框架中的Homestead 开发环境的部署内容. 首先白以下几个概念 VirtualB ...
随机推荐
- vue 嵌入倒计时组件( 亲测可用 )
由于花费了我不少时间才找到的组件,所以记录一下,后面方便自己好找一些,也算是分享出来给各位前端一起用. npm 下载npm install vue2-flip-countdown --save tem ...
- 将两个byte型拼接成16位二进制,再转化为十进制
short s = 0; //一个16位整形变量,初值为 0000 0000 0000 0000 byte b1 = 1; //一个byte的变量,作为转换后的高8位,假设初值为 0000 0001 ...
- centos7关于防火墙的一些操作
防火墙相关 # 检查防火墙状态 systemctl status firewalld # 开启防火墙 systemctl start firewalld # 关闭防火墙 systemctl stop ...
- 使用tasker定时打开一耳光应用
比如现在要做一个:到9点钟自动打开"酷狗音乐"1.任务→点击"+"→填写"打开酷狗音乐"→点击"√"2.任务编辑→点击& ...
- 利用k8s yaml配置文件起一个http能够让外部访问
1.首先建一个http的Deployment apiVersion: apps/v1 #版本信息 kind: Deployment #文件类型 metadata: #Deployment资源的元数据信 ...
- 【PHP数据结构】完全二叉树、线索二叉树及树的顺序存储结构
在上篇文章中,我们学习了二叉树的基本链式结构以及建树和遍历相关的操作.今天我们学习的则是一些二叉树相关的概念以及二叉树的一种变形形式. 完全二叉树 什么叫完全二叉树呢?在说到完全二叉树之前,我们先说另 ...
- PHP的可变变量与可变函数
什么叫可变.在程序世界中,可变的当然是变量.常量在定义之后都是不可变的,在程序执行过程中,这个常量都是不能修改的.但是变量却不同,它们可以修改.那么可变变量和可变函数又是什么意思呢?很明显,就是用另一 ...
- mysql将数据导入到另外一张操作
insert into ydcq_member_class (ClassId,signcount,UserId) select 64,2,`员工编号` from `学员名单`
- Java面向对象系列(7)- 什么是继承
继承 继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模 extands的意思是"扩展".子类是父类的扩展 JAVA中类只有单继承,没有多继承 继承是类和类之间的一种关系. ...
- Jmeter系列(27)- 常用逻辑控制器(6) | 如果(if)控制器If Controller
如果(if)控制器(If Controller) 在实际工作中,当使用JMeter做性能脚本或者接口脚本时,当遇到需要对不同的条件做不同的操作时,我们可以使用JMeter中if控制器来实现 if控制器 ...