目录
前言
博客系统基本情况
1 创建项目,引入依赖
2 数据库设计
2.1 分析
2.2 建库建表
3 封装数据库
3.1 在java目录下创建DBUtil类,通过这个类对数据库进行封装
3.2 在java目录下创建实体类(博客类Blog)
3.2 在java目录下创建实体类(用户类User)
3.3 在java目录下创建BlogDao类
3.4 在java目录下创建UserDao类
4 获取博客列表页
4.1 约定前后端交互接口
4.2 让前端给服务器发起请求
4.3 服务器处理上述请求,返回响应数据
4.4 让前端处理上述响应数据
5 获取博客详情页
5.1 约定前后端交互接口
5.2 让前端给服务器发起请求
5.3 服务器处理上述请求,返回响应数据
5.4 让前端处理上述响应数据
6 实现登录
6.1 分析
6.2 约定前后端交互接口
6.3 前端发起请求
6.4 服务器处理响应
7 实现强制登录
7.1 约定前后端交互接口
7.2 让前端给服务器发起请求
7.3 服务器处理上述请求,返回响应
8 实现显示用户信息
8.1 约定前后端交互接口
8.2 让前端给服务器发起请求
8.3 服务器处理上述请求,返回响应
8.4 让前端处理上述响应数据
9 实现退出功能
9.1 约定前后端交互接口
9.2 让前端给服务器发起请求
9.3 服务器处理上述请求,返回响应
10 实现发布博客
10.1 约定前后端交互接口
10.2 让前端给服务器发起请求
10.3 服务器处理上述请求,返回响应
前言
基于前后端交互实现一个博客系统
博客系统基本情况
这里实现的博客系统主要分为四个页面,博客列表页、博客详情页、博客编辑页、博客登录页,主要实现的功能有:实现博客列表页的展示功能,实现博客详情页的展示功能、登录功能、强制用户登录后查看信息、显示用户信息、实现注销(退出登录)、发布博客、删除博客。
1 创建项目,引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>blog_system</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.0</version>
</dependency>
</dependencies>
</project>
2 数据库设计
2.1 分析
创建一个数据库,两张表
1)博客表:blog(blogId,title,content,postTime,userld)
2)用户表:user(userId,username,password)
2.2 建库建表
create database if not exists blog_system charset utf8;
use blog_system;
drop table if exists blog;
drop table if exists user;
create table blog (
blogId int primary key auto_increment,
title varchar(1024),
content varchar(4096),
postTime datetime,
userId int
);
create table user(
userId int primary key auto_increment,
username varchar(50) unique,
password varchar(50)
);
-- 插入一些数据,方便后续进行测试
insert into blog values(1, '这是第一篇博客', '#今天开始写代码', now(), 1);
insert into blog values(2, '这是第二篇博客', '#今天开始写代码', now(), 1);
insert into blog values(3, '这是第三篇博客', '#今天开始写代码', now(), 1);
insert into user values(1, 'zhangsan', '123');
insert into user values(2, 'lisi', '456');
3 封装数据库
由于这个博客系统要给多个用户使用,因此对数据库进行封装
3.1 在java目录下创建DBUtil类,通过这个类对数据库进行封装
public class DBUtil {
private volatile static DataSource dataSource = null;
private static DataSource getDataSource() {
if (dataSource == null) { //这个if表示判断是否加锁
synchronized (DBUtil.class) {
if (dataSource == null) { //这个if确保有一个线程把dataSource给new出来
dataSource = new MysqlDataSource();
((MysqlDataSource) dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/blog_system?characterEncoding=utf8&useSSl=false");
((MysqlDataSource) dataSource).setUser("root");
((MysqlDataSource) dataSource).setPassword("22222");
}
}
}
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) {
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);
}
}
}
}
1.上述代码使用try、catch而不用throws来处理异常是因为,如果使用throws,一旦哪一个发生了异常,就有可能存在后面的还有需要释放的资源没释放掉的情况。
2.释放顺序和创建的顺序是相反的。
3.如果不判断是否为空直接close,就会对所有的资源close,有些时候ResultSet就没有创建,直接close,就会导致null异常,所以在close之前要判断一下是否为空。
3.2 在java目录下创建实体类(博客类Blog)
//blog对应博客表里的属性
public class Blog {
private int blogId;
private String title;
private String content;
private Timestamp postTime;
private int userId;
public int getBlogId() {
return blogId;
}
public void setBlogId(int blogId) {
this.blogId = blogId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getPostTime() {
//这里修改一下返回值的类型,让最终得到的时间是一个格式化时间
//使用Java标准库里的SimpleDateFormat类,来完成时间戳到格式化时间的转换
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
return simpleDateFormat.format(postTime);
}
public void setPostTime(Timestamp postTime) {
this.postTime = postTime;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
@Override
public String toString() {
return "Blog{" +
"blogId=" + blogId +
", title='" + title + '\'' +
", content='" + content + '\'' +
", postTime=" + postTime +
", userId=" + userId +
'}';
}
}
3.2 在java目录下创建实体类(用户类User)
//User对应用户表里的属性
public class User {
private int userId;
private String username;
private String password;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"userId=" + userId +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
3.3 在java目录下创建BlogDao类
//通过这个类来完成针对blog表的操作
public class BlogDao {
//1.新增操作
Connection connection = null;
PreparedStatement statement = null;
public void insert(Blog blog) {
try {
//1.建立连接
connection = DBUtil.getConnection();
//2.构造数据库
String sql = "insert into blog values(null,?,?,now(),?)";
statement = connection.prepareStatement(sql);
statement.setString(1,blog.getTitle());
statement.setString(2,blog.getContent());
statement.setInt(3,blog.getUserId());
//3.执行SQL
statement.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
DBUtil.close(connection,statement,null);
}
}
//2.查询博客列表
public List<Blog> getBlogs() {
List<Blog> blogList = new ArrayList<>();
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = DBUtil.getConnection();
//这里采用降序是为了后续新增的博客在第一个显示
String sql = "select * from blog order by postTime desc";
statement = connection.prepareStatement(sql);
resultSet = statement.executeQuery();
while (resultSet.next()) {
Blog blog = new Blog();
blog.setBlogId(resultSet.getInt("blogId"));
blog.setTitle(resultSet.getString("title"));
//此处读到正文是整个文章,在博客列表希望显示一笑部分
//此处需要对content做一个简单的截断
String content = resultSet.getString("content");
if (content.length() > 100) {
content = content.substring(0,100) + "...";
}
blog.setContent(content);
blog.setPostTime(resultSet.getTimestamp("postTime"));
blog.setUserId(resultSet.getInt("userId"));
blogList.add(blog);
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
DBUtil.close(connection,statement,resultSet);
}
return blogList;
}
//3.根据博客id查询指定的博客
public Blog getBlog(int blogId) {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = DBUtil.getConnection();
String sql = "select * from blog where blogId = ?";
statement = connection.prepareStatement(sql);
statement.setInt(1,blogId);
resultSet = statement.executeQuery();
//此处拿着blogId进行查询,blogId作为主键,是唯一的
//所以不用循环
if (resultSet.next()) {
Blog blog = new Blog();
blog.setBlogId(resultSet.getInt("blogId"));
blog.setTitle(resultSet.getString("title"));
//这个方法是在获取博客详情页的时候调用,所以不需要截断
blog.setContent(resultSet.getString("content"));
blog.setPostTime(resultSet.getTimestamp("postTime"));
blog.setUserId(resultSet.getInt("userId"));
return blog;
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
DBUtil.close(connection,statement,resultSet);
}
return null;
}
//4.根据博客id删除博客
public void delete(int blogId) {
Connection connection = null;
PreparedStatement statement = null;
try {
connection = DBUtil.getConnection();
String sql = "select * from blog where blogId";
statement = connection.prepareStatement(sql);
statement.setInt(1,blogId);
statement.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
DBUtil.close(connection,statement,null);
}
}
}
3.4 在java目录下创建UserDao类
public class UserDao {
//1.根据userId来查到对应的用户信息(获取用户信息)
public User getUserById(int userId) {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = DBUtil.getConnection();
String sql = "select * from user where userId=?";
statement = connection.prepareStatement(sql);
statement.setInt(1,userId);
resultSet = statement.executeQuery();
if (resultSet.next()) {
User user = new User();
user.setUserId(resultSet.getInt("userId"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
return user;
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
DBUtil.close(connection,statement,resultSet);
}
return null;
}
//2.根据username来查到对应的用户信息(登录)
public User getUserByName(String username) {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = DBUtil.getConnection();
String sql = "select * from user where username = ?";
statement = connection.prepareStatement(sql);
statement.setString(1,username);
resultSet = statement.executeQuery();
if (resultSet.next()) {
User user = new User();
user.setUserId(resultSet.getInt("userId"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
return user;
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
DBUtil.close(connection,statement,resultSet);
}
return null;
}
}
4 获取博客列表页
在博客列表页加载的时候,通过ajax给服务器发起请求,从服务器(数据库)拿到博客列表页数据并显示到页面上
4.1 约定前后端交互接口
请求
GET / blog
响应
HTTP/1.1 200 OK
Content-Type:application/json
[
{
blogId:1,
title:"这是标题",
content:"这是正文",
postTime:"2024-4-10 9:00:00",
userId:1
}
]
4.2 让前端给服务器发起请求
引入第三方库
<script>
//对这个函数进行封装
function getBlogs() {
$.ajax({
type: 'get',
url: 'blog',
success: function (body) {
//根据服务器返回的响应,构造页面的片段
}
});
}
//调用函数
getBlogs();
</script>
4.3 服务器处理上述请求,返回响应数据
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//查询数据库,得到博客列表
//之前已经对BlogDao进行封装了,因此直接调用
BlogDao blogDao = new BlogDao();
//得到的是一个List数组,jackson就会把结果转成数组,数组里的每个元素又是一个Blog对象
//转成数组的每个元素也就是json构成的blog对象
List<Blog> blogs = blogDao.getBlogs();
//把博客列表按照json格式返回给浏览器
String respJson = objectMapper.writeValueAsString(blogs);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(respJson);
}
}
4.4 让前端处理上述响应数据
将响应的数据构造成HTML片段,显示到页面上
<script>
//对这个函数进行封装
function getBlogs() {
$.ajax({
type: 'get',
url: 'blog',
success: function (body) {
//根据服务器返回的响应,构造页面的片段
let containerDiv = document.querySelector('.container-right');
for (let i = 0; i < body.length; i++) {
//blog就是一个形如{blogId:1,title,"这是标题" ...}
let blog = body[i];
//构造整个博客
let blogDiv = document.createElement('div');
blogDiv.className = 'blog';
//构建标题
let titleDiv = document.createElement('div');
titleDiv.className = 'title';
titleDiv.innerHTML = blog.title;
//构建发布时间
let dateDiv = document.createElement('div');
dateDiv.className = 'date';
dateDiv.innerHTML = blog.postTime;
//构建博客的摘要
let descDiv = document.createElement('div');
descDiv.className = 'desc';
descDiv.innerHTML = blog.content;
//构造全文查看按钮
let a = document.createElement("a");
a.innerHTML = '查看全文 >>'
//点击不同的博客,跳转到不同的博客详情页
a.href = 'blog_detail.html?blogId=' + blog.blogId;
//把上面的操作进行组合
blogDiv.appendChild(titleDiv);
blogDiv.appendChild(dateDiv);
blogDiv.appendChild(descDiv);
blogDiv.appendChild(a);
//把上面blogDiv最终组合到页面上
containerDiv.appendChild(blogDiv);
}
}
});
}
//调用函数
getBlogs();
</script>
此时我们通过访问页面观察
页面展示的时间是一个时间戳,我们期望展示的时间是一个格式化的时间。
这里修改一下返回值的类型,让最终得到的时间是一个格式化时间
此时我们的获取博客列表页就完成了
5 获取博客详情页
我们期望点击博客列表页不同的查看全文,会跳转过去并且URL后面会带有不同的query string
在博客详情页中,通过ajax给服务器发送请求,根据blogId来查询数据库中博客的具体内容在返回
5.1 约定前后端交互接口
请求
GET / blog?blogId=1
响应
HTTP / 1.1 200 OK
Content-Type:application/json
{
blogId:1,
title:"这是第一篇博客",
content:"这是博客正文",
postTime:"2024-4-10 9:00:00",
userId:1
}
5.2 让前端给服务器发起请求
<script>
function getBlog() {
$.ajax({
type:'get',
//通过这个location.search方式拿到页面url中的query string
url:'blog' + location.search,
success:function(blog) {
//这里的blog就是返回一篇博客的内容
}
});
}
getBlog();
</script>
5.3 服务器处理上述请求,返回响应数据
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//查询数据库,得到博客列表
//之前已经对BlogDao进行封装了,因此直接调用
BlogDao blogDao = new BlogDao();
String respJson = "";
String blogId = req.getParameter("blogId");
if (blogId == null) {
//请求中没有query string,请求来自博客列表页
List<Blog> blogs = blogDao.getBlogs();
//把博客列表按照json格式返回给浏览器
respJson = objectMapper.writeValueAsString(blogs);
}else {
//请求中有query string,请求来自博客详情页
Blog blog = blogDao.getBlog(Integer.parseInt(blogId));
respJson = objectMapper.writeValueAsString(blog);
}
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(respJson);
}
}
5.4 让前端处理上述响应数据
<script>
function getBlog() {
$.ajax({
type: 'get',
//通过这个location.search方式拿到页面url中的query string
url: 'blog' + location.search,
success: function (blog) {
//这里的blog就是返回一篇博客的内容
let h3 = document.querySelector('.container-right h3');
h3.innerHTML = blog.title;
let dataDiv = document.querySelector('.container-right .date');
dataDiv.innerHTML = blog.postTime;
let contentDiv = document.querySelector('.container-right .content');
contentDiv.innerHTML = blog.content;
}
});
}
getBlog();
</script>
此时我们的博客详情页就完成了,但是还有一个小问题,上述是md原始数据,这是因为content部分的内容是使用markdown写的,直接通过blog.content得到的正文内容是一个简单的文本格式,因此需要引入第三方库(editor.md)来完成渲染。
<!-- 引入 editor.md 的依赖 -->
<link rel="stylesheet" href="editor.md/css/editormd.min.css" />
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="editor.md/lib/marked.min.js"></script>
<script src="editor.md/lib/prettify.min.js"></script>
<script src="editor.md/editormd.js"></script>
<script>
function getBlog() {
$.ajax({
type: 'get',
//通过这个location.search方式拿到页面url中的query string
url: 'blog' + location.search,
success: function (blog) {
//这里的blog就是返回一篇博客的内容
let h3 = document.querySelector('.container-right h3');
h3.innerHTML = blog.title;
let dataDiv = document.querySelector('.container-right .date');
dataDiv.innerHTML = blog.postTime;
//这种设置方式,页面显示的是md原始数据
// let contentDiv = document.querySelector('.container-right.content');
// contentDiv.innerHTML = blog.content;
//在这个方法的第一个参数,必须是一个html标签的id属性
editormd.markdownToHTML('content', { markdown: blog.content });
}
});
}
getBlog();
</script>
此时我们的博客详情页就彻底完成了
6 实现登录
6.1 分析
1 在登录页面,在输入框中输入用户名和密码,点击登录,就会给服务器发起HTTP请求(这里我们使用form)
2 服务器在处理登录请求的时候,读取用户名和密码,在数据库中查询,如果匹配就登录成功,创建会话并跳转到博客列表页。
3 由于登录成功后直接进行重定向跳转,这种重定向跳转不需要前端写额外的代码。
6.2 约定前后端交互接口
请求
POST/login
Content-Type:application/x-www-form-urlencoded
username=zhangsan&password=123
响应
HTTP/1.1 302
Location:blog_list.html
6.3 前端发起请求
<form action="login" method="post">
<div class="row">
<span>用户名</span>
<input type="text" id="username" name="username">
</div>
<div class="row">
<span>密码</span>
<input type="password" id="password" name="password">
</div>
<div class="row">
<input type="submit" id="submit" value="登录">
</div>
</form>
6.4 服务器处理响应
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.读取参数中的用户名和密码
req.setCharacterEncoding("utf8");
String username = req.getParameter("username");
String password = req.getParameter("password");
//验证参数
if (username == null || username.length() == 0 || password == null || password.length() == 0) {
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("输入的用户名或密码为空");
return;
}
//2.查询数据库,看用户名密码是否正确
UserDao userDao = new UserDao();
User user = userDao.getUserByName(username);
if (user == null) {
//用户名不存在
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("输入的用户名或密码不正确");
return;
}
if (!password.equals(user.getPassword())) {
//密码不正确
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("输入的用户名或密码不正确");
return;
}
//3.创建会话
HttpSession session = req.getSession(true);
session.setAttribute("user",user);
//4.跳转到主页
resp.sendRedirect("blog_list.html");
}
}
此时实现登录就完成了
7 实现强制登录
在博客列表页、详情页、编辑页,判断当前用户是否已经登陆,如果未登录,则强制跳转到登录页
7.1 约定前后端交互接口
请求
GET / login
响应
登陆成功:HTTP/1.1 200
登录失败:HTTP/1.1 404
7.2 让前端给服务器发起请求
//定义新的函数,获取登录状态
function getLoginStatus() {
$.ajax({
type: 'get',
url: 'login',
success: function (body) {
//已经登录的状态
console.log("已经登录了!");
},
error: function () {
//error这里对应的回调函数就是在状态码不为2XX的时候出发
//服务器返回403的时候,就会触发error部分的逻辑
//这里强制页面跳转到博客登录页
//这里不适用302这样的重定向跳转,是因为302这种响应无法被ajax直接处理
location.assign('login.html');
}
});
}
getLoginStatus();
上面没有定义<script></script>标签是因为是在一个script标签里写的。
7.3 服务器处理上述请求,返回响应
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//通过这个方法来反馈当前用户的登录状态
//直接看会话是否存在
//此处不仅要求会话存在,还要求会话中要保存user对象
HttpSession session = req.getSession(false);
if (session == null) {
//会话不存在
resp.setStatus(403);
return;
}
User user = (User) session.getAttribute("user");
if (user == null) {
//user不存在
resp.setStatus(403);
return;
}
//说明两个都存在,直接返回200
//此处默认就是200,不写也行
resp.setStatus(200);
}
}
当我们访问博客列表页就会跳转到博客登录页,接下来我们抓包看一下。
第一请求就是我们访问博客列表数据
第二请求就是获取登录状态,此时返回的响应就是403
我们的目标是让博客列表页、详情页、编辑页都能够有强制登录
因此我们把获取登陆状态的代码,单独提出来,在博客列表页、详情页、编辑页中通过html中的script标签来引用这样的文件内容
现在我们的实现强制登录就完成了
8 实现显示用户信息
在博客列表页显示的是当前登录的用户信息,在博客详情页显示的是当前文章作者的信息,在页面加载的时候,给服务器发起ajax请求,服务器返回对应的用户数据,根据发起请求不同的页面,服务器返回不同的信息。
8.1 约定前后端交互接口
博客列表页,获取当前登录的用户信息
请求
GET / userInfo
响应
HTTP/1.1 200 OK
Content-Type:application/json
{
userId:1,
username:'zhangsan'
}
博客详情页,获取当前文章作者信息
请求
GET / authroInfo?blogId=1
响应
HTTP/1.1 200 OK
Content-Type:application/json
{
userId:1,
username:'zhangsan'
}
8.2 让前端给服务器发起请求
博客列表页
//获取当前登录的用户信息
function getUserInfo() {
$.ajax({
type:'get',
url:'userInfo',
success:function(user) {
}
});
}
getUserInfo();
博客详情页
//获取作者信息
function getAuthorInfo() {
$.ajax({
type:'get',
url:'authorInfo' + location.search,
success:function(user) {
}
});
}
8.3 服务器处理上述请求,返回响应
博客列表页
@WebServlet("/userInfo")
public class UserInfoServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//从会话中拿到用户的信息然后返回
HttpSession session = req.getSession(false);
if (session == null) {
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前用户未登录");
return;
}
User user = (User) session.getAttribute("user");
if (user == null) {
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前用户未登录");
return;
}
//此时把user对象转成json字符串并返回给浏览器
resp.setContentType("application/json;charset=utf8");
//这里需要把密码去掉在返回
user.setPassword("");
String respJson = objectMapper.writeValueAsString(user);
resp.getWriter().write(respJson);
}
}
博客详情页
@WebServlet("/authorInfo")
public class AuthorInfoServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.先拿到请求中的blogId
String blogId = req.getParameter("blogId");
if (blogId == null) {
resp.setContentType("text/html:charset=utf8");
resp.getWriter().write("请求中缺少blogId");
return;
}
//2.在blog表中查询对应的Blog对象
BlogDao blogDao = new BlogDao();
Blog blog = blogDao.getBlog(Integer.parseInt(blogId));
if (blog == null) {
resp.setContentType("text/html:charset=utf8");
resp.getWriter().write("blogId没有找到");
return;
}
//3.根据blog对象中的userId,从user表中查到对应的作者
UserDao userDao = new UserDao();
User user = userDao.getUserById(blog.getUserId());
if (user == null) {
resp.setContentType("text/html:charset=utf8");
resp.getWriter().write("userId没有找到");
return;
}
//4.把这个user对象返回给浏览器
user.setPassword("");
String respJson = objectMapper.writeValueAsString(user);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(respJson);
}
}
8.4 让前端处理上述响应数据
博客列表页
//获取当前登录的用户信息
function getUserInfo() {
$.ajax({
type: 'get',
url: 'userInfo',
success: function (user) {
//把拿到的响应数据取出其中的uesrname设置到页面的h3标签中
let h3 = document.querySelector('.card h3');
h3.innerHTML = user.username;
}
});
}
getUserInfo();
通过抓包看一下详情
此时在博客列表页就能获取到用户的信息了
博客详情页
//获取作者信息
function getAuthorInfo() {
$.ajax({
type: 'get',
url: 'authorInfo' + location.search,
success: function (user) {
let h3 = document.querySelector('.card h3');
h3.innerHTML = user.username;
}
});
}
getAuthorInfo();
我们使用lisi这个用户登录,当我们点击查看全文就可以获取到当前文章的作者信息了,也就是zhangsan
此时我们的博客详情页就能获取到当前文章的作何信息了
此时我们的实现显示用户信息就完成了
9 实现退出功能
在博客列表页、博客详情页、博客编辑页的导航栏中都有一个"注销"按钮,让用户点击"注销" 的时候,就能触发一个HTTP请求
9.1 约定前后端交互接口
请求
GET / logout
响应 直接重定向到博客登录页
HTTP/1.1 302
Location:login.html
9.2 让前端给服务器发起请求
直接给a标签设置href属性即可
9.3 服务器处理上述请求,返回响应
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//先拿到会话对象
HttpSession session = req.getSession(false);
if (session == null) {
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前未登录");
return;
}
//把会话中的user属性删除
session.removeAttribute("user");
//跳转到博客登录页
resp.sendRedirect("login.html");
}
}
我们在博客编辑页点击注销按钮,抓包看一下详情,我们可以看到页面会直接跳转到博客登录页
此时我们的实现退出功能就完成了
10 实现发布博客
当用户点击提交的时候,构造HTTP请求,把此时页面中的标题和正文都传输到服务器这边,然后服务器把这个数据存入数据库,由于这里需要提交数据,因此使用form来构造请求
10.1 约定前后端交互接口
请求
POST/blog
Content-Type:x-www-form-urlencoded
title=这是标题&content=这是正文
响应
HTTP/1.1 302
Location:blog_list.html
10.2 让前端给服务器发起请求
10.3 服务器处理上述请求,返回响应
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.读取请求中的参数
req.setCharacterEncoding("utf8");
String title = req.getParameter("title");
String content = req.getParameter("content");
if (title == null || title.length() == 0 || content == null || content.length() == 0) {
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前传过来的标题或者正文为空,无法新增博客");
return;
}
//2.拿到会话
HttpSession session = req.getSession(false);
if (session == null) {
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前用户未登录");
return;
}
//3.从会话中拿到用户的信息
User user = (User) session.getAttribute("user");
if (user == null) {
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前用户未登录");
return;
}
//4.构造blog对象
Blog blog = new Blog();
blog.setTitle(title);
blog.setContent(content);
//我们能访问当前的博客编辑页,说明我们处于登录状态
//因此我们从会话中拿到当前登录用户的userId,设置进去即可
blog.setUserId(user.getUserId());
//5.插入到数据库中
BlogDao blogDao = new BlogDao();
blogDao.insert(blog);
//6.返回302重定向,跳转到博客列表页
resp.sendRedirect("blog_list.html");
}
}
此时我们写下博客然后提交,此时我们新写的博客就能再页面展示了
此时我们的实现发布博客功能就完成了
总结:整个过程是基于前后端交互来实现博客系统,主要是前后端交互的过程,以及前后端是怎样交互的