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

news2025/1/17 0:16:44

目录

前言

博客系统简要分析

一、数据库的设计

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, -- 博客id
    title varchar(256), -- 标题
    content text,  -- 正文
    postTime datetime, -- 发布时间
    userId int -- 用户Id
);

drop table if exists user;
-- 这是用户table
create table user (
    userId int primary key auto_increment, -- 用户id
    username 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();
            //构造sql
            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.getBlogId());
            //执行sql
            int 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();
            //创建sql
            String sql = "select * from Blog where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1, blogId);
            //执行sql
            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.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();
            //构造sql
            String sql = "select * from blog order by postTime desc";
            statement = connection.prepareStatement(sql);
            //执行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");
                //在博客列表页,正文一旦超出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();
            //构造sql
            String sql = "delete from Blog where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setString(1, blogId);
            //执行sql
            int 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();
            //构造sql
            String sql = "select * from User where username = ?";
            statement = connection.prepareStatement(sql);
            statement.setString(1, username);
            //执行sql
            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;
    }
    
    //根据用户id查询用户信息
    public User selectById(int userId) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            //建立连接
            connection = DBUtil.getConnection();
            //构造sql
            String sql = "select * from user where userId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1, userId);
            //执行sql
            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;
    }
}

三、实现博客列表页

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) {
                        //构造blogDiv
                        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');
                        //这里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();

    @Override
    protected 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();

    @Override
    protected 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 string
                success: function(body) {//这里成功的话,会获取到一个blog
                    let 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 {
    @Override
    protected 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、实现服务器代码

    @Override
    protected 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();

    @Override
    protected 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 {
    @Override
    protected 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方法

    @Override
    protected 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 {
    @Override
    protected 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. 获取到 blogId
        String 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 里拿的登陆的用户是谁.
            // 不一样, 说明在删别人的文章.
            // 直接返回 403
            resp.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代码可以私信我哦~实际上大家也可以自己尝试一下,做一个大体的网页框架,本文已经详细给出了前后端交互的具体实现,希望可以帮助到你!


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

telnet配置设备远程管理—eNSP

案例&#xff1a;给路由器配置远程管理&#xff0c;使一台路由器远程管理另一台。 所需设备&#xff1a;两台路由器&#xff0c;一根网线 图示 一、给两台设备配置IP地址 AR1&#xff08;以下命令&#xff09; a. sy b. int g0/0/0 c. ip add 1.1.1.1 24AR2 a. sy b. int g0/0…

区间信息维护与查询【线段树 】 - 原理1 线段树的基本操作

区间信息维护与查询【线段树 】 - 原理1 线段树的基本操作 线段树&#xff08;segment tree&#xff09;是一种基于分治思想的二叉树&#xff0c;它的每个节点都对应一个[L , R ]区间&#xff0c;叶子节点对应的区间L R 。每一个非叶子节点[L , R ]其左子节点的区间都为[L , (…

进程与线程的区别及联系

目录 1. 操作系统功能简介 2. 进程 2.1 认识进程 2.2 进程操作系统中如何管理 2.3 PCB如何描述 2.3.1 pid 2.3.2 内存指针 2.3.3 文件描述符表 2.3.4 进程调度相关属性 3. 内存管理 4. 线程 4.1 认识线程 4.2 进程与线程的关系 4.3 线程安全问题 1.操作系统功能简…

[附源码]计算机毕业设计springboot电子相册管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Git 入门 拉取仓库和推送仓库

目录 基本操作 本地建立仓库并推送到远端仓库 关联仓库失败 解决方法 从远端仓库拉取文件到本地 私有的仓库的连接 修改 提交者名称 IDEA/Pycharm等如何使用git 如何关闭git 功能 Git操作主要分为两类 &#xff0c;如何把自己建的项目同步的网上的仓库&#xff0c;如何…

OpenHarmony编译系统

GN 简介 直接百度 GN 入门 可以参考下面的示例&#xff0c;作为入门参考学习https://blog.csdn.net/weixin_44701535/article/details/88355958https://gn.googlesource.com/gn//main/docs/reference.mdhttps://chromium.googlesource.com/chromium/src/tools/gn//48062805e…

Java项目_在线点餐系统(jsp+sevlet+mysql)(含论文)

在线点餐系统(jspsevletmysql一、系统介绍二、功能展示1.主页(用户)2.菜单(用户)3.用户注册(用户)4.用户登陆(用户)5.我的订单(用户)6.餐桌管理(管理员)7.菜系管理(管理员)8.菜品管理(管理员)9.订单管理(管理员)三、获取源码一、系统介绍 系统主要功能&#xff1a; 用户&#…

4位资深专家多年大厂经验分享出Flink技术架构设计与实现原理

时间飞逝&#xff0c;转眼间毕业七年多&#xff0c;从事 Java 开发也六年了。我在想&#xff0c;也是时候将自己的 Java 整理成一套体系。 这一次的知识体系面试题涉及到 Java 知识部分、性能优化、微服务、并发编程、开源框架、分布式等多个方面的知识点。 写这一套 Java 面试…

bootstrap下拉菜单学习(五)

组件&#xff1a;下拉菜单 bootstrap字体图标和下拉菜单组件的使用 这些图标都存在我们引入的font文件夹内&#xff1a; 复制bootstrap所用的包&#xff1a; 创建html页面&#xff1a; 图标不仅可以直接放文本里面&#xff0c; 还可有结合按钮去用。 要使用组件&#xff1a;不…

《网络空间测绘技术与实践》正式发售,让网络空间作战“有图可依”

近日&#xff0c;多位业界专家力推&#xff0c;由知道创宇CEO赵伟、CTO杨冀龙、CSO黑哥&#xff08;周景平&#xff09;等撰写的著作《网络空间测绘技术与实践》&#xff0c;正式出版并发售。网络空间已成为继“陆、 海、空、天”后的第五大空间&#xff0c;网络空间亦需要类似…

【uni-app高频面试题——精品一】

uni-app高频面试题谈谈你对uni-app的理解&#x1f355;uni中如何为不同的平台设置不同的代码uniapp中封装接口请求相较于微信小程序有什么要注意的uni-app中的本地存储数据和接收数据是什么✊uni-app 路由与页面跳转&#x1f4aa;uni-app全局变量怎么定义&#xff0c;怎么获取&…

【Python实战】“特种兵”们的专属游戏助手,助你吃鸡:极品小助手也是棒呆了~(“大吉大利,今W吃鸡”)

前言 有温度 有深度 有广度 就等你来关注哦~ 所有文章完整的素材源码都在&#x1f447;&#x1f447; 粉丝白嫖源码福利&#xff0c;请移步至CSDN社区或文末公众hao即可免费。 “注意左边&#xff0c;左边有人&#xff0c;打他&#xff01;” “快上车&#xff01;&#xff0…

Spring Security(十九)--OAuth2:实现授权服务器(下)--环境准备以及骨架代码搭建

一、前言 本章我们将在上一章代码骨架搭建好的前提下对三种授权类型进行测试以及讲解如何配置授权服务器以颁发刷新令牌&#xff0c;所以本章是一个比较轻松的章节&#xff0c;但是唯一的要求就是需要小伙伴们对上一章内容要完成代码的搭建&#xff0c;否则这章学习也不知道个…

[附源码]计算机毕业设计springboot飞越青少儿兴趣培训机构管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

嘉创房地产冲刺港交所:半年营收4.7亿 现金及现金等价物减少

雷递网 雷建平 11月28日嘉创房地产控股有限公司&#xff08;简称&#xff1a;“嘉创”&#xff09;日前递交招股书&#xff0c;准备在港交所上市。半年营收4.73亿嘉创为一家精品住宅物业发展商&#xff0c;主要在大湾区的东莞、惠州及佛山迅速发展的住宅市场&#xff08;如东莞…

m半分布式JAC联合接纳控制与用户位置信息的垂直切换matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 随着无线通信技术的飞速发展&#xff0c;为支持多种不同无线接入技术、不同系统间协作、不同业务类型及终端差异性等需求&#xff0c;未来的无线网络将是一种协作式的异构网络融合架构&#xff0…

文理导航杂志文理导航杂志社文理导航编辑部2022年第12期目录

专题研究《文理导航》投稿&#xff1a;cn7kantougao163.com 初中科学生活化作业优化策略 唐黎娜; 4-6 揭密2022年新高考1卷解析几何解答题 陈思伽; 7-9 初中数学错题资源的有效运用 王丹; 10-12 浅谈陶行知思想在初中体育教学中的应用 王树华; 13-15 高中数…

Error: error:0308010C:digital envelope routines::unsupported(vue2项目报错)

问题描述 在 终端输入 npm run dev 命令&#xff0c;项目运行报错 Error: error:0308010C:digital envelope routines::unsupported 问题原因 node 版本过高&#xff0c;可以在命令行 输入 node -v 查看版本 因为 Node.js 版本是 17 以上所以会运行失败&#xff0c; Node.j…

MySQL为自动编号的字段赋值

insert users values(NULL,ming,fasdfasdfasd,22,1); 或者 insert users values(DEFAULT,ming,fasdfasdfasd,22,1);

分布式消息中间件RabbitMQ解析

RabbitMQ作为分布式消息存储和转发系统&#xff0c;已广泛使用于分布式系统中。本文简要介绍RabbitMQ相关概念、集群架构和消息转发流程&#xff0c;并与Kafka做了简要对比&#xff0c;以加深理解。 1、RabbitMQ相关概念 1.1 AMQP介绍 消息&#xff08;Message&#xff09;是…