实现一个服务器版本的“表白墙”
实现的这个表白墙,就通过服务器,来保存这里的消息数据。
从而进一步的做到“持久化”存储~~
在实现这个程序的时候,就需要先考虑清楚,客户端和服务器如何进行交互~
约定前后端交互的接口~
(自定义应用层协议)
既然是搞一个服务器,服务器就得提供一些服务,具体是提供啥样的服务?以及这些服务该如何触发?
对于表白墙来说,主要要提供两个接口~
- 告诉服务器,当前留言了一条啥样的数据~(当用户点击提交按钮的时候,就会给服务器发送一个HTTP请求,让服务器把这个消息给存储下来)
- 从服务器获取到,当前都有哪些留言数据~(当页面加载,就需要从服务器获取到曾经存储的这些消息内容)
第一个接口
约定好,为了实现这个效果,客户端要发送一个啥样的HTTP请求,服务器返回一个啥样的HTTP响应~
请求:
POST /message
{
from: "黑猫",
to: "白猫",
message: "喵"
}
或者
POST /messageWall
from=黑猫&to=白猫&message=喵
或者
GET /message?from=黑猫&to=白猫&message=喵
或者
POST /message
{
f: "黑猫",
t: "白猫",
m: "喵"
}
按照上述的思路发散开,有无数种方式来约定请求格式!
方法可以变,路径可以变,参数的名字可以变~
这么多的约定方式,到底采取哪一种?
哪种都可以!只要能够明确下来其中的一种,并且在后续编写前端/后端代码的时候能够严格执行,就是ok的。
这也是约定前后端接口的意义!
自定义协议!(约束程序员的自由,不能随便乱写代码~)
当前就是用第一种方式!
请求:
POST /message
{
from: "黑猫",
to: "白猫",
message: "喵"
}
响应:
HTTP/1.1 200 OK
{
ok: true
}
第二个接口
请求:
GET /message
响应:
HTTP/1.1 200 OK
Content-Type:application/json
[
{
from: "黑猫",
to: "白猫",
message: "喵",
},
{
from: "黑猫",
to: "白猫",
message: "喵",
},
{
from: "黑猫",
to: "白猫",
message: "喵",
},
]
确定接口之后,就可以编写代码了~
后端开发
先进行准备工作.
1.创建项目(maven)
2.引入依赖
引入Servlet,去中央仓库找~
接下来就是jackson
3.创建目录结构
<!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.编写代码
这个和前面约定的前后端交互接口是匹配的~
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("/message")
public class MessageServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 处理提交消息请求
resp.getWriter().write("hello post");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取消息列表
resp.getWriter().write("hello get");
}
}
5/6. 打包和部署
7. 验证
初步后端代码
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 String from;
public String to;
public String message;
}
@WebServlet("/message")
public class MessageServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
private List<Message> messages = new ArrayList<>();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 处理提交消息请求
Message message = objectMapper.readValue(req.getInputStream(), Message.class);
// 最简单的保存方法就是保存在内存中
messages.add(message);
// 通过 ContentType 来告知页面,返回的数据是 json格式
// 有了这样的声明,此时 jquery ajax 就会自动的帮我们把字符串转成 js 对象
// 如果没有, jquery ajax 就只是当成字符串来处理
resp.setContentType("application/json; charset=utf8");
resp.getWriter().write("{ \"ok\":true }");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取消息列表,只要把消息列表中的内容整个返回给客户端即可
// 此处需要使用 ObjectMapper 把 Java 对象,转成 JSON 格式字符串
String jsonString = objectMapper.writeValueAsString(messages);
System.out.println("jsonString: " +jsonString);
resp.getWriter().write(jsonString);
}
}
当前我们已经把后端逻辑编写完毕,接下来来写前端代码
前端代码
在webapp里面放~
<!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>
</head>
<body>
<div class="container">
<h1>表白墙</h1>
<p>输入后点击提交, 会将信息显示在表格中</p>
<div class="row">
<span>谁: </span>
<input class="edit" type="text">
</div>
<div class="row">
<span>对谁: </span>
<input class="edit" type="text">
</div>
<div class="row">
<span>说什么: </span>
<input class="edit" type="text">
</div>
<div class="row">
<input type="button" value="提交" class="submit">
</div>
</div>
<style>
* {
margin: 0;
padding: 0;
}
.container {
width: 400px;
margin: 0 auto;
}
h1 {
text-align: center;
padding: 20px 0;
}
p {
color: #666;
text-align: center;
font-size: 14px;
padding: 10px 0;
}
.row {
height: 40px;
display: flex;
justify-content: center;
align-items: center;
}
span {
width: 100px;
line-height: 40px;
}
.edit {
width: 200px;
height: 30px;
}
.submit {
width: 304px;
height: 40px;
color: white;
background-color: orange;
border: none;
}
</style>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
// 加入 ajax 的代码,此处要加入的逻辑有两个部分
// 点击按钮提交的时候,ajax 要构造数据发送到服务器
// 页面加载的时候,从服务器获取消息列表,并在界面上直接显示
function getMessage() {
$.ajax({
type: 'get',
url: 'message',
success: function(body) {
// 当前 body 已经是 一个 js对象数组了,ajax 会根据响应的 content type 来在自动解析
// 如果服务器返回的 content type 已经是 application/json了, ajax 就会把body自动转成 js 的对象
// 如果客户端没有自动转,也可以通过 JSON.parse() 这个函数来手动转
// 依次来取数组中的每个元素
for (let message of body) {
let row =document.createElement('div');
row.className='row';
row.innerHTML=message.from+'对'+message.to+'说:'+message.message;
container.appendChild(row);
}
}
});
}
// 加上函数调用
getMessage();
// 给点击按钮注册点击事件
let submit = document.querySelector('.submit');
submit.onclick = function () {
// 1. 获取到编辑框内容
let edits = document.querySelectorAll('.edit');
let from = edits[0].value;
let to = edits[1].value;
let message = edits[2].value;
console.log(from + "," + to + "," + message);
if (from == '' || to == '' || message == '') {
return;
}
// 2. 构造 html 元素
let row = document.createElement('div');
row.className = 'row';
row.innerHTML = from + '对' + to + '说: ' + message;
// 3. 把构造好的元素添加进去
let container = document.querySelector('.container');
container.appendChild(row);
// 4. 同时清理之前输入框的内容
for (var i = 0; i < 3; i++) {
edits[i].value = '';
}
// 4. 把当前获取到的输入框内容,构造成以一个 HTTP POST 请求,通过 ajax 发送给服务器
let body = {
from: from,
to: to,
message: msg
};
$.ajax({
type: "post",
url: "message",
contentType: "application/json;charset=utf-8",
data: JSON.stringify(body),
success: function(body) {
alert("消息提交成功!");
},
error: function() {
alert("消息提交失败!");
}
})
}
</script>
</body>
</html>
当页面加载的时候,我们就能够看到有一个这个ajax请求,这个请求就是从服务器获取消息列表的~
此处看到是空着了,刚才不是明明往服务器上提交了三条记录?
刚刚这个代码是把消息保存到了list中,内容中的数据我们一旦程序重启就会丢失。
因此,上述逻辑,只能保证页面刷新后数据不丢失,而不能保证服务器重启后数据不丢失~
当然,要想更加彻底解决这个问题,就需要把数据保存到硬盘上(可以是文件,也可以是数据库)
当前确实可以证明,即使页面关闭之后,之前提交的消息是不丢失的~
(页面加载的时候,从服务器获取到了消息列表,并显示在了浏览器页面中)
后端代码优化
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.UUID;
public class DBUtil {
private static final String URL = "jdbc:mysql://127.0.0.1:3306/java107?characterEncoding=utf8&useSSL=false";
private static final String USERNAME = "root";
private static final String PASSWORD = "1234";
private volatile static DataSource dataSource = null;
private static DataSource getDataSource() {
if (dataSource == null) {
synchronized (DBUtil.class) {
if (dataSource == null) {
dataSource = new MysqlDataSource();
((MysqlDataSource) dataSource).setURL(URL);
((MysqlDataSource) dataSource).setUser(USERNAME);
((MysqlDataSource) dataSource).setPassword(PASSWORD);
}
}
}
return dataSource;
}
public static Connection getConnection() throws SQLException {
return getDataSource().getConnection();
}
public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
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.io.Reader;
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;
}
@WebServlet("/message")
public class MessageServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 处理提交消息请求
Message message = objectMapper.readValue(req.getInputStream(), Message.class);
// 最简单的保存方法就是保存在内存中
//messages.add(message);
// 通过 ContentType 来告知页面,返回的数据是 json格式
// 有了这样的声明,此时 jquery ajax 就会自动的帮我们把字符串转成 js 对象
// 如果没有, jquery ajax 就只是当成字符串来处理
save(message);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write("{ \"ok\":true }");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取消息列表,只要把消息列表中的内容整个返回给客户端即可
// 此处需要使用 ObjectMapper 把 Java 对象,转成 JSON 格式字符串
List<Message> messages = load();
String jsonString = objectMapper.writeValueAsString(messages);
System.out.println("jsonString: " + jsonString);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(jsonString);
}
private void save(Message message) {
// 把一条消息保存到数据库中
Connection connection = null;
PreparedStatement statement = null;
try {
// 1. 和数据库建立连接
connection = DBUtil.getConnection();
// 2. 构造 SQL
String sql = "insert into message values(?, ?, ?)";
statement = connection.prepareStatement(sql);
statement.setString(1, message.from);
statement.setString(2, message.to);
statement.setString(3, message.message);
// 3. 执行 SQL
statement.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
DBUtil.close(connection, statement, null);
}
}
private List<Message> load() {
// 从数据库中获取到所有的消息
List<Message> messages = new ArrayList<>();
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = DBUtil.getConnection();
String sql = "select * from message";
statement = connection.prepareStatement(sql);
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");
messages.add(message);
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
DBUtil.close(connection, statement, resultSet);
}
return messages;
}
}