表白墙的实现【前后端交互】
目录
一、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 {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {StringBuilder result = new StringBuilder();result.append(req.getProtocol());result.append("
");result.append(req.getMethod());result.append("
");result.append(req.getRequestURI());result.append("
");result.append(req.getQueryString());result.append("
");result.append(req.getContextPath());result.append("
");//在响应中设置上 body 的类型,方便浏览器进行解析resp.setContentType("text/html;charset=utf8");resp.getWriter().write(result.toString());}
}

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 {@Overrideprotected 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");}
}

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

3️⃣通过 body (json) 传递(最常用的传递方式)
json 也是键值对格式的数据,但是 Servlet 自身没有内置 json 解析功能,因此就需要借助其他第三方库;用来处理 json 的第三方库有很多,常见的如 fastjson、gson、jackson...,我们使用 jackson 来解析
引入依赖:
com.fasterxml.jackson.core jackson-databind 2.15.0
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();@Overrideprotected 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");}
}


readValue 里面做的事情:
- 解析 json 字符串,转换成若干个键值对
- 根据第二个参数 User.class(.class 是类对象,就是这个类的图纸),去找到 User 里的所有的 public 的属性(或者有 public getter setter 的属性),依次遍历......
- 遍历属性,根据属性的名字取上述准备好的键值对里查询,看看这个属性名字是否存在对应的 value ,如果存在就把 value 复制到该属性中
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 {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setStatus(404);resp.setContentType("text/html; charset=utf8");resp.getWriter().write("返回 404 响应!");}
}

抓包结果:

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 {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {// 每隔 1s 自动刷新一次.resp.setHeader("Refresh", "1");resp.getWriter().write("time=" + System.currentTimeMillis());}
}

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 {@Overrideprotected 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 访问, 可以看到, 页面自动跳转到 搜狗主页 了.
抓包结果:

二、表白墙
表白墙前端页面代码:message.html
表白墙
表白墙
输入内容后点击提交, 信息会显示到下方表格中
谁: 对谁: 说:
1.准备工作
1️⃣创建一个 maven 项目
2️⃣引入依赖:servlet、jackson
com.fasterxml.jackson.core jackson-databind 2.15.0
javax.servlet javax.servlet-api 3.1.0 provided
3️⃣创建必要的目录 webapp、WEB-INF、web.xml
web.xml 中的代码:
Archetype Created Web Application
4️⃣把之前实现的表白墙前端页面拷贝到 webapp 目录中


5️⃣配置 Smart Tomcat

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

2.约定前后端交互接口
写代码之前需要明确前后端交互端口

🙈什么时候发送请求❓❓
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;@Overridepublic String toString() {return "Message{" +"from='" + from + '\'' +", to='" + to + '\'' +", message='" + message + '\'' +'}';}
}@WebServlet("/message")
public class MessageServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();private List messageList = new ArrayList<>();@Overrideprotected 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);}@Overrideprotected 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.调整前端页面代码
表白墙
表白墙
输入内容后点击提交, 信息会显示到下方表格中
谁: 对谁: 说:
此时通过浏览器的URL:127.0.0.1:8080/message_wall/messageWall.htm 访问服务器即可看到

此时我们每次提交的数据都会发送给服务器. 每次打开页面的时候页面都会从服务器加载数据. 因此及时关闭页面, 数据也不会丢失.
但是数据此时是存储在服务器的内存中 ( private List
messages = new ArrayList (); ), 一旦服务器重启, 数据仍然会丢失
4.数据持久化
把数据存储到银盘上,才是让数据更让好的持久化的方法:1️⃣数据存储到文件里2️⃣数据存入数据可中
4.1 数据存入数据库
1️⃣引入 JDBC 依赖:
mysql mysql-connector-java 5.1.49
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;@Overridepublic String toString() {return "Message{" +"from='" + from + '\'' +", to='" + to + '\'' +", message='" + message + '\'' +'}';}
}@WebServlet("/message")
public class MessageServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();private List messageList = new ArrayList<>();//private List messageList = new ArrayList<>();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 通过这个方法来处理 "获取所有留言消息"// 需要返回一个 json 字符串数组. jackson 直接帮我们处理好了格式.List messageList = load();String respString = objectMapper.writeValueAsString(messageList);resp.setContentType("application/json; charset=utf8");resp.getWriter().write(respString);}@Overrideprotected 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 load() {List 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 操作页面内容)
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!


