- 数据存储:需要一个地方来存储每个帖子的顶数和踩数,最常用的是数据库表。
- 用户交互:用户点击“顶”或“踩”按钮。
- 数据更新:服务器接收到点击后,更新数据库中对应帖子的顶/踩计数。
- 防重复投票:这是关键,需要防止同一个用户在短时间内重复投票,通常使用 Session 或 Cookie 来实现。
经典 ASP (VBScript + Access/SQL Server)
这个方案适合于老旧项目或学习经典 ASP 的开发。

(图片来源网络,侵删)
第 1 步:数据库设计
我们需要一个数据库表来存储文章/帖子的信息,包括顶和踩的数量。
假设我们有一个名为 Articles 的表,结构如下:
CREATE TABLE Articles (
ArticleID INT PRIMARY KEY IDENTITY(1,1), -- 文章ID,主键NVARCHAR(255), -- 文章标题
Content NTEXT, -- 文章内容
CreateDate DATETIME, -- 创建时间
-- 核心字段
UpVotes INT DEFAULT 0, -- 顶的数量
DownVotes INT DEFAULT 0 -- 踩的数量
);
第 2 步:创建投票处理页面 (vote.asp)
这个页面将接收来自客户端的请求,并执行数据库更新,它不包含任何 HTML,只负责后台逻辑。
<%@ Language=VBScript %>
<%
' 1. 获取前端传递过来的参数
Dim articleId, action, voteType
articleId = Request("id")
action = Request("action") ' 'up' 或 'down'
' 简单的参数校验
If Not IsNumeric(articleId) Or (action <> "up" And action <> "down") Then
Response.Write "参数错误"
Response.End
End If
' 2. 防重复投票逻辑 (使用Session)
' Session("voted_" & articleId) 用于记录用户对这篇文章是否已经投过票
If Session("voted_" & articleId) = "" Then
' 如果用户未投票,则允许投票
' --- 数据库连接部分 (以Access为例) ---
Dim conn, connStr, sql
connStr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & Server.MapPath("database.mdb")
Set conn = Server.CreateObject("ADODB.Connection")
conn.Open connStr
' 根据投票类型更新数据库
If action = "up" Then
sql = "UPDATE Articles SET UpVotes = UpVotes + 1 WHERE ArticleID = " & articleId
Else ' action = "down"
sql = "UPDATE Articles SET DownVotes = DownVotes + 1 WHERE ArticleID = " & articleId
End If
conn.Execute(sql)
' 关闭连接
conn.Close
Set conn = Nothing
' 标记用户已投票,防止短时间内重复投票
Session("voted_" & articleId) = "true"
' 返回成功状态
Response.Write "success"
Else
' 如果用户已投票,则返回失败信息
Response.Write "您已经投过票了"
End If
' 3. 结束响应
Response.End
%>
第 3 步:在显示文章的页面中集成顶踩功能 (view_article.asp)
这个页面会显示文章内容,并包含顶踩按钮。

(图片来源网络,侵删)
<%@ Language=VBScript %>
<%
' 1. 获取文章ID并从数据库读取文章信息
Dim articleId, conn, connStr, rs, sql
articleId = Request.QueryString("id")
If Not IsNumeric(articleId) Then
Response.Write "无效的文章ID"
Response.End
End If
connStr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & Server.MapPath("database.mdb")
Set conn = Server.CreateObject("ADODB.Connection")
Set rs = Server.CreateObject("ADODB.Recordset")
conn.Open connStr
sql = "SELECT * FROM Articles WHERE ArticleID = " & articleId
rs.Open sql, conn, 1, 1 ' 1=adOpenKeyset, 1=adLockReadOnly
If rs.EOF Then
Response.Write "文章不存在"
rs.Close
conn.Close
Set rs = Nothing
Set conn = Nothing
Response.End
End If
%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8"><%= rs("Title") %></title>
<style>
.vote-container { margin-top: 20px; font-size: 14px; }
.vote-btn { padding: 5px 10px; margin-right: 10px; cursor: pointer; border: 1px solid #ccc; background: #f0f0f0; }
.vote-btn:hover { background: #e0e0e0; }
.vote-result { margin-left: 10px; }
</style>
</head>
<body>
<h1><%= rs("Title") %></h1>
<p><%= rs("Content") %></p>
<div class="vote-container">
<span>这个内容对您有帮助吗?</span>
<!--
使用JavaScript的AJAX来调用vote.asp,避免页面刷新
data: {id: <%= articleId %>, action: 'up'} 将参数传递给vote.asp
-->
<button class="vote-btn" onclick="vote(<%= articleId %>, 'up')">顶 👍</button>
<button class="vote-btn" onclick="vote(<%= articleId %>, 'down')">踩 👎</button>
<span class="vote-result">
顶: <span id="up-count"><%= rs("UpVotes") %></span> |
踩: <span id="down-count"><%= rs("DownVotes") %></span>
</span>
</div>
<script>
function vote(articleId, action) {
// 创建一个 XMLHttpRequest 对象
var xhr = new XMLHttpRequest();
// 指定请求方式和URL
xhr.open("GET", "vote.asp?id=" + articleId + "&action=" + action, true);
// 定义回调函数,当请求完成时执行
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
// 获取服务器返回的文本
var response = xhr.responseText;
if (response == "success") {
alert("投票成功!");
// 更新页面上的计数
updateCounts(articleId);
} else {
// 显示服务器返回的错误信息(如"您已经投过票了")
alert(response);
}
}
};
// 发送请求
xhr.send();
}
function updateCounts(articleId) {
// 这里可以再发一个请求去获取最新的顶踩数,然后更新页面
// 为了简化,我们这里只做前端逻辑演示,实际项目中应该再次调用一个获取数据的接口
// getVoteCounts.asp?id=...
// 这里我们简单模拟一下,实际项目中请替换为真实逻辑
var upSpan = document.getElementById('up-count');
var downSpan = document.getElementById('down-count');
if (upSpan && downSpan) {
// 注意:这里只是示例,真实数据需要从服务器获取
// upSpan.innerText = parseInt(upSpan.innerText) + 1; // 如果是顶
// downSpan.innerText = parseInt(downSpan.innerText) + 1; // 如果是踩
}
}
</script>
<%
' 清理资源
rs.Close
conn.Close
Set rs = Nothing
Set conn = Nothing
%>
</body>
</html>
ASP.NET (C# + SQL Server)
这是现代 ASP 开发的推荐方式,代码更结构化,性能也更好。
第 1 步:数据库设计
与经典 ASP 类似,我们可以使用一个 Entity Data Model (EDM) 来映射数据库表,假设我们有一个 Article 类:
// Article.cs (Model)
public class Article
{
public int ArticleID { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public System.DateTime CreateDate { get; set; }
public int UpVotes { get; set; }
public int DownVotes { get; set; }
}
第 2 步:创建投票处理 API (VoteController.cs)
在 ASP.NET MVC 或 Web API 中,创建一个 Controller 来处理投票请求。
using System.Web.Mvc;
using System.Web.SessionState; // 需要这个命名空间来使用Session
public class VoteController : Controller
{
// 你的数据库上下文
private MyDbContext db = new MyDbContext();
// GET: Vote
[HttpGet]
public ActionResult Vote(int id, string action)
{
// 1. 参数校验
if (id <= 0 || (action != "up" && action != "down"))
{
return Json(new { success = false, message = "参数错误" }, JsonRequestBehavior.AllowGet);
}
// 2. 防重复投票逻辑 (使用Session)
string sessionKey = "Voted_Article_" + id;
if (Session[sessionKey] == null)
{
// 使用数据库事务确保原子性
using (var transaction = db.Database.BeginTransaction())
{
try
{
var article = db.Articles.Find(id);
if (article == null)
{
return Json(new { success = false, message = "文章不存在" }, JsonRequestBehavior.AllowGet);
}
if (action == "up")
{
article.UpVotes++;
}
else // action == "down"
{
article.DownVotes++;
}
db.SaveChanges();
transaction.Commit();
// 标记用户已投票
Session[sessionKey] = true;
return Json(new { success = true, message = "投票成功" }, JsonRequestBehavior.AllowGet);
}
catch
{
transaction.Rollback();
return Json(new { success = false, message = "服务器错误,投票失败" }, JsonRequestBehavior.AllowGet);
}
}
}
else
{
return Json(new { success = false, message = "您已经投过票了" }, JsonRequestBehavior.AllowGet);
}
}
}
第 3 步:在 View 中集成顶踩功能 (Details.cshtml)
使用 Razor 语法的视图,并结合 jQuery 来进行 AJAX 调用。

(图片来源网络,侵删)
@model YourProject.Models.Article
@{
ViewBag.Title = Model.Title;
}
<h2>@Model.Title</h2>
<p>@Html.Raw(Model.Content)</p>
<div class="vote-container">
<span>这个内容对您有帮助吗?</span>
<button class="vote-btn" data-id="@Model.ArticleID" data-action="up">顶 👍</button>
<button class="vote-btn" data-id="@Model.ArticleID" data-action="down">踩 👎</button>
<span class="vote-result">
顶: <span id="up-count">@Model.UpVotes</span> |
踩: <span id="down-count">@Model.DownVotes</span>
</span>
</div>
@section Scripts {
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(document).ready(function () {
$(".vote-btn").click(function () {
var button = $(this);
var articleId = button.data("id");
var action = button.data("action");
// 禁用按钮,防止重复点击
button.prop("disabled", true);
// 使用jQuery的AJAX方法
$.ajax({
url: '/Vote/Vote', // 对应Controller的Action
type: 'GET',
data: { id: articleId, action: action },
dataType: 'json',
success: function (response) {
if (response.success) {
alert(response.message);
// 更新计数,这里需要重新获取数据或在前端逻辑中处理
// 更好的做法是再发一个请求去刷新数据
refreshVoteCounts(articleId);
} else {
alert(response.message);
}
},
error: function () {
alert("网络错误,请重试。");
},
complete: function () {
// 无论成功失败,都重新启用按钮
button.prop("disabled", false);
}
});
});
});
function refreshVoteCounts(articleId) {
// 这里可以调用一个专门获取投票数据的API
// $.get('/Article/GetVoteCounts', { id: articleId }, function(data) {
// $('#up-count').text(data.upVotes);
// $('#down-count').text(data.downVotes);
// });
// 为了简化,我们只刷新当前页面(用户体验不佳,但逻辑简单)
// window.location.reload();
}
</script>
}
总结与扩展
| 特性 | 经典 ASP (VBScript) | ASP.NET (C#) |
|---|---|---|
| 技术 | VBScript, ADO, Session, 原生JS AJAX | C#, LINQ, Entity Framework, Session, jQuery/AJAX, MVC/WebAPI |
| 结构 | 脚本式页面,逻辑与视图混合 | 控制器-模型-视图分离,结构清晰 |
| 安全性 | 较弱,需注意SQL注入(示例中未做参数化查询) | 较强,Entity Framework 默认防SQL注入,易于实现安全策略 |
| 可维护性 | 较差,复杂项目难以管理 | 很好,面向对象,易于扩展和维护 |
| 性能 | 较低 | 较高,编译型语言,有成熟的缓存机制 |
扩展建议:
- 更严格的防作弊:Session 和 Cookie 在用户清除后会失效,更高级的做法是将用户IP或登录用户ID与文章ID绑定,记录到一张
Votes表中,投票时先检查这张表。 - 使用异步 AJAX:在 ASP.NET 中,推荐使用
async/await和Task<ActionResult>来处理异步请求,避免阻塞线程,提高并发性能。 - 前端优化:投票成功后,不应使用
alert,而是用更友好的方式(如 Toast 提示)来反馈信息,并动态更新计数,避免页面刷新。 - 安全性:始终对用户输入进行校验,防止 SQL 注入和 XSS 攻击,在 ASP.NET 中,使用 Razor 语法和
@Html.Raw()时要格外小心。
