首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

基於 Ajax 的用戶名驗證、條款加載與驗證碼生成完整實戰

     編輯:Java 相關知識

前言

Ajax(Asynchronous JavaScript and XML)通過 XMLHttpRequest 實現瀏覽器與服務器異步通信,避免整頁刷新,提升用戶體驗。本文通過一個完整的用戶註冊案例,演示用戶名異步校驗、服務條款異步加載、驗證碼生成三大核心場景的前後端實現。

一、核心原理回顧

同步 vs 異步

模式特點用戶體驗
同步提交請求 → 等待響應 → 頁面跳轉/重繪阻塞,無法操作
異步事件觸發請求 → 後台處理 → 回調更新局部頁面非阻塞,可繼續操作

XMLHttpRequest 關鍵屬性

  • readyState:0(未初始化) 1(已open) 2(已send) 3(接收中) 4(完成)
  • status:HTTP 狀態碼(200 成功)
  • responseText:文本響應
  • responseXML:XML 響應

數據交換格式

  1. Text/HTML:簡單文本片段
  2. XML:結構化數據,解析較重
  3. JSON:輕量級,JS 原生支持,現代首選

二、前端實現(JSP + JS + CSS)

1. 頁面結構 regist.jsp

<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<html>
<head>
    <title>用戶註冊</title>
    <link href="css/regist.css" rel="stylesheet" type="text/css" />
    <script src="js/regist_ajax.js" type="text/javascript"></script>
</head>
<body onload="getRegistRuleTxt(), getCheckcode()">
    <form action="regist.do" onsubmit="return check(this);">
        <table>
            <tr>
                <td>用戶名:</td>
                <td><input type="text" name="username" id="username" onblur="postValidate()" />
                    <span id="checkMsg">* 用戶名由字母、數字、下劃線組成</span></td>
            </tr>
            <tr>
                <td>密碼:</td>
                <td><input type="password" name="password" id="password" /></td>
            </tr>
            <tr>
                <td>確認密碼:</td>
                <td><input type="password" name="repassword" id="repassword" /></td>
            </tr>
            <tr>
                <td>郵箱:</td>
                <td><input type="text" name="email" id="email" /></td>
            </tr>
            <tr>
                <td>驗證碼:</td>
                <td>
                    <input type="text" id="checkcode" />
                    <img id="ckcodeimage" src="imgsrc" />
                    <a href="javascript:;" onclick="getCheckcode()">看不清,換一張</a>
                    <span id="ckcodeMsg"></span>
                </td>
            </tr>
            <tr>
                <td>服務條款:</td>
                <td><textarea rows="5" cols="48" readonly id="item"></textarea></td>
            </tr>
            <tr><td><input type="submit" value="同意條款並註冊" /></td></tr>
        </table>
    </form>
</body>
</html>

2. 核心 JS 邏輯 regist_ajax.js

var xmlHttpRequest = getXmlHttpRequest();

// 跨瀏覽器創建 XHR
function getXmlHttpRequest() {
    var xhr = null;
    if (typeof XMLHttpRequest != 'undefined') {
        xhr = new XMLHttpRequest(); // 現代瀏覽器
    } else {
        xhr = new ActiveXObject('Microsoft.XMLHTTP'); // IE6-
    }
    return xhr;
}

// 防緩存時間戳
function addTimestamp(url) {
    return url + (url.indexOf('?') != -1 ? '&' : '?') + 'timestamp=' + new Date().valueOf();
}

// 1. 異步加載服務條款(頁面加載時觸發)
function getRegistRuleTxt() {
    var item = document.getElementById("item");
    var url = "rulesText.do";
    xmlHttpRequest.open("POST", encodeURI(url), true);
    xmlHttpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    xmlHttpRequest.onreadystatechange = function() {
        if (xmlHttpRequest.readyState == 4) {
            if (xmlHttpRequest.status == 200) {
                item.value = xmlHttpRequest.responseText;
            } else {
                item.value = "系統錯誤";
            }
        } else {
            item.value = "加載中...";
        }
    };
    xmlHttpRequest.send(null);
}

// 2. POST 方式驗證用戶名(失焦觸發)
function postValidate() {
    var username = document.getElementById("username");
    var url = "validatename.do";
    xmlHttpRequest.open('POST', url, true);
    xmlHttpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    xmlHttpRequest.onreadystatechange = callback;
    xmlHttpRequest.send('username=' + escape(username.value));
}

// 3. 統一回調處理
function callback() {
    if (xmlHttpRequest.readyState == 4) {
        if (xmlHttpRequest.status == 200) {
            innerHtmlMsg(xmlHttpRequest.responseText);
        } else {
            innerHtmlMsg("error");
        }
    } else {
        innerHtmlMsg("checking");
    }
}

function innerHtmlMsg(message) {
    var checkMsg = document.getElementById("checkMsg");
    switch (message) {
        case 'exists': checkMsg.innerHTML = "<font color='red'>* 對不起,該用戶名已存在</font>"; break;
        case 'noexists': checkMsg.innerHTML = "<font color='green'>* 恭喜,該用戶名可用</font>"; break;
        case 'checking': checkMsg.innerHTML = "<font color='#0099aa'>* 系統正在檢查...</font>"; break;
        default: checkMsg.innerHTML = "<font color='red'>系統錯誤</font>";
    }
}

// 4. 驗證碼刷新
function getCheckcode() {
    var codeimage = document.getElementById("ckcodeimage");
    codeimage.src = addTimestamp("checkcode.do");
}

3. 樣式 regist.css

table { margin-top:70px; width:780px; border-collapse:collapse; font-size:14px; border:1px solid gray; }
.left { font-weight:500; color:#708899; padding-right:20px; background-color:#D6ECF5; }
.inpt { border:solid #92CEDB 1px; width:210px; height:22px; margin-left:10px; }
.rghts { margin-top:20px; color:#708899; font-size:12px; }

三、後端實現(Servlet + 工具類)

1. web.xml 配置

<web-app>
    <servlet>
        <servlet-name>UserServlet</servlet-name>
        <servlet-class>com.webapp.servlet.UserServlet</servlet-class>
        <init-param>
            <param-name>rulesfilepath</param-name>
            <param-value>/txt/item.txt</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>UserServlet</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>regist.jsp</welcome-file>
    </welcome-file-list>
</web-app>

2. 驗證碼生成工具類

package com.webapp.util;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class CheckCodeImageUtil {
    private static final String[] CHARS = {"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H","I"};
    private static final int SIZE = 4, LINES = 4, WIDTH = 110, HEIGHT = 40, FONT_SIZE = 21;

    public static Map creatCheckImage() {
        StringBuffer buffer = new StringBuffer();
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        Map map = new HashMap<>();
        Graphics g = image.getGraphics();
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, WIDTH, HEIGHT);
        Random random = new Random();

        // 繪製隨機字符
        for (int i = 0; i < SIZE; i++) {
            int rand = random.nextInt(CHARS.length);
            g.setColor(randomColor());
            g.setFont(new Font(null, Font.BOLD | Font.ITALIC, FONT_SIZE));
            g.drawString(CHARS[rand], i * WIDTH / SIZE + 8, HEIGHT / 2 + 10);
            buffer.append(CHARS[rand]);
        }
        // 繪製干擾線
        for (int i = 1; i <= LINES; i++) {
            g.setColor(randomColor());
            g.drawLine(random.nextInt(WIDTH), random.nextInt(HEIGHT), random.nextInt(WIDTH), random.nextInt(HEIGHT));
            if (i == LINES) {
                g.setFont(new Font(null, Font.ITALIC, 13));
                g.setColor(Color.GRAY);
                g.drawString("工作室", 5, 15);
            }
        }
        map.put(buffer.toString(), image);
        return map;
    }

    private static Color randomColor() {
        Random r = new Random();
        return new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256));
    }
}

3. 統一 Servlet 處理器

package com.webapp.servlet;

import com.webapp.util.CheckCodeImageUtil;
import javax.imageio.ImageIO;
import javax.servlet.*;
import javax.servlet.http.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;

public class UserServlet extends HttpServlet {
    private List userList; // 模擬用戶庫
    private String txtFilePath;

    @Override
    public void init() throws ServletException {
        txtFilePath = getInitParameter("rulesfilepath");
        userList = new Vector<>(Arrays.asList("zhangsan", "lisi", "wangwu", "zhaoliu"));
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String path = req.getRequestURI().substring(req.getRequestURI().lastIndexOf("/"), req.getRequestURI().lastIndexOf("."));
        req.setCharacterEncoding("utf-8");

        if ("/validatename".equals(path)) {
            resp.setContentType("text/html;charset=utf-8");
            try { Thread.sleep(3000); } catch (InterruptedException e) {} // 模擬延遲
            String username = req.getParameter("username");
            resp.getWriter().print(userList.contains(username) ? "exists" : "noexists");
        }
        else if ("/rulesText".equals(path)) {
            resp.setContentType("text/html;charset=utf-8");
            String filePath = getServletContext().getRealPath(txtFilePath);
            StringBuilder sb = new StringBuilder();
            try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
                String line;
                while ((line = br.readLine()) != null) {
                    sb.append(new String(line.getBytes("gbk"), "utf-8")).append("\n");
                }
            }
            resp.getWriter().print(sb.toString());
        }
        else if ("/checkcode".equals(path)) {
            resp.setContentType("image/jpeg");
            Map map = CheckCodeImageUtil.creatCheckImage();
            String code = map.keySet().iterator().next();
            req.getSession().setAttribute("code", code); // 存入 Session
            ImageIO.write(map.get(code), "jpeg", resp.getOutputStream());
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }
}

四、關鍵技術點總結

場景關鍵技術注意事項
用戶名驗證POST + escape 編碼 + 異步回調防緩存時間戳、狀態碼判斷、提示用戶體驗
條款加載頁面 onload 觸發 + 文件讀取編碼轉換GBK→UTF-8 轉碼、大文本流式輸出
驗證碼生成BufferedImage + Graphics 繪圖 + Session 存儲干擾線/字符、隨機顏色、點擊刷新機制

五、現代化改進建議

  • 前端:使用 fetch API / axios 替代原生 XHR,配合 async/await 簡化回調
  • 後端:Spring MVC + @RestController + Jackson JSON 序列化,統一異常處理
  • 驗證碼:引入 Google Kaptcha / hCaptcha / 滑動驗證,提升安全性與易用性
  • 編碼:全鏈路 UTF-8,避免 GBK 轉碼陷阱
  • 安全:CSRF Token、XSS 防護、SQL 注入防護(參數化查詢)

結語

本案例完整展示了 Ajax 在表單驗證、內容異步加載、圖形驗證碼三大經典場景的落地實現。雖然技術棧偏傳統(JSP/Servlet/原生 JS),但核心思想——異步非阻塞、局部刷新、前後端協議約定——在現代前後端分離架構中依然通用。建議讀者以此為基礎,遷移至 Vue/React + Spring Boot + RESTful API 的現代棧中實踐。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved