1. 数据存储:需要一个地方来存储每个帖子的顶数和踩数,最常用的是数据库表。
  2. 用户交互:用户点击“顶”或“踩”按钮。
  3. 数据更新:服务器接收到点击后,更新数据库中对应帖子的顶/踩计数。
  4. 防重复投票:这是关键,需要防止同一个用户在短时间内重复投票,通常使用 Session 或 Cookie 来实现。

经典 ASP (VBScript + Access/SQL Server)

这个方案适合于老旧项目或学习经典 ASP 的开发。

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)

这个页面会显示文章内容,并包含顶踩按钮。

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 调用。

asp网页实现顶踩功能
(图片来源网络,侵删)
@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注入,易于实现安全策略
可维护性 较差,复杂项目难以管理 很好,面向对象,易于扩展和维护
性能 较低 较高,编译型语言,有成熟的缓存机制

扩展建议:

  1. 更严格的防作弊:Session 和 Cookie 在用户清除后会失效,更高级的做法是将用户IP或登录用户ID与文章ID绑定,记录到一张 Votes 表中,投票时先检查这张表。
  2. 使用异步 AJAX:在 ASP.NET 中,推荐使用 async/awaitTask<ActionResult> 来处理异步请求,避免阻塞线程,提高并发性能。
  3. 前端优化:投票成功后,不应使用 alert,而是用更友好的方式(如 Toast 提示)来反馈信息,并动态更新计数,避免页面刷新。
  4. 安全性:始终对用户输入进行校验,防止 SQL 注入和 XSS 攻击,在 ASP.NET 中,使用 Razor 语法和 @Html.Raw() 时要格外小心。