目录
1.分析问题
我们之前学习前端的时候写过一个表白墙的页面的案例,但是这个页面是存在问题的
问题
1.如果刷新页面或者关闭页面重开,之前的数据就丢失了
2.这些输入的数据都是在本地上的,一个机器输入了数据,其他机器是看不见的
需求
为了优化这些问题,我们可以让服务器来存储用户提交的数据,当有新的浏览器打开页面时,就从服务器获取数据,服务器就可以进行"读档”“存档”
每次提交数据,触发存档,将数据存到服务器
每次打开/刷新页面,触发读档,将所有数据从服务器读取加载到页面
2.设计程序
写web程序要重点考虑,数据交互的格式,要提前约定好前后端交互的格式,这件事叫做设计前后端交互接口(包括请求数据是什么格式,响应是什么格式,什么时候发请求,浏览器按什么格式解析)
前后端交互的环节:
1.点击提交(向浏览器提交数据,存档)
请求:POST/message
格式:json

响应:HTTP/1.1 200 OK
2.页面加载(从服务器获取数据,读档)
请求:GET/message
响应:HTTP/1.1 200 OK
数据格式:json

约定的前后端交互数据格式是很灵活的,存在很多的格式,主要是前后端要约定好
能够实现需求即可
3.编写代码
后端
和之前创建第一个Servlet程序相同,准备工作要做好
我们引入的依赖有
maven的依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.2</version>
</dependency>
我们还约定了数据交互格式是json,所以需要解析json的jackson库的依赖

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.2</version>
</dependency>
除了使用服务器存储,我们还可以用数据库存储数据,再导入数据库的依赖
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
刷新后就可以导入这些依赖了
准备工作完成,下来就编写代码了
创建一个Message Servlet类
注意这里的注解是前面约定过的

我们先写向服务器提交数据的代码
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//向服务器提交数据
}
我们约定过数据格式是json,那么在解析json数据时,就需要设置一个类来描述数据,将数据解析到对应的类的属性中
实现dopost,先定义一个类,来描述body的内容,方便jackson进行json解析
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//向服务器提交数据
ObjectMapper objectMapper = new ObjectMapper();
//将body中的数据读取出来,json数据=>java对象
Message message = objectMapper.readValue(req.getInputStream(),Message.class);
//保存数据
list.add(message);
//返回
resp.setStatus(200);
}
实现doget,将list的结果返回给前端
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//从服务器获取数据
}
响应数据也是json格式,刚才存到list中的是一个个message对象,list对应的是json数组,数组中的每个对象就是每个message对象,直接对应起来了,如图

那么数据格式转换成json可以通过 ObjectMapper的writeValue()方法将java对象转换成json格式数据,并且写入到resp响应对象中
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置数据格式
resp.setContentType("application/json;charset = utf8");
//从服务器获取数据
ObjectMapper objectMapper = new ObjectMapper();
//java对象=>json数据
objectMapper.writeValue(resp.getWriter(),list);
}
resp.getWriter()是表示转换的json字符串往哪写,list表示是被转换成json字符串的对象
分开写也可以

合成一步就是最开始的代码,那么后端代码就全部写完了
启动服务器,输入URL后:浏览器此时发送了GET请求

通过postman发送几个请求

还没有post数据,所以得到的是[],我们提交几个数据

然后再GET

当我们多post几次时,就会获得这种数组形式的数据了

前端
接下来我们编写前端的代码,也就是能够让前端能发起这些请求并解析响应
post是点击提交按钮发起,get是页面加载是发起
我们先将前端代码拷贝至webapp,然后增加交互的代码
先构造一个js对象body用来获取输入框内容
当前body还是个js对象,网络传输中是不能传输js对象的
需要把js对象构造成约定好的json字符串来传输.js内置了转换库,不用引入解析库了

重启服务器过后,输入数据提交,抓包观察结果

POST请求抓包结果:
可以看到body显然是一个json格式数据,符合我们最初的约定
我们分析一下前后端的交互过程:

写到这里我们实现了存档功能,但是刷新后,页面还是没有数据,还要实现读档功能
让浏览器发送GET请求

重启服务器后提交一些数据,再次刷新或者重开页面,数据都会被加载到页面上

通过抓包可以发现,当刷新页面时,会触发GET请求来获取服务器的数据
请求

响应

这里的数据时存储在服务器内存中的,重启服务器,数据仍然会丢失,所以我们需要将数据存储到数据库中,这样就没问题了,或者将数据存到文本文件中也行.这里我们使用数据库,功能更加强大
连接数据库
这里只有一个表,message(from,to,message)
mysql> create table message(`from` varchar(20),`to` varchar(20),message varchar(50));
Query OK, 0 rows affected (0.04 sec)
mysql> desc message
-> ;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| from | varchar(20) | YES | | NULL | |
| to | varchar(20) | YES | | NULL | |
| message | varchar(50) | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
实现和数据库的操作
我们先封装一些数据库的操作
//封装数据库连接过程
//作为工具类
public class DBUtil {
//单例饿汉模式
private static DataSource dataSource = new MysqlDataSource();
static{
//使用静态代码块初始化
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/wall?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("123456");
}
//建立连接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
//释放资源
public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet){
//此处的异常分开捕捉比较好,否则会影响后面的代码不能执行
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if(statement!=null){
try {
statement.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}
此时就不需要List来存储了
提供一对方法,存取消息
save方法用来提交一条信息,load方法用来加载所有信息
重启服务器后,提交几次数据

控制台打印日志

数据库的表中增加了数据,浏览器页面重新加载后,会通过服务器获取到数据库中的数据,以前的数据都会被加载到浏览器页面显示出来
mysql> select*from message;
+------+------+---------+
| from | to | message |
+------+------+---------+
| 1 | 2 | 3 |
| 3 | 4 | 5 |
+------+------+---------+
2 rows in set (0.00 sec)
4.完整代码
MessageServlet代码
import com.fasterxml.jackson.databind.ObjectMapper; import sun.plugin2.message.PrintAppletReplyMessage; 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.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 { //保存数据的list //private List<Message> list = new ArrayList<>(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置数据格式 resp.setContentType("application/json;charset = utf8"); //从服务器获取数据 ObjectMapper objectMapper = new ObjectMapper(); //java对象=>json数据 //objectMapper.writeValue(resp.getWriter(),list); List<Message> messageList = load(); //将list=>json字符串 String jsonResp = objectMapper.writeValueAsString(messageList); System.out.println(jsonResp); //将字符串写回到响应的body中 resp.getWriter().write(jsonResp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //向服务器提交数据 ObjectMapper objectMapper = new ObjectMapper(); //将body中的数据读取出来,json数据=>java对象 Message message = objectMapper.readValue(req.getInputStream(),Message.class); //保存数据 //list.add(message); save(message); //返回 resp.setStatus(200); } //存一条消息 private void save(Message message){ //JDBC操作 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 { //4.关闭连接 DBUtil.close(connection,statement,null); } } //取所有消息 private List<Message> load(){ List<Message> list = new ArrayList<>(); Connection connection = null; PreparedStatement statement = null; ResultSet resultSet = null; try { //1.建立连接 connection = DBUtil.getConnection(); //2.构造sql String sql = "select * from message"; statement = connection.prepareStatement(sql); //3.执行 resultSet = statement.executeQuery(); //4.遍历结果集 while(resultSet.next()){ Message message = new Message(); message.from = resultSet.getString("from"); message.to = resultSet.getString("to"); message.message = resultSet.getString("message"); list.add(message); } } catch (SQLException e) { throw new RuntimeException(e); }finally { //5.关闭资源 DBUtil.close(connection,statement,resultSet); } return list; } }封装数据库操作代码
DBUtil
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; //封装数据库连接过程 //作为工具类 public class DBUtil { //单例饿汉模式 private static DataSource dataSource = new MysqlDataSource(); static{ //使用静态代码块初始化 ((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/wall?characterEncoding=utf8&useSSL=false"); ((MysqlDataSource)dataSource).setUser("root"); ((MysqlDataSource)dataSource).setPassword("123456"); } //建立连接 public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } //释放资源 public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet){ //此处的异常分开捕捉比较好,否则会影响后面的代码不能执行 if(resultSet!=null){ try { resultSet.close(); } catch (SQLException e) { throw new RuntimeException(e); } } if(statement!=null){ try { statement.close(); } catch (SQLException e) { throw new RuntimeException(e); } } if(connection!=null){ try { connection.close(); } catch (SQLException e) { throw new RuntimeException(e); } } } }前端代码
<!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> <style> /* *是通配符选择器,会选中页面所有的元素 */ /* 消除浏览器的默认显示模式 */ *{ margin: 0; padding: 0; box-sizing: border-box; } .container{ width: 600px; margin: 0 auto; } h1{ margin-top: 100px; text-align: center; } p{ text-align: center; color: #666; margin: 20px auto; } .row{ /* 开启弹性布局 */ display: flex; height: 40px; /* 水平方向居中 */ justify-content: center; /* 垂直方向居中 */ align-items: center; } .row span{ width: 50px; } .row input{ width: 200px; height: 30px; } .row button{ width: 250px; height: 30px; color: aliceblue; background-color: orange; /* 去掉边框 */ border: none; border-radius: 3px; } /* 点击反馈 */ .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>提交</button> </div> </div> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script> <script> //实现提交操作,点击提交按钮,将用户输入的内容提交到页面上 //点击的时候获取输入框内容,然后创建一个新的div.row,将内容构造进去 let containerDiv = document.querySelector('.container'); let inputs = document.querySelectorAll('input'); let button = document.querySelector('button'); 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'; rowDiv.innerHTML = from + ' 对 ' + to + ' 说: ' + msg; containerDiv.appendChild(rowDiv) //3.清空之前的输入 for(let input of inputs){ input.value = ''; } //4.新增代码,将数据提交给服务器,构造post请求 //定义js对象,key是字符串,value是js中的变量常量 let body = { "from":from, "to":to, "message":msg }; //转换 strBody = JSON.stringify(body); console.log("strBody = "+strBody); $.ajax({ type:'post', url:'message', data:strBody, contentType:"application/json;charset = utf8", success :function(body){ console.log("发布成功"); } }); } //新增:浏览器在页面加载的时候发送get请求,从服务器获取数据并添加到页面中 $.ajax({ type:'get', url:'message', success:function(body){ //此处拿到的body是一个js数组 //本来服务器返回的是一个json字符串,但是jquery的ajax能够自动识别并转换成js对象数组 //遍历数组,取出元素,构造到页面 for(let message of body){ //2.构造新的div let rowDiv = document.createElement('div'); rowDiv.className = 'row message'; rowDiv.innerHTML = message.from + ' 对 ' + message.to + ' 说: ' + message.message; containerDiv.appendChild(rowDiv); } } }); </script> </body> </html>
至此,这个前后端可交互的网站就完成了,基于这样的机制,我们再添加其它的代码,就能做出其它更多的功能,可以说是从0到1的突破.
原文链接: https://blog.csdn.net/chenchenchencl/article/details/129898934