Java EE Servlet之服务器版表白墙

1. 准备工作

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

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

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

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

2. 约定前后端交互接口

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

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

请求:
GET/message

响应:
HTTP/1.1 200 OK
Content-Type: application/json
在这里插入图片描述
此处的请求和响应的细节,都是可以随意设计的,只要能达成效果

2)提交消息
用户点击 提交 按钮的时候 ajax 给服务器器发送一个请求
目的是为了把用户咋输入框输入的内容,给发送给服务器
在这里插入图片描述

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

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

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

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

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

3. 编写提交消息

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

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

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

在这里插入图片描述

在这里插入图片描述


编写前端代码,发送 http post 请求
在这里插入图片描述

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

这个代码在点击按钮的回调函数中
会在点击按钮之后被调用
在这里插入图片描述
前端 ajax 请求,url 路径,写作“message”,前面不带 / ,此时这是一个相对路径的写法

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


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

要确定 java 代码中,类的属性的名字 和 json 中的属性的名字保持一致
在这里插入图片描述
在这里插入图片描述


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

在这里插入图片描述
此处 success 回调函数,不是立即执行的,而是在浏览器收到服务器返回的,成功,这样的响应的时候,才会执行到 function

这个函数的第一个参数,就是响应数据的 body 中的内容
在这里插入图片描述
为了和请求对的上
一般,服务器返回的时候,也是用 json 格式
在这里插入图片描述

在这里插入图片描述


在这里插入图片描述

服务器收到的请求
在这里插入图片描述
浏览器收到的响应
在这里插入图片描述
用 抓包工具查看
在这里插入图片描述

在这里插入图片描述
在代码中写的是一个 相对路径
在这里插入图片描述
最终发送的请求,会被转成绝对路径
就是把相对路径前面,品尚当前 html 所在的 context path 里

响应数据
在这里插入图片描述

4. 数据存入文件

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

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

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

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

1)客户端在页面加载的时候,发起一个 http 请求
在这里插入图片描述
2)服务器收到这个请求,要处理这个请求并生成响应
在这里插入图片描述

服务器收到的每条消息,都转换成了 Message 对象,放到上述 List 中了
返回的结果,也就是这个 List 的数据
需要把 List 里的每个 Message 取出来,转成 json 字符创,最终拼到一起,得到了响应结果
在这里插入图片描述

jackson 看到了 messageList 是一个 List 类型
转成的json 字符串就是一个 json 数组[]
jackson 自动遍历 List 里的每个元素把每个元素,分别都转成 json 字符串
在这里插入图片描述

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

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

这段代码中,需要拼接出 html 片段
在这里插入图片描述
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<Message> 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;
    }
}

在这里插入图片描述
在这里插入图片描述


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

柒柒要开心

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

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

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

打赏作者

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

抵扣说明:

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

余额充值