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 ...
随机推荐
- GUI容器之Panel
Panel //panel可以看成是一个空间,但不能单独存在 public class MyPanel { public static void main(String[] args) { Frame ...
- Git 系列教程(6)- 查看 commit 提交历史
查看提交历史 在提交了若干更新,又或者克隆了某个项目之后,如何查看提交历史 git log 官方栗子 运行下面的命令获取该项目: git clone https://github.com/scha 运 ...
- golang isPowerOfTwo判断是否是2的幂
iota.go strconv包 func isPowerOfTwo(x int) bool { return x & (x -1) } 了解n&(n-1)的作用如下: n& ...
- Lua io.lines()
前言# 从文章的题目可以看出,今天的内容是和文件的行相关的,其实这个函可以看成是一个文件读取函数,只不过文件读取的形式固定了,就是只能一行一行的读,接下来我们就一起来看看这个函数究竟要怎么使用. 内容 ...
- 简单操作:10分钟实现在kubernetes(k8s)里面部署服务器集群并访问项目(docker三)
前言 经过docker安装.k8s开启并登录,我们终于到 "部署k8s服务器集群并访问项目" 这一步了,实现的过程中有太多坑,好在都填平了,普天同庆. 在进行当前课题之前,我们需要 ...
- 前端框架VUE——数据绑定及模板语法
一.数据绑定 Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统: <div id="app"> {{ msg }} </di ...
- VS Code + WSL 搭建 RaspberryPi Pico 开发环境
前面老周写一堆 .NET 与树莓派相关的水文.其实使用的是.net的 IOT 库,并不只是树莓派,其他运行 Linux 的开发板都适用,只要有 GPIO 就行.老周好像在哪看到过,有 USB 转GPI ...
- VB自制计算器
使用visual basic编写. 绘制如下的按钮界面: 然后代码如下: Dim a, temp, ans As Integer Dim op As String Sub showans() Text ...
- 【简单数据结构】二叉树的建立和递归遍历--洛谷 P1305
题目描述 输入一串二叉树,用遍历前序打出. 输入格式 第一行为二叉树的节点数n.(n \leq 26n≤26) 后面n行,每一个字母为节点,后两个字母分别为其左右儿子. 空节点用*表示 输出格式 前序 ...
- html回车键搜索内容
window.onkeydown = function(e){ // elsinput是搜索框 if(e.keyCode === 13 && elsinput.is(':focus') ...