如果你的视图页面(.cshtml)是由另一个视图页面复制而来的,就有可能出现开发调试时正常,发布到服务器后打开报 500 错误。
解决方法是:新建一个 .cshtml 页面,将代码移植到这个文件上,删除原来的页面,更改刚创建的文件名。
你也可以直接修改项目文件将视图页面重新包含到项目中。
ASP.NET 5 发布后出现 cs / de / es / fr / it / ja / ko / pl / pt-BR / ru / tr / zh-Hans / zh-Hant 语言目录
想要清除它们,只需要在项目上右键选择编辑项目文件
在 <PropertyGroup /> 节点添加以下节点即可:
<SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages>
本文记录于 2021 年 9 月。
升级前 | 期望(最新正式版) | 最终选择 | |
操作系统 | CentOS 6.5 | Alibaba Cloud Linux 3 | Alibaba Cloud Linux 3 |
管理面板 | lnmp | 宝塔面板 Linux 版 7.7.0 | 宝塔面板 Linux 版 7.7.0 |
Web 服务 | nginx 1.6 | nginx 1.21 | nginx 1.21 |
脚本语言 | PHP 5.6 | PHP 8.0 | PHP 7.4 |
数据库 | RDS MySQL 5.6 | RDS MySQL 8.0 | RDS MySQL 5.6 |
论坛程序 | Discuz! X3.2 GBK | Discuz! X3.5 UTF-8(即将发布) | Discuz! X3.4 GBK |
版本选择原因:
Alibaba Cloud Linux 完全兼容 CentOS,相比于 CentOS 较短的生命周期,Alibaba Cloud Linux 3 将于 2029 年 4 月 30 日结束生命周期。
Discuz! X3.4 不支持 PHP 8.0,安装时即报错,打开页面时一片空白。
MySQL 8.0 和阿里云 RDS 的 MySQL 7.5 不支持 MyISAM,而数据表 pre_common_member_grouppm 和 pre_forum_post 使用联合主键且自动递增字段不是第一主键,使用 InnoDB 引擎创建表时会报“1075 - Incorrect table definition; there can be only one auto column and it must be defined as a key”错误,而擅自更改主键次序会影响业务逻辑。因此,在必须选择阿里云 RDS 的情况下,只能选择 MySQL 5.6。(2023年8月注:查看如何更改为 InnoDB)
Discuz! X3.5 正式版尚未发布(截止发稿),即便发布,插件也可能不能得到及时更新。相比之下,X3.4 首个版本发布距今已有 4 年,相关第三方插件已经非常成熟。
完整升级步骤:
备份原网站程序、RDS 数据库;
购买新的 ECS、RDS,挂载磁盘,安装云监控;
迁移(或还原)数据库到新的 RDS;
安装宝塔面板并配置;
安装 nginx 及 PHP;
创建网站、配置 SSL、伪静态、防盗链、可写目录禁执行等(.conf);
配置 hosts;
上传原网站程序到新的站点目录下;
按 Discuz! X 升级文档升级 X3.2 至 X3.4;详情见下文 ↓;
配置 OSS、Redis、更新缓存等;
测试论坛基本功能是否正常;检查附件是否正常显示;全面检查控制台配置;
逐个开启插件并检查兼容性;
按二开备忘录逐个按需进行二开;
逐个修改调用论坛接口的项目及直接调用论坛数据库的项目;
调试 MAGAPP 接口;
尝试强制 https 访问;
将以上所有修改后的程序保留备份;发布升级公告并关闭论坛;重复以上步骤;修改域名解析;开启论坛;
配置 IP 封禁、定时器、日志、自动备份、配置其它 ECS 的 hosts 等;
查看搜索引擎中收录的地址,是否有无法访问的情况;
尝试将历史遗留的本地附件全部转移到 OSS;
参这篇文章,可能有其它需要配置的地方。
Discuz! X 升级步骤及注意点:
升级前务必先修改 ./config/ 目录下的数据库/缓存连接信息,以防出现新站连接老库的情况;
按官方文档进行升级;
【问题】运行到 ./install/update.php?step=data&op=notification 时白屏。
【排查】尝试切换到 PHP 5.6 后成功(但该版本过于陈旧不能使用);尝试升级 CPU 和内存 PHP 7.4 上升级仍不成功。
【原因】DB::result_first() 方法不对 SQL 语句追加“limit 1”,而是 SELECT 所有记录后在 PHP 端取第一条数据;
【解决】打开文件 update.php,查找 elseif($_GET['op'] == 'notification'),该节点的功能是在表 home_notification 中查找 category <= 0 的数据并修复它,如果数据库中所有 category 都大于 0,直接注释其内部 if 代码段继续升级即可(或改为 if(false && ...))。
【问题】发布主题遇到错误:(1062) Duplicate entry '*' for key 'pid'
【原因】forum_post 中的 pid 不是自动增长的,而是由表 forum_post_tableid 中自动增长的 pid 生成的。如果生成的 pid 值已在 forum_post 表中存在,则会出现此错误。
【解决】迁移数据库时应关闭论坛,以防止 forum_post 表有新数据插入。
【问题】打开帖子页面 ./thread-***-1-1.html 显示 404 Not Found,而 ./forum.php?mod=viewthread&tid=*** 可以正常打开
【原因】未配置伪静态(可在宝塔面板中选择)
【问题】打开 UCenter 时报错:UCenter info: MySQL Query Error SQL:SELECT value FROM [Table]vars WHERE name='noteexists'
【解决】打开文件 ./uc_server/data/config.inc.php 配置数据库连接
【问题】打开登录 UCenter 后一片空白
【解决】将目录 ./uc_server/data/ 设为可写
需要将原来安装的插件文件移回 ./source/plugin/ 目录,并设置可写;
界面-表情管理,界面-编辑器设置-Discuz!代码
后续 Discuz! X3.4 R 小版本升级注意事项:
确认插件是否支持新版本(如短信通)
先创建一个新网站测试二开代码
保留 /config/、/data/、/uc_client/data/、/uc_server/data/、/source/plugin/,其它移入 old
上传文件
移回其它需要的文件,如:
-- 勋章/loading/logo/nv 等:/static/image/common/
-- 表情:/static/image/smiley/
-- 水印:/static/image/common/watermark.*
-- 风格:/template/default/style/t2/nv.png 等
-- 默认头像:/uc_server/images/noavatar_***.gif
-- 根目录 favicon.ico 等
-- 及其它非 DZ 文件
再次检查可写目录的写入权限和禁止运行 PHP 效果。
HttpHelper类:
using System; using System.Collections.Specialized; using System.IO; using System.Net; using System.Text; namespace ConsoleApplication1 { public static class HttpHelper { private static readonly Encoding DEFAULTENCODE = Encoding.UTF8; /// <summary> /// HttpUploadFile /// </summary> /// <param name="url"></param> /// <param name="file"></param> /// <param name="data"></param> /// <returns></returns> public static string HttpUploadFile(string url, string file, NameValueCollection data) { return HttpUploadFile(url, file, data, DEFAULTENCODE); } /// <summary> /// HttpUploadFile /// </summary> /// <param name="url"></param> /// <param name="file"></param> /// <param name="data"></param> /// <param name="encoding"></param> /// <returns></returns> public static string HttpUploadFile(string url, string file, NameValueCollection data, Encoding encoding) { return HttpUploadFile(url, new string[] { file }, data, encoding); } /// <summary> /// HttpUploadFile /// </summary> /// <param name="url"></param> /// <param name="files"></param> /// <param name="data"></param> /// <returns></returns> public static string HttpUploadFile(string url, string[] files, NameValueCollection data) { return HttpUploadFile(url, files, data, DEFAULTENCODE); } /// <summary> /// HttpUploadFile /// </summary> /// <param name="url"></param> /// <param name="files"></param> /// <param name="data"></param> /// <param name="encoding"></param> /// <returns></returns> public static string HttpUploadFile(string url, string[] files, NameValueCollection data, Encoding encoding) { string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x"); byte[] boundarybytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n"); byte[] endbytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n"); //1.HttpWebRequest HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.ContentType = "multipart/form-data; boundary=" + boundary; request.Method = "POST"; request.KeepAlive = true; request.Credentials = CredentialCache.DefaultCredentials; using (Stream stream = request.GetRequestStream()) { //1.1 key/value string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}"; if (data != null) { foreach (string key in data.Keys) { stream.Write(boundarybytes, 0, boundarybytes.Length); string formitem = string.Format(formdataTemplate, key, data[key]); byte[] formitembytes = encoding.GetBytes(formitem); stream.Write(formitembytes, 0, formitembytes.Length); } } //1.2 file string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: application/octet-stream\r\n\r\n"; byte[] buffer = new byte[4096]; int bytesRead = 0; for (int i = 0; i < files.Length; i++) { stream.Write(boundarybytes, 0, boundarybytes.Length); string header = string.Format(headerTemplate, "file" + i, Path.GetFileName(files[i])); byte[] headerbytes = encoding.GetBytes(header); stream.Write(headerbytes, 0, headerbytes.Length); using (FileStream fileStream = new FileStream(files[i], FileMode.Open, FileAccess.Read)) { while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0) { stream.Write(buffer, 0, bytesRead); } } } //1.3 form end stream.Write(endbytes, 0, endbytes.Length); } //2.WebResponse HttpWebResponse response = (HttpWebResponse)request.GetResponse(); using (StreamReader stream = new StreamReader(response.GetResponseStream())) { return stream.ReadToEnd(); } } } }
调用示例:
using System; using System.Collections.Specialized; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { NameValueCollection data = new NameValueCollection(); data.Add("name", "木子屋"); data.Add("url", "http://www.mzwu.com/"); Console.WriteLine(HttpHelper.HttpUploadFile("http://localhost/Test", new string[] { @"E:\Index.htm", @"E:\test.rar" }, data)); Console.ReadKey(); } } }
“Web 部署”方式发布 ASP.NET Core 网站项目可解决发布到本地文件夹再通过 FTP 上传到 IIS 中会遇到的文件被锁定/占用的问题。相对于手动停止网站甚至结束进程来说,Web 部署更为方便。
服务器管理器 - 添加角色和功能 - 服务器角色 - Web 服务器(IIS) - 管理工具 - 管理服务
安装 Web Deploy
下载 Web 部署,安装时选择“完整”
在“服务”中设置“Web Management Service”和“Web 部署代理服务”自动启用。(若没有找到“Web 部署代理服务”,检查安装 Web Deploy 时是否勾选全部)
IIS 管理器 - 管理服务 - 启用远程连接
这里我们不使用 Windows 凭据(本地用户),而使用 IIS 管理器用户。
端口默认 8172,需要在防火墙中允许该端口。在阿里云 ECS 的安全组规则中添加该端口允许。
创建 IIS 管理器用户
打开后右侧添加用户,以“iisWebDeploy”为例
配置 IIS 管理器权限
选择单个网站
给整个网站目录添加 LOCAL SERVICE 的完全控制权限 2023年在 Windows Server 2022 上未设置 LOCAL SERVICE 的权限也能部署成功,所以忽略此步骤!2024年同样在 Windows Server 2022 上未设置 LOCAL SERVICE 部署失败,设置后部署成功。
发布
【建议】设置用户 WDeployAdmin 与 WDeployConfigWriter 的密码永不过期,否则会遇到:在远程计算机上处理请求时出错。
一些已知错误的解决办法:
若遇到已下载的 Microsoft SQL Server 2012 Transact-SQL ScriptDom 签名验证失败,手动下载安装即可:Microsoft SQL Server 2012 SP4 功能包,选择 SqlDom.msi(有两个同名文件,感觉上尺寸较大的应该是 x64,我没有具体对比,安装大的成功了)。
若遇到 SQL Server 2012 SP1 Shared Management Object (x86 / x64) 下载失败,同样点击上面的链接,选择 SharedManagementObjects.msi 下载安装。
Web Deploy 安装失败,如果 Web Deploy for Hosting Servers 下载失败,可以尝试安装 Web Deploy without bundled SQL support (last)。
如果是新项目,记得在 IIS 对应的网站中添加“IIS 管理器权限”中添加用户。
连接对话框中的“站点名称”或“网站名”必须与 IIS 中的网站名称一致。
还是无法连接?查看 IIS 管理服务中的 SSL 证书是否过期。
Microsoft.WebTools.Shared.Exceptions.WebToolsException: 生成失败。检查输出窗口了解更多详细信息。尝试在“控制面板-卸载或更改程序”中修复 Microsoft Web Deploy 程序。
错误Web 部署任务失败。 (无法为指定目录执行操作(“创建文件”)。如果服务器果管理员没有为你使用的用户凭据授予执行此操作的权限,则会发生这种情况。
在以下位置了解更多信息: http://go.microsoft.com/fwlink/?LinkId=221672#ERROR_INSUFFICIENT_ACCESS_TO_SITE_FOLDER。 在以下位置了解更多信息: https://go.microsoft.com/fwlink/?LinkId=221672#ERROR_INSUFFICIENT_ACCESS_TO_SITE_FOLDER。)
解决方法:设置该文件或网站目录的 LOCAL SERVICE 用户完全控制权限。
严重性代码说明项目文件行禁止显示状态
错误CS0121以下方法或属性之间的调用具有二义性:“System.Linq.Queryable.Where<TSource>(System.Linq.IQueryable<TSource>, System.Linq.Expressions.Expression<System.Func<TSource, bool>>)”和“System.Linq.AsyncEnumerable.Where<TSource>(System.Collections.Generic.IAsyncEnumerable<TSource>, System.Func<TSource, bool>)”活动
The call is ambiguous between the following methods or properties.
引用的扩展或其版本有冲突,暂时在查找的表后面加上“AsQueryable()”解决。