中学生也能独立上线网站?记录我折腾寻梦网站的一年

前言

初二时,我萌生了一个想法:自己做一个学习网站。

不是因为现有的学习网站不好用,也不是因为老师布置了什么作业,就是单纯觉得------如果有一个地方,能让我和同学在上面讨论,成为一个独属于学生自己的空间,好像挺酷的。

那时候我只会 C++ 和一点 html,拿过普及二等,但好像丝毫没用。前端基本靠抄,数据库是什么都还没搞明白。但我还是打开 Visual Studio,新建了一个项目,起名叫"寻梦教育科技"。

现在我是初三,网站已经跑起来了,有用户系统、题库、讨论区、私信、通知,还有社区和后台管理。这篇不是教程,就是想记录一下,这一年我是怎么一点点把它做出来的,踩了多少坑,又是怎么爬出来的。

中间的过程大概是这样的:写代码 → 报错 → 查半天 → 问 AI → 改 → 又报错 → 再查 → 终于跑起来了 → 发现下一个功能又要写。

技术栈

打开 VS 就被吓住了,网站各色技术栈看得我头疼。在查阅了许多资料后,我终于选择了 ASP.NET Core MVC(C#) + SQL Server,前端原生 CSS 和 JS,服务器是 Windows Server。听起来挺唬人,其实很多地方都是边学边做,至于为什么,大概是因为 C# 和我熟悉的 C++ 比较类似吧。但上手以后才发现,踩的坑比写的代码还多。

遇到的困难

说实话,网站能跑起来,一半时间在写代码,另一半时间在修 bug。我这里挑几个印象最深的写。

一、数据库迁移用不了,所有表结构变更只能手写

这是一个非常严重的问题,影响了我后续许多开发的项目,但我到现在还不知道为什么。

项目做到一半,我想给 Users 表加个字段,按照教程敲了 dotnet ef migrations add AddAvatar,结果直接报错------表已经存在,迁移记录对不上,EF Core 不知道从哪一步开始。

试了几种办法都不行,最后干脆放弃 Migration,改成在 Program.cs 里、app.Run() 之前,用 ExecuteSqlRawAsync 直接执行 SQL。每次要加字段或建表,就写一段这样的代码:

csharp 复制代码
using (var scope = app.Services.CreateScope())
{
    var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
    await dbContext.Database.ExecuteSqlRawAsync(
        "IF NOT EXISTS (SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Users') AND name = 'Avatar') " +
        "BEGIN ALTER TABLE Users ADD Avatar nvarchar(max) NULL; END"
    );
}

看起来笨,但很稳:每次启动自动检查,不存在就加,存在就跳过。后来建 Communities、CommunityMembers、CommunityMessages 三张表,也都是这么写的。

这个经历也给了我一个道理:Migration 不是唯一的路,项目小、表结构已经在跑的时候,手写幂等 SQL 反而更省心。

二、前端发了 JSON,后端收到的是 0

社区功能做完,点「加入社区」一直提示「社区不存在」。前端代码是这样的:

javascript 复制代码
fetch('/Community/JoinCommunity', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ communityId: communityId })
})

后端是这样:

csharp 复制代码
public async Task<IActionResult> JoinCommunity(int communityId)

查了半天,社区确实存在,但方法里的 communityId 永远是 0。后来才明白:请求头是 application/json 时,ASP.NET Core 默认不会把 JSON body 绑到普通参数上 ,除非显式加 [FromBody]。

改成:

csharp 复制代码
public async Task<IActionResult> JoinCommunity([FromBody] CommunityIdRequest request)

请求类:

csharp 复制代码
public class CommunityIdRequest
{
    public int CommunityId { get; set; }
}

问题解决。

前端发 JSON,后端就要用 [FromBody] + 请求类接收;发表单,才用普通参数绑定。这个坑我前后踩了三次(加入社区、退出社区、解散社区),每次都是同一个原因。

三、头像用 Base64 存数据库,页面加载变慢

用户头像我图省事,直接转成 Base64 字符串存在 Users.Avatar 字段里,前端这样用:

html 复制代码
<img src="@user.Avatar" />

功能没问题,但帖子列表一多,每行都要渲染一大段 Base64,页面明显变慢。

最简单的优化是加 loading="lazy",让浏览器只加载可视区域的头像:

html 复制代码
<img src="@user.Avatar" loading="lazy" style="width:32px;height:32px;border-radius:50%;object-fit:cover;" />

再进一步,可以在上传时就压缩图片,或者干脆把头像存成文件、数据库只存路径。这个我还在慢慢改。

现在我明白了:Base64 存图片省事,但不适合大量渲染。图片这种大块数据,能存文件就别塞数据库。

部署

部署是个大问题,未成年人不被受限开发和部署,但是没办法备案,再加上经费有限,所以只能选香港的廉价服务器。这边大致贴一下我的支出。

项目 费用 说明
域名 约 99 元/年 阿里云官方价
香港云服务器 约 288 元/年 2核4G,10M 带宽,免备案
SSL 证书 0 元 Let's Encrypt 免费证书,Win-ACME 自动续期
开发工具 0 元 Visual Studio 社区版、SQL Server Express 都免费
AI 动态调整 网站接入了 Deepseek API,价格看实际用量
合计 约 400 元/年 主要成本是服务器

时间成本:初二下学期开始,断断续续写了大概 4--5 个月,大部分时间花在踩坑和修 bug 上,真正写功能的时间反而不多。

部署的方式是这样的:

Windows Server + IIS,用 dotnet Xunmeng.Talk.dll --urls "http://0.0.0.0:5003" 跑起来,开机自启。

实际运营中的挑战

一:2GB 内存的服务器,装 SQL Server 直接内存溢出

一开始贪便宜,买了一台 2GB 内存的香港服务器。装 SQL Server 的时候,安装程序直接弹 System.OutOfMemoryException,装到一半就崩了。

后来才发现,Windows 系统本身就要吃掉 1.2--1.5GB,SQL Server 安装程序再要几百 MB,2GB 根本不够。最后换了轻量级的 SQL Server LocalDB,又手动限制了它的最大内存,才勉强跑起来。

二:中文路径,复制文件直接报"找不到路径"

后台资源管理做复制功能时,英文文件夹复制得好好的,一复制到中文名文件夹就报:

text 复制代码
Could not find a part of the path '...Resources&#x4F60;&#x597D;\xxx.png'

一眼就看出 &#x4F60;&#x597D; 是 你好 的 HTML 实体编码。前端传过来的路径被 HTML 编码了,后端 Path.Combine 不认识这种格式。

解决办法是在后端拿到路径后先解码:

csharp 复制代码
targetPath = WebUtility.HtmlDecode(targetPath);

三:域名突然跳转到陌生网站

有一段时间,访问自己的域名会被跳转到一个完全陌生的网址,换设备又能正常打开。

用网络拨测工具一查,发现全国大部分节点解析都正常,只有浙江、河北两个移动节点解析到了另一个 IP。查下来是那两个地区的运营商 DNS 被污染了,不是我的服务器被黑。

后来把域名 DNS 从 Cloudflare 换回阿里云,问题消失。

教训 :网站打不开,先分清是"服务器挂了"还是"DNS 解析错了"。用 ping 域名 和拨测工具一看就知道。

四:Cloudflare 代理导致无限重定向

换用 Cloudflare 之后,根域名 xunmeng.asia 想跳转到 www.xunmeng.asia,结果浏览器直接报:

text 复制代码
ERR_TOO_MANY_REDIRECTS

原因是 Cloudflare 的页面规则和 IIS 的绑定规则打架了,请求在两边来回弹。

最后放弃 Cloudflare 代理,直接换回阿里云 DNS,在服务器端用一条简单的重定向规则解决。

五:恶意小号批量注册

网站开放注册后,很快出现一批无意义小号,有的用临时邮箱收验证码,注册完就开始大量发帖,占用服务器资源,不亚于一次小规模的 DDOS 攻击。

试过验证码,对人工注册没用。最后的组合方案是:

  • 邮箱白名单:只允许 QQ、Gmail、163 等常用邮箱域名注册,临时邮箱直接被挡在门外;
  • 邮箱验证码:注册必须收验证码,且验证码 5 分钟过期、用后作废;
  • 新用户限制:注册后 24 小时内不能发帖;
  • 一键清空:后台可以一键清理举报和通知,方便应急。

这套组合拳下来,垃圾小号基本没了。

教训:防机器人的验证码,防不住人工小号。真正有效的是"提高注册成本 + 限制初期权限"。

现在是什么样

目前网站包含这些模块:

  • 导航页 :www.xunmeng.asia,入口页面,也可以免 www 访问;
  • 寻梦英语 :english.xunmeng.asia,英语题库,支持单选、阅读、完形、写作等 7 种题型,选择题可提交评测,写作题显示范文;
  • 寻梦数学 :math.xunmeng.asia,数学题库,支持选择、填空、解答题;
  • 寻梦论坛 :talk.xunmeng.asia,讨论区、私信、举报、通知,还接入了 DeepSeek 的 @AI 回复功能。
  • 寻梦应用 :app.xunmeng.asia,导航页的二级界面,实际好像没啥用
  • 寻梦论坛客户端 :download-talk.xunmeng.asia,用最简单的网页包装的方式弄了 Android 和 Windows 的"伪客户端"

用户量不大,主要是自己和同学在用。但每个功能都是自己一行一行写出来的,这种成就感很难形容。


写到这里,其实网站还很简陋。题库里的题目不算多,偶尔还会冒出几个我没测出来的 bug,用户也主要是自己和身边的同学。但每次打开 talk.xunmeng.asia,看到那个自己一行一行敲出来的导航栏,心里还是会有点不真实------这玩意儿居然真的跑在互联网上。

初二刚起步的时候,我连"域名"和"服务器"有什么区别都搞不清楚。买服务器那天,盯着 2 核 2G 的配置纠结了半天,最后为了省钱选了个最便宜的套餐,结果装 SQL Server 的时候内存直接爆了,报错刷了一屏。后来为了省内存,把数据库换成 LocalDB,又因为权限问题折腾到半夜。再后来,网站被临时邮箱注册的机器人刷了一堆垃圾帖子,我才第一次意识到"做网站"不只是把页面写出来,还得防着别人来搞破坏。

这些坑,当时每一个都让我想砸键盘。但现在回头看,恰恰是这些报错和深夜的搜索,让我真正搞懂了一些课本上不会讲的东西:什么是端口,什么是外键,什么是 DNS 解析,为什么中文路径会变成一堆 &#x4F60;&#x597D;。这些知识不是看视频看来的,是被 bug 逼出来的。

初三了,时间比以前更紧。网站更新得慢了,有时候一周才改一个小功能。但我没打算停。接下来想做的还有不少:把多小题的阅读题做得更顺手,给论坛加上真正好用的滑动验证,等中考结束,也许能把那个一直没做完的邮箱功能补上。

如果你也是中学生,也有一个"想做个什么东西"的念头,我的建议只有一句:先做出来,哪怕很丑 。不用等学完所有教程,不用等配置完美,先让它在 localhost 上跑起来,然后一个一个解决报错。你会发现,那些看起来高不可攀的东西,拆开来不过是无数个"下一步该干什么"。

网站地址是 xunmeng.asia,欢迎来看看。如果它哪天帮到了你,那就是我写这篇文章最大的意义。

中考加油,代码也加油。

------ 一个还在折腾的初三学生

相关推荐
ziven2 天前
02 | 工程视角:用微服务架构,将大模型封装为商用级 API 网关
全栈
SL_staff2 天前
制造业订单交付总踩坑?用结构化节点协同模型重构计划系统
java·开源·全栈
前端双越老师2 天前
程序员 1v1 访谈系列第九期:要不要晋升管理层?
agent·ai编程·全栈
vivo互联网技术3 天前
从知识沉淀到编程范式:AI Coding 项目冷启动落地实践
ai编程·全栈·vibecoding
2601_967987373 天前
Next.js 文档
react·开发·全栈·文档·next.js
挖掘狂人4 天前
全栈正在杀死前端?被淘汰的从来不是岗位,只会是执行层
前端·ai编程·全栈
codigger4 天前
前端没有被杀死,被杀死的是"只会把设计稿变成页面"
前端·ai·ai编程·编程语言·全栈·#人工智能
前端双越老师4 天前
程序员 1v1 访谈系列第八期:大龄女程序员的困境
agent·ai编程·全栈
SL_staff5 天前
运维救火实战:钉钉API变更致薪资计算中断,如何用可视化逻辑引擎5分钟热修复
java·开源·全栈