Java EE Servlet之服务器版表白墙

文章目录

  • 1. 准备工作
  • 2. 约定前后端交互接口
  • 3. 编写提交消息
  • 4. 数据存入文件
  • 5. 引入数据库

1. 准备工作

我们要把表白墙程序修改成服务器版本
这样即使页面关闭, 表白墙的内容也不会丢失

此处,服务器要实现的功能,主要是两个方面:

  1. 页面加载的时候,网页要从服务器这里获取到当前表白数据
    (让网页端给服务器发起 http 请求,服务器返回响应里就带着刚才的这些数据)
  2. 点击提交的时候,网页就要把用户输入的信息,发送给服务器这面,服务器扶着保存

在一个网站中,服务器起到最主要的效果,往往就是“存储数据”
因此服务器这边往往也就需要能够提供两种风格的接口,存数据,取数据

2. 约定前后端交互接口

服务器这边就需要给网页提供两个 http 的接口

1)获取消息
网页加载的时候,给服务器发送一个 ajax 请求

请求:
GET/message

响应:
HTTP/1.1 200 OK
Content-Type: application/json
Java EE Servlet之服务器版表白墙_第1张图片
此处的请求和响应的细节,都是可以随意设计的,只要能达成效果

2)提交消息
用户点击 提交 按钮的时候 ajax 给服务器器发送一个请求
目的是为了把用户咋输入框输入的内容,给发送给服务器
Java EE Servlet之服务器版表白墙_第2张图片

正式编写代码之前,一定要把前后端交互的接口给确定下来
这个就是后续编写代码的依据

编写前端代码:
构造 HTTP 请求(请求是什么样子的)
解析 HTTP 响应(响应是什么样子的)

编写后端代码:
解析 HTTP 请求(请求是什么样子的)
构造 HTTP 响应(响应是什么样子的)

这些都是需要设计好前后端交互接口才嫩个回答的问题

这个过程,就是 自定义应用层协议

3. 编写提交消息

  1. 先写前端代码,发送请求
  2. 再写后端代码,解析请求,构造响应
  3. 再写前端代码,解析响应

我们需要把 网页 放入到 webapp 目录里

tomcat 这样的项目,可以包含一些 html、css、js
这些内容都是在 webapp 目录中

Java EE Servlet之服务器版表白墙_第3张图片

Java EE Servlet之服务器版表白墙_第4张图片


编写前端代码,发送 http post 请求
Java EE Servlet之服务器版表白墙_第5张图片

使用 ajax,需要先引入 jquery 这个库
在这里插入图片描述
前端引入第三方库,往往就是通过 script 标签,写一个地址即可

这个代码在点击按钮的回调函数中
会在点击按钮之后被调用
Java EE Servlet之服务器版表白墙_第6张图片
前端 ajax 请求,url 路径,写作“message”,前面不带 / ,此时这是一个相对路径的写法

后端处理 ajax 请求,url 路径,写作“/message”,前面带 / ,此时是 Servlet 要求的写法


服务器读取上述请求,并计算出响应

要确定 java 代码中,类的属性的名字 和 json 中的属性的名字保持一致
Java EE Servlet之服务器版表白墙_第7张图片
Java EE Servlet之服务器版表白墙_第8张图片


回到前端代码,处理服务器返回的响应

Java EE Servlet之服务器版表白墙_第9张图片
此处 success 回调函数,不是立即执行的,而是在浏览器收到服务器返回的,成功,这样的响应的时候,才会执行到 function

这个函数的第一个参数,就是响应数据的 body 中的内容
Java EE Servlet之服务器版表白墙_第10张图片
为了和请求对的上
一般,服务器返回的时候,也是用 json 格式
Java EE Servlet之服务器版表白墙_第11张图片

Java EE Servlet之服务器版表白墙_第12张图片


Java EE Servlet之服务器版表白墙_第13张图片

服务器收到的请求
在这里插入图片描述
浏览器收到的响应
Java EE Servlet之服务器版表白墙_第14张图片
用 抓包工具查看
Java EE Servlet之服务器版表白墙_第15张图片

Java EE Servlet之服务器版表白墙_第16张图片
在代码中写的是一个 相对路径
在这里插入图片描述
最终发送的请求,会被转成绝对路径
就是把相对路径前面,品尚当前 html 所在的 context path 里

响应数据
Java EE Servlet之服务器版表白墙_第17张图片

4. 数据存入文件

当前已经把 数据提交到服务器保存了
目前还是需要能够把 服务器的数据 拿回到 客户端页面上,并显示

为什么还要从服务器拿小希?

  1. 当前界面上显示的数据,也是在浏览器内存中报讯的,刷新界面/关闭的重新打开 数据就没了
  2. 其他客户端打开页面也是有数据的

这个时候,就需要在页面加载的时候,发起请求

1)客户端在页面加载的时候,发起一个 http 请求
Java EE Servlet之服务器版表白墙_第18张图片
2)服务器收到这个请求,要处理这个请求并生成响应
在这里插入图片描述

服务器收到的每条消息,都转换成了 Message 对象,放到上述 List 中了
返回的结果,也就是这个 List 的数据
需要把 List 里的每个 Message 取出来,转成 json 字符创,最终拼到一起,得到了响应结果
Java EE Servlet之服务器版表白墙_第19张图片

jackson 看到了 messageList 是一个 List 类型
转成的json 字符串就是一个 json 数组[]
jackson 自动遍历 List 里的每个元素把每个元素,分别都转成 json 字符串
Java EE Servlet之服务器版表白墙_第20张图片

确保这几个代码的执行顺序 setStatus 和 setContentType 必须在 getWriter 前面
否则可能不会生效(构造出一个非法的 http 响应报文)
这个事情可以认为是 Servlet bug

3)客户端收到响应,就需要针对响应数据进行解析处理
把响应中的信息,构造成页面元素(html片段),并显示出来

这段代码中,需要拼接出 html 片段
Java EE Servlet之服务器版表白墙_第21张图片
body 就是服务器返回的响应
数据 json 格式的数组

当响应中,header 带有 Content-Type: application/jsonjquery
就会自动的把 json 字符串,解析成 js 对象了

如果没有带 Content-Type: application/json
就需要通过 js 代码 JSON.parse 方法来手动把 json 字符串转成js 对象

在这里插入图片描述
此时,响应数据中,带有 content type 的
所以此时 jquery 自定帮我们完成解析了

当下 body 就已经是一个 js 对象了(数组对象)

这个就是要构造的内容
在这里插入图片描述
构造出来后,加到这个后面
在这里插入图片描述

在这里插入图片描述
通过类选择器,针对 class 属性进行选择
在这里插入图片描述
html 中的每一个元素/标签,都存在一个 js 的对应的对象,来表示
称为 DOM(文档对象模型)

5. 引入数据库

如何把消息数据存储到数据库中
把数据库引入到代码中

1)引入数据库的依赖

2)建库建表
建库建表,需要用到 sql,都可以写到 文件 中,后续如果需要把表啥的往其他的机器上迁移,建表操作就会比较方便

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;

class Message {
    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();

    // 引入数据库, 此时 messageList 就不再需要了.
    // private List messageList = new ArrayList<>();

    private DataSource dataSource = new MysqlDataSource();

    @Override
    public void init() throws ServletException {
        // 1. 创建数据源
        ((MysqlDataSource) dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/message_wall?characterEncoding=utf8&useSSL=false");
        ((MysqlDataSource) dataSource).setUser("root");
        ((MysqlDataSource) dataSource).setPassword("123456");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 读取前端发来的数据, 把这个数据保存到服务器这边.
        Message message = objectMapper.readValue(req.getInputStream(), Message.class);
        System.out.println("请求中收到的 message: " + message);
        // 最简单的办法, 直接在内存中保存. 可以使用一个集合类, 把所有收到的 message 都存到内存中.
        // 很明显, 保存到内存, 并非是一个非常合理的办法. 后续一旦重启服务器, 数据丢失了.
        // 相比之下, 把这个数据持久化存储到数据库中, 更科学的.
        // messageList.add(message);
        // 插入数据库
        try {
            save(message);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        // 返回一个响应
        resp.setStatus(200);
        resp.getWriter().write("ok");
        // resp.setContentType("application/json");
        // resp.getWriter().write("{ ok: true }");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 通过这个方法来处理当前获取消息列表的 get 请求. 不需要解析参数, 直接返回数据即可.
        resp.setStatus(200);
        resp.setContentType("application/json; charset=utf8");
        // 从数据库查询
        List<Message> messageList = null;
        try {
            messageList = load();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        String respJson = objectMapper.writeValueAsString(messageList);
        resp.getWriter().write(respJson);
    }

    private void save(Message message) throws SQLException {
        // 通过这个方法把 message 插入到数据库中

        // 1. 建立连接
        Connection connection = dataSource.getConnection();

        // 2. 构造 SQL
        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);

        // 3. 执行 SQL
        statement.executeUpdate();

        // 4. 回收资源
        statement.close();
        connection.close();
    }

    private List<Message> load() throws SQLException {
        // 通过这个方法从数据库读取到 message.

        // 1. 建立连接
        Connection connection = dataSource.getConnection();

        // 2. 构造 SQL
        String sql = "select * from message";
        PreparedStatement statement = connection.prepareStatement(sql);

        // 3. 执行 sql
        ResultSet resultSet = statement.executeQuery();

        // 4. 遍历结果集合
        List<Message> messageList = new ArrayList<>();
        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);
        }

        // 5. 回收资源
        resultSet.close();
        statement.close();
        connection.close();

        // 6. 返回 messageList
        return messageList;
    }
}

Java EE Servlet之服务器版表白墙_第22张图片
Java EE Servlet之服务器版表白墙_第23张图片


到此一个简单的,依靠 servlet 的web 页面到此结束了~
下次再见~

你可能感兴趣的:(Java,EE,java-ee,servlet,java)