Pomelo.EntityFrameworkCore.MySql 升级到 7.0 后出现:
System.InvalidOperationException:“The 'sbyte' property could not be mapped to the database type 'tinyint(1)' because the database provider does not support mapping 'sbyte' properties to 'tinyint(1)' columns. Consider mapping to a different database type or converting the property value to a type supported by the database using a value converter. See https://aka.ms/efcore-docs-value-converters for more information. Alternately, exclude the property from the model using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.”
对比 6.0 生成的 DbContext.cs 发现缺少了 UseLoggerFactory,按旧版修改即可。
找到 OnConfiguring 方法,上方插入:
public static readonly LoggerFactory MyLoggerFactory = new LoggerFactory(new[] {
new DebugLoggerProvider()
});
在 OnConfiguring 方法中将:
optionsBuilder.UseMySql("连接字符串");
改为:
optionsBuilder.UseLoggerFactory(MyLoggerFactory).UseMySql("连接字符串");
错误 BLAZOR102 The scoped css file was defined but no associated razor component or view was found for it.
错误 BLAZOR106 The JS module file was defined but no associated razor component or view was found for it.
解决方法:将相关文件从项目中排除,再包括到项目中即可正常生成。
若仍报错,备份这些文件,删除并新建文件,复制代码进去尝试生成。
若仍报错,通过注释代码的方式排查问题原因。
LigerShark.WebOptimizer.Core | BuildBundlerMinifier | |
特点 | 在运行时捆绑和缩小 CSS 和 JavaScript 文件 具有完整的服务器端和客户端缓存,以确保高性能 可禁用缩小 支持使用通配模式 标记帮助程序与缓存破坏 支持内联 支持 SCSS | 将 CSS、JavaScript 或 HTML 文件捆绑到单个输出文件中 保存源文件会自动触发重新捆绑 支持通配模式 支持 CI 方案的 MSBuild 支持 缩小单个或捆绑的 CSS、JavaScript 和 HTML 文件 每种语言的缩小选项是可定制的 打开生成的文件时显示水印 任务运行程序资源管理器集成 命令行支持 快捷更新解决方案中所有捆绑包 禁止生成输出文件 转换为 Gulp |
注入(Program.cs) | services.AddWebOptimizer(); app.UseWebOptimizer(); | |
指定捆绑的项目 | 在运行时自动缩小 css 和 js,也可以在 AddWebOptimizer 中自定义。默认情况下,生成的文件不会保存到磁盘,而是保存在缓存中。 | 手动编辑 bundleconfig.json 文件来指定需要合并和缩小的文件 |
功能配置 |
| 在 bundleconfig.json 捆绑时设置
|
热重载(在开发模式中保存文件自动更新浏览器效果) | VS 安装扩展,方法:菜单 - 扩展 - 管理扩展 - 联机 搜索“Bundler & Minifier” | |
其它资料 | ASP.NET 官方文档 |
原生判断
window.addEventListener('scroll', scroll)
function scroll() {
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
const clientHeight = document.documentElement.clientHeight || document.body.clientHeight;
const scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
if (scrollTop === 0) {
console.log('滚动到顶了');
}
if (scrollTop + clientHeight >= scrollHeight - 100) {
console.log('滚动到底了');
}
}
window.removeEventListener('scroll', scroll) // 移除滚动事件
scrollTop:文档在可视区上沿的位置距离文档顶部的距离
clientHeight:可视区的高度
scrollHeight:文档总高度
100:用于在滚动到底部之前提前加载内容(如瀑布流),提高用户体验,请自行修改数值(建议不小于 2 像素,以规避在部分浏览器上因获得值为小数时可能无法触发的问题)
jQuery 判断
window.addEventListener('scroll', scroll)
function scroll() {
const doc_height = $(document).height();
const scroll_top = $(document).scrollTop();
const window_height = $(window).height();
if (scroll_top === 0) {
console.log('滚动到顶了');
} else if (scroll_top + window_height >= doc_height - 100) {
console.log('滚动到底了');
}
}
操作系统 | 版本 | 发布时间 | 支持设备 |
macOS Sequoia | 15 | 2024年6月 | iMac (2019 年及后续机型) Mac Pro (2019 年及后续机型) iMac Pro (2017 年及后续机型) Mac Studio (2022 年及后续机型) MacBook Air (2020 年及后续机型) Mac mini (2018 年及后续机型) MacBook Pro (2018 年及后续机型) |
macOS Sonoma | 14 | 2023年秋 | iMac (2019 年及后续机型) Mac Pro (2019 年及后续机型) iMac Pro (2017 年机型) Mac Studio (2022 年及后续机型) MacBook Air (2018 年及后续机型) Mac mini (2018 年及后续机型) MacBook Pro (2018 年及后续机型) |
macOS Ventura | 13 | 2022年10月 | iMac (2017 年及后续机型) Mac Pro (2019 年及后续机型) iMac Pro (2017 年机型) Mac Studio (2022 年机型) MacBook Air (2018 年及后续机型) Mac mini (2018 年及后续机型) MacBook Pro (2017 年及后续机型) MacBook (2017 年机型) |
macOS Monterey | 12 | 2021年6月8日 | iMac (2015 年末及后续机型) Mac Pro (2013 年末及后续机型) iMac Pro (2017 年及后续机型) Mac mini (2014 年末及后续机型) MacBook Air (2015 年初及后续机型) MacBook (2016 年初及后续机型) MacBook Pro (2015 年初及后续机型) |
macOS Big Sur | 11 | 2020年6月23日 | MacBook (2015 年或后续机型) MacBook Air (2013 年或后续机型) MacBook Pro (2013 年后期或后续机型) Mac mini (2014 年或后续机型) iMac (2014 年或后续机型) iMac Pro (2017 年或后续机型) Mac Pro (2013 年或后续机型) |
macOS Catalina | 10.15 | 2019年10月8日 | MacBook (2015 年前期或之后的机型) MacBook Air (2012 年中期或之后的机型) MacBook Pro (2012 年中期或之后的机型) Mac mini (2012 年后期或之后的机型) iMac (2012 年后期或之后的机型) iMac Pro (2017 年) Mac Pro (2013 年后期或之后的机型) |
macOS Mojave | 10.14 | 2018年6月5日 | MacBook (2015 年前期或之后的机型) MacBook Air (2012 年中期或之后的机型) MacBook Pro (2012 年中期或之后的机型) Mac mini (2012 年后期或之后的机型) iMac (2012 年后期或之后的机型) iMac Pro (2017 年) Mac Pro (2013 年后期的机型,以及支持特定 Metal 技术图形卡的 2010 年中期和 2012 年中期的机型) |
macOS High Sierra | 10.13 | 2017年6月5日 | MacBook (2009 年后期或之后的机型) MacBook Pro (2010 年中期或之后的机型) MacBook Air (2010 年后期或之后的机型) Mac mini (2010 年中期或之后的机型) iMac (2009 年后期或之后的机型) Mac Pro (2010 年中期或之后的机型) |
macOS Sierra | 10.12 | 2016年9月20日 | MacBook (2009 年后期或之后的机型) MacBook Pro (2010 年中期或之后的机型) MacBook Air (2010 年后期或之后的机型) Mac mini (2010 年中期或之后的机型) iMac (2009 年后期或之后的机型) Mac Pro (2010 年中期或之后的机型) |
OS X El Capitan | 10.11 | 2015年9月29日 | MacBook (2015 年前期的机型) MacBook (2008 年后期的铝金属机型以及 2009 年前期或之后的机型) MacBook Pro (2007 年中期/后期或之后的机型) MacBook Air (2008 年后期或之后的机型) Mac mini (2009 年前期或之后的机型) iMac (2007 年中期或之后的机型) Mac Pro (2008 年前期或之后的机型) Xserve (2009 年前期的机型) |
OS X Yosemite | 10.10 | 2014年10月17日 | |
OS X Mavericks | 10.9 | 2013年10月23日 | iMac (2007 年中期或之后的机型) MacBook (2008 年后期的铝金属机型、2009 年前期或之后的机型) MacBook Pro (2007 年中后期或之后的机型) Xserve (2009 年前期) MacBook Air (2008 年后期或之后的机型) Mac mini (2009 年前期或之后的机型) Mac Pro (2008 年前期或之后的机型) |
OS X Mountain Lion | 10.8 | 2012年7月25日 | iMac (2007 年中期或之后的机型) MacBook (2008 年后期的铝制机型、2009 年前期或之后的机型) MacBookPro(2007 年中期/后期或之后的机型) Xserve (2009 年前期) MacBook Air (2008 年后期或之后的机型) Mac mini (2009 年前期或之后的机型) Mac Pro (2008 年前期或之后的机型) |
OS X Lion | 10.7 | 2010年10月20日 | |
OS X Snow Leopard | 10.6 | 2008年6月9日 |
参考资料:
首先,禁止网站下所有 .php 等文件均不允许被访问到。
在 nginx 网站配置文件中,include enable-php-**.conf; 上方插入:
location ~ ^/.*\.(php|php5|py|sh|bash|out)$ { deny all; }
其中,^ 匹配开始,/.* 匹配所有目录和文件名,\.(php|php5|py|sh|bash|out) 匹配文件后缀名,$ 匹配结束。
即便如此,仍然忽略了 nginx 中 .php 文件名后加斜杠仍然能访问到的情况,譬如我们访问这个网址:
https://xoyozo.net/phpinfo.php/abc.html
nginx 仍然运行了 phpinfo.php,给了后门可趁之机,所以改进为:
location ~ ^/.*\.(php|php5|py|sh|bash|out)(/.*)?$ { deny all; }
Discuz! X3.4 需要写入权限的目录:
/自研目录/upload/
/config/
/data/
/uc_client/data/
/uc_server/data/
/source/plugin/
但,这些都不重要,我们已经禁止了所有目录的 .php 访问了。
第二步,解禁需要直接被访问到的文件路径。
Discuz! X3.4 根目录下的 .php 文件都是入口文件,需要能够被访问到:
/admin.php
/api.php
/connect.php
/forum.php
/group.php
/home.php
/index.php
/member.php
/misc.php
/plugin.php
/portal.php
/search.php
其它目录中需要被直接访问到的文件:
/archiver/index.php
/m/index.php
/uc_server/admin.php
/uc_server/avatar.php
/uc_server/index.php
部分插件文件需要能够被直接访问到:
/source/plugin/magmobileapi/magmobileapi.php
/source/plugin/smstong/accountinfo.php
/source/plugin/smstong/checkenv.php
另外如果有自建目录需要有 .php 访问权限,那么也需要在此处加白,本文以 /_/ 目录为例
最终拼成:工具
location ~ ^(?:(?!(/admin\.php|/api\.php|/connect\.php|/forum\.php|/group\.php|/home\.php|/index\.php|/member\.php|/misc\.php|/plugin\.php|/portal\.php|/search\.php|/archiver/index\.php|/m/index\.php|/uc_server/admin\.php|/uc_server/avatar\.php|/uc_server/index\.php|/source/plugin/magmobileapi/magmobileapi\.php|/source/plugin/smstong/accountinfo\.php|/source/plugin/smstong/checkenv\.php|/_/.*\.php))/.*\.(php|php5|py|sh|bash|out)(/.*)?)$ { deny all; }
这里用到正则表达式中的“不捕获”和“负向零宽断言”语法,格式为:
^(?:(?!(允许的目录或文件A|允许的目录或文件B))禁止的目录和文件)$
值得注意的是,此句负向零宽断言中的匹配内容是匹配前缀的,也就是说
https://xoyozo.net/index.php
https://xoyozo.net/index.php/abc.html
都可以访问到,而
https://xoyozo.net/forbidden.php
https://xoyozo.net/forbidden.php/abc.html
都访问不到,这是符合需求的。
如果自建目录 /_/ 下有写入需求,单独禁止即可,以 /_/upload/ 为例:
location ~ ^/_/upload/.*\.(php|php5|py|sh|bash|out)(/.*)?$ { deny all; }
ThinkPHP 网站有统一的访问入口,可按本文方法配置访问权限。
特别注意:上面的代码必须加在 PHP 引用配置(include enable-php-**.conf;)的上方才有效。
外星人笔记本电脑键盘突然失灵(我的是 m15 R6),尝试过重启电脑、长按电源键(强制关机、切换电脑性能)、卸载键盘驱动、BIOS 恢复默认设置都没有效果。多次尝试后恢复正常,总结经验:
办法一:关机、拔掉电源、插上电源、再开机,就能恢复正常。
办法二:长按电源键直到关机成功,长按电源键直至开机完成。
若无效,强制关机、插拔电源,多试几次。
总之,这电脑不用的时候尽量关机,直接合上盖子会出现自动苏醒、键盘失灵等一系列问题。
2023年9月补充,最近发现直接合上盖子没有出现自动苏醒,先睡眠再拔电源也没有出现苏醒,可能与我经常在 Alienware Update 和 SupportAssist 中更新驱动和固件有关。
2024年6月补充,出现键盘失灵可以试试合上盖子再打开,大概率能恢复。
本文使用 ASP.NET 6 版本 Senparc.Weixin.Sample.MP 示例项目改造。
第一步 注册多公众号
方法一:打开 appsettings.json 文件,在 SenparcWeixinSetting 节点内添加数组节点 Items,该对象类型同 SenparcWeixinSetting。
//Senparc.Weixin SDK 设置
"SenparcWeixinSetting": {
"IsDebug": true,
"Token": "",
"EncodingAESKey": "",
"WeixinAppId": "",
"WeixinAppSecret": "",
"Items": [
{
"IsDebug": true,
"Token": "a",
"EncodingAESKey": "a",
"WeixinAppId": "a",
"WeixinAppSecret": "a"
},
{
"IsDebug": true,
"Token": "b",
"EncodingAESKey": "b",
"WeixinAppId": "b",
"WeixinAppSecret": "b"
}
]
}
方法二:修改 Program.cs 文件,在 UseSenparcWeixin 方法中注册多个公众号信息。
var registerService = app.UseSenparcWeixin(app.Environment,
null /* 不为 null 则覆盖 appsettings 中的 SenparcSetting 配置*/,
null /* 不为 null 则覆盖 appsettings 中的 SenparcWeixinSetting 配置*/,
register => { /* CO2NET 全局配置 */ },
(register, weixinSetting) =>
{
//注册公众号信息(可以执行多次,注册多个公众号)
//register.RegisterMpAccount(weixinSetting, "【盛派网络小助手】公众号");
foreach (var mp in 从数据库或配置文件中获取的公众号列表)
{
register.RegisterMpAccount(new SenparcWeixinSetting
{
//IsDebug = true,
WeixinAppId = mp.AppId,
WeixinAppSecret = mp.AppSecret,
Token = mp.Token,
EncodingAESKey = mp.EncodingAeskey,
}, mp.Name);
}
});
完成后,我们可以从 Config.SenparcWeixinSetting.Items 获取这些信息。
第二步 接入验证与消息处理
打开 WeixinController 控制器,将构造函数改写为:
public WeixinController(IHttpContextAccessor httpContextAccessor)
{
AppId = httpContextAccessor.HttpContext!.Request.Query["appId"];
var MpSetting = Services.MPService.MpSettingByAppId(AppId);
Token = MpSetting.Token;
EncodingAESKey = MpSetting.EncodingAESKey;
}
示例中 Services.MPService.MpSettingByAppId() 方法实现从 Config.SenparcWeixinSetting.Items 返回指定 appId 的公众号信息。
为使 IHttpContextAccessor 注入生效,打开 Program.cs,在行
builder.Services.AddControllersWithViews();
下方插入
builder.Services.AddHttpContextAccessor();
这样,我们就可以在构造函数中直接获取地址栏中的 appId 参数,找到对应的公众号进行消息处理。
第三步 消息自动回复中的 AppId
打开 CustomMessageHandler.cs,将
private string appId = Config.SenparcWeixinSetting.MpSetting.WeixinAppId;
private string appSecret = Config.SenparcWeixinSetting.MpSetting.WeixinAppSecret;
替换为:
private string appId = null!;
private string appSecret = null!;
并在构造函数中插入
appId = postModel.AppId;
appSecret = Services.MPService.MpSettingByAppId(postModel.AppId).WeixinAppSecret;
这样,本页中使用的 appId / appSecret 会从 postModel 中获取,而非默认公众号。postModel 已在 WeixinController 中赋值当前的 appId。
改造后,我们可以在 OnTextRequestAsync() 等处理消息的方法中可以判断 appId 来处理不同的消息。
第四步 其它功能和接口
其它功能和接口均可用指定的 AppId 和对应的 AppSecret 进行调用。