基于Servlet+JDBC实现的基础博客系统>>系列3 -- Servlet后端服务器搭建

目录

前言

1. 前期准备

2. Model层

2.1 数据库的设计

2.2 数据库表对应的实体类实现

User类

Blog类

2.3 JDBC 工具类实现

2.4 UserDao 的实现

2.5 BlogDao 的实现

3. Controller 层实现

3.1 博客列表页

3.1.1 约定前后端交互接口

3.1.2 编写后端代码 

3.1.3 编写前端代码

3.2 博客详情页

3.2.1 约定前后端交互接口

3.2.2 编写后端代码

3.2.3 编写前端代码

3.3 登录功能        

3.3.1 约定前后端交互接口

 3.3.2 编写后端代码

3.3.3 编写前端代码

3.4 检查用户登录状态

3.4.1 约定前后端交互接口

3.4.2 编写后端代码

3.4.3 编写前端代码

3.5 显示用户信息

3.5.1 约定前后端交互接口 

3.5.2 编写后端代码

3.5.3 编写前端代码 

3.6 退出登录功能(注销)

3.7 发布博客功能

3.7.2 编写后端代码

3.7.3 编写前端代码

3.8 删除博客功能


前言

        上一篇文章:基于Servlet+JDBC实现的基础博客系统>>系列2 -- 前端基础页面 对整个博客系统进行了简要的构建,接下来将博客系统进行实现前后端的交互. 全文项目的代码链接已经上传到git,欢迎大家访问,也希望得到大家的指点.谢谢.

1. 前期准备

 1. 了解MVC模式

        MVC(Model View Controller)是一种软件设计的框架模式,它采用模型(Model)-视图(View)-控制器(controller)的方法把业务逻辑、数据与界面显示分离。把众多的业务逻辑聚集到一个部件里面,当然这种比较官方的解释是不能让我们足够清晰的理解什么是MVC的。用通俗的话来讲,MVC的理念就是把数据处理、数据展示(界面)和程序/用户的交互三者分离开的一种编程模式。

2. 创建Maven项目,引入相关依赖 

下面是pom.xml的内容,主要是引入了Servlet Jackson MySQL的环境依赖

<?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>blogSystem</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/com.fasterxml.jackson.core/jackson-databind --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.15.0</version></dependency><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.49</version></dependency></dependencies></project>

3. 整个项目使用idea中的插件SmartTomCat ,方便进行调试.

  

2. Model层

2.1 数据库的设计

 在博客系统中,主要包含登录功能、注销功能、发布博客、删除博客、博客展示的功能。涉及到的实体即博客和用户。数据库表设计如下,具体见代码注释:

-- 一般对于建表的 sql 都会单独搞个 .sql 文件来保存.
-- 后续程序可能需要在不同的主机上部署. 部署的时候就需要在对应的主机上把数据库也给创建好.
-- 把建表 sql 保存好, 方便在不同的机器上进行建库建表-- 1.创建数据数据库
create database if not exists java107_blog_system character set utf8mb4;use java107_blog_system;-- 2. 创建各种表
drop table if exists blog;
-- 2.1 blog表
create table blog(blogId int primary key auto_increment,title varchar(128),content varchar(4096),userId int,postTime datetime
);drop table if exists user;
-- 2.2 user表
create table user(userId int primary key auto_increment,username varchar(50) unique,password varchar(50)
);insert into user values(null, '张三', '123'), (null, '李四', '123');insert into blog values(null, '我的第一篇博客', '这是博客正文', 1, '2023-06-04 12:00:00');
insert into blog values(null, '我的第二篇博客', '这是博客正文', 1, '2023-06-05 12:00:00');
insert into blog values(null, '我的第二篇博客', '如果你想用Java的SimpleDateFormat类来格式化时间,你可以使用以下代码:import java.text.SimpleDateFormat;import java.util.Date;public class Main{public static void main(String[] args){Date date = new Date();String strDateFormat = "yyyy-MM-dd HH:mm:ss";SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);System.out.println(sdf.format(date));}}', 1, '2023-06-05 12:00:00');

2.2 数据库表对应的实体类实现

User类

每个 user 对象,对应 user 表的一条记录。

package model;/*** Created with IntelliJ IDEA.* Description:用户user表的实体* User: YAO* Date: 2023-06-21* Time: 10:47*/
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;}
}

Blog类

每个 blog 对象,对应 blog 表的一条记录。

package model;import java.sql.Timestamp;
import java.text.SimpleDateFormat;/*** Created with IntelliJ IDEA.* Description:博客blog表的实体* 一个blog对象的实例就代表表中的一条记录* User: YAO* Date: 2023-06-21* Time: 10:46*/
public class Blog {private int blogId;private String title;private String content;private int userId;private Timestamp postTime;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 int getUserId() {return userId;}public void setUserId(int userId) {this.userId = userId;}public Timestamp getPostTimeStamp() {return postTime;}public String getPostTime() {// TimeStamp 返回的是时间戳// 这里要将时间戳进行格式化 使用SimpleDataFormatString strDateFormat = "yyyy-MM-dd HH:mm:ss";SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);return sdf.format(postTime);}public void setPostTime(Timestamp postTime) {this.postTime = postTime;}
}

 特别关注:针对时间戳在后端进行格式化字符串处理,便于前端的展示。

2.3 JDBC 工具类实现

 DBUtil 封装了用于获取数据库连接和关闭数据库连接资源的方法,便于 各个实体的Dao 使用,降低代码冗余度。

package model;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;/*** Created with IntelliJ IDEA.* Description:通过封装处理数据库的连接操作* User: YAO* Date: 2023-06-21* Time: 10:29*/
public class DBUtil {// 这个类要提供DataSourceprivate static volatile DataSource dataSource = null;private static DataSource getDataSource(){// 懒汉模式进行创建数据库连接实例if (dataSource == null){synchronized (DBUtil.class){if (dataSource == null){dataSource = new MysqlDataSource();((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java107_blog_system?characterEncoding=utf8&useSSL=false");((MysqlDataSource)dataSource).setUser("root");((MysqlDataSource)dataSource).setPassword("111111");}}}return dataSource;}public static Connection getConnection() throws SQLException {// 建立数据库连接return getDataSource().getConnection();}public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet) throws SQLException {if (resultSet != null){resultSet.close();}if (statement!=null){statement.close();}if (connection!=null){connection.close();}}
}

2.4 UserDao 的实现

DAO: Data Access Object 就是通过这个对象进行访问数据

主要实现方法:

1. selectUserById

2. selectUserById

package model;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;/*** Created with IntelliJ IDEA.* Description:user表的增删改查* DAO : Data Access Object  通过这样的对象进行访问数据* User: YAO* Date: 2023-06-21* Time: 10:52*/
public class UserDao {public User selectUserById(int userId) throws SQLException {// 根据id查询用户对象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) {e.printStackTrace();}finally {DBUtil.close(connection,statement,resultSet);}return null;}public User selectUserByName(String username) throws SQLException {// 根据name查询用户对象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) {e.printStackTrace();}finally {DBUtil.close(connection,statement,resultSet);}return null;}
}

2.5 BlogDao 的实现

 该类封装了有关 blog 表的操作。包括插入博客,返回博客列表,返回单一一条博客以及删除博客功能。具体见代码与注释:

package model;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;/*** Created with IntelliJ IDEA.* Description:blog表的增删改查* User: YAO* Date: 2023-06-21* Time: 10:51*/
public class BlogDao {public void insert(Blog blog) throws SQLException {// 将blog对象插入到数据库Connection connection = null;PreparedStatement statement = null;try {// 1. 建立连接connection = DBUtil.getConnection();// 2. 构建sql语句String sql = "insert into blog values(null,?,?,?,?)";// 3. 使用preparedStatement 填充信息statement = connection.prepareStatement(sql);statement.setString(1,blog.getTitle());statement.setString(2,blog.getContent());statement.setInt(3,blog.getUserId());statement.setTimestamp(4,blog.getPostTimeStamp());// 4. 执行sqlstatement.executeUpdate();} catch (SQLException e) {e.printStackTrace();}finally {// 5. 关闭所有的连接DBUtil.close(connection,statement,null);}}public List<Blog> selectAll() throws SQLException {// 查询所有的博客记录List<Blog> blogs = 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"));String content = resultSet.getString("content");if(content == null){content = "";}if (content.length() > 100){content = content.substring(0,100)+"...";}blog.setContent(content);blog.setUserId(resultSet.getInt("userId"));blog.setPostTime(resultSet.getTimestamp("postTime"));blogs.add(blog);}} catch (SQLException e) {e.printStackTrace();}finally {DBUtil.close(connection,statement,resultSet);}return blogs;}public Blog selectOne(int blogId) throws SQLException {// 查询指定博客id的博客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();if (resultSet.next()){Blog blog = new Blog();blog.setBlogId(resultSet.getInt("blogId"));blog.setTitle(resultSet.getString("title"));blog.setContent(resultSet.getString("content"));blog.setUserId(resultSet.getInt("userId"));blog.setPostTime(resultSet.getTimestamp("postTime"));return blog;}} catch (SQLException e) {e.printStackTrace();}finally {DBUtil.close(connection,statement,resultSet);}return null;}public void delete(int blogId) throws SQLException {// 指定博客id进行删除Connection connection = null;PreparedStatement statement = null;try {connection = DBUtil.getConnection();String sql = "delete from blog where blogId= ?";statement = connection.prepareStatement(sql);statement.setInt(1,blogId);statement.executeUpdate();} catch (SQLException e) {e.printStackTrace();}finally {DBUtil.close(connection,statement,null);}}
}

3. Controller 层实现

无论是博客详情页、博客列表页还是登录功能诸如此类,其核心逻辑都是一样的,具体分为如下几个步骤:

  • 约定前后端交互的接口;
  • 实现服务器代码,分别为controller层的servlet实现的api,以及model层使用jdbc来操作数据库;
  • 实现客户端代码:form / ajax / a标签跳转等。

3.1 博客列表页

3.1.1 约定前后端交互接口

该页面用于展示数据库中的博客列表。约定请求:GET/blog,响应为 json 格式。

3.1.2 编写后端代码 

 主要内容是根据请求中blogId是否为空,进行返回不同的响应,如果请求中包含指定的blogId就返回单条博客信息,为null就返回数据库中所有的博客信息.

创建BlogServlet.java文件:

/*获取博客信息*/
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();// 创建objectMapper对象用来将数据库中博客的信息转换成json格式@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {try {BlogDao blogDao = new BlogDao();String blogId = req.getParameter("blogId");if (blogId == null){// 分两种情况:// 1.当在请求中查找blogId的为空的时候,默认为查找所有的博客List<Blog> blogs = blogDao.selectAll();// 将查询到的信息转换成json格式String respString = objectMapper.writeValueAsString(blogs);// 设置响应内容的格式以及字符集resp.setContentType("application/json; charset=utf8");resp.getWriter().write(respString);}else {// 2.请求中blogId不为空的时候,进行查找相应id的博客Blog blog = blogDao.selectOne(Integer.parseInt(blogId));String respString = objectMapper.writeValueAsString(blog);resp.setContentType("application/json; charset=utf8");resp.getWriter().write(respString);}} catch (SQLException e) {e.printStackTrace();}}

3.1.3 编写前端代码

使用Ajax进行构造请求

列表页需要向后端请求的内容有:

  •  1. 所有的博客内容(右侧栏信息)
  •  2. 用户的登录状态(左侧栏信息) 

 1. 所有的博客内容(右侧栏信息) 

<script>// 通过ajas给服务器进行请求,获取所有的博客数据,并且构造到页面上function getBlogs(){jQuery.ajax({type:'get',url:'blog',success:function(body){// 回调函数// 根据返回的数据,构造出页面中对应的元素let containerRight = document.querySelector('.container-right')for(let blog of body){// 此时body就是一个jsd对象// 1.创建一个divlet blogDiv = document.createElement("div");// 2.设置格式类名classblogDiv.className = 'blog';// 3.设置标题divlet titleDiv = document.createElement("div");titleDiv.className = 'title';titleDiv.innerHTML = blog.title;// 4.设置时间divlet dateDiv = document.createElement("div");dateDiv.className = 'date';dateDiv.innerHTML = blog.postTime;// 5.设置描述divlet descDiv = document.createElement("div");descDiv.innerHTML = blog.content;// 6.设置查看全文的标签<a>let a = document.createElement("a")a.href = 'blog_detail.html?blogId='+blog.blogId;a.innerHTML = '查看全文 &gt;&gt;';// 7.将上述创建好的标签进行添加blogDiv.appendChild(titleDiv);blogDiv.appendChild(dateDiv);blogDiv.appendChild(descDiv);blogDiv.appendChild(a);containerRight.appendChild(blogDiv)}}})}// 定义完函数必须进行调用getBlogs();</script>

  2. 用户的登录状态(左侧栏信息) 

<script>function cheakLogin(){jQuery.ajax({type:'get',url:'login',success: function(body){if(body.userId && body.userId > 0){// 登录成功console.log('当前用户登录成功')let h3 = document.querySelector('.container .container-left .card h3')h3.innerHTML = body.username;}else{// 当前未登录// 设置重定向,到登录页location.assign('login.html');}}})}cheakLogin();</script>

3.2 博客详情页

3.2.1 约定前后端交互接口

 博客列表中有查看全文的按钮,点击查看全文就可以直接跳转到指定的博客详情页,此时查询的路径中拼接了查询字符串,其中包含blogId属性

3.2.2 编写后端代码

 在BlogServlet.java文件中

3.2.3 编写前端代码

 分别设置标题 时间 以及内容(使用editor.md自带的方法,将内容改成渲染之后的样式显示在HTML页面中)

<script>function getBlog(){jQuery.ajax({type: 'get',url: 'blog' + location.search,success: function(body) {// 设置博客的标题let h3 = document.querySelector('.container-right h3');h3.innerHTML = body.title;// 设置发布时间let dateDiv = document.querySelector('.container-right .date');dateDiv.innerHTML = body.postTime;// 设置正文. 正文内容应该是 markdown 格式的数据. // 此处要显示的应该是渲染过的 markdown 的内容, 而不是 markdown 的原始字符串. // 第一个参数, 是一个 html 元素的 id, 接下来渲染的结果会放到对应的 元素中. editormd.markdownToHTML('content', {markdown: body.content});}});}getBlog();</script>

3.3 登录功能        

        登录功能就相对简单了,约定前后端接口,请求Post/login,响应为 Http/1.1 302,当用户登录成功后跳转到博客列表页。登录失败,则提示相应的信息。需要注意的是,当用户登录成功后需要创建一个会话 session,用于保存必要的用户信息,便于其他页面使用。例如:验证获取登录状态,展示用户信息等。

3.3.1 约定前后端交互接口

 3.3.2 编写后端代码

package api;import com.fasterxml.jackson.databind.ObjectMapper;
import model.User;
import model.UserDao;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.servlet.http.HttpSession;
import java.io.IOException;
import java.sql.SQLException;/*** Created with IntelliJ IDEA.* Description:* User: YAO* Date: 2023-06-23* Time: 16:32*/
@WebServlet("/login")
public class LoginServlet extends HttpServlet {// 用来将数据转换成json的数据格式private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 设置请求的编码. 告诉 servlet 按照啥格式来理解请求req.setCharacterEncoding("utf8");// 设置响应的编码. 告诉 servlet 按照啥格式来构造响应// resp.setCharacterEncoding("utf8");resp.setContentType("text/html;charset=utf8");// 1. 读取参数中的用户名和密码//    注意!! 如果用户名密码包含中文, 此处的读取可能会乱码.String username = req.getParameter("username");String password = req.getParameter("password");if (username == null || "".equals(username) || password == null || "".equals(password)) {// 登录失败!!String html = "<h3>登录失败! 缺少 username 或者 password 字段</h3>";resp.getWriter().write(html);return;}// 2. 读数据库, 看看用户名是否存在, 并且密码是否匹配UserDao userDao = new UserDao();User user = null;try {user = userDao.selectUserByName(username);} catch (SQLException e) {throw new RuntimeException(e);}if (user == null) {// 用户不存在.String html = "<h3>登录失败! 用户名或密码错误</h3>";resp.getWriter().write(html);return;}if (!password.equals(user.getPassword())) {// 密码不对String html = "<h3>登录失败! 用户名或密码错误</h3>";resp.getWriter().write(html);return;}// 3. 用户名密码验证通过, 登录成功, 接下来就创建会话. 使用该会话保存用户信息.HttpSession session = req.getSession(true);session.setAttribute("user", user);// 4. 进行重定向. 跳转到博客列表页resp.sendRedirect("blog_list.html");}}

3.3.3 编写前端代码

使用form表单进行构造POST请求

<form action="login" method="post"><div class="row"><span>用户名</span><input type="text" id="username" placeholder="手机号/邮箱" 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>

3.4 检查用户登录状态

当用户处于博客列表页和详情页的时候,需要检查用户的登录状态。如果用户未登录,则给用户弹窗提示,并返回到登录页面。

3.4.1 约定前后端交互接口

请求 

响应(返回的是一个user对象包含User的所有信息)

3.4.2 编写后端代码

在loginServlet.java文件中重写doPost方法

1. 获取当前Session会话对象 ,检查Session对象是否为空,如果不为空也要检查是否存在用户对象.

@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("application/json");// 用来回去用登录的状态// 如果用户处于未登录的状态,就不会拿到会话HttpSession session = req.getSession(false);if (session == null){// 未登录User user = new User();String respJson = objectMapper.writeValueAsString(user);resp.getWriter().write(respJson);return;}User user = (User) session.getAttribute("user");if (user == null) {user = new User();String respJson = objectMapper.writeValueAsString(user);resp.getWriter().write(respJson);return;}// 确实成功取出了 user 对象, 就直接返回即可.String respJson = objectMapper.writeValueAsString(user);resp.getWriter().write(respJson);}

3.4.3 编写前端代码

将下述代码分别点加到blog_list.html blog_detail.html blog_edit.html 中.执行强制登录,才能访问.

<script>
function cheakLogin(){jQuery.ajax({type:'get',url:'login',success: function(body){if(body.userId && body.userId > 0){// 登录成功console.log('当前用户登录成功')let h3 = document.querySelector('.container .container-left .card h3')h3.innerHTML = body.username;}else{// 当前未登录// 设置重定向,到登录页location.assign('login.html');}}})}
cheakLogin();
</script>

3.5 显示用户信息

        对于博客列表页,我们希望展示的是当前登录用户的信息;对于博客详情页,我们希望展示的是文章的作者信息。约定前后端交互接口,请求:get/authorInfo,响应依然为json格式。

因此,我们对登录状态检查的函数进行改动,添加一个参数,用于区分当前页是博客列表页还是博客详情页。

  • 对于博客列表页,我们正常展示用户信息即可;
  • 对于博客详情页,我们只需要判断当前用户和作者是否为同一人,如果不是,则更改当前页用户信息为作者信息即可

3.5.1 约定前后端交互接口 

3.5.2 编写后端代码

package api;import com.fasterxml.jackson.databind.ObjectMapper;
import model.Blog;
import model.BlogDao;
import model.User;
import model.UserDao;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.SQLException;@WebServlet("/author")
public class authorServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String blogId = req.getParameter("blogId");if (blogId == null) {resp.setContentType("text/html; charset=utf8");resp.getWriter().write("参数非法, 缺少 blogId");return;}// 根据 blogId 查询 Blog 对象BlogDao blogDao = new BlogDao();Blog blog = null;try {blog = blogDao.selectOne(Integer.parseInt(blogId));} catch (SQLException e) {throw new RuntimeException(e);}if (blog == null) {// 博客不存在.resp.setContentType("text/html; charset=utf8");resp.getWriter().write("没有找到指定博客: blogId = " + blogId);return;}// 根据 blog 中的 userId 找到对应的用户信息UserDao userDao = new UserDao();User author = null;try {author = userDao.selectUserById(blog.getUserId());} catch (SQLException e) {throw new RuntimeException(e);}String respJson = objectMapper.writeValueAsString(author);resp.setContentType("application/json; charset=utf8");resp.getWriter().write(respJson);}
}

3.5.3 编写前端代码 

<script>
// 从服务器获取当前作者的用户信息, 并显示在页面上function getAuthorInfo(user){$.ajax({type: 'get',url: 'author' + location.search,success: function(body){// 此处的 body 就是服务器返回的 user 对象if (body.username) {// 如果响应中的 username 存在, 则直接更新到页面上changeUsername(body.username);if (user.username == body.username) {// 如果登录的用户和发布文章的用户为同一个人// 在详情页的导航栏添加一个删除按钮let navDiv = document.querySelector(".nav");// let a = document.createElement("a");// a.innerHTML = "删除博客";// a.href = "blogDelete" + location.search;// navDiv.appendChild(a);// 创建 a 标签元素var link = document.createElement("a");link.href = "blogDelete" + location.search;link.textContent = "删除博客";// 添加点击事件监听器link.addEventListener("click", function(event) {event.preventDefault();var confirmResult = confirm("确认要删除此博客?");if (confirmResult) {window.location.href = link.href; // 执行原有的功能}});navDiv.appendChild(link);                           }} else {console.log("获取作者信息失败! " + body.reason);}}});}
</script>

3.6 退出登录功能(注销)

注销功能非常简单,定义前后端交互接口,请求:get/logout,响应302跳转到登录页面。请求只需要在用户登录的时候添加一个 a 标签到导航栏即可。

怎样才算登录呢?

  • session 会话存在;
  • session 中包含 user 属性。

因此,实现注销,只需要将 user 属性从 session 中删除即可。

package api;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.servlet.http.HttpSession;
import java.io.IOException;/*** Created with IntelliJ IDEA.* Description:* User: YAO* Date: 2023-06-24* Time: 18:55*/
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {HttpSession session = req.getSession(false);if (session == null){resp.setContentType("txt/html; charset=utf-8");resp.getWriter().write("用户未登录");}// 将当前User对象进行删除session.removeAttribute("user");resp.sendRedirect("login.html");}
}

3.7 发布博客功能

同样,先约定前后端交互接口。请求:POST/blog,以form表单的形式将title和content正文传给服务器。响应:HTTP/1.1 302 发布成功后跳转到博客详情页。 

3.7.1 约定前后端交互接口

3.7.2 编写后端代码

 在BlogServlet.java中重写doPost方法

@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 发布博客// 读取请求, 构造 Blog 对象, 插入数据库中即可!!HttpSession httpSession = req.getSession(false);if (httpSession == null) {resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前未登录, 无法发布博客!");return;}User user = (User) httpSession.getAttribute("user");if (user == null) {resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前未登录, 无法发布博客!");return;}// 确保登录之后, 就可以把作者给拿到了.// 获取博客标题和正文req.setCharacterEncoding("utf8");String title = req.getParameter("title");String content = req.getParameter("content");if (title == null || "".equals(title) || content == null || "".equals(content)) {resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前提交数据有误! 标题或者正文为空!");return;}// 构造 Blog 对象Blog blog = new Blog();blog.setTitle(title);blog.setContent(content);blog.setUserId(user.getUserId());// 发布时间, 在 java 中生成 / 数据库中生成 都行blog.setPostTime(new Timestamp(System.currentTimeMillis()));// 插入数据库BlogDao blogDao = new BlogDao();try {blogDao.insert(blog);} catch (SQLException e) {throw new RuntimeException(e);}// 跳转到博客列表页resp.sendRedirect("blog_list.html");}

3.7.3 编写前端代码

<div class="blog-edit-container"><form action="blog" method="post" style="height: 100%;"><!-- 标题编辑区 --><div class="title"><input type="text" id="title-input" placeholder="输入标题文章" name="title"><input type="submit" id="submit" value="发布文章"></div><!-- 博客编辑器 --><!-- 把 md 编辑器放到这个 div 中 --><div id="editor"><!-- editor.md对于form表单是支持的,就是在form表单中设置了一个隐藏的textarea, editor.md会将用户输入的填写进去 --><!-- style="display: none 是指的移仓了这个元素 --><textarea name="content" style="display: none;"></textarea></div></form></div>

3.8 删除博客功能

         只有作者本人才能删除博客,因此,在进入博客详情页的时候,进行判断。如果是作者本人,则添加删除按钮

我们可以在博客详情页中检查用户的登录信息的同时,检测到如果登陆用户与博客作者相同的时候,添加删除按钮,并且在点击删除按钮之后进行弹窗确认之后在进行执行.

 后端代码

package api;
import model.Blog;
import model.BlogDao;
import model.User;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.servlet.http.HttpSession;
import java.io.IOException;
import java.sql.SQLException;/*** Created with IntelliJ IDEA.* Description:* User: YAO* Date: 2023-06-24* Time: 20:25*/
@WebServlet("/blogDelete")
//@SuppressWarnings({"all"})
public class BlogDeleteServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 1. 检验用户是否登录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;}// 2. 获取要删除的博客的 blogIdString blogId = req.getParameter("blogId");if (blogId == null || "".equals(blogId)) {resp.setContentType("text/html; charset=utf8");resp.getWriter().write("参数不正确!");return;}// 3. 获取要删除的博客信息BlogDao blogDao = new BlogDao();Blog blog = null;try {blog = blogDao.selectOne(Integer.parseInt(blogId));} catch (SQLException e) {throw new RuntimeException(e);}if (blog == null) {resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前博客不存在");return;}// 4. 再次校验用户是否为博客的作者if (blog.getUserId() != user.getUserId()) {resp.setContentType("text/html; charset=utf8");resp.getWriter().write("不是您的博客, 无法删除!");return;}// 5. 删除并跳转try {blogDao.delete(Integer.parseInt(blogId));} catch (SQLException e) {throw new RuntimeException(e);}resp.sendRedirect("blog_list.html");}
}

前端代码

<script>function getBlog(){jQuery.ajax({type: 'get',url: 'blog' + location.search,success: function(body) {// 设置博客的标题let h3 = document.querySelector('.container-right h3');h3.innerHTML = body.title;// 设置发布时间let dateDiv = document.querySelector('.container-right .date');dateDiv.innerHTML = body.postTime;// 设置正文. 正文内容应该是 markdown 格式的数据. // 此处要显示的应该是渲染过的 markdown 的内容, 而不是 markdown 的原始字符串. // 第一个参数, 是一个 html 元素的 id, 接下来渲染的结果会放到对应的 元素中. editormd.markdownToHTML('content', {markdown: body.content});}});}getBlog();// 从服务器获取当前作者的用户信息, 并显示在页面上function getAuthorInfo(user){$.ajax({type: 'get',url: 'author' + location.search,success: function(body){// 此处的 body 就是服务器返回的 user 对象if (body.username) {// 如果响应中的 username 存在, 则直接更新到页面上changeUsername(body.username);if (user.username == body.username) {// 如果登录的用户和发布文章的用户为同一个人// 在详情页的导航栏添加一个删除按钮let navDiv = document.querySelector(".nav");// let a = document.createElement("a");// a.innerHTML = "删除博客";// a.href = "blogDelete" + location.search;// navDiv.appendChild(a);// 创建 a 标签元素var link = document.createElement("a");link.href = "blogDelete" + location.search;link.textContent = "删除博客";// 添加点击事件监听器link.addEventListener("click", function(event) {event.preventDefault();var confirmResult = confirm("确认要删除此博客?");if (confirmResult) {window.location.href = link.href; // 执行原有的功能}});navDiv.appendChild(link);                           }} else {console.log("获取作者信息失败! " + body.reason);}}});}function getUserInfo(){$.ajax({type: 'get',url: 'login',success: function(body){// 判断此处的 body 是不是一个有效的 user 对象(userId = 0无效)if (body.userId && body.userId > 0) {// 登录成功console.log("当前用户登录成功! 用户名:" + body.username);// if (pageName == 'blog_list.html') {//     // 更新用户名//     changeUsername(body.username);// }changeUsername(body.username);// 判断当前登录的用户和作者是否相同getAuthorInfo(body);} else {// 登录失败 提示信息 并跳转到 login.htmlalert("当前未登录, 请登录后重试!");location.assign('login.html');}},error: function(){alert("当前未登录, 请登录后重试!");location.assign('login.html');}});}// 获取当前用户的登录状态getUserInfo();// 用于更新用户名信息的方法function changeUsername(username){let h3 = document.querySelector(".card h3");h3.innerHTML = username;}</script>

以上就是博客系统的所有功能了.还有其他的细节可以进行完善.

完整的博客系统的代码可以在下方链接进行访问👇👇👇欢迎参考和指正.''😊😊😊

博客系统代码(前端+后端)https://gitee.com/yao-fa/java-ee-elementary/tree/master/blogSystem​​​​​​​谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/2164.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis复习

文章目录 分布式锁基本概念及问题超时问题可重入锁 Pub/Sub消息多播Pub/Sub模式订阅消息结构PubSub缺陷 线程 I/O模型非阻塞I/O事件轮询&#xff08;多路复用&#xff09;select函数指令队列 分布式锁 基本概念及问题 如果操作要修改用户状态&#xff0c;需要先读取再修改用户…

Nginx | 苹果电脑Mac安装和验证Nginx服务过程记录

common wx&#xff1a;CodingTechWork&#xff0c;一起学习进步。 引言 本文主要总结如何在Mac电脑上进行Nginx服务的安装&#xff0c;重点讲解使用brew命令进行安装和验证的过程及问题记录。 安装步骤 安装过程记录 查看nginx信息 首先使用命令brew info nginx进行本机ng…

Linux下Master-Master Replication Manager for MySQL 双主故障切换

简述&#xff1a; Master-Master Replication Manager for MySQL&#xff08;MMRM&#xff09;是一种用于MySQL数据库的主-主复制管理工具。它允许在多个MySQL主机之间建立双向的主-主复制关系&#xff0c;实现数据的同步和高可用性。 工作原理是通过在每个MySQL主机上配置双…

传统后端漏洞----(Web Server) 解析漏洞

笔记 前言IIS解析漏洞文件夹解析漏洞原理限制条件 ";" 分号截断漏洞原理 IIS解析漏洞检测IIS 文件夹解析漏洞检测IIS 分号截断漏洞检测 防御手段 Nginx解析漏洞Nginx 文件类型错误解析漏洞导致任意PHP代码执行原理Nginx 空字节解析漏洞导致任意文件可解析&#xff08…

【详细分析】thinkphp反序列化漏洞

文章目录 配置xdebug反序列化漏洞利用链详细分析poc1&#xff08;任意文件删除&#xff09;测试pocpoc2&#xff08;任意命令执行&#xff09;poc3&#xff08;任意命令执行&#xff09; 补充代码基础函数trait关键字应用案例优先级多trait 配置xdebug php.ini [Xdebug] zend…

Spring Boot 中的 WebMvc 是什么,原理,如何使用

Spring Boot 中的 WebMvc 是什么&#xff0c;原理&#xff0c;如何使用 介绍 在 Spring Boot 中&#xff0c;WebMvc 是非常重要的一个模块。它提供了一系列用于处理 Web 请求的组件和工具。在本文中&#xff0c;我们将介绍 Spring Boot 中的 WebMvc 是什么&#xff0c;其原理…

“配置DHCP Snooping实验:保护网络中的DHCP服务和防止欺骗攻击“

"配置DHCP Snooping实验&#xff1a;保护网络中的DHCP服务和防止欺骗攻击" 【实验目的】 部署DHCP服务器。熟悉DHCP Snooping的配置方法。验证拓扑。 【实验拓扑】 实验拓扑如图所示。 设备参数如下表所示。 设备 接口 IP地址 子网掩码 默认网关 R1 F0/0 …

vue element UI在button按钮使用 @keyup.enter不生效

如图所示&#xff0c;没效果。在按钮上绑定keyup事件&#xff0c;加上.native覆盖原有封装的keyup事件 解决办法 created () {document.onkeyup e > {if (e.keyCode 13 && e.target.baseURI.match(/login/)) {// 调用登录 验证方法this.submitForm()}}}成功解决&…

MySQL子查询

&#x1f607;作者介绍&#xff1a;一个有梦想、有理想、有目标的&#xff0c;且渴望能够学有所成的追梦人。 &#x1f386;学习格言&#xff1a;不读书的人,思想就会停止。——狄德罗 ⛪️个人主页&#xff1a;进入博主主页 &#x1f5fc;专栏系列&#xff1a;进入MySQL专栏知…

如何使用upupw搭建服务器,并映射外网访问

作为计算机行业从业人员&#xff0c;相信很多人都接触并使用过phpstudy等类似环境集成包&#xff0c;着对于upupw就比较好理解了。UPUPW绿色服务器平台是Windows下很有特色的一款免费服务器PHP套件&#xff0c;UPUPW PHP套件简化了PHP环境搭建步骤&#xff0c;一个压缩包解压到…

【MOOC 作业】第4章 网络层

不是标答也不是参考答案 仅从个人理解出发去做题 1、(20分) 考虑如图示的网络。 a. 假定网络是一个数据报网络。显示路由器 A 中的转发表&#xff0c;其中所有指向主机 H3 的流量通过接口 3 转发。 目的网络链路接口H33 b. 假定网络是一个数据报网络。你能写出路由器 A 中的…

设计模式篇(Java):单例模式

上一篇&#xff1a;设计模式篇(Java)&#xff1a;前言(UML类图、七大原则) 四、单例模式 所谓类的单例设计模式&#xff0c;就是采取一定的方法保证在整个的软件系统中&#xff0c;对某个类只能存在一个对象实例&#xff0c;并且该类只提供一个取得其对象实例的方法(静态方法)…