目录

前言

博客系统简要分析

一、数据库的设计

1.1 分析

1.2 代码实现(创建数据库和表)

二、封装数据库(JDBC代码的编写)

2.1、首先通过创建Maven项目,基于Small Tomcat部署 servlet;

2.2、封装数据库的DataSource

三、实现博客列表页

3.1、业务逻辑及解释

3.2、约定前后端交互接口

3.3实现客户端代码代码

3.4、实现服务器代码

四、实现博客详情页

4.1、业务逻辑及解释

4.2 约定前后端交互接口

4.3、实现服务器代码

4.4、实现客户端代码

五、实现博客登录页

5.1、业务逻辑及解释

5.2、约定前后端交互接口

5.3、实现客户端代码

5.4、实现服务器代码

六、实现强制登录逻辑

6.1、业务逻辑及解释

6.2、前后端交互接口

6.3、实现客户端代码

6.4、实现服务器代码

七、博客列表页中的显示用户信息

7.1、业务逻辑及解释

7.2、前后端交互接口

7.3实现服务器代码

7.4、实现客户端代码

八、实现注销功能(退出登录)

8.1、业务逻辑及解释

8.2、前后端交互接口

8.3、实现客户端代码

8.4、实现服务器代码

九、实现发布博客功能

9.1、业务逻辑及解释

9.2、前后端交互接口

9.3、实现服务器代码

9.4、实现客户端代码

十、实现删除博客功能

10.1、业务逻辑及解释

10.2、约定前后端接口

10.3、实现服务器代码

10.4、实现客户端代码

十一、结语


前言

这里所要介绍的博客系统,是一个简单的网站程序,如果你能吃透这个博客系统,也是可以作为一个项目写到简历上的;


博客系统简要分析

咱们将要实现的博客系统,分为四个网页:博客列表页、博客详情页、博客登录页、博客编辑页;要实现的功能有:实现博客列表的展示功能、实现博客详情的展示功能、登录功能、限制用户权限(强制要求用户登录后查看信息)、显示用户信息、实现注销(退出登录)、发布博客、删除博客;

接下来就来带大家实现一下~


一、数据库的设计

1.1 分析

实际上,咱们的业务逻辑较为简单,只需要一个库,两张表即可;

两张表分别是:

1. 博客表 :blog(blogId, title, content, postTime, userId);  ——>  (博客ID,博客标题,博客正文,发布时间,用户ID);

2. 用户表 user(userId, username, password); ——> (用户ID,用户名,用户密码)

1.2 代码实现(创建数据库和表)

create database if not exists blog_system;
use blog_system;
-- 这里删除是为了以防以前过相同的表有干扰
drop table if exists blog;-- 这是博客table
create table blog (blogId int primary key auto_increment, -- 博客idtitle varchar(256), -- 标题content text,  -- 正文postTime datetime, -- 发布时间userId int -- 用户Id
);drop table if exists user;
-- 这是用户table
create table user (userId int primary key auto_increment, -- 用户idusername varchar(50) unique, -- 用户名(类似于账户不可以重复)password varchar(50) -- 密码
);
-- 插入数据用于测试insert into blog values(null, "这是第一篇博客", "努力敲代码,走上人生巅峰", '2022-11-24 18:00:00', 1);
insert into blog values(null, "这是第二篇博客", "努力敲代码,走上人生巅峰", '2022-11-24 18:00:00', 1);insert into user values(null, "zhangsan", "123");
insert into user values(null, "lisi", "123");

二、封装数据库(JDBC代码的编写)

2.1、首先通过创建Maven项目,基于Small Tomcat部署 servlet;

这里如果不了解的,可以去看看我的这篇博客:

http://t.csdn.cn/QSt3L

所需要引入的依赖:

    <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.49</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.13.4.1</version></dependency></dependencies>

2.2、封装数据库的DataSource

因为我们的博客系统网站是要给很多人用的,所以在数据库的创建数据源、建立连接、断开连接这里,可以引入单例模式中的懒汉模式,对以上所说步骤进行封装;

第一步:在java目录下创建类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 volatile DataSource dataSource = null;public 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/blog_system?characterEncoding=utf8&useSSL=false");((MysqlDataSource)dataSource).setUser("root");((MysqlDataSource)dataSource).setPassword("1111");}}}return dataSource;}//建立连接public static Connection getConnection() throws SQLException {return getDataSource().getConnection();}//关闭连接public void close(Connection connection, PreparedStatement statement, ResultSet resultSet) {//[注意1]这里不要直接用throws抛异常,因为一旦哪一个发生了异常,// 就有可能存在后面的还有需要释放的资源没释放掉的情况;//[注意2]注意释放顺序,和创建顺序是相反的if(resultSet != null) {//这里判断是因为有的jdbc操作不需要他,但却要close其他的try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}if(statement != null) {try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if(connection != null) {try {connection.close();} catch (SQLException e) {e.printStackTrace();}}}
}

【对以上代码的解释】:为什么close方法中采用try catch的方式处理异常而不是throws?

这里不要直接用throws抛异常,因为一旦哪一个发生了异常,就有可能存在后面的还有需要释放的资源没释放掉的情况;

【对以上代码的解释】:为什么close方法中需要判断是否为null才执行close操作?

这里是将所有的close操作放在一起了,所以一旦执行close操作,就会对所以资源进行close,但实际上,有时候可能有的数据源我们都没有创建(例如,有时不用创建ResultSet),这时,对没有创建的数据进行close,就会导致null异常,所以这里需要判断是否为空,再释放;

第二步:在java目录下创建Blog类,用来表示实体类(如下代码)

import java.sql.Timestamp;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() {//这里时间不能直接返回(直接返回是一个时间戳)//所以需要改一下这个方法,让他返回一个格式化的时间日期SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年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;}
}

【对以上代码的解释】:

简单来说,就是你的数据库中创建的blog表有什么属性,就写什么就ok;

【对以上代码的解释】:对getPostTime方法的解释

这里不能通过getter直接返回日期数据,直接返回的话,是一个时间戳(如下图)

这里需要通过SimpleDateFormat这个类将时间戳格式化成时间日期,初始化这个类的时候就需要填写的格式,具体怎么填写,建议大家不要背,因为不同语言之间这个大小写格式差异很大,所以建议用的时候查一下官方文档就好;(使用此类后,效果如下)

第三步:在java目录下创建类BlogDao,用数据库来操作博客数据

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;//对博客信息的相关操作
// +----------+--------------+------+-----+---------+----------------+
// | Field    | Type         | Null | Key | Default | Extra          |
// +----------+--------------+------+-----+---------+----------------+
// | blogId   | int(11)      | NO   | PRI | NULL    | auto_increment |
// | title    | varchar(256) | YES  |     | NULL    |                |
// | content  | text         | YES  |     | NULL    |                |
// | postTime | datetime     | YES  |     | NULL    |                |
// | userId   | int(11)      | YES  |     | NULL    |                |
// +----------+--------------+------+-----+---------+----------------+
public class BlogDao {//1.插入一个博客到数据库中 -- 发布博客public void insert(Blog blog) {Connection connection = null;PreparedStatement statement = null;try {//建立连接connection = DBUtil.getConnection();//构造sqlString 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.getBlogId());//执行sqlint ret = statement.executeUpdate();if(ret == 1) {System.out.println("用户" + blog.getUserId() + "插入1个博客!");} else {System.out.println("用户" + blog.getUserId() + "博客插入失败!");}//[注意]:不可以在这里close,上面代码一旦抛出异常,这里就无法执行close;} catch (SQLException e) {e.printStackTrace();} finally {//关闭数据源DBUtil.close(null, statement, connection);}}//2.根据博客id查询博客 -- 博客详情页public Blog selectOne(int blogId) {Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {//建立连接connection = DBUtil.getConnection();//创建sqlString sql = "select * from Blog where blogId = ?";statement = connection.prepareStatement(sql);statement.setInt(1, blogId);//执行sqlresultSet = 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.setPostTime(resultSet.getTimestamp("postTime"));blog.setUserId(resultSet.getInt("userId"));return blog;}} catch(SQLException e) {e.printStackTrace();} finally {DBUtil.close(resultSet, statement, connection);}return null;}//3.查询博客列表 -- 博客列表页public List<Blog> selectAll() {Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;List<Blog> blogList = new ArrayList<>();try {//建立连接connection = DBUtil.getConnection();//构造sqlString sql = "select * from blog order by postTime desc";statement = connection.prepareStatement(sql);//执行sqlresultSet = statement.executeQuery();while(resultSet.next()) {//遍历结果集Blog blog = new Blog();blog.setBlogId(resultSet.getInt("blogId"));blog.setTitle(resultSet.getString("title"));String content = resultSet.getString("content");//在博客列表页,正文一旦超出100个字,就截断,用...代替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) {e.printStackTrace();} finally {DBUtil.close(resultSet, statement, connection);}return blogList;}//4.删除指定博客 -- 删除博客public void delete(String blogId) {Connection connection = null;PreparedStatement statement = null;try {//建立连接connection = DBUtil.getConnection();//构造sqlString sql = "delete from Blog where blogId = ?";statement = connection.prepareStatement(sql);statement.setString(1, blogId);//执行sqlint ret = statement.executeUpdate();if(ret != 1) {System.out.println("博客删除成功!");} else {System.out.println("博客删除失败!");}} catch(SQLException e) {e.printStackTrace();} finally {DBUtil.close(null, statement, connection);}}
}

【对以上代码的解释】:BlogDao类名这样叫什么用意?

DAO是 Data Access Object 的缩写,也就是说访问数据库操作,就可以通过后缀含有DAO的类来操作;

【对以上代码的解释】:为什么使用try catch finally,而不用throws?

以防代码还没有关闭数据库连接就发生了异常,导致内存泄漏;

【对以上代码的解释】:为什么要对博客正文内容进行substring?

此处是博客列表页,不因该把博客正文全部显示出来,而是因该只显示文章摘要,所以这里就对摘要部分进行截断,长度超出限制(这里限制自己根据需求定义),就取出一部分子串,剩下部分用"..."代替;

第四步:在java目录下创建User类,用来实体话对象(和Blog类同理)

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;}
}

第五步:在java目录下创建UserDao,用数据库来操作用户数据(和BlogDao类同理)

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;//用户表的相关操作
public class UserDao {//用户登录public User selectByName(String username) {Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {//建立连接connection = DBUtil.getConnection();//构造sqlString sql = "select * from User where username = ?";statement = connection.prepareStatement(sql);statement.setString(1, username);//执行sqlresultSet = 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;}//根据用户id查询用户信息public User selectById(int userId) {Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {//建立连接connection = DBUtil.getConnection();//构造sqlString sql = "select * from user where userId = ?";statement = connection.prepareStatement(sql);statement.setInt(1, userId);//执行sqlresultSet = 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;}
}

三、实现博客列表页

3.1、业务逻辑及解释

在博客列表页中,在页面加载的时候,需要通过ajax发起HTTP请求,从服务器获取到博客列表数据,所以这里我们就要提前约定好前后端交换接口:考虑好发什么样的请求,返回什么样的响应。

3.2、约定前后端交互接口

在blog_list.html页面(前端HTML)中,发起如下请求:

在java目录下创建BlogServlet类,用来处理get请求,并返回如下http响应

3.3实现客户端代码代码

    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script><script>//发送 ajax 从服务器获取数据function getBlogs() {$.ajax({type: 'get',url: 'blog',success: function(body) {//获取成功,那么body就是js对象数据,每个元素就是一篇博客let container = document.querySelector('.container-right');for(let blog of body) {//构造blogDivlet 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');//这里href加上了一个query string,是为了查看全文的内容可以通过ID对应到对应的文章a.href = 'blog_detail.html?blogId=' + blog.blogId;a.innerHTML = "查看全文 &gt;&gt;";//拼装container.appendChild(blogDiv);blogDiv.appendChild(titleDiv);blogDiv.appendChild(dateDiv);blogDiv.appendChild(descDiv);blogDiv.appendChild(a);}}});}getBlogs();</script>

3.4、实现服务器代码

这里创建了一个新的类BlogServlet;

import com.fasterxml.jackson.databind.ObjectMapper;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;public class BlogServlet extends HttpServlet {//json格式转换ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//按照约定格式返回数据resp.setContentType("application/json; charset=utf8");BlogDao blogDao = new BlogDao();List<Blog> blogList = blogDao.selectAll();resp.getWriter().write(objectMapper.writeValueAsString(blogList));}
}

四、实现博客详情页

4.1、业务逻辑及解释

  在博客列表页点击查看详情,跳转到详情页,并看到详情页的博客正文;

点击查看全文,就在blog_html页面发起一个get请求,这里就需要告诉服务器,是请求的哪一个博客,那么就要约定,在请求页面url中加上query string(这里之前已经加好了,如下图)

进入详情页后,就需要让博客详情页再次发送ajax请求,向服务器获取当前blogId对应的博客内容,再由博客详情也把数据展示到页面中;

4.2 约定前后端交互接口

(例如获取博客ID为1的博客)

4.3、实现服务器代码

这里实际上对BlogServlet类进行了修改

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.List;@WebServlet("/blog")
public class BlogServlet extends HttpServlet {//json数据格式的转换private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//博客列表页中已经使用了过了/blog下的doGet请求,但是//博客详情页还想继续用,这里就需要用query string来区分了//若请求中带有query string,也就是含有blogId这个参数,就说明是博客详情页发起的请求//若没有,则说明是博客列表页发起的请求resp.setContentType("application/json; charset=utf8");BlogDao blogDao = new BlogDao();String blogId = req.getParameter("blogId");if(blogId == null) { //说明是博客列表页List<Blog> blogList = blogDao.selectAll();resp.getWriter().write(objectMapper.writeValueAsString(blogList));} else { //说明是博客详情页Blog blog = blogDao.selectOne(Integer.valueOf(blogId));resp.getWriter().write(objectMapper.writeValueAsString(blog));}}
}

4.4、实现客户端代码

    <!-- 通过ajax请求获取详细博客详细 --><script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script><script>function getBlog() {$.ajax({type: 'get',url: 'blog' + location.search, //location.search 是用来获取当前url中的query stringsuccess: function(body) {//这里成功的话,会获取到一个bloglet h3 = document.querySelector('.container-right>.blog-detail>h3');h3.innerHTML = body.title;let dateDiv = document.querySelector('.blog-detail>.date');dateDiv.innerHTML = body.postTime;//content部分需要使用editor.md来渲染,否则就是一个普通文本格式(注意引入editor.md依赖)//以下写法是官方约定的写法:editormd.markdownToHTML('content', { markdown: body.content });}});}getBlog();</script>

【对以上代码的解释】:location.search是干什么的?

这是用来获取当前url里的query string;

例如:假设当前url为: 127.0.0.1:8080/blog?blogId = 1;通过location.search就可以获取到

"?blogId = 1";

【对以上代码的解释】:editormd.markdownToHTML是干什么的?

由于content部分的内容是使用markdown写的,如果直接通过body.content得到正文内容,就是一个简单的文本格式,所以这里需要editor.md渲染文本;这里的这段代码是官方文档上的固定写法;

注意:这里用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>

五、实现博客登录页

5.1、业务逻辑及解释

用户访问login.html,输入用户名和密码,点击登录按钮,发起一个请求,将用户名和密码交给服务器;服务器对身份进行验证,若验证成功,就让页面跳转到博客列表页,若验证失败,就返回错误信息;

5.2、约定前后端交互接口

5.3、实现客户端代码

    <div class="login-container"><form action="login" method="post"><!-- 登录对话框 --><div class="dialog"><h3>登录</h3><div class="row"><span>用户名</span><!-- 这两个框很关键, 后面还要用, 就起个 id 作为标识 --><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="login-btn" value="登录"></div></div></form></div>

【对以上代码的解释】:

1.注意action和method和约定的请求格式是匹配的;

2.input标签中的name属性就是构造请求的键值对中的“键”,用户输入的内容则是“值”;

3.form约定,必须使用 input type="submit"来提交表单;

5.4、实现服务器代码

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;@WebServlet("/login")
public class LoginServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.从请求中获取用户名和密码req.setCharacterEncoding("utf-8");//用户名有可能为中文String username = req.getParameter("username");String password = req.getParameter("password");if(username == null || username.equals("") || password == null || password.equals("")) {//判断用户名和密码为空,返回登录失败resp.setContentType("text/html; charset=utf8");resp.getWriter().write("用户名或密码为空,登录失败!");return;}//2.查询数据库,检验用户名和密码是否正确UserDao userDao = new UserDao();User user = userDao.selectByName(username);//用户名获取用户信息if(user == null || !user.getPassword().equals(password)) {//用户名不存在,或者密码错误resp.setContentType("text/html; charset=utf8");resp.getWriter().write("用户名或密码错误,登录失败!");return;}//4.若正确,创建一个会话HttpSession session = req.getSession(true);//会话中保存user信息,为了后续访问其他页面,就可以直接通过会话拿到当前页面是哪一个用户在访问session.setAttribute("user", user);//5.构造重定向302报文resp.sendRedirect("blog_list.html");}
}

六、实现强制登录逻辑

6.1、业务逻辑及解释

在博客列表页、详情页、编辑页、都在页面加载后、发起一个ajax;这个ajax就从服务器获取当前登陆状态,若当下是未登录,就直接重定向到登录页,若已登录,不做任何处理;

6.2、前后端交互接口

6.3、实现客户端代码

         function getLoginStatus() {$.ajax({type: 'get',url: 'login',success: function() {//状态码是200,就不用做任何事情console.log("当前页面已经登录过~");},error: function() {//只要是非2开头的状态码,就会触发这个error//就让页面强制跳转到登录页面console.log("当前还未登录");location.assign('login.html');}});}getLoginStatus();

【对以上代码的解释】:

判断状态码:若状态码是200则没有事情发生,若状态码是403,则强制跳转;

6.4、实现服务器代码

    @Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//这个方法用来检验当前的登录状态//1.获取当前会话HttpSession session = req.getSession(false);if(session == null) {//没有回话就是未登录状态resp.setStatus(403);return;}//检查user对象是否存在//已经判断了session,为什么还要判断user?//因为当点击“退出登录”之后,我们实现的逻辑是删除user对象,session并没有删除//所以这里需要判断User user = (User)session.getAttribute("user");if(user == null) {//有会话,但是没有user对象,也认为是未登录状态resp.setStatus(403);return;}//2.返回状态码200(也可以不返回,因为默认状态码是200)resp.setStatus(200);}

【对以上代码的解释】:已经判断了session,为什么还要判断user?

我们实现退出登录的逻辑是只删除user对象,不删除session,那么当点击退出登录之后,就会出现session存在,而user对象不存在的情况,所以这里仍需要判断user,即使有会话,user不存在,也认为是未登录状态;


七、博客列表页中的显示用户信息

7.1、业务逻辑及解释

博客列表页中:加载页面时,从服务器获取当前登录用户信息,将信息显示到页面上;

博客详情页中:加载页面时,从服务器获取博客作者用户信息,把信息显示到页面上;

7.2、前后端交互接口

7.3实现服务器代码

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 javax.servlet.http.HttpSession;
import java.io.IOException;@WebServlet("/userInfo")
public class UserInfoServlet 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) {//说明当前访问的是博客列表页,获取登录用户信息即可//直接从session中获取即可getUserInfoFromSession(req, resp);}else {//说明当前访问的是博客详情页,获取文章作者即可//从数据库中获取getUserInfoFromDB(req, resp, Integer.valueOf(blogId));}}private void getUserInfoFromDB(HttpServletRequest req, HttpServletResponse resp, int blogId) throws IOException {//1.根据blogId查询Blog对象,获取到userId(作者)BlogDao blogDao = new BlogDao();Blog blog = blogDao.selectOne(blogId);if(blog == null) {//若blogId是瞎写的,数据库中查询不到resp.setStatus(404);resp.setContentType("text/html; charset=utf8");resp.getWriter().write("blogId不存在");return;}//2.根据userId查询User对象UserDao userDao = new UserDao();User user = userDao.selectById(blog.getUserId());if(user == null) {resp.setStatus(404);resp.setContentType("text/html; charset=utf8");resp.getWriter().write("userId不存在");return;}//2.获取到user后,将其中的password删除(保护用户信息安全),并返回user.setPassword("");resp.setContentType("application/json; charset=utf8");resp.getWriter().write(objectMapper.writeValueAsString(user));}private void getUserInfoFromSession(HttpServletRequest req, HttpServletResponse resp) throws IOException {//1.获取session会话HttpSession session = req.getSession(false);if(session == null) {resp.setStatus(404);resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前未登录");}User user = (User) session.getAttribute("user");if(user == null) {resp.setStatus(404);resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前未登录");}//2.获取到user后,将其中的password删除(保护用户信息安全),并返回user.setPassword("");resp.setContentType("application/json; charset=utf8");resp.getWriter().write(objectMapper.writeValueAsString(user));}
}

7.4、实现客户端代码

博客详情页:

        //博客详情页,对当前作者信息进行获取function getUserInfo() {$.ajax({type: 'get',url: 'userInfo' + location.search,//注意这里要加上qurey string,才能得到当前作者信息success: function(body) {//这里主要改用户名,其他内容要改逻辑都是一样的let h3 = document.querySelector('.container-left>.card>h3');h3.innerHTML = body.username;}});}getUserInfo();

博客列表页:

        //博客列表页,对当前用户信息进行获取function getUserInfo() {$.ajax({type: 'get',url: 'userInfo',success: function(body) {//这里主要改用户名,其他内容要改逻辑都是一样的let h3 = document.querySelector('.container-left>.card>h3');h3.innerHTML = body.username;}});}getUserInfo();

八、实现注销功能(退出登录)

8.1、业务逻辑及解释

注销按钮是 a标签 ,点击他就会给服务器发送一个http请求,发送的http请求会告诉服务器要退出登录了,服务器就会把user对象删除(此处不是删除HttpSession对象的是因为HttpSession没有一个直接用于删除的方法,即使可以通过过期时间来删除,但并不是一个很好的办法;这里就呼应了前写判断登录状态的条件是HttpSession&&user存在),并且重定向到 登录页;

8.2、前后端交互接口

8.3、实现客户端代码

这里只需要修改前端代码中的注销a标签的href属性即可;

<a href="logout">注销</a>

8.4、实现服务器代码

这里需要重新创建一个类LogoutServlet,用来服务注销;

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;@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.获取session会话HttpSession session = req.getSession(false);if(session == null) {resp.setStatus(404);return;}//2.直接删除session中的user对象session.removeAttribute("user");//3.重定向到登录界面resp.sendRedirect("login.html");}
}

九、实现发布博客功能

9.1、业务逻辑及解释

用户在博客编辑页里,填写博客标题和正文,点击发布博客,发起HTTP请求,服务器接收到这些数据,构造Blog对象保存到数据库中;

9.2、前后端交互接口

9.3、实现服务器代码

在BlogServlet类下,创建doPost方法

    @Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.检查用户登录状态HttpSession session = req.getSession(false);if(session == null) {resp.setStatus(403);resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前未登录,不能发布博客!");return;}User user = (User) session.getAttribute("user");if(user == null) {resp.setStatus(403);resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前未登录,不能发布博客!");return;}//2.获取博客标题和正文req.setCharacterEncoding("utf8");String title = req.getParameter("title");String content = req.getParameter("content");//3.构造Blog对象,插入数据库中Blog blog = new Blog();//blogId是自增主键,不用设置blog.setTitle(title);blog.setContent(content);blog.setUserId(user.getUserId());BlogDao blogDao = new BlogDao();blogDao.insert(blog);//4.返回主页resp.sendRedirect("blog_list.html");}

9.4、实现客户端代码

    <div class="blog-edit-container"><form action="blog" method="post" style="height: 100%;"><!-- 标题的编辑区 --><div class="title"><!-- 输入的标题内容 --><input type="text" id="blog-title" placeholder="在这里输入博客标题" name="title"><input id="submit" value="发布文章" type="submit"></div><!-- 正文的编辑区 --><div id="editor"><textarea name="content" style="display:none"></textarea></div></form></div><script src="js/app.js"></script><script>// 初始化编辑器, 代码也是截取自 官方文档 . var editor = editormd("editor", {// 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉. width: "100%",// 设定编辑器高度height: "calc(100% - 50px)",// 编辑器中的初始内容markdown: "## hello world",// 指定 editor.md 依赖的插件路径path: "editor.md/lib/",//用户输入markdown内容想要被textarea获取到,就需要如下写法(官方文档规定)saveHTMLToTextarea: true});//强制登录逻辑getLoginStatus();

【对以上代码的解释】:saveHTMLToTextarea是什么?

当用户使用markdown输入内容时,要想让textarea获取到被渲染的内容,就需要这样写,这是官方文档规定;


十、实现删除博客功能

10.1、业务逻辑及解释

点击删除按钮,会触发删除操作;通过a标签的href属性发起一个HTTP GET请求,但是删除的时候会做出一个判定,只有当前登录用户时文章作者,才能执行删除;

10.2、约定前后端接口

10.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 javax.servlet.http.HttpSession;
import java.io.IOException;@WebServlet("/blog_delete")
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.setStatus(403);resp.setContentType("text/html; charset=utf8");resp.getWriter().write("您当前未登录, 不能删除!");return;}User user = (User) session.getAttribute("user");if (user == null) {resp.setStatus(403);resp.setContentType("text/html; charset=utf8");resp.getWriter().write("您当前未登录, 不能删除!");return;}// 2. 获取到 blogIdString blogId = req.getParameter("blogId");if (blogId == null) {// 这个 blogId 参数不存在, 无法删除resp.setStatus(404);resp.setContentType("text/html; charset=utf8");resp.getWriter().write("您当前删除的 blogId 有误");return;}// 3. 查询出这个 blogId 对应的 Blog 对象BlogDao blogDao = new BlogDao();Blog blog = blogDao.selectOne(Integer.parseInt(blogId));if (blog == null) {// 这个 blogId 参数不存在, 无法删除resp.setStatus(404);resp.setContentType("text/html; charset=utf8");resp.getWriter().write("您当前删除的博客不存在! blogId=" + blogId);return;}// 4. 判定登陆用户是否就是文章作者if (blog.getUserId() != user.getUserId()) {// blog.getUserId() 文章的作者// user.getUserId() 从 session 里拿的登陆的用户是谁.// 不一样, 说明在删别人的文章.// 直接返回 403resp.setStatus(403);resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前您不能删除别人的博客!");return;}// 5. 真正执行删除操作.blogDao.delete(blogId);// 6. 返回 302 重定向resp.sendRedirect("blog_list.html");}
}

【对以上代码的解释】:

大部分逻辑都是判定非法情况,这是一个好意识,这些情况都尽可能进行处理;

10.4、实现客户端代码

1.加上一个a标签即可

<a href="#" id="delete-btn">删除</a>

2.初始化的时候给href赋值上对应的url加上blogId,就能识别出当前是谁在删博客

        //删除博客function updateDeleteURL() {let deleteBtn = document.querySelector('#delete-btn');deleteBtn.href = 'blog_delete' + location.search;}updateDeleteURL();

十一、结语

想要前端HTML代码可以私信我哦~实际上大家也可以自己尝试一下,做一个大体的网页框架,本文已经详细给出了前后端交互的具体实现,希望可以帮助到你!


【Java】博客系统——详细解释+代码+详细注释(课设必过)相关推荐

  1. 博客系统前端页面代码实现及页面展示(代码版)

    hi,大家好,今天为大家带来博客系统的前端代码及页面展示 我们使用VS code 这个编码工具来编写代码 博客系统前端页面分为四个部分 1.博客列表页 2.博客编辑页 3.博客登录页 4.博客详情页

  2. java 博客系统_讲解开源项目:5分钟搭建私人Java博客系统

    本文适合刚学习完 Java 语言基础的人群,跟着本文可了解和运行 Tale 项目.示例均在 Windows 操作系统下演示 本文作者:HelloGitHub-秦人 HelloGitHub 推出的< ...

  3. html博客源码_5分钟搭建私人Java博客系统——Tale

    本文适合刚学习完 Java 语言基础的人群,跟着本文可了解和运行 Tale 项目.示例均在 Windows 操作系统下演示 本文作者:HelloGitHub-秦人 HelloGitHub 推出的< ...

  4. Github 标星 11.5K!这可能是最好的 Java 博客系统

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源:github.com/halo-dev/halo 简介 快速 ...

  5. Github 标星 13K+!这可能是最好的 Java 博客系统

    点击上方"后端技术精选",选择"置顶公众号" 技术文章第一时间送达! 来源:GitHub https://github.com/halo-dev/halo Ha ...

  6. Github 标星 12.8K!这可能是最好的 Java 博客系统

    点击上方"后端技术精选",选择"置顶公众号" 技术文章第一时间送达! 来源:GitHub https://github.com/halo-dev/halo Ha ...

  7. 如何快速部署国人开源的 Java 博客系统 Tale

    喜欢我们的文章?!欢迎大家关注腾讯云技术社区-简书主页哦~ 文末有彩蛋,不要错过! 除了闷头专研技术之外,程序员还需要不断地写作进行技术积累,写博客是其中最重要的方式之一.商业博客平台不少,但是更符合 ...

  8. java基于ssm的个人博客系统_调研了 100 来个 Java 博客系统,发现这5个最好用

    大家好!我是 Guide 哥,Java 后端开发.一个会一点前端,喜欢烹饪的自由少年. 最近想倒腾一下博客,看了很多现成的比较成熟的开源博客系统,自己也简单从下面几个维度总结对比了一下: star数量 ...

  9. Java博客系统halo的搭建

    github上star比较多的开源博客系统halo,SpringBoot+Gradle+Hibernate,推荐的Java版本是11,我的本地环境是mac+idea halo官方文档:https:// ...

最新文章

  1. August 14, 2009 - Choice
  2. gdb php-fpm,使用 gdb 调试 php-fpm 异常错误
  3. rabbitmq 网络分区错误
  4. FM的调制matlab仿真
  5. 我使用的博客和通讯工具汇总
  6. klee错误汇报二:KLEE的optimize选项的一个困惑
  7. linux gcc make cmake 三工具的关系
  8. [转载]读塔莎奶奶的美好生活
  9. maven工程src/main/java目录无法创建问题
  10. 测试驱动开发 测试前移_测试驱动开发:它是什么,什么不是。
  11. C#中的模块化软件开发
  12. Linux工作笔记034---linux tail命令详解_linux在vi中查找字符串
  13. IOS中设置圆角图片
  14. 在 NetBeans IDE 6.0 中分析 Java 应用程序性能
  15. AUTO CAD快捷键常见命令
  16. nginx判断手机端还是电脑
  17. Android Studio 使用技巧
  18. Tomcat启动报Exception in thread “main“ java.lang.UnsatisfiedLinkError: no secure-tomcat in java.library
  19. 新浪微博开放平台开发总结
  20. 中国结构性心脏病年度报告2020发布会圆满召开!

热门文章

  1. java中的js是什么意思_js和java的区别是什么?
  2. TypeDB Forces 2023 (Div. 1 + Div. 2, Rated, Prizes!)(A~E)
  3. vs2017 有时候双击打不开解决办法
  4. Java Collection接口
  5. 绷不住了,该来的终究躲不掉!
  6. jsp实现简单聊天室
  7. 转载:――放心油条自己做
  8. 小飞鱼通达二开 PHP使用钉钉接口发送带链接的卡片消息(代码)
  9. STM32H750获取OV7670摄像头图像及上位机解码(一维码二维码)
  10. 市场人员被 CEO、销售要求去证明“数量指标”作用的现象越来越常见