我的博客系统[Servlet]

目录

后端程序

1. 需求分析

2. 概要设计

3. 编写数据库操作的代码

3.1.使用maven,引入依赖

3.2 封装 数据库的 DataSource

3.2.1 使用单例模式,把数据库的 DataSource 和 建立连接 还有 断开连接也给封装进去

3.2.2 创建实体类

3.2.3 针对这两个实体类涉及到的 增删改查 进行进一步的封装,也就是把 jdbc 代码给封装一下

4. 进行前后端交互

4.1 实现博客列表页(blog_list.html)

4.2 实现博客详情页(blog_datail.html) 

4.3 实现登录页面(login.html)

4.4 实现在博客列表页/博客详情页/博客编辑页中,必须强制登录才可以访问

4.5 实现在博客列表页和博客详细页中显示用户信息

4.6 实现注销(退出登录状态)

4.7 实现发布博客(blog_edit.html)

4.8 实现删除博客


gitee代码链接https://gitee.com/lei-xiaoheng/java-ee/tree/master/blog_system/src/main

后端程序

1. 需求分析

软件开发基本流程

  1. 可行性分析

  2. 需求分析

  3. 概要设计

  4. 详细设计

  5. 编码

  6. 测试

  7. 发布

但在实际上也不需要每个步骤都要详细的设置,这里我们进行[博客系统]设计就先主要进行需求分析

需求分析:也就是明确程序要解决什么问题,做成啥样子.实现什么功能

下面进行简单需求分析

1. 实现博客列表的展示功能

2. 实现博客详情的展示功能

3. 登录功能 (暂时不实现注册功能)

4. 限制用户权限 (强制要求用户要登录)

5. 显示用户的信息

6. 实现注销 (退出登录)

7. 发布博客

8. 删除博客 

总结起来8个功能,而这除了这8个功能外,还可以加一些别的.这里就不再赘述了

1. 实现博客列表的展示功能

2. 实现博客详情的展示功能

3. 登录功能 (暂时不实现注册功能)

4. 限制用户权限 (强制要求用户要登录)

5. 显示用户的信息

6. 实现注销 (退出登录)

7. 发布博客

8. 删除博客 


2. 概要设计

下面进行概要设计中的 [数据库设计]

数据库设计:也就是要想清楚,有几个库,几个表,每个表什么样子的

当前这个 博客系统 的业务比较简单,只需要两个表

 下面进行创建数据库等一系列的操作

create database if not exists java_blog_system;

use java_blog_system;

drop table if exists blog;
create table blog (
    blogId int primary key auto_increment,
    title varchar(256),
    content text,
    postTime datetime,
    userId int
);

drop table if exists user;
create table user (
    userId int primary key auto_increment,
    username varchar(50),
    password varchar(50)
);

insert into blog values (null, "这是第一篇博客", "这是认真写博客的第一天", "2022-11-25 20:00:00", 1);
insert into blog values (null, "这是第二篇博客", "这是认真写博客的第二天", "2022-11-26 20:00:00", 1);

insert into user values (null, "zhangsan", "123");
insert into user values (null, "lisi", "123");

这个是两个表中的测试数据 


3. 编写数据库操作的代码

3.1.使用maven,引入依赖

3.2 封装 数据库的 DataSource

3.2.1 使用单例模式,把数据库的 DataSource 和 建立连接 还有 断开连接也给封装进去

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/java_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 preparedStatement, ResultSet resultSet) {
        if(resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(preparedStatement != null) {
            try {
                preparedStatement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

3.2.2 创建实体类

一个实体类的对象就对应表里的一条记录

表结构怎么写, 实体类就怎么写

创建两张表对应的实体类  User Blog

import java.sql.Timestamp;

public class Blog {
    private int blogId;
    private String title;//博客标题
    private String content;//博客正文
    // mysql 里的 datetime 和 timestamp 类型都是在 java 中使用 Timestamp 表示的
    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 Timestamp getPostTime() {
        return postTime;
    }

    public void setPostTime(Timestamp postTime) {
        this.postTime = postTime;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }
}
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;
    }
}

3.2.3 针对这两个实体类涉及到的 增删改查 进行进一步的封装,也就是把 jdbc 代码给封装一下

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

//封装针对博客表的相关操作
public class BlogDao {
    //1. 插入一个博客到数据库中 -- 发布博客
    public void insert(Blog blog) {
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            //1. 和数据库建立连接
            connection = DBUtil.getConnection();
            //2. 构造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.getUserId());
            //3. 执行 SQL
            int ret = statement.executeUpdate();
            if(ret != 1) {
                System.out.println("博客插入失败!");
            }else {
                System.out.println("博客插入成功!");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //4. 释放对应资源
            DBUtil.close(connection,statement,null);
        }
    }

    //2. 根据博客 id 来查询指定博客 -- 博客详情页
    public Blog selectOne(int blogId) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            //1. 和数据库建立连接
            connection = DBUtil.getConnection();
            //2. 构造 SQL
            String sql = "select * from blog where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,blogId);
            //3. 执行 SQL
            resultSet = statement.executeQuery();
            //4. 遍历结果集合
            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(connection,statement,resultSet);
        }
        return null;
    }

    //3. 直接查询博客列表 -- 博客列表页
    public List<Blog> selectAll() {
        List<Blog> blogs = new ArrayList<>();
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            //1. 和数据库建立连接
            connection = DBUtil.getConnection();
            //2. 构造 SQL
            String sql = "select * from blog";
            statement = connection.prepareStatement(sql);
            //3. 执行 SQL
            resultSet = statement.executeQuery();
            //4. 遍历结果集合
            while(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"));
                blogs.add(blog);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return blogs;
    }

    //4. 删除指定博客 -- 删除博客
    public void delete(int blogId) {
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            //1. 和数据库建立连接
            connection = DBUtil.getConnection();
            //2. 构造 SQL
            String sql = "delete from blog where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,blogId);
            //3. 执行 SQL
            int ret = statement.executeUpdate();
            if(ret != 1) {
                System.out.println("博客删除失败!");
            }else {
                System.out.println("博客删除成功!");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection,statement,null);
        }
    }
}
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 {
            //1. 和数据库建立连接
            connection = DBUtil.getConnection();
            //2. 构造 SQL
            String sql = "select * from user where username = ?";
            statement = connection.prepareStatement(sql);
            statement.setString(1,username);
            //3. 执行 SQL
            resultSet = statement.executeQuery();
            //4. 遍历结果集合
            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 {
            //1. 和数据库建立连接
            connection = DBUtil.getConnection();
            //2. 构造 SQL
            String sql = "select * from where userId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,userId);
            //3. 执行 SQL
            resultSet = statement.executeQuery();
            //4. 遍历结果集合
            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;
    }
}

4. 进行前后端交互

让页面发起 http 请求,服务器返回 http 响应

需要约定好请求是啥样的,响应是啥样的

4.1 实现博客列表页(blog_list.html)

之前这里是固定的数据,现在想要让页面从数据库这里来拿当前的博客列表

下面将前面写的前端页面文件拷贝到 idea 这里的 webapp下

在博客列表页中,需要做一个重要的事情

页面在加载的时候,通过 ajax 发起 HTTP 请求, 从服务器获取到博客列表数据

考虑好要发个啥样的请求, 返回啥样的响应 ====> 约定前后端交互接口

(1) 先写后端的代码   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 {
    private 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> blogs = blogDao.selectAll();//查询博客列表
        //writeValueAsString 把 java 对象转成 json 格式的字符串
        resp.getWriter().write(objectMapper.writeValueAsString(blogs));
    }
}

(2) 下面修改一下,前端blog_list.html 的代码

注意 通过ajax 从服务器获取数据后,别忘了进行函数调用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客列表</title>

    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/blog.css">
</head>
<body>
    <div class="nav">
        <img src="img/暂无消息.png" alt="">
        <span class="title">我的博客系统</span>
        <!-- spacer只是占位 -->
        <div class="spacer"></div>
        <a href="blog_list.html">主页</a>
        <a href="blog_edit.html">写博客</a>
        <a href="#">注销</a>
    </div>

    <!-- 这个div表示页面的主区域(版心) -->
    <div class="container">
        <!-- 左侧用户信息 -->
        <div class="container-left">
            <div class="card">
                <!-- 用户头像 -->
                <img src="./img/头像.png" alt="">
                <h3>XXX</h3>
                <a href="https://gitee.com/lei-xiaoheng">gitee地址</a>
                <div class="counter">
                    <span>文章</span>
                    <span>分类</span>
                </div>
                <div class="counter">
                    <span>2</span>
                    <span>1</span>
                </div>
            </div>
        </div>
        <!-- 右侧内容区域 -->
        <div class="container-right">
            <!-- 每个.blog用来表示一篇博客 -->
            <!-- <div class="blog">
                <div class="title">我的第一篇博客</div>
                <div class="date">2022-11-07 12:00:00</div>
                <div class="desc">
                    Lorem, ipsum dolor sit amet consectetur adipisicing elit. Quae at esse eum nam in dicta impedit expedita, id voluptas eius labore velit ipsa sapiente. Voluptatum quasi quis odio debitis recusandae?
                    Lorem ipsum dolor sit, amet consectetur adipisicing elit. Magni non ea et dignissimos, dolor neque, maiores modi minima laborum iure totam atque quaerat earum dicta necessitatibus omnis quo aperiam hic.
                </div>
                <a href="blog_datail.html">查看全文 &gt; &gt;</a>
            </div> -->

        </div>
    </div>
    <script src="https://apps.bdimg.com/libs/jquery/2.1.4/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');
                        //加上一个 query string, blogId 用这个区分,要跳到哪个博客的页面
                        a.href = 'blog_datail.html?blogId=' + blog.blogId;
                        a.innerHTML = '查看全文 &gt;&gt;'

                        //拼装最终结构
                        blogDiv.appendChild(titleDiv);
                        blogDiv.appendChild(dateDiv);
                        blogDiv.appendChild(descDiv);
                        blogDiv.appendChild(a);
                        container.appendChild(blogDiv);
                    }
                }
            });
        }
        //要进行函数调用
        getBlogs();
    </script>
</body>
</html>

(3) 需要注意前面的postTime返回的响应是一个时间戳(这里可以通过抓包进一步分析),而我们需要的是一个格式化的时间

Blog.java 的代码

import java.sql.Timestamp;
import java.text.SimpleDateFormat;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 28463
 * Date: 2022—11—25
 * Time: 15:17
 */
public class Blog {
    private int blogId;
    private String title;//博客标题
    private String content;//博客正文
    // mysql 里的 datetime 和 timestamp 类型都是在 java 中使用 Timestamp 表示的
    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;
    }
}

 (4) 正常情况下博客列表展示,最上面的也就是刚发布的,所以在数据库查询时要根据时间进行降序排列,这就要修改 BlogDao.java 中的代码了

(5) 还需要考虑的一个是, 博客如果内容很多,就会导致博客列表展示这个页面,放不下几个博客,所以最好的是当这个博客的内容超过多少字时,只显示前面,比如说只显示100字("摘要"的信息,也就是进行内容的截断) 

比如说这里新插入一条博客,可以看到这个博客列表的展示页面这里内容显示太多了,不合理 

 BlogDao.java

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

//封装针对博客表的相关操作
public class BlogDao {
    //1. 插入一个博客到数据库中 -- 发布博客
    public void insert(Blog blog) {
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            //1. 和数据库建立连接
            connection = DBUtil.getConnection();
            //2. 构造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.getUserId());
            //3. 执行 SQL
            int ret = statement.executeUpdate();
            if(ret != 1) {
                System.out.println("博客插入失败!");
            }else {
                System.out.println("博客插入成功!");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //4. 释放对应资源
            DBUtil.close(connection,statement,null);
        }
    }

    //2. 根据博客 id 来查询指定博客 -- 博客详情页
    public Blog selectOne(int blogId) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            //1. 和数据库建立连接
            connection = DBUtil.getConnection();
            //2. 构造 SQL
            String sql = "select * from blog where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,blogId);
            //3. 执行 SQL
            resultSet = statement.executeQuery();
            //4. 遍历结果集合
            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(connection,statement,resultSet);
        }
        return null;
    }

    //3. 直接查询博客列表 -- 博客列表页
    public List<Blog> selectAll() {
        List<Blog> blogs = new ArrayList<>();
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            //1. 和数据库建立连接
            connection = DBUtil.getConnection();
            //2. 构造 SQL
            String sql = "select * from blog order by postTime desc";
            statement = connection.prepareStatement(sql);
            //3. 执行 SQL
            resultSet = statement.executeQuery();
            //4. 遍历结果集合
            while(resultSet.next()) {
                Blog blog = new Blog();
                blog.setBlogId(resultSet.getInt("blogId"));
                blog.setTitle(resultSet.getString("title"));

                // 进行内容的 "摘要",避免博客列表页显示内容过多,展示不了几个博客
                String content = resultSet.getString("content");
                if(content.length() > 100) {
                    content = content.substring(0,100) + "...";
                }
                blog.setContent(content);

                blog.setPostTime(resultSet.getTimestamp("postTime"));
                blog.setUserId(resultSet.getInt("userId"));
                blogs.add(blog);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return blogs;
    }

    //4. 删除指定博客 -- 删除博客
    public void delete(int blogId) {
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            //1. 和数据库建立连接
            connection = DBUtil.getConnection();
            //2. 构造 SQL
            String sql = "delete from blog where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,blogId);
            //3. 执行 SQL
            int ret = statement.executeUpdate();
            if(ret != 1) {
                System.out.println("博客删除失败!");
            }else {
                System.out.println("博客删除成功!");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection,statement,null);
        }
    }
}

运行代码后


4.2 实现博客详情页(blog_datail.html) 

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

处理流程:

流程分析完毕后,就要做这样三件事

(1) 约定前后端交互接口

(2) 实现服务器代码

(3) 实现客户端代码

  (1) 约定前后端交互接口

在博客详情页再次发起 ajax 请求, 获取到具体博客的内容

注意: 在博客列表页中,已经使用 BlogServlet.doGet 方法了

而在博客详情页中也想使用,那就要做出区分,可以使用 query string 来区分

如果请求中带有 query string, 有 blogId这个参数,就认为是博客详情页的请求

如果请求中不带有 query string,就认为是博客列表页的请求

(2) 实现服务器代码

BlogServlet,java 的代码 

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 {
    private 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();

        String blogId = req.getParameter("blogId");

        if(blogId == null) {
            //在博客列表页发起的请求
            List<Blog> blogs = blogDao.selectAll();//查询博客列表
            //writeValueAsString 把 java 对象转成 json 格式的字符串
            resp.getWriter().write(objectMapper.writeValueAsString(blogs));
        } else {
            //博客详情页发起的请求
            Blog blog = blogDao.selectOne(Integer.parseInt(blogId));
            resp.getWriter().write(objectMapper.writeValueAsString(blog));
        }


    }
}

 (3) 实现客户端代码

让页面发起 ajax 请求,获取到博客数据

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客详情</title>
    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/blog-detail.css">
</head>
<body>
    <div class="nav">
        <img src="img/暂无消息.png" alt="">
        <span class="title">我的博客系统</span>
        <!-- spacer只是占位 -->
        <div class="spacer"></div>
        <a href="blog_list.html">主页</a>
        <a href="blog_edit.html">写博客</a>
        <a href="#">注销</a>
    </div>

       <!-- 这个div表示页面的主区域(版心) -->
    <div class="container">
        <!-- 左侧用户信息 -->
        <div class="container-left">
            <div class="card">
                <!-- 用户头像 -->
                <img src="./img/头像.png" alt="">
                <h3>XXX</h3>
                <a href="https://gitee.com/lei-xiaoheng">gitee地址</a>
                <div class="counter">
                    <span>文章</span>
                    <span>分类</span>
                </div>
                <div class="counter">
                    <span>2</span>
                    <span>1</span>
                </div>
            </div>
        </div>
        <!-- 右侧内容区域 -->
        <div class="container-right">
            <div class="blog-detail">
                <h3></h3>
                <div class="date"></div>
                <!-- #content 这个 div 表示用来存放博客正文的部分-->
                <div id="content">

                </div>
            </div>
        </div>
    </div>
        <!-- 通过 ajax 请求,获取到博客详情页数据 -->
        <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
        <script>
            function getBlog() {
                $.ajax({
                    type: 'get',
                    url: 'blog' + location.search,
                    success: function(body) {
                        let h3 = document.querySelector('.blog-detail>h3');
                        h3.innerHTML = body.title;
                        let dateDiv = document.querySelector('.blog-detail>.date');
                        dateDiv.innerHTML = body.postTime;
                        let contentDiv = document.querySelector('#content');
                        contentDiv.innerHTML = body.content;
                    }
                });
            }
            getBlog();
        </script>
</body>
</html>

运行程序

(4) 现在还有个问题是,博客的正文希望是 markdown 格式

(因为博客编辑页,就是一个 markdown 的编辑器)

所以提交的数据就是 markdown,数据库里存的也是 markdown,所以最终也要显示一个 markdown格式的内容.

但是现在这个只是以文本的格式来显示内容,这个不太符合我们的要求

现在插入一条 markdown结构的内容,可以看到还是以纯文本内容显示的

 所以,可以使用 editor.md 这个库,来完成渲染

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客详情</title>
    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/blog-detail.css">
    
    <!-- 引入 editor.md 的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    <script src="https://cdn.staticfile.org/jquery/1.10.2/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>
</head>
<body>
    <div class="nav">
        <img src="img/暂无消息.png" alt="">
        <span class="title">我的博客系统</span>
        <!-- spacer只是占位 -->
        <div class="spacer"></div>
        <a href="blog_list.html">主页</a>
        <a href="blog_edit.html">写博客</a>
        <a href="#">注销</a>
    </div>

       <!-- 这个div表示页面的主区域(版心) -->
    <div class="container">
        <!-- 左侧用户信息 -->
        <div class="container-left">
            <div class="card">
                <!-- 用户头像 -->
                <img src="./img/头像.png" alt="">
                <h3>XXX</h3>
                <a href="https://gitee.com/lei-xiaoheng">gitee地址</a>
                <div class="counter">
                    <span>文章</span>
                    <span>分类</span>
                </div>
                <div class="counter">
                    <span>2</span>
                    <span>1</span>
                </div>
            </div>
        </div>
        <!-- 右侧内容区域 -->
        <div class="container-right">
            <div class="blog-detail">
                <h3></h3>
                <div class="date"></div>
                <!-- #content 这个 div 表示用来存放博客正文的部分-->
                <div id="content">

                </div>
            </div>
        </div>
    </div>
            <!-- 通过 ajax 请求,获取到博客详情页数据 -->
        <script>
            function getBlog() {
                $.ajax({
                    type: 'get',
                    url: 'blog' + location.search,
                    success: function(body) {
                        let h3 = document.querySelector('.blog-detail>h3');
                        h3.innerHTML = body.title;
                        let dateDiv = document.querySelector('.blog-detail>.date');
                        dateDiv.innerHTML = body.postTime;
                        // let contentDiv = document.querySelector('#content');
                        // contentDiv.innerHTML = body.content;

                        // 此处使用 editer.md 来进行渲染
                        editormd.markdownToHTML('content', { markdown: body.content});
                    }
                });
            }
            getBlog();
        </script>
</body>
</html>

下面再次运行代码,可以看到刚刚插入的那个纯文本格式的转为了, markdown格式了

(5) 也可以把这个显示内容的背景,设置成半透明的效果

可以在这里加上这个属性 


4.3 实现登录页面(login.html)

处理流程:

用户访问 login.html , 输入用户名和密码. 点击登录按钮

发起一个请求,把用户名和密码提交给服务器

服务器对身份进行验证,验证成功,就让页面跳转到 博客列表页

(1) 约定前后端交互接口

(2) 实现客户端前端代码

前面都是用 ajax构造请求,这里试一下用form表单构造请求

ajax 是各种http方法都可以构造,并且默认发起的请求不会进行跳转(也可以手动设置跳转)

而 form 表单 只能构造 Get 和 POST请求,并且一定会触发页面跳转

我们这里是登陆成功后,跳转到 博客登录页,所以 使用 from表单 没啥问题

(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("/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("type/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;
        }

        //3. 如果正确,创建一个会话对象
        HttpSession session = req.getSession(true);
        //在会话中保存一下 user,后面再访问其他页面
        //就可以直接通过会话拿到当前是哪个用户在访问了
        session.setAttribute("user",user);

        //4. 构造 302 响应报文(重定向)
        resp.sendRedirect("blog_list.html");
    }
}


4.4 实现在博客列表页/博客详情页/博客编辑页中,必须强制登录才可以访问

业务逻辑:

博客列表页/博客详情页/博客编辑页,都在页面加载后,发起一个 ajax

这个 ajax 就从服务器获取一下当前的登录状态

如果当前是从未登录的情况,就直接重定向到 登录页

如果是已经登录,则不做任何处理

(1) 前后端交互接口

(2) 前端代码

判定状态码,是 200 还是 403 ,如果是 200 没啥影响,如果是403,强行跳转

因为是要在三个页面:博客列表页/博客详情页/博客编辑页 都要判定,所以可以单独写一个函数,然后在这三个页面中引用

// 创建新的函数
function getLoginStatus() {
    $.ajax({
        type: 'get',
        url: 'login',
        success: function(body) {
            // 得到 200,此处不做任何工作
            console.log("当前已经登录过!");
        },
        error: function() {
            // 得到 403[没有访问权限],(非 2 开头的状态码都会触发这个 error 分支)
            // 让页面强制跳转到 login.html
            console.log("当前还未登录!");
            location.assign('login.html');
        }
    });
}

(3) 后端代码

只是获取一下会话,看能不能拿到会话以及里面的 user 对象,能拿到说明已经登录过了,返回200即可

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("type/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;
        }

        //3. 如果正确,创建一个会话对象
        HttpSession session = req.getSession(true);
        //在会话中保存一下 user,后面再访问其他页面
        //就可以直接通过会话拿到当前是哪个用户在访问了
        session.setAttribute("user",user);

        //4. 构造 302 响应报文(重定向)
        resp.sendRedirect("blog_list.html");
    }

    @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 user = (User) session.getAttribute("user");
        if(user == null) {
            // 虽然有会话,但是里面没有 user 对象,也认为是未登录状态
            resp.setStatus(403);
            return;
        }
        
        //2. 返回 200 这样的响应即可,这行代码不写也可,默认状态码200
        resp.setStatus(200);
    }
}

4.5 实现在博客列表页和博客详细页中显示用户信息

 业务逻辑

在博客列表页,加载页面的同时,从服务器获取到当前登陆的用户信息,把这个信息显示到页面上

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

(1) 前后端交互接口

(2) 前端代码

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客列表</title>

    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/blog.css">
</head>
<body>
    <div class="nav">
        <img src="img/暂无消息.png" alt="">
        <span class="title">我的博客系统</span>
        <!-- spacer只是占位 -->
        <div class="spacer"></div>
        <a href="blog_list.html">主页</a>
        <a href="blog_edit.html">写博客</a>
        <a href="login.html">登录</a>
        <a href="#">注销</a>
    </div>

    <!-- 这个div表示页面的主区域(版心) -->
    <div class="container">
        <!-- 左侧用户信息 -->
        <div class="container-left">
            <div class="card">
                <!-- 用户头像 -->
                <img src="./img/头像.png" alt="">
                <h3></h3>
                <a href="https://gitee.com/lei-xiaoheng">gitee地址</a>
                <div class="counter">
                    <span>文章</span>
                    <span>分类</span>
                </div>
                <div class="counter">
                    <span>2</span>
                    <span>1</span>
                </div>
            </div>
        </div>
        <!-- 右侧内容区域 -->
        <div class="container-right">
            <!-- 每个.blog用来表示一篇博客 -->
            <!-- <div class="blog">
                <div class="title">我的第一篇博客</div>
                <div class="date">2022-11-07 12:00:00</div>
                <div class="desc">
                    Lorem, ipsum dolor sit amet consectetur adipisicing elit. Quae at esse eum nam in dicta impedit expedita, id voluptas eius labore velit ipsa sapiente. Voluptatum quasi quis odio debitis recusandae?
                    Lorem ipsum dolor sit, amet consectetur adipisicing elit. Magni non ea et dignissimos, dolor neque, maiores modi minima laborum iure totam atque quaerat earum dicta necessitatibus omnis quo aperiam hic.
                </div>
                <a href="blog_datail.html">查看全文 &gt; &gt;</a>
            </div> -->

        </div>
    </div>
    <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
    <script src="JS/app.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');
                        //加上一个 query string, blogId 用这个区分,要跳到哪个博客的页面
                        a.href = 'blog_datail.html?blogId='+ blog.blogId;
                        a.innerHTML = '查看全文 &gt;&gt;'

                        //拼装最终结构
                        blogDiv.appendChild(titleDiv);
                        blogDiv.appendChild(dateDiv);
                        blogDiv.appendChild(descDiv);
                        blogDiv.appendChild(a);
                        container.appendChild(blogDiv);
                    }
                }
            });
        }
        //要进行函数调用
        getBlogs();

        getLoginStatus();

        // 针对博客列表页, 获取到当前用户的登录信息
        function getUserInfo() {
            $.ajax({
                type: 'get',
                url: 'userInfo',
                success: function(body) {
                    //获取成功, body 就是一个 User 对象
                    //把 user 对象里的内容填写到页面上即可
                    //此处主要是填用户名
                    let h3 = document.querySelector('.container-left>.card>h3');
                    h3.innerHTML = body.username;
                }
            });
        }
        getUserInfo();
    </script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客详情</title>
    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/blog-detail.css">
    
    <!-- 引入 editor.md 的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    <script src="https://cdn.staticfile.org/jquery/1.10.2/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>
</head>
<body>
    <div class="nav">
        <img src="img/暂无消息.png" alt="">
        <span class="title">我的博客系统</span>
        <!-- spacer只是占位 -->
        <div class="spacer"></div>
        <a href="blog_list.html">主页</a>
        <a href="blog_edit.html">写博客</a>
        <a href="login.html">登录</a>
        <a href="#">注销</a>
    </div>

       <!-- 这个div表示页面的主区域(版心) -->
    <div class="container">
        <!-- 左侧用户信息 -->
        <div class="container-left">
            <div class="card">
                <!-- 用户头像 -->
                <img src="./img/头像.png" alt="">
                <h3>雷晓恒</h3>
                <a href="https://gitee.com/lei-xiaoheng">gitee地址</a>
                <div class="counter">
                    <span>文章</span>
                    <span>分类</span>
                </div>
                <div class="counter">
                    <span>2</span>
                    <span>1</span>
                </div>
            </div>
        </div>
        <!-- 右侧内容区域 -->
        <div class="container-right">
            <div class="blog-detail">
                <h3></h3>
                <div class="date"></div>
                <!-- #content 这个 div 表示用来存放博客正文的部分-->
                <div id="content" style="background-color: transparent">

                </div>
            </div>
        </div>
    </div>
            <!-- 通过 ajax 请求,获取到博客详情页数据 -->
            <script src="JS/app.js"></script>
        <script>
            function getBlog() {
                $.ajax({
                    type: 'get',
                    url: 'blog' + location.search,
                    success: function(body) {
                        let h3 = document.querySelector('.blog-detail>h3');
                        h3.innerHTML = body.title;
                        let dateDiv = document.querySelector('.blog-detail>.date');
                        dateDiv.innerHTML = body.postTime;
                        // let contentDiv = document.querySelector('#content');
                        // contentDiv.innerHTML = body.content;

                        // 此处使用 editer.md 来进行渲染
                        editormd.markdownToHTML('content', { markdown: body.content});
                    }
                });
            }
            getBlog();
            getLoginStatus();

            function getUserInfo() {
                $.ajax({
                    type: 'get',
                    url: 'userInfo' + location.search,
                    success: function(body) {
                    let h3 = document.querySelector('.container-left>.card>h3');
                    h3.innerHTML = body.username;
                    }
                });
            }
            getUserInfo();
        </script>
</body>
</html>

(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 {
    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.parseInt(blogId));
        }
    }

    private void getUserInfoFromSession(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        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;
        }
        // user 获取到了,把 user 中的 password 给赋值为空字符串,然后返回
        user.setPassword("");

        resp.setContentType("application/json; charset=utf8");
        resp.getWriter().write(objectMapper.writeValueAsString(user));
    }

    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("blogId 不存在");
            return;
        }
        // 把这个 user 对象返回给浏览器
        user.setPassword("");
        resp.setContentType("application/json; charset=utf8");
        resp.getWriter().write(objectMapper.writeValueAsString(user));
    }
}

4.6 实现注销(退出登录状态)

在博客列表页/博客详情页/博客编辑页,导航栏里有注销按钮

注销按钮是一个 a 标签,点击的时候就会给服务器发送一个 http 请求(这个不是 ajax,如果用 ajax 做也可以)

发送的 http 请求就告诉服务器,现在要退出登陆了

服务器就会把会话中的 user 对象,给删除了,同时重定向跳转到 登录页

需要注意的是: 此处删除的是 user 对象,不是 HttpSession 对象, HttpSession 没有一个直接的用于删除的方法(直接删除会话,Servlet没提供),虽然可以通过设置过期时间的方式来删,但这不是一个好选择

(1) 前后端交互接口

(2) 前端代码

只需要给 a 标签里的 href 属性设置个值就可以了,

(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("/logout")
public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession(false);
        if(session == null) {
            resp.setStatus(403);
            return;
        }
        // 直接把 session 中之间的 user 对象给删掉即可
        session.removeAttribute("user");
        // 重定向到登录页面
        resp.sendRedirect("login.html");
    }
}

4.7 实现发布博客(blog_edit.html)

业务逻辑:

在博客编辑页中,能够把用户提交的博客数据给获取到,保存到数据库中,

用户在博客编辑页中,填写博客标题和正文,点击发布博客

(1) 前后端交互接口

(2) 前端代码

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客编辑</title>
    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/blog_edit.css">


    <!-- 引入 editor.md 的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    <!-- <script src="js/jquery.min.js"></script> -->
    <script src="https://cdn.staticfile.org/jquery/1.10.2/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>
    
</head>
<body>
    <div class="nav">
        <img src="img/暂无消息.png" alt="">
        <span class="title">我的博客系统</span>
        <!-- spacer只是占位 -->
        <div class="spacer"></div>
        <a href="blog_list.html">主页</a>
        <a href="blog_edit.html">写博客</a>
        <a href="login.html">登录</a>
        <a href="logout">注销</a>
    </div>

    <!-- 整个编辑页的版心 -->
    <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: "# 在这里写下一篇博客",
            //指定 editor.md 依赖的插件路径
            path: "editor.md/lib/",
            saveHTMLToTextarea: true
        });

        getLoginStatus();
    </script>
    
</body>
</html>

(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;
import java.util.List;

@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    private 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();

        String blogId = req.getParameter("blogId");

        if(blogId == null) {
            //在博客列表页发起的请求
            List<Blog> blogs = blogDao.selectAll();//查询博客列表
            //writeValueAsString 把 java 对象转成 json 格式的字符串
            resp.getWriter().write(objectMapper.writeValueAsString(blogs));
        } else {
            //博客详情页发起的请求
            Blog blog = blogDao.selectOne(Integer.parseInt(blogId));
            resp.getWriter().write(objectMapper.writeValueAsString(blog));
        }
    }

    @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();
        blog.setTitle(title);
        blog.setContent(content);
        blog.setUserId(user.getUserId());
        //blogId 是自增主键,不需要设置,postTime 直接在数据库操作中,已经使用 now 来设置了
        BlogDao blogDao = new BlogDao();
        blogDao.insert(blog);

        //4. 构造 重定向报文, 回到 博客列表页
        resp.sendRedirect("blog_list.html");

    }
}

运行程序,下面来试着写一篇试试看 


4.8 实现删除博客

业务逻辑

作者能删除自己的文章,不能删除别人的

暂时没有管理员这个角色

在博客详情页导航栏上,加个 删除 按钮

点击删除按钮,就会触发删除操作,通过 a 标签. href 属性发起一个 HTTP GET 请求

但是删除的时候,会做一个判断,如果当前登录的用户就是文章作者,才能真正删除,否则就提示不能删除

(1) 约定前后端交互接口

(2) 前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客详情</title>
    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/blog-detail.css">
    
    <!-- 引入 editor.md 的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    <script src="https://cdn.staticfile.org/jquery/1.10.2/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>
</head>
<body>
    <div class="nav">
        <img src="img/暂无消息.png" alt="">
        <span class="title">我的博客系统</span>
        <!-- spacer只是占位 -->
        <div class="spacer"></div>
        <a href="blog_list.html">主页</a>
        <a href="blog_edit.html">写博客</a>
        <a href="#" id="delete-btn">删除</a>
        <a href="logout">注销</a>
    </div>

       <!-- 这个div表示页面的主区域(版心) -->
    <div class="container">
        <!-- 左侧用户信息 -->
        <div class="container-left">
            <div class="card">
                <!-- 用户头像 -->
                <img src="./img/头像.png" alt="">
                <h3>雷晓恒</h3>
                <a href="https://gitee.com/lei-xiaoheng">gitee地址</a>
                <div class="counter">
                    <span>文章</span>
                    <span>分类</span>
                </div>
                <div class="counter">
                    <span>2</span>
                    <span>1</span>
                </div>
            </div>
        </div>
        <!-- 右侧内容区域 -->
        <div class="container-right">
            <div class="blog-detail">
                <h3></h3>
                <div class="date"></div>
                <!-- #content 这个 div 表示用来存放博客正文的部分-->
                <div id="content" style="background-color: transparent">

                </div>
            </div>
        </div>
    </div>
            <!-- 通过 ajax 请求,获取到博客详情页数据 -->
            <script src="JS/app.js"></script>
        <script>
            function getBlog() {
                $.ajax({
                    type: 'get',
                    url: 'blog' + location.search,
                    success: function(body) {
                        let h3 = document.querySelector('.blog-detail>h3');
                        h3.innerHTML = body.title;
                        let dateDiv = document.querySelector('.blog-detail>.date');
                        dateDiv.innerHTML = body.postTime;
                        // let contentDiv = document.querySelector('#content');
                        // contentDiv.innerHTML = body.content;

                        // 此处使用 editer.md 来进行渲染
                        editormd.markdownToHTML('content', { markdown: body.content});
                    }
                });
            }
            getBlog();
            getLoginStatus();

            function getUserInfo() {
                $.ajax({
                    type: 'get',
                    url: 'userInfo' + location.search,
                    success: function(body) {
                    let h3 = document.querySelector('.container-left>.card>h3');
                    h3.innerHTML = body.username;
                    }
                });
            }
            getUserInfo();

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

            updateDeleteURL();
        </script>
</body>
</html>

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

// 8. 删除博客
@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 参数不存在,无法删除
            // 比如本身 blogId 只有 1-10,现在要删除100,当前就不行了
            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) {
            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(Integer.parseInt(blogId));

        //6. 返回 302 重定向
        resp.sendRedirect("blog_list.html");
    }
}

运行代码,插入两篇博客测试一下

删除一篇试试看

如果删除一篇不是自己的博客,就会这样提示

  • 42
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 37
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 37
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快到锅里来呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值