您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

Ajax 前后端交互例子

前面的章节,我们主要从什么是 Ajax为什么要用 AjaxAjax 是如何实现的以及如何封装 Ajax来对 Ajax 做多方位的学习。从前面章节的学习中,相信同学们对 Ajax 都会有比较完整的概念了。那么,接下来的这个章节,我们着重列举示例,来讲述 Ajax 是如何进行前后端交互的。

构建简单的课程录入和。

录入的课程涉及 4 个字段,分别为 课程名( name )、老师( teacher )、开始时间( start_time )、结束时间( end_time )

CREATE TABLE `course`  (
`id` int(11) UNSIGNED ZEROFILL NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`teacher` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`start_time` date NOT NULL,
`end_time` date NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `course`(`id`, `name`, `teacher`, `start_time`, `end_time`) VALUES (00000000001, 'vue源码', '陈小小', '2020-03-15', '2020-03-31');

这条数据是初始化数据,主要是让我们能够一开始就在的表格上看到数据而已。不插入也可以。只是结果就是空。

如果你使用了可视化管理工具,比如 Navicat,那么此时,你会看到这样表格:

<!DOCTYPE html>
<html lang="en">
    <head>
        < charset="utf-8" />
        <title>ajax example</title>
    </head>

    <style>
        table {
            border-collapse: collapse;
            text-align: center;
            width: px;
        }
        table td,
        table th {
            border: px solid #b8cfe9;
            color: #666;
            height: px;
        }
        table tr:nth-child(odd) {
            background: #fff;
        }
        table tr:nth-child(even) {
            background: rgb(, , );
        }

        input {
            outline-style: none;
            border: px solid #ccc;
            border-radius: px;
            padding: px px;
            width: px;
            font-size: px;
        }

        button {
            border: ;
            background-color: rgb(, , );
            color: #fff;
            padding: px;
            border-radius: px;
            margin-top: px;
        }
    </style>
    
    <body>
        <div id="container">
            <!--------列表模块------------->
            <div class="query">
                <h3>课程列表</h3>
                <table id="courseTable"></table>
            </div>
			<!--------列表模块 结束------------->
            
            <!--------课程录入模块------------->
            <div class="create">
                <h3>课程</h3>
                <div>
                    <label for="name">课程:</label><br />
                    <input type="text" id="name" name="name" /><br />

                    <label for="teacher">老师:</label><br />
                    <input type="text" id="teacher" name="teacher" /><br />

                    <label for="startTime">开始时间:</label><br />
                    <input type="date" id="startTime" name="startTime" /><br />

                    <label for="endTime">结束时间:</label><br />
                    <input type="date" id="endTime" name="endTime" /><br />

                    <button id="submitBtn">点击提交</button>
                </div>
            </div>
            <!--------课程录入模块  结束------------->
        </div>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
        <script src="/__build__/example.js"></script>
    </body>
</html>

如上所示,我们首先定义好的结构和样式。可以清晰看出。主要分为两块,上面一块展示的是所有课程的结果,并且是表格呈现的,这里的 table 之所以没有嵌套,是因为我们会在后面 JavaScript 部分进行插入。下面一块则是录入课程的模块,分别有 课程、老师、开始时间和结束时间 4 个 input 。

首先我们先不去管如何向后端一条数据,我们来做简单的数据。

那么, 前后端分别什么?

简单来说,前后端按顺序应该是这样的:

Talk is cheap,接下来我们来实现一下。

const getValues = () => {
    return Ajax({
        method: 'get',
        url: '/course/get'
    })
}

这个我们返回Ajax请求对象,实际上是 promise,当服务端返回数据的时候,我们可以在后续的 ·then() 中进行表格更新。

这里要注意的是,由于 Ajax() 返回的是 promise, 所以 getValues() 返回的将会是个 promise。

const updateTable = (data = []) => {
    const table = document.querySelector('#courseTable'); // 的table
    
    // table 的项, 这里初始化会表格头
    let tableItems = [
    `<tr>
        <th>课程</th>
        <th>老师</th>
        <th>开始时间</th>
        <th>结束时间</th>
    </tr>`];
    
    // 对数据做遍历, 处理为 HTML 字符串并到对应的 tableItems 的项中。
    data.forEach(item => {
        const {name, teacher, startTime, endTime} = item
        tableItems.push(
            `<tr>
                <td>${name}</td>
                <td>${teacher}</td>
                <td>${moment(startTime).format('YYYY-MM-DD')}</td>
                <td>${moment(endTime).format('YYYY-MM-DD')}</td>
            </tr>`
        );
        table.innerHTML = tableItems.join(''); // 数组转为字符串, 到 table 中
    })
}

表格更新接收数组数据,并且会遍历这个数组,分别把每一项处理为 HTML 字符串并到 tableItems 中。最后会把 tableItems 这个数组 通过 .join('') 的方式转化及拼接为 HTML 字符串,并到 table 中。

有了上面操作,负责数据,负责更新表格,那么我们可以合二为一,创造来控制流程。

const updateTableProcess = () => {
    return getValues().then(res => {
        const {code, data} = res
        if (code === ) { // 返回结果 code 为 0, 这里视为正确响应
            const {items} = data // 返回结果中的列表
            updateTable(items) // 更新表格
        }
    }).catch(e => {
        alert('数据失败啦')
    })
}

updateTableProcess(); // 执行

有求也要有应。服务端也需要在前端发出请求的时候做出相应的响应。

const express = require("express");
const  = require('');
const bodyParser = require("body-parser");

const router = express.Router(); // express 路由

const app = express();

// 使用 bodyParser 中间件
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

registerRouter();  // 路由执行

app.use(router); // 使用 router

/**
 * 构建返回结果
 * @param {*} code 
 * @param {*} data 
 * @param {*} error 
 */
const esponse = (code, data = {}, error = null) => {
    return {
        code,
        data,
        error
    }
}

// 创建  , 本地为 mk
const connection = .createConnection({
    host     : 'localhost',
    user     : 'root',
    password : 'ok36369ok',
    database : 'mk'
});
connection.connect();

// 端口号
const port = process.env.PORT || ;

// 监听
module.exports = app.listen(port, () => {
    console.log(`Server listening on http://localhost:${port}, Ctrl+C to stop`);
});




/***********路由模块*************/

/**
*路由
*/
function registerRouter() {
    // 课程
    router.get("/course/get", function(req, res) {
        connection.query('SELECT id, name, teacher, start_time as startTime, end_time as endTime from course', function (error, results, fields) {
            if (error) throw error;
            const responseData = esponse(, {items: results}) //  结果包装后进行返回
            res.send(responseData); // send 结果
        });
    });
    
    // other router ..
}

如上所示,我们引入了 Express 框架、bodyParser 以及 相关的库。其中, bodyParser 可以解析请求中 body 的。 不过我们的重点应该是最下面的 registerRouter,这个是用来我们所有的路由的。我们之后的路由也都会写个里面。

好了,回归正题。为了使服务端响应前端的请求,我们在上面的中了路由:

 router.get("/course/get", callback)

如果前端发送请求到 “/course/get” ,那服务端会触发回调 callback,对应到上面中,我们可以看到:

相同的,我们也可以使用 java 来实现后端的逻辑。

package com.demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java..*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@WebServlet("/course/get")
public class HelloWorld extends HttpServlet {

    // JDBC 驱动名
    static final String JDBC_DRIVER = "com..jdbc.Driver";
    //  URL
    static final String DB_URL = "jdbc:://localhost:3306/mk?useUnicode=true&useJDBCCompliantTimezoneShift\n" +
            "=true&useLegacyDatetimeCode=false&serverTimezone=UTC";

    // 的
    static final String USER = "root";
    // 的密码
    static final String PW = "ok36369ok";

    /**
    * 包装返回结果
    */
    private Map esponse(int code, Object data, String error) {
        Map<String, Object> res = new HashMap<String, Object>();
        res.put("code", code);
        res.put("data", data);
        res.put("error", error);
        return res;
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    // 课程使用 GET, 会进入 doGet
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Connection conn = null;
        Statement stmt = null;
        
        // 设置编码格式
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/json; charset=utf-8");
        PrintWriter out = response.getWriter();
        Map<String,Object> resMap = new HashMap<String,Object>(); // 返回结果, Map 类型
        try{
            //  JDBC 驱动
            Class.forName(JDBC_DRIVER);

            // 打开
            Sy.out.println("连接...");
            conn = DriverManager.getConnection(DB_URL,USER,PW);

            // 执行
            Sy.out.println(" 实例化Statement对象...");
            stmt = conn.createStatement();
            String ;
             = "SELECT * FROM course";
            ResultSet rs = stmt.executeQuery();
            List<Map> Courses = new ArrayList<Map>();

            // 展开结果集
            while(rs.next()){
                // 通过字段检索
                Map<String,Object> map = new HashMap<String, Object>();
                int id  = rs.getInt("id");
                String name = rs.getString("name");
                String teacher = rs.getString("teacher");
                Date startTime = rs.getDate("start_time");
                Date endTime = rs.getDate("end_time");
                
                // 分别将  结果 put 到 map中
                map.put("id", id);
                map.put("name", name);
                map.put("teacher", teacher);
                map.put("startTime", startTime);
                map.put("endTime", endTime);
                Courses.add(map);
            }
            Map<String, List> data = new HashMap<String, List>(); // 定义返回数据的 data部分, Map 类型
            data.put("items", Courses); // data  items, items 就是我们要的课程列表数据

            // 构建数据
            resMap = esponse(, data, null);
            // 完成后
            rs.close();
            stmt.close();
            conn.close();
        }catch(Exception se){
            // 处理 JDBC 
            se.printStackTrace();
        }catch(Exception e){
            // 处理 Class.forName 
            e.printStackTrace();
        }finally{
            // 资源
            try{
                if(stmt!=null) stmt.close();
            }catch(Exception se2){
            }
            try{
                if(conn!=null) conn.close();
            }catch(Exception se){
                se.printStackTrace();
            }
        }
        String responseData = JSON.toJSONString(resMap);// 将 Map 类型的结果序列化为 String
        out.println(responseData); // 返回结果
    }
}

这里主要使用的是 servlet 的方式来为前端提供服务,对于请求课程列表来说,使用到 GET ,因此本例子中会进入到 doGet 中。另外,所用到的技术在章节须知中也有讲到,这里就不再累赘。实现的虽然和 node 端有所差别,但是思想都是一样的。无非也是使用 的结果, 拼装成前端所需要的数据。并进行返回。

好了,前后端都有相应的部署后,我们可以来看看了:

我们可以看到,刷新的时候,前端会通过 Ajax 发出 get 请求,服务端返回数据后,前端便在上绘制出表格。

向服务端数据,我们会采用 POST 请求。思路也非常简单,大致过程如下:

咱一步一步来,首先我们需要有些和事件,来处理提交按钮的点击事件,并发送请求。

const submitBtn = document.getElementById('submitBtn'); // 提交按钮

/**
* 提交数据, 返回的是 promise
*/
const submitValues = (values) => {
    return Ajax({
        method: 'post',
        url: '/course/post',
        data: values
    })
}

// 提交按钮绑定点击事件处理
submitBtn.addEventListener('click', () => {
    const inputItems = ['name', 'teacher', 'startTime', 'endTime'] // 表单项几个 key
    const values = {} // 初始化返回数据
    inputItems.forEach(key => {
        // 遍历 inputItems,对应的 input 上的 value
        // 如果 value 有值, 则相应的项
        const value = document.getElementById(key).value;
        if (!value) return;
        values[key] = value
    })
    
    // 最后发送提交请求
    submitValues(values).then(data => {
        updateTableProcess(); // 成功后重新更新表格
    })
})

这段并显然没有考虑多重情况,比如填写的项是否可以为空,或者填写的值的格式控制,不过这都不重要。我们这里认全部填写,并且是正确的情况。着重关注发送请求并带上 data 到后端的过程。

假如我们已经录入了数据并点击了提交按钮,那此时服务端也需要有相应的路由来响应这个请求。简单的步骤,我们需要分三步走:

对照着这几个步骤,我们来看看下面的:

// 课程
router.post("/course/post", function(req, res) {
    const {name, teacher, startTime, endTime} = req.body // 解构出每个字段
    
    //  语句, “?” 会对应后面 values
    const  = `INSERT INTO course(name, teacher, start_time, end_time) VALUES (?,?,?,?);` 
    const values = [name, teacher, startTime,endTime] // 插入的所有值
    
    // 执行  语句
    connection.query(, values,function (error, results, fields) {
        if (error) throw error;
        const responseData = esponse(, null) // 包装结果
        res.send(responseData); // 返回结果
    });
});

在服务端的响应中,我们可以看到。当服务端接收到请求的时候,会执行 语句,把前端发送的请求中的数据到 中,然后返回成功的结果。

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 解析 body 中的参数,结果将会到 data 上
    StringBuffer data = new StringBuffer();
    String line = null;
    BufferedReader reader = null;
    try {
        reader = request.getReader();
        while (null != (line = reader.readLine()))
            data.append(line);
    } catch (IOException e) {
    } finally {
    }
    
    JSONObject body= JSON.parbject(data.toString()); // 将解析后的结果转变为 JSONObject 对象
    Connection conn = null;
    PreparedStatement stmt = null;
    
    // 设置编码
    request.setCharacterEncoding("utf-8");
    response.setContentType("text/json; charset=utf-8");
    
    // 对象
    PrintWriter out = response.getWriter();
    
    // 返回结果, Map 类型
    Map<String,Object> resMap = new HashMap<String,Object>();
    try{
        //  JDBC 驱动
        Class.forName(JDBC_DRIVER);

        // 打开
        Sy.out.println("连接...");
        conn = DriverManager.getConnection(DB_URL,USER,PW);

        // 执行
        String ;

        // 插入的数据, 执行
         = "INSERT INTO course(name, teacher, start_time, end_time) VALUES (?,?,?,?);";
        stmt = conn.prepareStatement();
        stmt.setString(, body.getString("name"));
        stmt.setString(, body.getString("teacher"));
        stmt.setString(, body.getString("startTime"));
        stmt.setString(, body.getString("endTime"));
        stmt.executeUpdate();

        stmt.close();
        conn.close();

        // 数据
        resMap = esponse(, null, null);
        String responseData = JSON.toJSONString(resMap);
        out.println(responseData);
    }catch(Exception se){
        // 处理 JDBC 
        se.printStackTrace();
    }catch(Exception e){
        // 处理 Class.forName 
        e.printStackTrace();
    }finally{
        // 资源
        try{
            if(stmt!=null) stmt.close();
        }catch(Exception se2){
        }
        try{
            if(conn!=null) conn.close();
        }catch(Exception se){
            se.printStackTrace();
        }
    }
}

这里 Java 部分只给出 doPost 部分,这里有个地方需要注意,在 doPost 中,我们在一开始需要对 body 进行解析,我们需要的参数。其他的思路也是一样的,在 语句成功执行之后,我们包装好结果进行返回。

可以看到,当我们点击提交的时候,前端发送 POST 请求,触发服务端插入新数据。当前端接收到服务端的响应之后,又发送了 GET 请求,最新的课程列表数据,并绘制到网页上。

讲到这里,本章节也就到了尾声。

本章节旨在通过课程录入的例子,并使用 GET 和 POST 两种请求方式来诠释前后端交互的完整过程。希望同学们个过程中,能够学习到 Ajax 在实际交互中的简单的应用,:

在实际的开发工作中, Ajax 好比前后端之桥梁,我们可以通过这个桥梁进行通信。然而,如何通信,何时通信,更需要我们有良好的程序控制。

总而言之,希望同学们能够多用、善用 Ajax,并且让它在你们的程序中发光发亮。


联系我
置顶