Normal view

There are new articles available, click to refresh the page.
Before yesterdayHran 的博客

胖胖「近」照

By: Hran
1 December 2023 at 22:13
大概是由近到远,或者是不分先后

要搬家了,但是关我啥事?

要搬家了,但是关我啥事?

IMG_4647.jpeg

搬家路上:好吧,确实关我事,有点害怕捏

IMG_4734.jpeg

这是哪,我领地呢?

IMG_1831.jpeg

你叫辣么大声干嘛~

IMG_0868.jpeg

抠脚大猫

IMG_4078.jpeg

O^O

IMG_4074.jpeg

睡着了

IMG_1907.jpeg

IMG_1923.jpeg

快快关灯,睡觉了

IMG_1085.jpeg

IMG_0145.jpeg

阳光不错,喵喵喵喵

IMG_1050.jpeg

吃手手

IMG_2140.jpeg

看什么看,再看揍屎你

Mirages - 简洁的 Typecho 主题

By: Hran
1 October 2016 at 23:40

这是一款简洁的单栏的适合阅读的 Typecho 主题,适合放大段大段的文字、代码。因为考虑到一些阅读的舒适性的问题,所以栏宽设置的并不宽。

详细的介绍和说明在主题售价及购买方式后面

主题售价

系列售价
Mirages For Typecho88元

购买方式

自助购买
咨询作者后购买
前往商店购买主题

支持使用支付宝扫码微信扫码付款(支持花呗及信用卡)。
购买完成后即可下载主题,安装使用,无需人工介入。

如果您无法或不喜欢自助购买,则可以通过下面的联系方式联系作者购买主题。

作者联系方式:

  • QQ:278448087
  • 微信:Namekuji
  • 支付宝:baibaidd@foxmail.com
  • 屏幕右下角的联系按钮(可能会回复不及时)

如果可以的话,建议使用 QQ,毕竟微信添加好友没有提醒。

Mirages For Typecho主题不适用于 WordPress 等其他非 Typecho 博客程序

购买主题后,您将获得主题的最新版本,后续所有的升级版本均为免费。主题需要绑定域名授权,一个订单最多可以自行绑定 3 个域名授权(超限后需要联系作者添加),授权可以在主题小商店中自行绑定。本主题购买后不得分发、转售。因主题是可复制的商品,在代码发出后不予退款,敬请谅解。

售后服务包括您在使用本主题时遇到的问题,但不包含主机及 Typecho 安装、Typecho 的使用教程,也不提供主题的个性化定制服务。

主题介绍

预览图 - 导航栏

主题提供了两种样式的导航栏:顶部导航栏和侧边导航栏,可以根据喜好在设置中自由切换。

顶部导航栏
侧边导航栏
顶部导航栏
侧边导航栏 - 收起
侧边导航栏 - 展开

预览图 - 文章列表

主题提供了两种样式的文章列表:卡片式文章列表和内容式文章列表,同样可以根据喜好在设置中自由切换。

卡片式文章列表
内容式文章列表
卡片式文章列表
内容式文章列表

预览图 - 其他内容页

文章目录树
文章页
评论区
文章目录树
文章页标题可以设置显示在文章主图中,或仅显示普通样式的标题。

以下为二者的区别:

标题显示在头图中
文章页

评论区

功能与特性

功能与特性

  1. 简洁(这也是 Mirages 主题一直以来的理念)
  2. 简洁,但不简陋。主题功能非常全面
  3. 响应式设计,适配各种大小屏幕
  4. 两种文章列表样式自由切换(目前有卡片式和文章详情式)
  5. 两种导航栏样式自由切换(目前有顶部导航栏和侧边栏模式)
  6. 针对 Retina 设备优化
  7. 针对移动设备优化
  8. 针对 macOS 优化
  9. 支持 PJAX 无刷新操作
  10. 黑、白及黑白混搭多种基础色调
  11. 漂亮的夜间模式,根据用户当地时间自动切换(22 点到第二天凌晨 6 点前为夜间模式),护眼从我做起
  12. 适配 macOS 的 Dark Mode
  13. 自定义主题色调
  14. 自带卡片式友链样式
  15. 支持数学公式及流程图、时序图、甘特图的显示
  16. 支持短代码,快速生成特殊样式,如按钮、标签、回复可见等。
  17. 漂亮的文章目录树显示
  18. 自带代码高亮功能
  19. 响应式表格设计
  20. 简单好用的表情解析功能(评论、文章中均可使用)
  21. 强大友好的后台配置选项
  22. 与主题适配的专用插件
  23. 简单易用的版本在线更新功能
  24. 多语言支持
  25. 其他很多主题应有的功能

主题安装及使用帮助

新用户必看
完整帮助文档
这里的两份文档包含了新安装主题时需要的进行的配置和绝大多数新用户可能会遇到的问题,且内容不长,建议阅读。

快速上手 - 安装配置文档

常见问题列表

历史更新记录

稳定版
开发版
所有更新日志
这里仅列出了最近稳定版的更新日志,所有更新日志可以在右侧的「所有更新日志」标签页中查看。

关于域名绑定的重要内容

7.10.2 开始,主题将启用域名绑定功能,在更新前您需要先前往主题小商店完成域名登记。

更多详细信息请参考:了解如何登记域名及如何无缝的更新到新版本

2023-04-23 日更新 v7.10.6 最新稳定版
  • 更换评论 IP 归属地服务
2023-03-14 日更新 v7.10.5
  • 解决 Typecho 1.2 版本下,评论提交不成功时,错误消息不展示的问题
  • 解决 Typecho 1.2 版本下,阿鲁表情包会带有反斜线的问题
  • Vditor 本地化
  • 适配 PHP 8.1
  • 添加自定义字段 targetBlank:支持导航栏的独立页面在新窗口中打开
  • 添加评论 IP 归属地展示

当前版本更新后进入Mirages插件设置页面会报错,完成更新后请前往插件列表页面禁用并重新启用 Mirages 插件以解决此问题

当前博客使用的主题版本为「最新开发版」,博客中的所有功能均可通过设置实现。

这里仅列出了最新开发版的更新日志,所有更新日志可以在右侧的「所有更新日志」标签页中查看。

开发版主题仅可通过插件自动更新的形式使用。如要更新开发版主题,您需要到插件中将「仅接受正式版更新」选项置为「否」,保存后执行插件里的「更新主题和插件至最新版」操作按钮即可。

暂无 开发版
  • 暂无

LG UltraFine 4K 开箱

By: Hran
18 February 2020 at 18:17

由于众所周知 (Qiong) 的原因,我这 5 年前的笔记本还是没办法换掉,但不知道为什么,手一直觉得很痒,于是,在 2020 年的某一天,我终于有一台外接显示器了!

所以,2020 年的今天,我为什么买了一个边框可以停航母的显示器呢,主要还是不想外接显示器以后,显示效果比原来的还差,如果新的显示器只有大这一个优点的话,那想想就觉得不香了。本来的话,老的 4K 版本尺寸过小,且必须要雷电 3 接口才能正常使用,所以综合还是考虑买 LG UltraFine 5K ,当然得换新款 MBP,所以才一直没下手去买。不过后来发现 4K 版本更新成常规 4K 分辨率了,这样我这台老古董配个雷电转换器也能用,然后尺寸的话,新版的 23.7 寸也比老版 4K 的 21.5 寸要好接受的多,虽然 PPI 降到了 186,但思考了半天最终决定还是先买它了。

开箱(伪)

LG UltraFine 包装箱

话说这个箱子是真的大,体积大概是朋友 25 寸显示器包装箱的 2 倍左右,打开箱子以后,显示器是组装好的,掏出来接上电源就能用了。

因为我本子是 2015 款的,只有雷电二的接口,所以这里还要配上雷电三转雷电二的转接器,然后用雷电二的线连上就可以了。

桌面图

完成版桌面图,手机直出(因为并没有其他设备🥺)凑活着看吧。

简单说下感受吧。

  • 显示效果的话,其实还是超出预期的,本来以为 PPI 降到 180 多会有一点影响,但其实考虑到眼睛与显示器的距离的话,仍然是够用的(因为我没看出啥差别,甚至因为色域及亮度的提升,显示效果还更棒了)
  • 显示器尺寸的话,对于我来讲算是刚刚好的,这个看个人了。
  • 性能的话,因为是 15 款的老本子连接,所以之前一直担心本子性能不够,可能会有带不动的问题,现在感觉的话还好,一切正常,日常使用的话,本子温度都在 40 ~ 60 度左右,还是可以接受的。
  • 亮度的话最高 500 尼特,对我来说完全够了,自动亮度调节是可以使用的,不过有时会有点飘,这个我把锅安在老本子头上了(找个机会把它换掉)
  • 对角线方向可视角度有点糟心,画面会明显开始泛白,勉强忍了
  • 机身没有任何物理按键,连接上就可以使用,显示器自带音箱,能用级别;调整音量及显示亮度全都要靠快捷键或者设置。
  • 和 5K 版本比,缺少了麦克风和摄像头,主要是缺麦克风的话对我这种把本子扔桌子下面的人来讲有点不方便,QQ 微信语音什么的就只能用手机接了。不过换来的好处是四边等宽(都能停航母)
  • Magic TrackPad 和我老本子连接时,时不时的会有「卡顿」的现象,试了下朋友的 18 款本子没有这个情况,所以锅自然也安在老本子上了,然后平时就以有线的形式使用了。

整体还是挺满意的

最后

猫还活着。。。恩。。。

DA84A44CE9CFCDBC503A3EA7A3A47477.png

最近捡了只猫

By: Hran
9 June 2019 at 00:46

上个月五一回家的时候,朋友捡到了两只流浪猫,然后我领养了其中一只。据说当时下雨,天比较冷,两只跑到屋里躲雨,然后就被抓到了,只有一个多月大,超级小的两只。姐姐是一只奶牛猫,他的话,就是常见的狸花猫,不过我也更喜欢这种花色。说来惭愧,人家当初可是住景区的,有山有湖的,结果现在被我禁锢到了这小破房子里。

因为是做高铁回上海的,没办法带宠物,所以又上淘宝办了托运,到上海以后又去接的他,整体的话还算比较顺利,但对猫来说还是太受罪了。

到家了

刚到家里的时候,因为这货比较胆小,所以前几天都是躲起来的,完了还不停地叫,24 小时不间断的那种,嗓子都要哑了的那种,当时感觉真的是要绝望了哈哈

躲到床头柜下面

躲到柜子里

不过一起来看到他的样子就不想再说什么了,忍几天就差不多了。

然后差不多 3 天以后就不怎么叫了,然后玩具也到了,后面就一起 (chuang) (tou) (beng) (di) 了。

这么多天下来,每天给他喂食,换水,铲猫砂,最重要的还要陪他玩,教育他不要咬我什么的,感觉像养了个孩子一样,痛苦并快乐着。不过这家伙长得是真的丑,翻了一遍相册,就没一张拿得出手的::quyin:hematemesis::

长大了

一个月下来,感觉这货长的是真的快,长度翻了应该有一倍。虽然平时也有种「这货好像变大了」的感觉,不过今天翻相册的时候才发现,原来变化已经这么大了。。。

社会你猫哥来了

社会你猫哥来了

社会你猫哥怂了

社会你猫哥怂了

社会你猫哥睡了

社会你猫哥睡了

生病了

刚到家的时候,也有点小毛病,比如右眼发炎什么的,用点眼药水什么的就好了,后来有一天,发现他屁股那里有一块皮肤特别的硬,所以赶紧带着去医院看了下,小医生看完说是猫癣,过几天去打疫苗的时候,又让老医生看了一遍,说应该不是猫癣,可能是被什么虫子之类的咬了,那块皮肤发炎了,反正不管怎么说,这耻辱圈是摘不下来了🤣🤣🤣

蓝瘦

蓝瘦

然后今天又去了一趟医院,之前的伤口差不多已经好了,然后又发现了一处,好了,终于如愿以偿的得了猫癣了::quyin:fue::

本来以为能拿下来的耻辱圈又要继续戴一阵子了🤣🤣🤣

香菇

香菇

没有了

好了,没有了,憋看了

别拍了

关于 MathJax 渲染中断的问题

By: Hran
14 April 2019 at 17:38

今天发现页面在通过 PJAX 的方式加载的时候,包含 MathJax 的文章渲染过程中会出现:

Error Preparing CommonHTML output (postProcess)

文章中的数学公式也只渲染了一半,这个问题的可能的原因是 MathJax 渲染过程中,由外部修改了 DOM 导致的。

具体到我这边的话,主要是通过 AJAX 的方式加载一个表情列表,在加载完成后,会将文章中的占位符替换为相应的表情,而替换的时候 MathJax 还没有渲染完成,所以导致了报错。

解决方法:

将表情最终的替换过程加到 MathJax 的队列中:

MathJax.Hub.Queue(() => {
    // 会操作 DOM 的代码
});

说说主题的云存储优化功能

By: Hran
23 March 2019 at 22:31

这个功能放出来大概也有两年的时间了,但是一直没有详细的介绍它,这两天我想了一下,主要原因大概有两个,一个是懒,另一个是懒。

不过随着主题的更新,云存储优化功能也越来越完善了,所以,趁着现在有空,刚好拉出来讲一讲。

功能简介

先来说说这个功能是干什么的吧。

云存储是第三方托管的在线存储服务,其在全国乃至世界范围内都有大量的 CDN 节点,用户在访问相关资源时会被分配到就近的 CDN 节点,因此在访问速度上通常比我们单台服务器要好很多。常见的云存储服务有七牛云存储、又拍云存储、阿里 OSS、腾讯云存储等,鉴于本主题就支持这四个,所以其他的就当做不常见吧::quyin:1huaji::

到了主题这边,简单来说,云存储优化功能,就是可以把博客上除了页面的 HTML 以外的静态资源全都通过云存储进行加载,包括主题的 JS、CSS、字体、表情图片文件,以及文章正文内图片、头图等图片资源(第三方的插件里的资源文件不包含在内),通过云存储加载这些静态图片,可以加速网页的访问速度,特别是对于一些服务器在国外的朋友。

这是云存储优化最基本的功能。在此基础上,主题还提供了以下功能:

1. 为文章中的图片自动转换合适的大小和格式

主题可以根据用户使用的设备,自动调整头图及文章内图片的大小,在保证图片质量不受影响的前提下,降低图片的尺寸,大大节约手机用户的流量,提高页面加载速度。

该功能可以在手机设备上加载小图,电脑设备上加载大图,因此,你在文章等其他地方使用图片时,丝毫不用在意图片尺寸对于页面加载速度及页面大小的影响,直接无脑上最大尺寸图片即可。什么?几 M 的原图?照样给你安排的明明白白。

2. 启用 WebP 图像格式

「启用 WebP 图像格式」功能可以在支持 WebP 的浏览器上使用 WebP 格式的图片,可以以较小的图片大小获得相同质量的图片(相较于 JPEG),进一步提升页面加载速度,减少页面大小。

WebP 可以用于 Chrome 等浏览器下,详细的浏览器兼容情况可以到 CANIUSE 上查看:

WebP浏览器兼容情况

关于 WebP 的更多介绍,可以参考这里:

3. 图像加载动画

图片加载动画就是图片在加载过程中的动画效果,包含了首页及文章主图、文章列表图片、文章内容图片等。

图片加载动画可以使图片在加载过程中也有较好的观感,在图片资源加载速度较慢时尤为明显。

另外文章内容图片在加载的过程中,可以根据图片大小生成占位,不会有图片在加载过程中导致的文章内容高度变化,避免了加载过程中的文章内容跳动等。

具体的效果可以看一下下面的视频:

使用说明

好了,说了这么多废话,关键的地方到了。

1. 设置镜像存储

镜像存储服务是一种快速的数据迁移和加速服务。可以帮助用户实现无缝数据迁移。它实现的功能是用户在访问镜像域名上的一个资源时,云存储会先检测该资源在云存储服务器上是否已经存储,如果有则直接返回,没有的话,会根据路径到源站(也就是你的博客)获取对应的资源,然后返回给用户。除了用户第一次访问时要回源速度稍慢,后面就会完整发挥 CDN 的优势,速度都会非常快。

举个栗子,假如我博客的域名是 https://get233.com,配置的镜像存储域名是 https://assets.get233.com,那么,在第一次访问下面这个链接时:

https://assets.get233.com/path/to/whatever/pig.jpg

镜像存储就会到下面这个地址获取内容, 然后返回给用户:

https://get233.com/path/to/whatever/pig.jpg

后面镜像存储已经存储了这张图片时,就会直接返回给用户,速度很快。

配置镜像存储的方法如下:

配置完成后,将配置的「CDN 镜像加速域名」复制然后准备填入后面的设置中。

后面的操作都非常简单,只要到主题外观设置里配置相应的设置就可以了。

需要注意的是:该功能依赖插件,因此必须安装并启用主题自带插件才可以使用。

2. 配置 CDN 镜像加速域名

前往 主题外观设置里 => CDN 镜像加速域名 ,配置 CDN 镜像加速域名:

CDN 镜像加速域名

这里的域名就填你第一步配置中的「CDN 镜像加速域名」。

3. 配置域名的云存储类型

前往插件设置中,配置如下选项:

插件设置

其中,自定义 CDN 域名的是告诉 Mirages 哪个域名是云存储域名,然后他是七牛还是又拍什么的。参照说明配置即可。

如果你使用的是又拍云存储,且你变更了又拍云存储的间隔标识符,则配置第二个设置,否则,第二个设置保持默认即可。

4. 开启云存储优化功能

前往 主题外观设置里 => 云存储优化 ,配置云存储优化功能:

云存储优化

这一步很简单,把需要的功能选项启用即可。

5. 图片加载动画

前往 主题外观设置里 => 图像加载动画 ,开启图像加载动画:

图片加载动画

啊,同上。开启了这个功能,图片加载动画就可以用了。

最后

最后就没有了,去试试功能好不好用吧::quyin:1huaji::

如何对 Java 中的集合进行去重

By: Hran
19 March 2019 at 15:32

整体去重

如果是普通的去重,就用最常见的 HashSet 就好:

Set<Employee> employeeSet = new HashSet<>(employeeList);
employeeList.clear();
employeeList.addAll(employeeSet);

或者使用 Java8 的 Stream API:

List<Employee> uniqueList = employeeList.stream().distinct().collect(Collectors.toList());

Employee 类需要实现 hashCodeequals 方法。

根据对象中的某个属性进行去重

例如:不重写 equals 方法的情况下,根据 Employee 的 id 字段进行去重处理

方式 1:

List<Employee> uniqueList = employeeList.stream().collect(
        Collectors.collectingAndThen(
                Collectors.toCollection(
                        () -> new TreeSet<>(Comparator.comparingLong(Employee::getId))
                ),
                ArrayList::new
        )
);

如果是依照两个字段进行去重,则重写 Comparator 方法即可。

方式 2:

HashSet<Object> idSet = new HashSet<>();
employeeList.removeIf(employee -> !idSet.add(employee.getId()));

参考

如何使网页自动适配 Mac 的 Dark Mode

By: Hran
15 March 2019 at 11:25

前段时间 Safari Technology Preview 中添加了对媒体查询 prefers-color-scheme 的支持,该特性可以帮助我们的网页自动切换到深色模式以适配 Mac 中的 Dark Mode(深色模式)。虽然目前 Mojave 10.14.3 版本的 Safari 中尚未添加该特性,但其将随着 Safari 12 在下个小版本(macOS 10.14.4, 10.13.6 and 10.12.6)更新中获得支持。目前最新版本的 Safari 及 Chrome 均已支持该特性。

prefers-color-scheme 可以检测到用户是否已请求操作系统使用浅色或深色主题,其有三个可选的选项:

  • no-preference:未能检测到用户的选择
  • light:用户倾向于使用浅色的主题
  • dark:用户倾向于使用深色的主题,例如用户开启了 Mac 中的深色模式

所以,在这里我们可以使用 prefers-color-scheme: dark 来检测用户是否开启了 Dark Mode。

开始使用

prefers-color-scheme 的使用很简单,和普通的 Media Query 一样:

/* 操作系统及浏览器未支持或用户未开启 Dark Mode */
body {
    background-color: white;
    color: black;
}

@media (prefers-color-scheme: dark) {
    /* 操作系统及浏览器支持且用户开启了 Dark Mode */
    body {
        background-color: black;
        color: white;
    }
}

这样,用户在平时看到的是白色背景,而开启了深色模式后,页面就会自动变为黑色背景。当然,夜间模式的适配需要做的工作还很多,需要你慢慢调整各元素的颜色等。

顺便说一下,目前版本中,如果使用 @supports (prefers-color-scheme: dark) 包裹上述 Media Query 代码,则可能不会生效,这个看上去应该是目前版本浏览器的 Bug。

通过 JavaScript 获取是否开启了深色模式 Dark Mode

很多网站已经有了夜间模式,使用 prefers-color-scheme 适配可能比较麻烦,那么可以使用 JS 获取用户是否开启了 Dark Mode (深色模式),然后按照旧的逻辑开启夜间模式。

这种模式有个缺点:无法像直接使用 Media Query 的情况一样,在用户开启深色模式的时候,已打开的浏览器页面会自动变为深色模式,只有刷新页面或新进入的用户才会使用深色模式,不过个人感觉体验上影响不大。

当然,JS Api 是没有的,这里只能通过迂回的方式通过 JS 获取。

评论区的大佬告诉我有

用过 matchMedia 方法可以直接判断浏览器当前是否倾向于使用深色模式:

let prefersDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (prefersDarkMode) {
    // 搞事情
}
监听事件

通过监听 matchMediachange 事件,可以在用户切换深色/浅色模式的时候,将浏览器中已打开的页面自动切换为系统对应的模式。

let media = window.matchMedia('(prefers-color-scheme: dark)');
let callback = (e) => {
    let prefersDarkMode = e.matches;
    if (prefersDarkMode) {
        // 搞事情
    }
};
if (typeof media.addEventListener === 'function') {
    media.addEventListener('change', callback);
} else if (typeof media.addListener === 'function') {
    media.addListener(callback);
}

参考

呐~ 自用 Typora 皮肤一个

By: Hran
23 April 2018 at 20:26

Typora 这个 Markdown 编辑器是我非常喜欢的一个了,很久以前就写过一篇文章介绍他。

目前见到的编辑器基本都是输入与预览分离的设计,像 Mou、MWeb 等等。而 Typora 最明显的优点也在于此。它将编辑与预览合并了,就像那些所见即所得编辑器一样,但又使用着 Markdown 简洁的语法,从而使我们不需要像传统的所见即所得编辑器一样,经常需要使用鼠标点击来调整各种格式。「虽然看上去不像」,但它确实是一款 Markdown 编辑器,一款优秀的 Markdown 编辑器,甚至我觉得,这才应该是一款 Markdown 编辑器所应有的样子。

这是我当时的评价,毫不夸张的说,为了它,我放弃了很多功能更加强大的 Markdown 编辑器。

关于 Typora 的更多介绍可以百度 Google 或看我以前写的一篇:《Markdown 编辑器:Typora

今天呢,放出来的是一款自用 N 久的皮肤,最开始的时候超懒,就只支持配备 Retina 显示器的 macOS,最近稍作修改,在 Windows 上效果也还可以,即便是低分屏。

皮肤风格的话,大概和 Mirages 主题很像,代码高亮什么风格都一样。所以这款皮肤的名字也叫 Mirages。

最初其实是有夜间模式的,但后面因为懒得更新,所以算是放弃了。

概览

原谅我懒,用了 MWeb 的介绍哈哈,MWeb 也是款功能超级强大的 Markdown 编辑器,而且有 iOS 版。

以上。

下载地址

Mirages For Typora

使用方法

下载解压后,将 mirages 文件夹和 mirages.css 文件丢进 Typora 的主题文件夹(可以在 Typora 的设置里找到)内,然后主题选择启用 Mirages 即可。

Mac 复制文件路径的快捷键

By: Hran
22 March 2018 at 23:02

复制文件路径这个快捷键个人感觉是非常的常用了,Mac OS X 在 10.11上才加上了这个快捷键,所以一直以来感觉都很少有人注意到。

快捷键为: option + command + C

选中一个文件后,按下快捷键 option + command + C,然后按下 command + V 或者 粘贴 粘贴就可以了。

此外,还有另一种操作方法:

选中一个文件,按住 option 键, 右键选择「将 xxxxx 拷贝为路径名称」即可。

吐槽

现在再补充这个快捷键是因为即使这个快捷键放出来两年多了,但感觉知道的人还是太少,刚刚尝试 Google 了一下,得到的答案基本还都是比较老的版本,很少有提到这个快捷键的,某乎上的问题也已经是很久以前的了(那个问题下有个两年前的我的回答😂😂😂),所以还是重新写一下,给小伙伴们再加深一下印象。

Nginx 反向代理后 Spring Boot 下 WebSocket 会无法连接的问题

By: Hran
27 January 2018 at 15:16

最近在瞎搞点东西,用上了Spring Boot 的 WebSocket (SockJS) 服务,但因为只有一台乞丐🐔,而且还挂着 PHP 博客,所以就让某汪帮忙用 Nginx 做反代,将来自某个子域名的请求全都转到 8080 端口的 Tomcat 上,以上为背景。

瞎搞的东西本地开发一直没有问题,但做了反代以后发现 WebSocket 连接不上了,浏览器端连接请求是报 403,而且服务器会有如下错误:

Handshake failed due to invalid Upgrade header: null

以及警告:

o.s.w.s.s.t.h.DefaultSockJsService: Origin check enabled but transport 'jsonp' does not support it.

这个很好解决,搜索一下就会有解决方案 —— 只要添加如下配置就好了(然而还是某汪帮我加的):

nginx.confhttp 模块添加如下内容

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

在反代的 location 设置中添加如下内容:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

配置完以上内容即可正常访问websocket,经过测试没有发现问题。

再然后,项目准备对外开放之前,想了下是要加 HTTPS 证书的,然后让某汪帮我搞了下看首页没啥问题以为 OK 了,就没做啥测试,结果第二天发现 WebSocket 又特么崩了,访问直接返回403,还以为某汪又改配置了?我看了一下配置文件,没变,还是之前的样子,相关配置也都在,但浏览器访问就是返回 403,同时 SockJS 报下面的异常(后续找的,当时的完整的找不到了,也不想再试一遍了。。。):

in a frame because it set multiple 'x-frame-options' headers with conflicting values ('deny, sameorigin'). falling back to 'deny'.

然后服务端报警告:

o.s.w.s.s.t.h.DefaultSockJsService: Origin check enabled but transport 'jsonp' does not support it.

同时过几秒后 Freemarker 会大量抛出异常(这个可能是我异常处理的锅):

java.lang.IllegalStateException: getOutputStream() has already been called for this response

最后经过测试发现是 HTTPS 的问题。HTTP 就木有问题,联想到反代以后 Tomcat 获取不到用户 IP 的问题,所以我觉得可能是 Tomcat 没有获取到正确的协议的问题,遂让某汪帮我加了X-Forwarded-Proto 头,为了避免后续的未知问题,把X-Forwarded-Port也加进来了。

Nginx 添加的配置如下:

proxy_set_header X-Forwarded-Port $Server_port;
proxy_set_header X-Forwarded-Proto $scheme;

同时,Spring Boot 配置文件里也要加上如下配置:

server: 
    use-forward-headers: true

这里多说几句,因为我用的是内嵌的 Tomcat 容器,所以只要在 Spring Boot 的配置文件里加上这个配置就好了,如果是打包成 War 包的形式,可以搜索并参考 Tomcat 在 Nginx 反代的情况下获取用户真实 IP 的做法。

如: Jetty/Tomcat + Nginx反向代理获取客户端真实IP、域名、协议、端口

另外,这里添加这一句配置是使用默认的 Forward 请求头,如果有需要使用自定义请求头的情况,可以使用如下配置(根据情况自定义即可):

server: 
    tomcat:
        remote-ip-header: X-Forwarded-For
        port-header: X-Forwarded-Port
        protocol-header: X-Forwarded-Proto
        protocol-header-https-value: https

配置完以后,WebSocket 就可以正常使用了😆

最后附上完整的 Nginx Location 配置:

location / {

    proxy_set_header Host $Host;
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Port $Server_port;
    proxy_set_header X-Forwarded-Proto $scheme;

    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

}

以上。

参考

  1. Spring WebSocket: Handshake failed due to invalid Upgrade header: null
  2. Jetty/Tomcat + Nginx反向代理获取客户端真实IP、域名、协议、端口
  3. Spring Boot Use Forward Headers

如何在 Typecho 开发版中直接输出 HTML 代码

By: Hran
28 October 2017 at 16:35
温馨提示:第一段开始直到正文标题前全是废话,可直接略过

在此之前,我一直不肯使用 Typecho 开发版的原因就是没办法在 Markdown 的模式下直接输出 HTML 代码了,导致很多插件或自己写的一些乱七八糟的东西不能用了。。。

前几天 Typecho 时隔两年的又一个 Beta版发布了,即使知道 HTML 的这个问题,还是按耐不住还是把博客程序换成了最新的 Beta 版1.1(11.10.24),并针对该问题提出了自己的解决方案并洋洋得意。。然鹅。。就在当晚,我闲着*疼逛 Typecho issue 的时候发现。。。我特么真是个辣鸡啊哈哈哈

因为开发版后面又换了一次 Markdown 引擎,而该引擎是 Joyqi 大佬在为SegmentFault开发的 Markdown 引擎的基础上发展而来的,而对于 SegmentFault的环境而言,直接输出 HTML 代码是极其不安全的,所以其引擎将其中的 HTML代码全部转义后输出,Typecho 开发版的Markdown 引擎自然也继承了这一特性。但对于个人博客而言却并不是这样,因此,大佬在后续的更新中添加了一个特权模式,也就是以下正文所说的东西。

正文

要在 Typecho开发版中使用特权模式直接输出 HTML 代码,只需要使用!!!将需要直接输出的代码包起来即可。

!!!
<p>Hran is S<span class="hiden">B</span></p>
!!!

如上,即可直接输出:

<p>Hran is S<span class="hiden">B</span></p>

例如:某些插件要求特殊的格式以便插件进行识别并输出特定的内容,但不巧的是,他们是用了会被转义的<>或其他类似字符,导致最终结果出来以后,插件并没有生效,那么,这种情况下便可以使用此特权模式,将插件的特殊内容进行包裹,以使插件正常工作。

!!!
<jw params="forgot"></jw>
!!!

参考

❌
❌