目录
HttpServlet
HttpServletRequest
HttpServletResponse
错误页面
设置网页自动刷新时间
构造重定向相应
js发起http请求
服务器端对js发起的http请求进行处理
前端获取后端数据,添加到当前页面的末尾,代码示例:
前后端交互:引入数据库(存储数据)
1)引入数据库依赖
编辑
编辑2)建库建表
3)编写数据库代码
整体代码
利用Cookie和Session实现登录逻辑
1.登录页面(html)
2.通过一个Servlet处理上述的登录请求
3.网站主页,通过另一个servlet生成的动态页面
掌握如下三个类,就可以完成Servlet的大部分开发了
- HttpServlet
- HttpServletRequest
- HttpServletResponse
URI:唯一资源标识符
URL:唯一资源定位/地址符
HttpServlet
HttpServlet继承这个类,重写里面的方法,目的就是为了把咱们子集定义的代码,“插入到”tomcat中,HttpServlet的常用方法如下:
方法名称 | 调用时机 |
init | 在 HttpServlet 实例化之后被调用一次,初始化 |
destory | 在 HttpServlet 实例不再使用的时候调用一次,释放资源 |
service | 收到 HTTP 请求的时候调用 |
doGet | 收到 GET 请求的时候调用 ( 由 service 方法调用 ) |
doPost | 收到 POST 请求的时候调用 ( 由 service 方法调用 ) |
doPut/doDelete/doOptions/... | 收到其他请求的时候调用 ( 由 service 方法调用 ) |
- 直接干掉Tomcat进程,完全来不及调用destory的;
- 通过8005管理端口,给Tomcat发送一个“停机”指令,这个时候是能够执行destory的。
init / destory / service 这三个方法都不需要手动调用,会被tomcat在合适的时机,自动调用,咱们写好代码,让别人来帮忙调用,这种方式就叫做 “框架” ,也就是一个程序的主体部分,都已经被其他大佬们写完了,有些细节内容,允许咱们插入咱们自己写的自定义的逻辑.
HttpServletRequest
方法 | 描述 |
String getProtocol() | 返回请求协议的名称和版本。 |
String getMethod() | 返回请求的 HTTP 方法的名称,例如, GET 、 POST 或 PUT 。 |
String getRequestURI() | 从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该 请求的 URL 的一部分。 |
String getContextPath() | 返回指示请求上下文的请求 URI 部分。 |
String getQueryString() | 返回包含在路径后的请求 URL 中的查询字符串。 |
InputStream getInputStream() | 用于读取请求的 body 内容 . 返回一个 InputStream 对象 . |
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 。 |
上述的方法都是get系列方法(都是读方法),没有set系列(没有写方法),当前拿到的HttpServletRequest,这些数据的内容已经确定下来了,程序员是不应该修改的.
前端将数据交给后端
除了query string之外,还可以通过http请求的body来传递参数(POST)。
(1)直接通过form表单
(body的格式就是query string的格式)
Content-Type:application/x-www-form-urlencoded
(2)直接使用json
(body的格式就是json)
Content-Type:application/json
这三种方式本质上是等价的,都是把键值对数据交给服务器,前两种方法servlet天然支持的,json这种方法需要引入第三方库。
在java中,json的第三方库是非常多的,这里我们使用jackson(jackson是spring官方推荐的库,也被spring集成起来了)
步骤如下:
1)下载导入jackson到项目中,通过maven
Maven Repository: Search/Browse/Explore (mvnrepository.com)
(2)使用jackson
一个类两个方法
ObjectMapper
- 把json字符串,映射成java的一个对象(read方法)
- 把一个java对象,映射成json字符串(write方法)
Request request = objectMapper.readValue(req.getInputStream(),Request.class);1.核心工作就是把json字符串,映射成java 对象,参数就是json字符串(json字符串是在http的body中的,就需要通过HttpServletRequest中的getInputStream来获取到)
此处把这个流对象直接传给readValue,readValue内部就会读取InputStream中的所有数据(http请求的body,也就是json字符串),进一步尝试解析
2.按照json的格式,进行解析,把json字符串,解析成map(键值对)
3.把map转换成对象,在方法的第二个参数
如下示例:
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.ObjectInputValidation;class Request()
{public String username;public String password;
}
class Response{public boolean ok;
}@WebServlet("/json")
public class JsonParameterServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {ObjectMapper objectMapper = new ObjectMapper();Request request = objectMapper.readValue(req.getInputStream(),Request.class);System.out.println("username="+request.username);System.out.println("password="+request.password);Response response=new Response();response.ok=true;//把响应对象转成json字符串String respJson=objectMapper.writeValueAsString(response);resp.setContentType("application/json;charset=utf8");resp.getWriter().write(respJson);}
}
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 中写入二进制格式数据 |
错误页面
resp.sendError(404,"这个页面是一个错误页面")
设置网页自动刷新时间
resp.setHeader("refresh","1");
构造重定向相应
resp.setStatus(302); resp.setHeader("Location","https://www.baidu.com");
header需要有一个Location属性,描述要跳转到哪里 ,除了这种写法外,还有如下另一种方法:
resp.sendRedirect("https://www.baidu.com");
使用ajax,需要先引入JQeury第三方库
jquery (v3.7.1) - jQuery 是一个高效、精简并且功能丰富的 JavaScript 工具库。它提供的 API 易于使用且兼容众多浏览器,这让诸如 HTML 文档遍历和操作、事件处理、动画和 Ajax 操作更加简单。 | BootCDN - Bootstrap 中文网开源项目免费 CDN 加速服务https://www.bootcdn.cn/jquery/
链接如上,选择如下链接
在script标签引入jquery
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
js发起http请求
// 把用户填写的内容,发送给服务器,让服务器来保存// $ 是jquery提供的全局变量,ajax是$的一个方法// ajax的参数是一个js对象,可以有很多属性let body={"from":from,"to":to,"message":message}//上述body是一个js对象,还需要转换成json字符串let jsonString = JSON.stringify(body)$.ajax({type:'post',url:'message',contentType:'application/json; charset=utf8',data:jsonString,//这里的body与上面的body不是同一个,是响应报文的正文success:function(body){// 这个回调就是收到响应之后要执行的代码了}});
此处success回调函数,不是立即执行的,而是在浏览器收到服务器返回的成功这样的响应的时候,才会执行function ,这个函数的第一个参数,是响应数据的body中的内容。
服务器端对js发起的http请求进行处理
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;class Message{
// 确保java代码中类的属性名字和json中的属性名字保持一致,才能够自动填充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<Message> messageList = new ArrayList<>();@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//使用jackson读取前端发来的数据,把这个数据保存到服务器这边Message message = objectMapper.readValue(req.getInputStream(),Message.class);System.out.println("请求中收到的message:"+ message);//保存数据最简单的是直接内存中存储,使用集合类//但是这样做一旦重启服务器,一切数据都没有了,一般存储到数据库中messageList.add(message);//返回一个响应resp.setStatus(200);resp.getWriter().write("ok");}
}
这里启动服务器后,不能直接访问/message,需要访问.html路径
当浏览器要向服务器获取资源, 服务器方使用List类型的数组存储历史数据,转成的json字符串就是一个json数组,jackson自动遍历List里的每个元素,把每个元素分别转成json字符串。
@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setStatus(200);resp.setContentType("application/json;charset=utf-8");String respJson = objectMapper.writeValueAsString(messageList);resp.getWriter().write(respJson);}
上述代码setStatus和setContentType必须在getWriter前面,否则不会生效
$.ajax{
type:'get',
url:'message',
success:function(body){
//处理服务器返回的响应数据。(json格式的数组)
}
}
//当响应中,header带有ContentType:"application/json",JQuery就会自动把json字符串解析成js对象了,如果没有带ContentType:"application/json"就需要通过js代码JSON.parse方法手动把json字符串转成js对象
前端获取后端数据,添加到当前页面的末尾,代码示例:
$.ajax{type:'get',url:'message',success:function(body){//处理服务器返回的响应数据。(json格式的数组)//由于响应中已经有ContentType:"application/json"了,就不需要使用parse方法手动转换了//body = JSON.parse(body);//拿到 container这个元素let containerDiv = document.querySelector('.container');//处理服务器返回的响应数据。(json格式的数组了)for(let i=0;i<body.length;i++){//body是一个数组,此时的message也就是js对象了//这个message对象里有三个属性:from、to、messagelet message = body[i];//根据message对象构建html片段,把这个片段给显示到网页上//createElement 方法就能构造一个html标签//此时就得到了<div></div>let div = document.createElement('div');//还需要给这个div设置一个属性div.className = 'row';//设置内容div.innerHTML = message.from + " 对 " + message.to + " 说:" +message.message;//将这个div添加到containerDiv末尾containerDiv.appendChild(div);}}
前后端交互:引入数据库(存储数据)
1)引入数据库依赖
Maven仓库链接
Maven Repository: Search/Browse/Explore (mvnrepository.com)
搜索MySQL,选择如下:
这里选择5.1.47版本
复制如下代码
复制到pom.xml里
2)建库建表
create database if not exists message_wall charset utf8;
use message_wall;
-- 删表的目的是为了防止之前数据库里有一样的表
drop table if exists message;
use message_wall;
create table message(`from` varchar(1024),`to` varchar(1024),message varchar(1024));
3)编写数据库代码
// 1. 创建数据源
private DataSource dataSource = new MysqlDataSource();
((MysqlDataSource) dataSource).setUrl("jdbc:mysql://127.0.0.1:3307/message_wall?characterEncoding=utf8&useSSL=false");
((MysqlDataSource) dataSource).setUser("root");
((MysqlDataSource) dataSource).setPassword("root");
// 2. 建立连接
Connection connection = dataSource.getConnection();// 3. 构造 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();
整体代码
MessageServlet.java
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;@Overridepublic 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();@Overridepublic void init() throws ServletException {// 1. 创建数据源((MysqlDataSource) dataSource).setUrl("jdbc:mysql://127.0.0.1:3307/message_wall?characterEncoding=utf8&useSSL=false");((MysqlDataSource) dataSource).setUser("root");((MysqlDataSource) dataSource).setPassword("root");}@Overrideprotected 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 }");}@Overrideprotected 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. 构造 SQLString 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. 执行 SQLstatement.executeUpdate();// 4. 回收资源statement.close();connection.close();}private List<Message> load() throws SQLException {// 通过这个方法从数据库读取到 message.// 1. 建立连接Connection connection = dataSource.getConnection();// 2. 构造 SQLString sql = "select * from message";PreparedStatement statement = connection.prepareStatement(sql);// 3. 执行 sqlResultSet 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. 返回 messageListreturn messageList;}
}
message_wall.html
<!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: 20px auto;}h1 {text-align: center;}p {text-align: center;color: #666;margin: 20px 0;}.row {/* 开启弹性布局 */display: flex;height: 40px;/* 水平方向居中 */justify-content: center;/* 垂直方向居中 */align-items: center;}.row span {width: 80px;}.row input {width: 200px;height: 30px;}.row button {width: 280px;height: 30px;color: white;background-color: orange;/* 去掉边框 */border: none;border-radius: 5px;}/* 点击的时候有个反馈 */.row button:active {background-color: grey;}</style><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
</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 id="submit">提交</button></div><!-- <div class="row">xxx 对 xx 说 xxxx</div> -->
</div><script>// 实现提交操作. 点击提交按钮, 就能够把用户输入的内容提交到页面上显示.// 点击的时候, 获取到三个输入框中的文本内容// 创建一个新的 div.row 把内容构造到这个 div 中即可.let containerDiv = document.querySelector('.container');let inputs = document.querySelectorAll('input');let button = document.querySelector('#submit');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. 构造新 divlet rowDiv = document.createElement('div');rowDiv.className = 'row message';rowDiv.innerHTML = from + ' 对 ' + to + ' 说: ' + msg;containerDiv.appendChild(rowDiv);// 3. 清空之前的输入框内容for (let input of inputs) {input.value = '';}// 4. 把用户填写的内容, 发送给服务器. 让服务器来保存.// $ 是 jquery 提供的全局变量. ajax 就是 $ 的一个方法.// ajax 的参数是一个 js 对象, 可以有很多属性let requestBody = {"from": from, // from 变量里的值, 就是第一个输入框的内容, "张三""to": to, // to 变量的值, 就是第二个输入框的内容, "李四""message": msg // msg 变量的值, 就是第三个输入框的内容, "我喜欢你很久了"};// 上述 body 是一个 js 对象, 还需要转成 json 字符串.let jsonString = JSON.stringify(requestBody);$.ajax({type: 'post',url: 'message',contentType: 'application/json; charset=utf8',data: jsonString,success: function(responseBody) {// 这个回调就是收到响应之后要执行的代码了.// 前端使用 console.log 打印日志到控制台. (chrome 开发者工具的控制台)console.log("responseBody: " + responseBody);}});}// 直接在 script 里面写的 js 代码, 就是在页面加载时被执行到的.// 发起一个 get 请求, 从服务器获取到数据// get 请求不需要 body, 也就不需要上述 data 和 contentType 属性了.$.ajax({type: 'get',url: 'message',success: function(body) {// 由于响应中已经有 Content-Type: application/json 了, 就不需要使用 parse 方法手动转换了.// body = JSON.parse(body);// 拿到 container 这个元素let containerDiv = document.querySelector('.container');// 处理服务器返回的响应数据. (json 格式的数组了)for (let i = 0; i < body.length; i++) {// body 是一个数组, 此时 message 也就是 js 对象了.// 这个 message 对象里, 有三个属性, from, to, messagelet message = body[i];// 根据 message 对象构建 html 片段, 把这个片段给显示到网页上.// createElement 方法就能构造出一个 html 标签.// 此时就得到了 <div></div>let div = document.createElement('div');// 还需要往里面设置一个 属性 , class="row" (设置这个属性, 是为了让 css 能够给这个元素设置一些样式)// 此时就得到了 <div class="row"></div>div.className = 'row';// 给这个 div 里设置内容// 此时就得到了 <div class="row">张三 对 李四 说: 我喜欢你很久了</div>div.innerHTML = message.from + " 对 " + message.to + " 说: " + message.message;// 把 div 添加到 containerDiv 的末尾containerDiv.appendChild(div);}}});
</script>
</body>
</html>
利用Cookie和Session实现登录逻辑
Cookie是客户端存储数据的机制
Session是服务器存储数据的机制(不算持久化存储)
1.登录页面(html)
2.通过一个Servlet处理上述的登录请求
通过这个Servlet读取用户名和密码,并且验证是否登录成功。
如果登陆成功,就会给当前用户,创建一个会话 ,并且把得到的sessionid,通过cooki返回给客户端,客户端就把cookie保存起来了。
3.网站主页,通过另一个servlet生成的动态页面
在这个页面中,就会把刚才这里的用户数据给显示到页面上。
getSession(true)
这个方法,就是根据请求的cookie中的sessionid,查询服务器的hash表,找到对应的session对象。如果cookie中没有sessionid(首次登录的时候,就是没有的)或者sessionid没有查找到对应的session对象,就可以创建出一个session对象出来。
参数为true,允许不存在时自动创建;false不能创建,直接返回null
//可以给会话中保存一些自定义的数据,通过Attribute的方式来保存 HttpSession session = req.getSession(true); //此处Atrribute也是键值对,这里的内容存储什么都可以 session.setAttribute("username",username); session.setAttribute("loginTime",System.currentTimeMillis()); //此处相当于登录成功,让页面跳转网站首页 resp.sendRedirect("index");
此处的getSession会创建新会话
1)生成sessionid和HttpSession对象
2)把上述sessionid和HttpSession对象保存到内存hash表中
3)把sessionid设置到响应报文的header中的 Set-Cookie字段
浏览器拿到响应,就会把这个Set-Cookie的内容保存到浏览器的Cookie中 。