ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

经典ASP商城源码解析:BS结构下的购物车与订单实现

经典ASP商城源码解析:BS结构下的购物车与订单实现 简介这是一份ASP基于B/S结构的工艺品销售系统毕业设计资料包含完整源代码与配套论文面向计算机相关专业的学生尤其适合用于毕业设计、课程设计或工程实训。系统围绕工艺品在线展示、购物车、订单管理等典型销售流程展开可帮助学习者理解ASP动态网页技术、SQL Server数据库设计以及B/S三层架构的落地实现。压缩包整体约2.77MB便于快速下载与部署。目前已有22人学习资料提供源码可直接运行论文内容涵盖系统分析、数据库设计、功能实现与测试等环节结构完整具备较高的参考与二次开发价值。对准备做Web类毕设或初学ASP开发的同学而言既可借鉴其设计思路与代码风格也可在现有模块上修改扩充完成个性化功能。整体适合作为初期项目立项或动手实操的起点。1. 一套ASP工艺品商城源码的阅读路线BS结构下的经典实现很多人打开这套rar第一眼看到的是ASP——一个在.NET之前的老牌服务端脚本。它没有MVC分层没有ORM甚至SQL就写在HTML旁边但正因如此BS结构Browser/Server里浏览器发起请求、IIS执行脚本、ADO读写数据库、HTML回传页面这条链路被展示得淋漓尽致。这套工艺品销售系统完整覆盖了商品管理、会员注册、购物车、订单提交、后台维护五大模块适合做课程设计、软件综合实践或毕业设计的选题底稿。接下来不按说明书讲直接把数据库、购物车、图片上传和IIS部署四个最值得动手复现的部分拆开结合源码逐段说明改动点。2. 数据库表设计与ADO数据访问工艺品系统的数据底座BS结构的业务状态全部落在数据库里ASP页面只做数据搬运。rar里通常同时给出.mdbAccess和.sqlSQL Server脚本两份数据库文件本地调试跑Access最省事部署服务器或应付外审时换SQL Server两者ADO连接方式略有差异SQL主体语句可复用。下面的表和代码直接从源码抽取标注了可改造的位置。2.1 六张核心表的字段规划与设计意图工艺品销售的业务闭环是访客浏览分类 → 查看商品详情 → 注册登录 → 加入购物车 → 生成订单。源码对应的核心表共六张其中Orders和OrderDetail必须成对出现这是订单业务的基础结构。表名核心字段设计意图AdminAdminID, AdminName, AdminPwd后台管理员登录密码用MD5散列存储CategoryCategoryID, CategoryName, CategoryDesc工艺品分类如陶瓷、木雕、金属工艺ArtworkArtworkID, CategoryID, ArtName, ArtPrice, ArtImg, ArtStock, ArtDesc, ArtDate商品主表ArtImg只存相对路径不存完整URLMemberMemberID, UserName, UserPwd, RealName, Phone, RegDate前台会员注册信息OrdersOrderID, MemberID, OrderNo, TotalMoney, PayStatus, OrderDate订单主表PayStatus用0/1表示未付/已付OrderDetailDetailID, OrderID, ArtworkID, ArtNum, ArtPrice订单明细ArtPrice是成交瞬间的价格快照ArtPrice在OrderDetail里重复存一份不是冗余设计。商品表Artwork.ArtPrice会随后台调价变化订单明细必须保留下单时的成交价否则半年后查历史订单会发现金额对不上。这个细节在课程设计答辩里值得主动讲出来。2.2 ADO连接字符串与conn.asp公共文件经典ASP访问数据库统一走ADO连接字符串是第一个高频踩坑点。Access和SQL Server写法区别如下 Access 连接示例 Dim conn Set conn Server.CreateObject(ADODB.Connection) conn.Open ProviderMicrosoft.Jet.OLEDB.4.0;Data Source Server.MapPath(data/arts.mdb) SQL Server 连接示例 Dim conn Set conn Server.CreateObject(ADODB.Connection) conn.Open ProviderSQLOLEDB;Serverlocalhost;DatabaseArtShop;Uidsa;Pwd123456;Access那一行的Server.MapPath(data/arts.mdb)把虚拟路径转成物理绝对路径这一步不能省直接用相对路径连库会在运行时报告找不到文件。SQL Server连接则要核对数据库名、登录名和密码64位系统下SQLOLEDB有时不被识别换成ProviderSQLNCLI11或Microsoft OLE DB Provider for SQL Server能解决。提示64位Windows的IIS默认不加载32位数据库驱动。Access读取报错时打开对应应用程序池的高级设置把启用32位应用程序改为True这是最常被忽略的一步。源码里所有页面通过!--#include fileconn.asp--引入公共连接文件。conn.asp内容通常是创建conn对象、打开连接、定义关闭连接的子过程。我一般会在conn.asp里再加一个查询函数减少页面重复代码Function GetRS(sql) Set GetRS conn.Execute(sql) End Function页面调用就直接Set rs GetRS(SELECT * FROM Artwork)所有SQL集中收口后续换数据库或加日志只需改conn.asp一处。源码没做这层封装页面里全是裸的conn.Execute读的时候可以自己补上。2.3 商品列表分页与详情查询的SQL细节商品列表页是典型的分类分页查询先取分类ID再查该分类下的商品总数按页计算偏移量最后取出当前页数据。Dim page, pageSize, rs, sql, totalCount, totalPage page Request(page) If page Or Not IsNumeric(page) Then page 1 pageSize 12 sql SELECT COUNT(*) FROM Artwork WHERE CategoryID cateId Set rs conn.Execute(sql) totalCount rs(0) totalPage Int((totalCount pageSize - 1) / pageSize) sql SELECT * FROM Artwork WHERE CategoryID cateId _ ORDER BY ArtDate DESC _ LIMIT (page - 1) * pageSize , pageSize Set rs conn.Execute(sql) Do While Not rs.EOF Response.Write div classitema hrefart_show.asp?artId rs(ArtworkID) Response.Write img src rs(ArtImg) //a Response.Write p rs(ArtName) /p Response.Write p rs(ArtPrice) /p/div rs.MoveNext Loop分页参数说明page从QueryString读入必须用IsNumeric校验否则用户传?pageabc会触发类型不匹配错误。上面的LIMIT n,m写法只适用于MySQL。Access版本要改成WHERE ArtworkID NOT IN (SELECT TOP (page-1)*pageSize ArtworkID FROM Artwork WHERE CategoryID cateId ORDER BY ArtDate DESC)外层再取TOP pageSize。源码在不同版本里写法不一样运行报错就优先查这一句。rs(0)是COUNT(*)的结果。用conn.Execute返回的记录集必须先赋值给rs再引用不能写成totalCount conn.Execute(...)(0)VBScript不支持这种链式取值。详情页的SQL就是SELECT * FROM Artwork WHERE ArtworkID Request(artId)但同样建议在查询前加If Not IsNumeric(Request(artId)) Then Response.Redirect art_list.asp。这是一个防注入习惯不复杂写进毕设代码里是加分项。3. Session购物车与订单生成从加购到结算的事务链路HTTP是无状态协议经典ASP里最常用的状态保持手段是Session。这套源码的购物车没有用Cookie也没有落数据库临时表而是把购物车数据挂在Session(Cart)上——用户关闭浏览器前购物车都在服务器端保存。相比Cookie方案Session数据用户改不了伪造价格和数量的成本更高缺点是IIS回收进程或超过超时时间会丢购物车数据。这个取舍在毕设答辩里常被问到先理解再改代码。3.1 Session数组的存取结构与加购逻辑源码用一个二维数组存购物车cart(0)存商品IDcart(1)存购买数量。采用数组而不是字符串拼ID是因为后续删除和改数量更直观。Session(Cart)索引存储内容cart(0)(i)0 ~ N-1第i个商品的ArtworkIDcart(1)(i)0 ~ N-1第i个商品的购买数量初始化加购代码Dim cart, artId, i, found artId Request(artId) If Not IsNumeric(artId) Then Response.Redirect art_list.asp If IsEmpty(Session(Cart)) Then cart Array(Array(0), Array(0)) cart(0)(0) artId cart(1)(0) 1 Else cart Session(Cart) found False For i 0 To UBound(cart(0)) - 1 If CStr(cart(0)(i)) CStr(artId) Then cart(1)(i) cart(1)(i) 1 found True Exit For End If Next If Not found Then ReDim Preserve cart(0), UBound(cart(0)) 1 ReDim Preserve cart(1), UBound(cart(1)) 1 cart(0)(UBound(cart(0))) artId cart(1)(UBound(cart(1))) 1 End If End If Session(Cart) cart两个关键点Session读出来之后必须写回。VBScript里数组是值类型Session(Cart) cart这句不写所有修改只在当前请求内生效下一页再加购又是旧数据。ReDim Preserve只能针对一维数组逐个扩展。不能直接对cart整体ReDim Preserve二维数组VBScript不支持。必须像上面这样分别Preserve cart(0)和cart(1)再逐个赋值新下标。报下标越界时大多是这里写错了。购物车页面展示时要把Session里的商品ID拼成IN列表一次性从Artwork表取回商品信息Dim idList, rs idList For i 0 To UBound(cart(0)) - 1 If idList Then idList cart(0)(i) Else idList idList , cart(0)(i) End If Next sql SELECT * FROM Artwork WHERE ArtworkID IN ( idList ) Set rs conn.Execute(sql)IN查询对毕设级项目足够但要意识到SQL文本长度有上限购物车商品数量过大时IN方案会失效。可以在答辩前准备好这句话当商品量增大时应当把购物车迁移到数据库临时表或Redis而不是继续用Session数组。修改数量与删除商品的逻辑是数组操作的两个变体。删除即把该下标之后的所有元素前移一位再ReDim Preserve缩容修改数量直接对cart(1)(i)赋值新值。源码里用了两个独立asp文件处理这两个动作结构与加购类似。3.2 结算时用事务保证订单与库存一致结算要同时写Orders、OrderDetail、Artwork三处。任何一个失败都会导致账目对不上比如扣了库存没生成明细或者生成了订单没扣库存。ADO Connection自带事务方法把多步SQL包进一个原子操作conn.BeginTrans On Error Resume Next sql INSERT INTO Orders(MemberID, OrderNo, TotalMoney, PayStatus) VALUES( _ memberId , orderNo , totalMoney , 0) conn.Execute sql sql SELECT IDENTITY Set rs conn.Execute(sql) orderId rs(0) For i 0 To UBound(cart(0)) - 1 conn.Execute INSERT INTO OrderDetail(OrderID, ArtworkID, ArtNum, ArtPrice) VALUES( _ orderId , cart(0)(i) , cart(1)(i) , priceArr(i) ) conn.Execute UPDATE Artwork SET ArtStock ArtStock - cart(1)(i) _ WHERE ArtworkID cart(0)(i) Next If Err.Number 0 Then conn.RollbackTrans Response.Write scriptalert(下单失败请重试);location.hrefcart.asp;/script Else conn.CommitTrans Session(Cart) Empty End If事务关键点说明BeginTrans之后的SQL统一进入事务没有CommitTrans之前其他会话看不到这些改动。RollbackTrans会撤销全部。On Error Resume Next吞掉了错误必须靠Err.Number判断成败。如果去掉这句ASP默认遇到错误就停止执行Err.Number永远为0事务反而失去意义。IDENTITY取当前连接最近一次自增主键。INSERT和SELECT IDENTITY之间不要执行任何其它会产生IDENTITY值的操作否则取到的ID是错的。Access老版本驱动对IDENTITY支持不稳定备选方案是用Rs.Open加rs.AddNew/rs.Update方式插入再读rs(OrderID)。订单号生成源码用时间拼随机数DH Year(Now) Month(Now) Day(Now) Hour(Now) Minute(Now) Second(Now) Int(Rnd * 1000)。这个方案在并发下容易生成重复单号因为Rnd以秒为单位随机同一秒内两次下单的后缀可能相同。改成Timer取当前秒的小数部分拼进去比如Int((Rnd Timer) * 1000)重复概率会大幅下降。写完订单一定要执行Session(Cart) Empty。漏掉这行用户再次打开购物车会看到已购商品还在最终重复下单。源码在CommitTrans分支里已经处理扩展时要注意新加的结算入口也需要这一步。4. 商品图片上传与后台表格展示经典ASP的边界处理后台商品管理里最值得拆的是两个功能图片上传和列表展示。搜索词里常出现的asp:gridview列中的数据怎么换行是ASP.NET WebForms的控件问题经典ASP没有GridView表格输出全靠手写HTML和字符串处理。理解了这个区别看这套源码的后台页面就顺了。4.1 用ADODB.Stream实现multipart/form-data图片上传经典ASP没有封装好的文件上传控件浏览器把整个表单按multipart/form-data格式打包发送服务器端需要手工解析。表单写法form methodpost actionadmin_art_save.asp enctypemultipart/form-data 商品名称input typetext nameartName / 商品图片input typefile nameartImg / 库存数量input typetext nameartStock / input typesubmit value保存商品 / /formenctypemultipart/form-data是必须的。用了它Request.BinaryRead才能读到完整请求体Request.Form里的普通字段依然能用file域则需要拆二进制流取文件字节。服务端接收二进制并落盘的核心代码Dim formData, stream formData Request.BinaryRead(Request.TotalBytes) 从二进制数据中提取文件名完整版需处理边界分隔符 Set stream Server.CreateObject(ADODB.Stream) stream.Type 1 二进制模式 stream.Open stream.Write fileData stream.SaveToFile Server.MapPath(upload/) fileName, 2 stream.Close二进制模式Type1必须在Open之前设置。如果默认Type2文本模式遇到PNG/JPG里的二进制字节会被按字符编码处理写出来的文件直接损坏。SaveToFile第二个参数2表示允许覆盖已存在文件如果想避免覆盖传1。文件名是另一个坑。源码直接保存客户端原始文件名比如青花瓷 瓶.jpg存到服务器后URL带着空格和中文页面引用时经常404。更稳妥的做法是服务端重命名用时间戳加随机数Dim extName, newName extName LCase(Mid(fileName, InStrRev(fileName, .) 1)) newName Year(Now) Month(Now) Day(Now) Hour(Now) Minute(Now) Second(Now) _ Int(Rnd * 10000) . extNameLCase把扩展名转小写执行上传后把upload/ newName存入Artwork.ArtImg字段。4.2 文件类型白名单与大小限制源码里没有做类型校验任何扩展名都能传上来。一个合格的后台至少要加白名单Dim allowExt, ok allowExt jpg,jpeg,png,gif,webp ok InStr(allowExt, LCase(extName)) 0 If Not ok Then Response.Write scriptalert(仅支持图片格式);history.back();/script Response.End End If大小限制可以用Request.TotalBytes正常商品图应在2MB以内If Request.TotalBytes 2 * 1024 * 1024 Then Response.Write scriptalert(图片不能超过2MB);history.back();/script Response.End End If判断顺序先查扩展名再查大小最后才落盘。实际部署中上传相关的问题可以按这个表排查现象排查点处理上传后页面裂图upload目录物理文件是否存在查看Server.MapPath(upload/)对应的磁盘目录Request.BinaryRead为空表单少了enctype属性form标签补上enctypemultipart/form-data文件名中文变乱码服务器代码页与文件名字符集不一致改用时间戳重命名杜绝中文文件名图片超过大小限制TotalBytes超出预期上调阈值或对图片做压缩4.3 后台列表中换行、价格与图片显示的通用处理后台的商品列表是手写table循环输出记录的。多行文本ArtDesc直接输出会把换行符丢掉挤成一段。常见做法是定义一个格式化函数先转义HTML再替换换行Function FormatDesc(str) If IsNull(str) Then FormatDesc nbsp; Else FormatDesc Server.HTMLEncode(str) FormatDesc Replace(FormatDesc, vbCrLf, br /) FormatDesc Replace(FormatDesc, vbLf, br /) End If End Function必须先HTMLEncode再Replace换行。顺序反了的话br /会被第二次转义成lt;br /gt;显示为文本。ASP.NET开发者经常在这一步翻车因为GridView有TemplateField和RowDataBound可以方便控制换行和样式经典ASP只能在输出前把字符串加工好没有事件钩子可挂。价格列建议统一格式化成两位小数Function FormatPrice(p) If IsNumeric(p) Then FormatPrice FormatNumber(CDbl(p), 2) Else FormatPrice 0.00 End If End Function图片列直接用ArtImg字段拼相对路径输出img src%rs(ArtImg)% width80 height80 /。注意一个实际部署问题上传目录权限不够时图片能写进数据库路径但磁盘保存失败表面看记录有值页面却是裂图。排查顺序是先确认upload目录在物理磁盘上有对应文件再确认IIS_IUSRS对该目录有写权限最后才看数据库里的路径字段。删除商品时同步删除原图文件也是值得补的优化。源码里delete语句只删了数据库记录磁盘图片变成孤儿文件Dim fs, imgPath imgPath Server.MapPath(rs(ArtImg)) Set fs Server.CreateObject(Scripting.FileSystemObject) If fs.FileExists(imgPath) Then fs.DeleteFile imgPath5. Win11 IIS部署经典ASP与500错误排查rar解压后放在Win11本地跑比想象中多几步。Win11默认的IIS不带ASP模块直接访问.asp会看到404.3。部署流程控制面板进入启用或关闭Windows功能勾选Internet Information Services再在应用程序开发功能里勾上ASP或者用管理员权限PowerShell执行Enable-WindowsOptionalFeature -Online -FeatureName IIS-ASP -All。站点目录权限要给IIS_IUSRS读权限upload目录还要加写权限否则图片上传那步会报拒绝访问。经典ASP最常见的500错误可以按这张表快速定位报错文本原因处理ASP 0240 脚本超时脚本执行超过默认90秒IIS ASP设置里增大脚本超时500.100 行号VBScript运行时错误按行号查页面源码多数是SQL字段名拼错或rs未关闭JET Database Engine 找不到文件Access路径错误或64位驱动未装检查Server.MapPath路径应用池启用32位数据库或对象为只读目录写权限不足给站点目录授权IIS_IUSRS写权限未找到用于标识的组件ADODB.Stream组件未注册确认IIS里ASP的ISAPI扩展已启用排查阶段临时注释掉页面里的On Error Resume Next或者在关键位置打印错误信息If Err.Number 0 Then Response.Write 错误号 Err.Number br / Response.Write 错误描述 Err.Description br / Response.End End IfSession丢失问题集中在应用池回收。默认IIS应用池每隔1740分钟回收一次回收后所有Session清空。购物车不想每次都莫名其妙消失把应用池固定时间间隔改为0或在IIS ASP设置里把Session超时从20分钟调大。调试SQL最简单的手段是输出SQL文本Response.Write sql加Response.End拼接的SQL字符串直接打在页面上引号多一个、字段名拼错一眼就能看出来确认无误再删掉这两行。现在直接在浏览器输入http://localhost/art_list.asp能正常出商品列表这套源码就算真正跑起来了。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进