表白墙的实现【前后端交互】

目录

一、Servlet API 详解

1. HttpServletRequest

1.1 HttpServletRequest 方法

1.2 getParameter

2.HttpServletResponse

2.1  HttpServletResponse 方法 

2.2 代码示例: 设置状态码

2.3 代码示例: 重定向

二、表白墙

1.准备工作

 2.约定前后端交互接口

2.1 接口一:页面获取当前所有的留言消息

2.2 接口二:提交新消息给服务器

3.调整前端页面代码

4.数据持久化

4.1 数据存入数据库


🌈上节 Servlet 我们学习了 Tomcat 提供的 API;并且编写一个 Hello World 基本步骤:1️⃣创建项目(maven)2️⃣引入依赖(servler)3️⃣创建目录4️⃣编写代码(webapp/WEB-INF/web.xml)5️⃣打包程序(waven package)6️⃣部署程序(把 war 拷贝到 webapps 目录中)7️⃣验证代码;最后我们学习了 Servlet 中的三个核心类

一、Servlet API 详解

1. HttpServletRequest

HttpServletRequest 是一个 HTTP 请求:Tomcat 通过 Socket API 读取 HTTP 请求(字符串),就会按照 HTTP 协议的格式把字符串解析成 HttpServletRequest

1.1 HttpServletRequest 方法

String getProtocol()
返回请求协议的名称和版本
String getMethod()
返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT
String getRequestURI()
从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该请求的 URL 的一部分
String getContextPath()
返回指示请求上下文的请求 URI 部分
String getQueryString()
返回包含在路径后的请求 URL 中的查询字符串
Enumeration getParameterNames()
返回一个 String 对象的枚举,包含在该请求中包含的参数的名称
String getParameter(String name)
以字符串形式返回请求参数的值,或者如果参数不存在则返回 null
String[] getParameterValues(String name)
返回一个字符串对象的数组,包含所有给定的请求参数的值,如果参数不存在则返回 null
Enumeration getHeaderNames()
返回一个枚举,包含在该请求中包含的所有的头名
String getHeader(String name)
以字符串形式返回指定的请求头的值
String getCharacterEncoding()
返回请求主体中使用的字符编码的名称
String getContentType()
返回请求主体的 MIME 类型,如果不知道类型则返回 null
int getContentLength()
以字节为单位返回请求主体的长度,并提供输入流,或者如果长度未知则返回 -1
InputStream getInputStream()
用于读取请求的 body 内容. 返回一个 InputStream 对象

1️⃣ 其中 getRequestURI 不是 URL:URL 唯一资源定位符;URI 唯一资源标识符

2️⃣ query string 查询字符串:例如 http://餐厅:18/熏肉大饼?葱=少放,其中 葱=少放 就是查询字符串

3️⃣Enumeration getParameterNames() 和 String getParameter(String name) 是获取请求中的字符串:上述中 query string 中的键值对

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

//继承 HttpServlet
@WebServlet("/showRequest")
public class ShowRequest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        StringBuilder result = new StringBuilder();
        result.append(req.getProtocol());
        result.append("<br>");
        result.append(req.getMethod());
        result.append("<br>");
        result.append(req.getRequestURI());
        result.append("<br>");
        result.append(req.getQueryString());
        result.append("<br>");
        result.append(req.getContextPath());
        result.append("<br>");

        //在响应中设置上 body 的类型,方便浏览器进行解析
        resp.setContentType("text/html;charset=utf8");
        resp.getWriter().write(result.toString());
    }
}

013de802d4194a2eb5e8d1c1f39405b7.png

1.2 getParameter

getParameter 是最常用的 API 之一,是前端和后端传递数据非常常见的需求

1️⃣通过 query string 传递

约定:前端通过 query string 传递 username 和 password

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;

@WebServlet("/getParameter")
public class GetParameter extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //前端通过 url 的 query string 传递 username 和 password 两个属性
        String username = req.getParameter("username");
        if (username == null) {
            System.out.println("username 这个 key 在 query string 中不存在!");
        }
        String password = req.getParameter("password");
        if (password == null) {
            System.out.println("password 这个 key 在 query string 中不存在!");
        }
        System.out.println("username" + username + ", password" + password);

        resp.getWriter().write("ok");
    }
}

87fa650f30624e49b6c14d89750eb55a.png

在 URL 中 query string 如果是包含 中文/特殊字符,务必要使用 urlencode 的方式转码;如果是直接写 中文/特殊字符,就会存在很大风险;如果不转码,在有些浏览器/http服务器下对中文支持不好的花,会出现问题

b9946e19665c48bf9c0c22dfa812b300.png

2️⃣通过 body (from) 传递

相当于 body 里存的数据的格式,和 query string 一样,但是 Content-Type 是 application/x-www-form-urlencoded,此时也是通过 getParameter 来获取到键值对

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;

@WebServlet("/getParameter")
public class GetParameter extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //给请求设置编码方式
        req.setCharacterEncoding("utf8");        
        //前端通过 body,以 form 表单的格式,把 username 和 password 传给服务器
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if (username == null) {
            System.out.println("username 这个 key 在 body 中不存在!");
        }
        if (password == null) {
            System.out.println("password 这个 key 在 body 中不存在!");
        }
        System.out.println("username" + username + ", password" + password);

        resp.getWriter().write("ok");
    }
}

 由于是 post 请求,在网页中不好表现,我们打开 postman 

3e2b30aa981049c59b28bb9b782a69e0.png

3️⃣通过 body (json) 传递(最常用的传递方式)

 json 也是键值对格式的数据,但是 Servlet 自身没有内置 json 解析功能,因此就需要借助其他第三方库;用来处理 json 的第三方库有很多,常见的如 fastjson、gson、jackson...,我们使用 jackson 来解析

 引入依赖:

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.0</version>
</dependency>
import com.fasterxml.jackson.databind.ObjectMapper;
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;

class User {
    public String username;
    public String password;
}

@WebServlet("/json")
public class JsonServlet extends HttpServlet {
    // 使用 jackson, 最核心的对象就是 ObjectMapper
    // 通过这个对象, 就可以把 json 字符串解析成 java 对象; 也可以把一个 java 对象转成一个 json 格式字符串.
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 通过 post 请求的 body 传递过来一个 json 格式的字符串.
        User user = objectMapper.readValue(req.getInputStream(), User.class);
        System.out.println("username=" + user.username + ", password=" + user.password);

        resp.getWriter().write("ok");
    }
}

6a75893b1cac4a12adbc0e2c5071291f.png

c4f81e1bb0dd4c2ab2202a7d74c9e07b.png

 readValue 里面做的事情:

  1. 解析 json 字符串,转换成若干个键值对
  2. 根据第二个参数 User.class(.class 是类对象,就是这个类的图纸),去找到 User 里的所有的 public 的属性(或者有 public getter setter 的属性),依次遍历......
  3. 遍历属性,根据属性的名字取上述准备好的键值对里查询,看看这个属性名字是否存在对应的 value ,如果存在就把 value 复制到该属性中

58970f15f5ac4a1c9318d75533f74558.png

2.HttpServletResponse

HttpServletResponse 表示一个 HTTP 响应

Servlet 中的 doXXX 方法的目的就是根据请求计算得到相应, 然后把响应的数据设置到 HttpServletResponse 对象中;然后 Tomcat 就会把这个HttpServletResponse 对象按照 HTTP 协议的格式, 转成一个字符串, 并通过 Socket 写回给浏览器.

2.1  HttpServletResponse 方法 

void setStatus(int sc)
为该响应设置状态码
void setHeader(String name, String value)
设置一个带有给定的名称和值的 header. 如果 name 已经存在, 则覆盖旧的值
void addHeader(String name, String value)
添加一个带有给定的名称和值的 header. 如果 name 已经存在, 不覆盖旧的值, 并列添加新的键值对
void setContentType(String type)
设置被发送到客户端的响应的内容类型
void setCharacterEncoding(String charset)
设置被发送到客户端的响应的字符编码(MIME 字符集)例如, UTF-8
void sendRedirect(String location)
使用指定的重定向位置 URL 发送临时重定向响应到客户端
PrintWriter getWriter()
用于往 body 中写入文本格式数据
OutputStream getOutputStream()
用于往 body 中写入二进制格式数据

2.2 代码示例: 设置状态码

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;

@WebServlet("/status")
public class StatusServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setStatus(404);
        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write("返回 404 响应!");
    }
}

dc8e7b7c104f47c6bbb0f6d7e58254d0.png

 抓包结果

fddd7c72d4ff40aeaf7a1e3d0a5a7091.png

 2.3 代码示例: 自动刷新

通过 header 实现自动刷新效果:给 HTTP 响应中,设置 Refresh——时间

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;

//通过 header 实现自动刷新效果:给 HTTP 响应中,设置 Refresh——时间

@WebServlet("/refresh")
public class RefreshServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
        // 每隔 1s 自动刷新一次.
        resp.setHeader("Refresh", "1");
        resp.getWriter().write("time=" + System.currentTimeMillis());
    }
}

e99b037b5c6b4077b5743954a3d98d87.png

2.3 代码示例: 重定向

 代码案例:用户访问这个供暖这个路径的时候,自动重定向到搜狗主页

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;

@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 用户访问这个路径的时候, 自动重定向到 搜狗主页 .
        //resp.setStatus(302);
        //resp.setHeader("Location", "https://www.sogou.com");
        resp.sendRedirect("https://www.sogou.com");
    }
}

部署程序, 通过 URL http://127.0.0.1:8080/hello_servlet/redirectServlet 访问, 可以看到, 页面自动跳转到 搜狗主页 了.

抓包结果:

8b65f3f96c794c99919bfc72c71ffd27.png

二、表白墙

表白墙前端页面代码:message.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>表白墙</title>
    <!-- 引入 jquery -->
    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
    <style>
        /* * 通配符选择器, 是选中页面所有元素 */
        * {
            /* 消除浏览器的默认样式. */
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        .container {
            width: 600px;
            margin: 20px auto;
        }

        h1 {
            text-align: center;
        }

        p {
            text-align: center;
            color: #666;
            margin: 20px 0;
        }

        .row {
            /* 开启弹性布局 */
            display: flex;
            height: 40px;
            /* 水平方向居中 */
            justify-content: center;
            /* 垂直方向居中 */
            align-items: center;
        }

        .row span {
            width: 80px;
        }

        .row input {
            width: 200px;
            height: 30px;
        }

        .row button {
            width: 280px;
            height: 30px;
            color: white;
            background-color: orange;
            /* 去掉边框 */
            border: none;
            border-radius: 5px;
        }

        /* 点击的时候有个反馈 */
        .row button:active {
            background-color: grey;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>表白墙</h1>
        <p>输入内容后点击提交, 信息会显示到下方表格中</p>
        <div class="row">
            <span>谁: </span>
            <input type="text">
        </div>
        <div class="row">
            <span>对谁: </span>
            <input type="text">
        </div>
        <div class="row">
            <span>说: </span>
            <input type="text">
        </div>
        <div class="row">
            <button id="submit">提交</button>
        </div>
        <div class="row">
            <button id="revert">撤销</button>
        </div>
        <!-- <div class="row">
            xxx 对 xx 说 xxxx
        </div> -->
    </div>

    <script>
        // 实现提交操作. 点击提交按钮, 就能够把用户输入的内容提交到页面上显示. 
        // 点击的时候, 获取到三个输入框中的文本内容
        // 创建一个新的 div.row 把内容构造到这个 div 中即可. 
        let containerDiv = document.querySelector('.container');
        let inputs = document.querySelectorAll('input');
        let button = document.querySelector('#submit');
        button.onclick = function() {
            // 1. 获取到三个输入框的内容
            let from = inputs[0].value;
            let to = inputs[1].value;
            let msg = inputs[2].value;
            if (from == '' || to == '' || msg == '') {
                return;
            }
            // 2. 构造新 div
            let rowDiv = document.createElement('div');
            rowDiv.className = 'row message';
            rowDiv.innerHTML = from + ' 对 ' + to + ' 说: ' + msg;
            containerDiv.appendChild(rowDiv);
            // 3. 清空之前的输入框内容
            for (let input of inputs) {
                input.value = '';
            }
        }
        let revertButton = document.querySelector('#revert');
        revertButton.onclick = function() {
            // 删除最后一条消息. 
            // 选中所有的 row, 找出最后一个 row, 然后进行删除
            let rows = document.querySelectorAll('.message');
            if (rows == null || rows.length == 0) {
                return;
            }
            containerDiv.removeChild(rows[rows.length - 1]);
        }
    </script>
</body>
</html>

1.准备工作

1️⃣创建一个 maven 项目

2️⃣引入依赖:servlet、jackson

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>

3️⃣创建必要的目录 webapp、WEB-INF、web.xml

web.xml 中的代码:

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
        <display-name>Archetype Created Web Application</display-name>
</web-app>

4️⃣把之前实现的表白墙前端页面拷贝到 webapp 目录中

db4af3e1db924cb99a30f53e3a49b930.png

7bf6fe4175374055aa2db4f03bf48472.png

 5️⃣配置 Smart Tomcat

df7eae4d6ac94c2296c07ad2a05ed568.png

 6️⃣运行程序就可以访问前端页面

0677bb86d49e49bda82f2a2962874c88.png

 2.约定前后端交互接口

 写代码之前需要明确前后端交互端口

4649121621d543bfb071822adc6e2ebd.png

 🙈什么时候发送请求❓❓


1️⃣页面加载完毕之后,需要给服务器发送请求,获取当前的留言数据都有什么

2️⃣用户点击提交的时候,就需要告诉服务器,当前用户发送了的消息是啥

在交互的过程中,有涉及到关键的问题:请求具体是什么样子❓❓响应具体是什么样子❓❓这些都需要程序猿来设计,这就叫“约定前后端交互端口

此处给出一份典型的约定方式(并不是唯一的方式)

2.1 接口一:页面获取当前所有的留言消息

约定

1️⃣请求:GET/message

2️⃣响应:HTTP/1.1 200 OK

    Content-Type:application/json

[
   {
        from: "从哪里来",
        to: "到哪里去",
        message: "消息是啥"
   },
   {
        from: "从哪里来",
        to: "到哪里去",
        message: "消息是啥"
   },
    ......
]

json 中使用[ ] 表示数组,[ ] 中的每个元素是一个 { } json 对象;每个对象里又有三个属性 from、to、message

2.2 接口二:提交新消息给服务器

1️⃣请求:POST/message

    Content-Type:application/json

[
   {
        from: "从哪里来",
        to: "到哪里去",
        message: "消息是啥"
   },
   {
        from: "从哪里来",
        to: "到哪里去",
        message: "消息是啥"
   },
    ......
]

2️⃣响应:HTTP/1.1 200 OK

处理 "获取所有留言消息":创建 Message 类、创建 MessageServlet 类

import com.fasterxml.jackson.databind.ObjectMapper;
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.util.ArrayList;
import java.util.List;

class Message {
    // 这几个属性必须设置 public !!!!
    // 如果设置 private, 必须生成 public 的 getter 和 setter !!!
    public String from;
    public String to;
    public String message;

    @Override
    public String toString() {
        return "Message{" +
                "from='" + from + '\'' +
                ", to='" + to + '\'' +
                ", message='" + message + '\'' +
                '}';
    }
}

@WebServlet("/message")
public class MessageServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();
    private List<Message> messageList = new ArrayList<>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 通过这个方法来处理 "获取所有留言消息"
        // 需要返回一个 json 字符串数组. jackson 直接帮我们处理好了格式.
        String respString = objectMapper.writeValueAsString(messageList);
        resp.setContentType("application/json; charset=utf8");
        resp.getWriter().write(respString);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 通过这个方法来处理 "提交新消息"
        Message message = objectMapper.readValue(req.getInputStream(), Message.class);
        messageList.add(message);
        System.out.println("消息提交成功! message=" + message);

        // 响应只是返回 200 报文. body 为空. 此时不需要额外处理. 默认就是返回 200 的.
    }
}

3.调整前端页面代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>表白墙</title>
    <!-- 引入 jquery -->
    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
    <style>
        /* * 通配符选择器, 是选中页面所有元素 */
        * {
            /* 消除浏览器的默认样式. */
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        .container {
            width: 600px;
            margin: 20px auto;
        }

        h1 {
            text-align: center;
        }

        p {
            text-align: center;
            color: #666;
            margin: 20px 0;
        }

        .row {
            /* 开启弹性布局 */
            display: flex;
            height: 40px;
            /* 水平方向居中 */
            justify-content: center;
            /* 垂直方向居中 */
            align-items: center;
        }

        .row span {
            width: 80px;
        }

        .row input {
            width: 200px;
            height: 30px;
        }

        .row button {
            width: 280px;
            height: 30px;
            color: white;
            background-color: orange;
            /* 去掉边框 */
            border: none;
            border-radius: 5px;
        }

        /* 点击的时候有个反馈 */
        .row button:active {
            background-color: grey;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>表白墙</h1>
        <p>输入内容后点击提交, 信息会显示到下方表格中</p>
        <div class="row">
            <span>谁: </span>
            <input type="text">
        </div>
        <div class="row">
            <span>对谁: </span>
            <input type="text">
        </div>
        <div class="row">
            <span>说: </span>
            <input type="text">
        </div>
        <div class="row">
            <button id="submit">提交</button>
        </div>
        <div class="row">
            <button id="revert">撤销</button>
        </div>
        <!-- <div class="row">
            xxx 对 xx 说 xxxx
        </div> -->
    </div>

    <script>
        // 实现提交操作. 点击提交按钮, 就能够把用户输入的内容提交到页面上显示. 
        // 点击的时候, 获取到三个输入框中的文本内容
        // 创建一个新的 div.row 把内容构造到这个 div 中即可. 
        let containerDiv = document.querySelector('.container');
        let inputs = document.querySelectorAll('input');
        let button = document.querySelector('#submit');
        button.onclick = function() {
            // 1. 获取到三个输入框的内容
            let from = inputs[0].value;
            let to = inputs[1].value;
            let msg = inputs[2].value;
            if (from == '' || to == '' || msg == '') {
                return;
            }
            // 2. 构造新 div
            let rowDiv = document.createElement('div');
            rowDiv.className = 'row message';
            rowDiv.innerHTML = from + ' 对 ' + to + ' 说: ' + msg;
            containerDiv.appendChild(rowDiv);
            // 3. 清空之前的输入框内容
            for (let input of inputs) {
                input.value = '';
            }
            //4. 通过 ajax 构造 post 请求,把这个新的消息提交给服务器
            let body = {
                "from": from,
                "to": to,
                "message": msg
            }
            $.ajax({
                type: 'post',
                url: 'message',
                contentType: "application/json;charset=utf8",
                // java 中使用 jackson 完成对象和 json 字符串的转换;objectMapper.writeValue 用来把 java 对象转成 json 格式字符串
                //objectMapper.readValue 用来把 json 格式字符串转成 java对象
                // JS 中使用 JSON 这个特殊对象,完成对象和json字符串的转化;JSON.stringify 把js对象转成json格式字符串
                //JSON.parse 把 json格式字符串转成js对象
                data: JSON.stringify(body),
                success: function(nody) {
                    //这是响应成功之后要调用的回调
                    console.log("消息发送给服务器成功!")
                }
            })
        }
        let revertButton = document.querySelector('#revert');
        revertButton.onclick = function() {
            // 删除最后一条消息. 
            // 选中所有的 row, 找出最后一个 row, 然后进行删除
            let rows = document.querySelectorAll('.message');
            if (rows == null || rows.length == 0) {
                return;
            }
            containerDiv.removeChild(rows[rows.length - 1]);
        }

        // 在页面加载的时候, 希望能够从服务器获取到所有的消息, 并显示在网页中. 
        $.ajax({
            type: 'get',
            url: 'message',  // url 都是使用相对路径的写法. 相对路径意味着工作路径就是当前文件所在的路径. 
                             // 当前文件所在路径是 /message_wall/ , 因此此时构造的请求就是 /message_wall/message
            success: function(body) {
                // body 是收到的响应的正文部分. 如我们之前的约定, body 应该是 json 数组
                // 由于响应的 Content-Type 是 application/json, 此时收到的 body 会被 jquery 自动的把它从 字符串 
                // 转成 js 对象数组. 此处就不需要手动的进行 JSON.parse 了. 
                // 此处的 body 已经是一个 JSON.parse 之后得到的 js 对象数组了. 
                // 就需要遍历这个 body 数组, 取出每个元素, 再依据这样的元素构造出 html 标签, 并添加到页面上. 
                let container = document.querySelector('.container');//通过css选择器查找页面中的元素
                for (let message of body) {
                    let rowDiv = document.createElement('div');
                    rowDiv.className = "row";
                    rowDiv.innerHTML = message.from + " 对 " + message.to + " 说: " + message.message;
                    container.appendChild(rowDiv);
                }
            }
        });
    </script>
</body>
</html>

此时通过浏览器的URL:127.0.0.1:8080/message_wall/messageWall.htm 访问服务器即可看到

1b0bbe33fe83463abb4898b23bc05ceb.png

 此时我们每次提交的数据都会发送给服务器. 每次打开页面的时候页面都会从服务器加载数据. 因此及时关闭页面, 数据也不会丢失.

但是数据此时是存储在服务器的内存中 ( private List<Message> messages = new ArrayList<Message>(); ), 一旦服务器重启, 数据仍然会丢失

4.数据持久化

把数据存储到银盘上,才是让数据更让好的持久化的方法:1️⃣数据存储到文件里2️⃣数据存入数据可中

4.1 数据存入数据库

1️⃣引入 JDBC 依赖:

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.49</version>
</dependency>

2️⃣建库建表

create table messages (`from` varchar(255), `to` varchar(255), `message` varchar(2048));

3️⃣修改代码

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

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 javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import static java.lang.System.load;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: Lenovo
 * Date: 2023-06-02
 * Time: 19:56
 */
class Message {
    // 这几个属性必须设置 public !!!!
    // 如果设置 private, 必须生成 public 的 getter 和 setter !!!
    public String from;
    public String to;
    public String message;

    @Override
    public String toString() {
        return "Message{" +
                "from='" + from + '\'' +
                ", to='" + to + '\'' +
                ", message='" + message + '\'' +
                '}';
    }
}

@WebServlet("/message")
public class MessageServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();
    private List<Message> messageList = new ArrayList<>();
    //private List<Message> messageList = new ArrayList<>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 通过这个方法来处理 "获取所有留言消息"
        // 需要返回一个 json 字符串数组. jackson 直接帮我们处理好了格式.
        List<Message> messageList = load();
        String respString = objectMapper.writeValueAsString(messageList);
        resp.setContentType("application/json; charset=utf8");
        resp.getWriter().write(respString);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 通过这个方法来处理 "提交新消息"
        Message message = objectMapper.readValue(req.getInputStream(), Message.class);
        messageList.add(message);
        save(message);
        System.out.println("消息提交成功! message=" + message);

        // 响应只是返回 200 报文. body 为空. 此时不需要额外处理. 默认就是返回 200 的.
    }

    // 这个方法用来往数据库中存一条记录
    private void save(Message message) {
        DataSource dataSource = new MysqlDataSource();
        ((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java107?characterEncoding=utf8&useSSL=false");
        ((MysqlDataSource)dataSource).setUser("root");
        ((MysqlDataSource)dataSource).setPassword("2222");

        try {
            Connection connection = dataSource.getConnection();
            String sql = "insert into message values(?, ?, ?)";
            PreparedStatement statement = connection.prepareStatement(sql);
            statement.setString(1, message.from);
            statement.setString(2, message.to);
            statement.setString(3, message.message);
            statement.executeUpdate();

            statement.close();
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    // 这个方法用来从数据库查询所有记录
    private List<Message> load() {
        List<Message> messageList = new ArrayList<>();

        DataSource dataSource = new MysqlDataSource();
        ((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java107?characterEncoding=utf8&useSSL=false");
        ((MysqlDataSource)dataSource).setUser("root");
        ((MysqlDataSource)dataSource).setPassword("2222");

        try {
            Connection connection = dataSource.getConnection();
            String sql = "select * from message";
            PreparedStatement statement = connection.prepareStatement(sql);
            ResultSet resultSet = statement.executeQuery();

            while (resultSet.next()) {
                Message message = new Message();
                message.from = resultSet.getString("from");
                message.to = resultSet.getString("to");
                message.message = resultSet.getString("message");
                messageList.add(message);
            }

            resultSet.close();
            statement.close();
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return messageList;
    }
}

通过上述代码就已经写出一个很简单的网站;未来写的复杂网站都是这一套逻辑

1️⃣约定前后端交互接口

2️⃣实现服务器代码(通常回操作数据库)

3️⃣实现客户端代码(通常会使用 ajax 构造请求,并使用一些 js 的 webapi 操作页面内容)

 

  • 117
    点赞
  • 99
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 148
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 148
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奋斗小温

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值