JavaWeb 学习笔记 8:AJAX

JavaWeb 学习笔记 8:AJAX

AJAX(Asynchronous JavaScript And XML,异步 js 和 XML)是一种用 js 代码异步(或同步)的方式请求服务端数据,并在页面显示或加载的技术。

1.快速入门

先看如何用纯 js 的方式使用 AJAX:

定义一个用于响应 AJAX 请求的 Servlet:

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().println("Hello World!");
    }
}

定义一个 html 页面:

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
<button onclick="doAjaxRequest()">sendbutton>
body>
<script>
    function doAjaxRequest(){
        // 创建 XMLHttpRequest 对象
        const xhttp = new XMLHttpRequest();

        // 定义回调函数
        xhttp.onreadystatechange = function() {
            if (this.readyState == 4 && this.status == 200) {
                let result = this.responseText;
                console.log(result);
            }
        };
        // 发送请求
        xhttp.open("GET", "http://localhost:8080/ajax-demo/hello", true);
        xhttp.send();
    }
script>
html>

js 方法doAjaxRequest负责创建一个 XMLHttpRequest 对象,并用其发送 AJAX 请求,然后将返回的内容打印在控制台中。

关于原生 AJAX 的详细说明,可以阅读这里。

点击按钮,可以看到浏览器发送了一个类型为 XHR(XMLHttpRequest) 的请求到服务端:

image-20230913114146316

并且可以看到控制台输出。

2.案例:验证用户是否存在

服务端:

@WebServlet("/user/exist")
public class ExistController extends HttpServlet {
    private UserService userService = new UserServiceImpl();
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        boolean exists = userService.checkUsernameExists(username);
        if (exists){
            resp.getWriter().print("true");
        }
        else{
            resp.getWriter().print("false");
        }
    }
}

在页面表单中输入用户名的元素上设置光标焦点事件:

<input name="username" type="text" id="username" onblur="checkUsernameExists()">

对应的 js:

// 检查用户名是否存在
function checkUsernameExists() {
    let username = $("input#username").val();
    const xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
            let result = this.responseText;
            console.log(result)
            if (result == "true"){
                console.log("用户名已存在")
                $("span#username_err").html("用户名已存在");
                $("span#username_err").show();
            }
            else{
                $("span#username_err").hide();
            }
        }
    };
    xhttp.open("POST", "http://localhost:8080/login-demo/user/exist?username="+username, true);
    xhttp.send();
}

3.Axios

Axios 是封装好的一个异步调用框架,可以运行于浏览器或 Node.js 服务器上。

使用 Axios 可以让我们之前的 AJAX 调用的代码更为简单。

在 html 中添加对 Axios 的引用:

<head>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js">script>
head>

修改 AJAX 部分代码,使用 Axios 进行调用:

// 检查用户名是否存在
function checkUsernameExists() {
    let username = $("input#username").val();
    axios({
        method: 'post',
        url: '/login-demo/user/exist?username=' + username
    })
        .then(function (response) {
        let result = response.data;
        if (result) {
            $("span#username_err").html("用户名已存在");
            $("span#username_err").show();
        } else {
            $("span#username_err").hide();
        }
    });
}

axios对象可以接收一个 js 对象,其method属性指定了 HTTP Method,url指定请求的 url。

需要注意的是,这里 Axios 将返回的响应报文体进行了解析,所以这里response.data不是一个字符串形式的truefalse,而是 bool 类型。

除了上边的方式,还可以使用一种简化的方式:

// 检查用户名是否存在
function checkUsernameExists() {
    let username = $("input#username").val();
    axios.post('/login-demo/user/exist?username=' + username)
        .then(function (response) {
        let result = response.data;
        if (result) {
            $("span#username_err").html("用户名已存在");
            $("span#username_err").show();
        } else {
            $("span#username_err").hide();
        }
    });
}

4.JSON

通常浏览器和服务端通过异步调用方式传输的数据结构都很复杂,所以会使用 JSON 格式的字符串进行传输。

浏览器端:

// 检查用户名是否存在
function checkUsernameExists() {
    let username = $("input#username").val();
    axios.post('/login-demo/user/exist', {"username": username})
        .then(function (response) {
        let result = response.data;
        if (result.exist) {
            $("span#username_err").html("用户名已存在");
            $("span#username_err").show();
        } else {
            $("span#username_err").hide();
        }
    });
}

服务端:

@WebServlet("/user/exist")
public class ExistController extends HttpServlet {
    private UserService userService = new UserServiceImpl();
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        BufferedReader reader = req.getReader();
        StringBuilder sb = new StringBuilder();
        do{
            String line = reader.readLine();
            if (line == null){
                break;
            }
            sb.append(line);
        }
        while (true);
        String content = sb.toString();
        System.out.println(content);
        JSONObject jsonObject = JSON.parseObject(content);
        String username = (String) jsonObject.get("username");
        boolean exists = userService.checkUsernameExists(username);
        JSONObject resultJO = new JSONObject();
        if (exists){
            resultJO.put("exist", true);
        }
        else{
            resultJO.put("exist", false);
        }
        resp.setContentType("application/json;charset=UTF-8");
        resp.getWriter().print(resultJO.toJSONString());
    }
}

这里使用了一个中间件 FastJSON,用于在服务端解析和编码 JSON 字符串:

<dependency>
    <groupId>com.alibabagroupId>
    <artifactId>fastjsonartifactId>
    <version>1.2.75version>
dependency>

5.案例:异步加载品牌列表

可以用 Axios+JSON 异步加载品牌列表。

后端:

@WebServlet("/brand/list")
public class ListController extends HttpServlet {
	// ...
    /**
     * 获取品牌列表
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/json;charset=utf-8");
        List<Brand> brands = brandService.getAllBrands();
        response.getWriter().print(JSON.toJSONString(brands));
    }
}

前端:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>



    
    Title
    
    


${username},欢迎您



序号 品牌名称 企业名称 排序 品牌介绍 状态 操作

这里为表格添加了一个用于示例数据的行,并使用 JQuery 选择器获取这个行的 DOM 对象,并进行拷贝,填充数据后依次添加到表格的末尾。

用类似的方式可以将新增品牌也修改为前端异步提交而非表单提交,这里不再赘述。

6.Template

在前边的示例中,使用一个表单中的“隐藏行”作为模板,用于遍历品牌信息并填充数据,然后添加到表格 DOM 树中,并最终称为一个有数据的表格。

这样做是可行的,但是“隐藏行”只是用户看不见,实际上依然是存在的,并且会影响页面的渲染速度。对此,有一个专门用于此类问题的 Html 标签 template,可以用它作为模板 Html 代码存放的位置。这样做的好处在于,