简略版博客系统(使用前后端分离)

一:博客前端代码

(1)获取前端代码

🌟点击获取前端代码(云盘密码:lzh7)

(2)页面分类

①博客详情页:点击列表页中的某个博客,就能跳转到详情页


②博客列表页:表示一个博客网站中都有哪些博客


③博客登录页:即用户登录页面


④博客编辑页:可以在此编辑并发布博客

(博客编辑页这里我们引入了第三方库editor.md;可以让我们编写markdown格式内容)

二:后端的准备工作和思路

(1)准备工作

①创建项目,取名为blog_system


②打开中央仓库,在pom.xml引入依赖

(依赖分别是Servlet、mysql、jackson)

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

③创建必要的目录,给web.xml写入必要的代码

//web.xml里的代码(复制后把这段注释删掉!)
<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
    <display-name>Archetype Created Web Application</display-name>
</web-app>

④把前端的文件夹blog_system里的所有文件复制到webapp目录下


⑤创建SmartTomcat,让网页顺利运行

(2)大体思路

💛大体思路主要分为两个部分


①前端和后端(服务器)的交互

②后端(服务器)和数据库的交互


💜先②再①,即先进行后端(服务器)与数据库的交互,再来搞前后端交互

三:后端(服务器)和数据库的交互

💗思路:

1.创建数据库和数据表

2.使用JDBC在DBUtil中封装数据库的连接和断开操作

3.创建实体类,后续数据库操作围绕实体类展开

4.创建不同实体的Dao类,通过不同Dao类针对不同表进行增删改查


(1)创建数据库和数据表

🌟①先创建数据库和数据表

💙此时需要先在main目录下创建一个db.sql文件,在此文件下创建数据库和数据表


🌟②在db.sql文件下编写代码

-- 在此文件下编写SQL完成建库建表操作

-- 创建数据库
create database if not exists hlizoo_blog_system charset utf8;

use hlizoo_blog_system;

-- 创建两个表,一个博客表,一个表示用户表
-- 博客表用来管理博客,用户表用来管理用户

-- 我们一般的操作会先删表再重新创建,防止之前的表的数据对我们产生不好的影响
drop table if exists user;
drop table if exists blog;

create table blog(
    -- 博客的编号
    blogId int primary key auto_increment,
    -- 博客的标题
    title varchar(256),
    -- 博客的内容
    content varchar(4096),
    -- 博客的作者
    userId int,
    -- 博客的发布时间
    postTime datetime
);

create table user (
    -- 用户编号
    userId int primary key auto_increment,
    -- 用户名, 约定用户名不能重复.
    username varchar(64) unique,
    -- 密码
    password varchar(64)
    -- user 里还可以增加很多别的属性. github 链接, 头像链接.....
);

-- 构造一些初始数据,方便后续的测试
insert into user values(1, 'zhangsan', '123'), (2, 'lisi', '123');
insert into blog values(1,'这是我的第一篇博客','从今天起我要好好敲代码',1,'2023-9-25 21:17:30');
insert into blog values(2,'这是我的第二篇博客','从昨天起我要好好敲代码',1,'2023-9-26 23:07:11');
insert into blog values(3,'这是我的第三篇博客','从前天起我要好好敲代码',1,'2023-9-27 18:20:00');

🌟③把在db.sql文件下编写的SQL代码复制到Mysql数据库去执行

 (然后就可以在数据库里去查询数据了)

(2)了解dao包

1.dao包的概念

💗Data Access Object,即数据访问对象

2.dao包的作用

💗即通过在dao包下创建和写一些类,通过这些类里的方法封装数据库的操作,此时数据库就可以通过这些类的对象来操作,总的来说,数据库的操作一般都放在dao包下

3.dao包的创建

💗一般创建于src/main/java中,即创建在Java目录下

(3)DBUtil中封装数据库的连接和断开操作

①在dao包下创建一个Java文件,取名为DBUtil

(通过这个类,把数据库建立连接的逻辑进行封装)


②在DBUtil文件下,我们会使用单例模式组织DataSource

(通过DBUtil,实现数据库的连接和断开操作)

package dao;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
import java.sql.*;

//通过这个类,把数据库建立连接的逻辑进行封装
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/hlizoo_blog_system?characterEncoding=utf8&AllowPublicKeyRetrieval=True");
                    ((MysqlDataSource)dataSource).setUser("root");
                    ((MysqlDataSource)dataSource).setPassword("hlizoo777");
                }
            }
        }
        return dataSource;
    }


    //提供一个方法,和数据库进行连接
    public static Connection getConnection() {
        try {
            return getDataSource().getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }


    //提供一个方法,和数据库断开连接
    public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet){
        if (resultSet!=null){
            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();
            }
        }
    }
}

(4)创建实体类

1.实体

💗数据库的设计,就是先要根据需求找出有哪些实体,实体就是比如职工、学生、教师、课程等都是实体,然后梳理实体与实体之间的关系

2.实体类

💛实体类是需要和数据库中的表存在对应关系


💜通常,每个表都需要一个实体类

💜后续,就可以通过这个实体类的对象来表示表里的每一条记录

💜因此,这个实体类对象的属性,要求和表里的每一列都要对应

3.创建实体类

①由于我们只有两张表,我们只需要创建两个实体类

(在dao包下创建两个类,一个叫Blog,一个叫User)


②创建Blog实体类

package dao;
import java.sql.Timestamp;

//通过Blog这个类的对象,表示一条blog表中的记录
//这个Blog类的属性,要和blog表里的每一列匹配
public class Blog {
    private int blogId;
    private String title;
    private String content;
    private int userId;
    //Sql里面有timestamp和datatime来表示日期的类型,但在sql中我们建议用datatime,因为timestamp只能表示到2038年
    //而在Java里,timesatmp没有这个限制
    private Timestamp postTime;

    public int getBlogId() {
        return blogId;
    }

    public void setBlogId(int blogId) {
        this.blogId = blogId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public Timestamp getPostTime() {
        return postTime;
    }

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

    @Override
    public String toString() {
        return "Blog{" +
                "blogId=" + blogId +
                ", title='" + title + '\'' +
                ", content='" + content + '\'' +
                ", userId=" + userId +
                ", postTime=" + postTime +
                '}';
    }
}

③创建User实体类

package dao;

//通过User这个类的对象,表示一条user表中的记录
//这个User类的属性,要和user表里的每一列匹配
public class User {
    private int userId;
    private String username;
    private String password;

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

(5)创建Dao类

1.创建BlogDao类

🌟通过BlogDao类,封装针对blog表进行增删改查


💗代码:
package dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

//通过BlogDao类,封装针对blog表进行增删改查
public class BlogDao {
    //1.新增一个博客
    //注:调用insert时,需要先构造一个Blog对象,再作为参数传递给insert,最后由insert内部完成数据库插入操作
    public void insert(Blog blog) {
        Connection connection = null;
        PreparedStatement statement = null;
        try{
            //(1)与数据库建立连接
            connection = DBUtil.getConnection();
            //(2)构造SQL语句进行插入操作
            //此处的博客发布时间,正好是执行SQL时刻,直接使用SQL里的库函数now,用来获取当前时间
            String sql = "insert into blog values(null,?,?,?,now())";
            statement = connection.prepareStatement(sql);
            statement.setString(1, blog.getTitle());
            statement.setString(2, blog.getContent());
            statement.setInt(3, blog.getUserId());
            //(3)执行SQL语句
            statement.executeUpdate();
        }catch (SQLException e){
            e.printStackTrace();
        } finally {
            //(4)关闭连接
            DBUtil.close(connection,statement,null);
        }
    }


    //2.查询blog表里所有的博客
    public List<Blog> getBlogs() {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        List<Blog> blogs = new ArrayList<>();
        try {
            //(1)与数据库建立连接
            connection = DBUtil.getConnection();
            //(2)构造SQL语句进行查询操作
            //使用order by是为了让新发布的博客先展示
            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"));
                blog.setContent(resultSet.getString("content"));
                blog.setUserId(resultSet.getInt("userId"));
                blog.setPostTime(resultSet.getTimestamp("postTime"));
                blogs.add(blog);
            }
        }catch(SQLException e){
            e.printStackTrace();
        }finally {
            //(5)关闭连接
            DBUtil.close (connection,statement,resultSet);
        }
        return blogs;
    }


    //3.指定一个blogId来查询某一个博客
    public Blog getblog(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)遍历结果集合
            while (resultSet.next()){
                Blog blog = new Blog();
                blog.setBlogId(resultSet.getInt("blogId"));
                blog.setTitle(resultSet.getString("title"));
                blog.setContent(resultSet.getString("content"));
                blog.setUserId(resultSet.getInt("userId"));
                blog.setPostTime(resultSet.getTimestamp("postTime"));
                return blog;
            }
        }catch(SQLException e){
            e.printStackTrace();
        }finally {
            //(5)关闭连接
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }


    //4.指定一个blogId来删除某一个博客
    public void delete(int blogId){
        Connection connection = null;
        PreparedStatement statement = null;
        try{
            //(1)与数据库建立连接
            connection = DBUtil.getConnection();
            //(2)构造SQL语句进行插入操作
            //此处的博客发布时间,正好是执行SQL时刻,直接使用SQL里的库函数now,用来获取当前时间
            String sql = "delete from blog where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,blogId);
            //(3)执行SQL语句
            statement.executeUpdate();
        }catch (SQLException e){
            e.printStackTrace();
        } finally {
            //(4)关闭连接
            DBUtil.close(connection,statement,null);
        }
    }
}
2.创建UserDao类

🌟通过UserDao类,封装针对user表进行增删改查


💗代码:

package dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

//通过UserDao类,封装针对user表进行增删改查
public class UserDao {
    // 对于新增User,其实就是实现一个“注册”功能,这里我们暂时不实现
    // 对于删除User,其实就是实现一个“注销”功能,这里我们暂时不实现

    //1.根据userId来查询用户信息(方便我们后续根据博客查询出作者详情)
    public User getUserById(int userId){
        Connection connection  = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            //(1)与数据库建立连接
            connection = DBUtil.getConnection();
            //(2)构造SQL语句
            String sql = "select * from user 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;
    }


    //2.根据username来查询用户信息(主要是为了实现一个登录的效果)
    public User getUserByName(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;
    }
}

四:前后端的交互

(1)整体思路

💗思路:以每个功能点为维度展开


🌟针对每个功能点分别进行以下操作

①设计前后端交互接口➜②设计后端代码➜③设计前端代码➜④调试


功能一:实现博客列表页;让博客列表页能够加载博客列表


功能二:实现博客详情页;当我们点击某一篇博客的“查看全文”时能看到整篇博客


功能三:实现博客登录页;输入用户的账号和密码;然后重定向到博客列表页


功能四:实现强制检查登录;如果用户在未登录情况下访问博客列表页/详情页/编辑页,都会自动跳转到登录页


功能五:实现显示个人信息;在博客列表页左侧显示当前登录用户的信息;在博客详情页左侧显示博客作者信息


功能六:实现退出登录功能


功能七:实现发布博客功能;在博客编辑页面写出博客后将其展示在博客列表页,保存于数据库中

(2)实现功能一

💗思路与步骤:

①先由前端发起一个HTTP请求,向后端索要博客列表数据

②后端收到请求之后查询数据库获取到数据库中的博客列表,并返回给前端

③前端拿到响应之后,会根据这里的内容,构造出html片段,最终显示出来

1.设计前后端交互接口

💚在进行上述这三个步骤之前,先要约定前后端交互接口,即约定请求和响应分别是啥


2.设计后端代码

💙后端代码要做的,就是当收到一个上述格式的请求之后,就构造并返回一个约定的响应数据给前端即可


①在main/java目录下创建一个包,取名为api


②在api包下创建一个类,取名为BlogServlet

(注意,此处写的所有东西都得遵守上述的约定,即请求和响应都应该按约定好的写)

package api;
import com.fasterxml.jackson.databind.ObjectMapper;
import dao.Blog;
import dao.BlogDao;
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;

//因为上述前后端交互接口中我们已经约定了方法是GET,路径是blog,因此这里我们得遵守约定
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    //定义ObjectMapper对象,这是jackson库,我们用它来解析或者组织JSON格式数据
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //此处的工作:查询数据库,获取到数据库中的数据之后,构造成要求的json格式并返回
        BlogDao blogDao = new BlogDao();
        List<Blog> blogs = blogDao.getBlogs();
        //此处就会把博客列表blogs构造成json格式
        String respJson = objectMapper.writeValueAsString(blogs);
        //写到响应并返回
        resp.setContentType("application/json;charset=utf8");
        resp.getWriter().write(respJson);
    }
}

3.设计前端代码

💙前端代码要做的,就是构造请求向服务器获取到博客列表数据,然后接收服务器返回的响应并展示到页面上


①使用VsCode打开blog_list.html这个前端代码


②把右侧信息的大部分代码都删除掉,只留下如图所示的部分


③引入jQuery

要让前端和后端交互,就需要使用ajax

这里的ajax我们不使用原生的,而是使用jQuery提供的

具体可点击➜应用层-详解HTTP协议(目录九)


④构造ajax请求向服务器获取到博客列表数据,然后接收服务器返回的响应并展示到页面上

💜(只修改右侧信息)

        <!-- 右侧信息 -->
        <div class="container-right">
            <!-- <div class="blog">
                <div class="title">我的第一篇博客博客博客博客</div>
                <div class="date">2023-05-11 20:00:00</div>
                <div class="desc">
                    从今天起, 我要认真敲代码. Lorem ipsum dolor sit amet consectetur, adipisicing elit. Debitis repellendus voluptatum, reiciendis rem consectetur incidunt aspernatur eveniet excepturi magni quis sint, provident est at et pariatur dolorem aliquid fugit voluptatem.
                </div>
                <a href="blog_detail.html?blogId=1">查看全文 &gt;&gt; </a>
            </div> -->
            </div>
        </div>
    </div>

      <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
      <script>
        //编写JS代码
        //构造ajax请求向服务器获取到博客列表数据,然后接收服务器返回的响应并展示到页面上
        function getBlogs(){
            $.ajax({
                type:'get',
                url:'blog',
                success:function(body){
                    //在这里根据响应的内容,构造出html片段,展示到页面上
                    //由于在服务器响应中已经设置了Content-Type为application/json
                    //此时jQuery就能够自动把此处响应的内容解析成js对象数组
                    let container = document.querySelector('.container-right');
                    for(let blog of body){
                         //这个for循环就相当于Java里的for-each循环
                        //根据当前这个blog,构造出一个html片段
                        //构造的格式按上面注释部分来
                        //构造blog的div标签
                        let blogDiv = document.createElement('div');
                        blogDiv.className = 'blog';
                        //第一步:构造标题
                        let titleDiv = document.createElement('div');
                        titleDiv.className = 'title'; 
                        titleDiv.innerHTML = blog.title;
                        blogDiv.appendChild(titleDiv);
                        //第二步:构造发布时间信息
                        let dateDiv = document.createElement('div');
                        dateDiv.className = 'date'; 
                        dateDiv.innerHTML = blog.postTime;
                        blogDiv.appendChild(dateDiv);
                        //第三步:构造内容摘要信息
                        let descDiv = document.createElement('div');
                        descDiv.className = 'desc'; 
                        descDiv.innerHTML = blog.content;
                        blogDiv.appendChild(descDiv);
                        //第四步:构造“查看全文”点击链接按钮
                        let a = document.createElement("a");
                        a.href = 'blog_detail.html?blogId=' + blog.blogId;
                        a.innerHTML = '查看全文 &gt;&gt;';
                        blogDiv.appendChild(a);
                        //最后一步:将拼好的blogDiv添加到container
                        container.appendChild(blogDiv);
                    }
                }
            })
        }
        getBlogs();
      </script>


⑤效果展示

💜(1)得益于前面order by排序,使得新发布的博客先展示在前面

💜(2)这些数据全都是数据库里面存储的


⑥引出问题-时间格式

🌟问题:时间格式不对,页面展示的是时间戳,这并不方便我们去观察

🔯1.先明确问题如何出现

答:后端服务器返回的就是一个时间戳,且前端没有做任何处理,就导致最后显示了时间戳

🔯2.如何解决

答:使用Java标准库提供的SimpleDateFormat类,使用SimpleDateFormat可以将日期对象转换为指定格式的字符串,或将字符串解析为对应的日期对象

🔯3.如何使用SimpleDateFormat

import java.text.SimpleDateFormat;
import java.util.Date;

//SimpleDateFormat使用步骤
public class date {
    public static void main(String[] args) {
        //(1)先创建SimpleDateFormat对象,指定日期格式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        //(2)format()方法将当前日期格式化为指定格式的字符串
        Date date = new Date();
        String formattedDate = sdf.format(date);
        System.out.println("格式化后的日期:" + formattedDate);
    }
}

🔯4.在dao包下的Blog文件中修改getPostTime()方法

    public String getPostTime() {
        //此处需要把时间戳格式化
        //return postTime;
        SimpleDateFormat format_time = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        return format_time.format(postTime);
    }

🔯5.效果马上就变成了如图所示

(3)实现功能二

💗思路与步骤:

①先由前端发起一个HTTP请求,向后端索要指定的博客内容数据

②后端收到请求之后查询数据库获取到数据库中指定的博客内容数据,并返回给前端

③前端拿到响应之后,会根据这里的内容,构造出html片段,最终显示出来

1.设计前后端交互接口

💚在进行上述这三个步骤之前,先要约定前后端交互接口,即约定请求和响应分别是啥


2.设计后端代码

💙后端代码要做的,就是当收到一个上述格式的请求之后,就构造并返回一个约定的响应数据给前端即可


🌟依旧在BlogServlet文件下修改代码

package api;
import com.fasterxml.jackson.databind.ObjectMapper;
import dao.Blog;
import dao.BlogDao;
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;

//因为上述前后端交互接口中我们已经约定了方法是GET,路径是blog,因此这里我们得遵守约定
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    //定义ObjectMapper对象,这是jackson库,我们用它来解析或者组织JSON格式数据
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //先获取一下blogId的value值,根据能否获取判断是博客列表页还是博客详情页
        String blogId = req.getParameter("blogId");
        BlogDao blogDao = new BlogDao();

        if (blogId==null){
            //如果blogId为空,说明query string为空,此时就是博客列表页
            //此处的工作:查询数据库,获取到数据库中的所有博客数据之后,构造成要求的json格式并返回
            List<Blog> blogs = blogDao.getBlogs();
            //此处就会把博客列表blogs构造成json格式
            String respJson = objectMapper.writeValueAsString(blogs);
            //写到响应并返回
            resp.setContentType("application/json;charset=utf8");
            resp.getWriter().write(respJson);
        }else {
            //如果blogId不为空,说明query string不为空,此时就是博客详情页
            //根据不同的blogId查看到不同的blog对象和数据
            //注意:这里是单个博客,而不是所有博客
            Blog blog = blogDao.getblog(Integer.parseInt(blogId));
            if (blog==null){
                //如果blog为空,也就是blogId不存在没有这篇博客
                //就返回一个blogId为0的blog对象,因为blogId不可能为0,此时再由前端来进行判定
                blog=new Blog();
            }
            //将指定的博客数据构造成json格式
            String respJson = objectMapper.writeValueAsString(blog);
            //写到响应并返回
            resp.setContentType("application/json;charset=utf8");
            resp.getWriter().write(respJson);
        }
    }
}
3.设计前端代码

💙前端代码要做的,就是构造请求向服务器获取到指定blogId的具体博客数据,然后接收服务器返回的响应并展示到页面上


①使用VsCode打开blog_detail.html这个前端代码


②把右侧信息的大部分代码都删除掉,只留下如图所示的部分

(把content的标签改为<div id>)

🌟到时候构造页面内容,我们就根据这个格式去构造


③引入jQuery


④使用ajax

💜(只修改右侧信息)

        <!-- 右侧信息 -->
        <div class="container-right">
            <h3></h3>
            <div class="date"></div>
            <div id="content">
            </div>
        </div>
    </div>

      <!-- 引入 ajax 的依赖 -->
      <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
      <!-- 引入 editor.md 的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    <script src="editor.md/lib/marked.min.js"></script>
    <script src="editor.md/lib/prettify.min.js"></script>
    <script src="editor.md/editormd.js"></script>
      
    <script>
        function getBlog(){
            $.ajax({
                type:'get',
                url:'blog' + location.search,
                success:function(body){
                    //根据服务器返回的响应内容,构造页面
                    //第一步:构造标题
                    let h3 = document.querySelector('.container-right h3');
                    h3.innerHTML = body.title;
                    //第二步:构造时间
                    let dateDiv = document.querySelector('.container-right .date');
                    dateDiv.innerHTML = body.postTime;
                    //第三步:构造内容
                    //格式:editormd.markdownToHTML('content',{markdown:markdown});
                    editormd.markdownToHTML('content',{markdown:body.content});
                }
            });
        }
        getBlog(); 
    </script>


⑤效果展示

如图所示:第三篇博客的详情页

(4)实现功能三

💗思路与步骤:

①在页面中输入用户的账号和密码

②点击登录之后触发登录逻辑,比如验证用户的账号和密码是否正确,创建Session等等

③如果登录成功,则重定向到博客列表页

1.设计前后端交互接口

2.设计后端代码

💙需要写一个新的Servlet,处理login这个请求的路径

(每个Servlet都需要关联一个路径,像之前的博客列表页和博客详情页,因为它们的路径都是blog,所以共用一个Servlet即可;但是login是新的路径,我们需要创建一个新的Servlet)


💛在api包下创建新的文件,取名为LoginServlet


package api;
import dao.User;
import dao.UserDao;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //第一步:获取请求中的用户名和密码
        //(给请求req设置字符集utf8,保证传过来的用户名和密码即使是中文,也能够正确处理)
        req.setCharacterEncoding("utf8");
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if (username==null || password==null || "".equals(username) || "".equals(password)){
            //进入if循环,说明提交的用户名和密码有错误;此时我们返回错误信息
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前传过来的username或者password为空");
            return;
        }

        //第二步:代码来到这,说明用户名和密码已经输入,此时需要去数据库验证用户名和密码是否正确
        UserDao userDao = new UserDao();
        User user = userDao.getUserByName(username);
        if (user==null){
            //进入循环说明返回的用户为空;则说明用户名不存在
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("您的用户名错误或者密码错误!");
            return;
        }
        if (!password.equals(user.getPassword())){
            //进入循环说明用户输入的密码与数据库中的密码不匹配
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("您的用户名错误或者密码错误!");
            return;
        }

        //第三步:代码来到这,说明用户名和密码正确,我们则创建会话
        HttpSession session = req.getSession(true);
        //把当前登录的用户信息保存到session中,方便后续进行获取
        session.setAttribute("user",user);

        //第四步:跳转到博客列表页
        resp.sendRedirect("blog_list.html");
    }
}
3.设计前端代码

💙调整之前的登录页,让这个页面能够构造出form表单的请求


①使用VsCode打开login.html这个前端代码


②form表单在代码中已经写的差不多了,我们只需要稍作修改即可


③具体代码

    <!-- 登录页的版心 -->
    <div class="login-container">
        <!-- 登录对话框 -->
        <div class="login-dialog">
            <h3>登录</h3>
            <!-- 使用 form 包裹一下下列内容, 便于后续给服务器提交数据 -->
            <form action="login" method="post">
                <div class="row">
                    <span>用户名</span>
                    <input type="text" id="username" name="username">
                </div>
                <div class="row">
                    <span>密码</span>
                    <input type="password" id="password" name="password">
                </div>
                <div class="row">
                    <input type="submit" id="submit" value="登录">
                </div>
            </form>
        </div>
    </div>


④查看数据库有哪些用户和密码


⑤效果展示-当输入的用户名或者密码有误时


⑥效果展示-当输入正确时

(5)实现功能四

1.设计前后端交互接口

2.设计后端代码

💙在LoginServlet文件下添加一个doGet方法,用来检测是否登录

(前面的代码都一样,主要看下面的doGet方法)


package api;
import dao.User;
import dao.UserDao;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //第一步:获取请求中的用户名和密码
        //(给请求req设置字符集utf8,保证传过来的用户名和密码即使是中文,也能够正确处理)
        req.setCharacterEncoding("utf8");
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if (username==null || password==null || "".equals(username) || "".equals(password)){
            //进入if循环,说明提交的用户名和密码有错误;此时我们返回错误信息
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前传过来的username或者password为空");
            return;
        }

        //第二步:代码来到这,说明用户名和密码已经输入,此时需要去数据库验证用户名和密码是否正确
        UserDao userDao = new UserDao();
        User user = userDao.getUserByName(username);
        if (user==null){
            //进入循环说明返回的用户为空;则说明用户名不存在
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("您的用户名错误或者密码错误!");
            return;
        }
        if (!password.equals(user.getPassword())){
            //进入循环说明用户输入的密码与数据库中的密码不匹配
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("您的用户名错误或者密码错误!");
            return;
        }

        //第三步:代码来到这,说明用户名和密码正确,我们则创建会话
        HttpSession session = req.getSession(true);
        //把当前登录的用户信息保存到session中,方便后续进行获取
        session.setAttribute("user",user);

        //第四步:跳转到博客列表页
        resp.sendRedirect("blog_list.html");

    }



    //通过这个方法,来检测当前登录状态
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //尝试获取对话session,会话不存在则认为未登录
        HttpSession session = req.getSession(false);
        if (session==null){
            //未登录状态
            resp.setStatus(403);
            return;
        }
        //此时session虽然存在,但是还需要判断user对象是否存在(也是为了实现后面的“退出登录”功能)
        User user = (User) session.getAttribute("user");
        if (user==null){
            //未登录状态
            resp.setStatus(403);
            return;
        }
        //来到这里证明用户已经登录
        resp.setStatus(200);
    }
}
3.设计前端代码

💙分别在博客列表页/博客详情页/博客编辑页添加一个get方法的ajax请求,用来判断是否登录


①因为每个页面都需要添加一个get方法的ajax请求,所以我们先在WEBAPP下创建一个文件夹,取名为js,再在js文件夹下面添加一个文件,取名为app.js


②在app.js文件下写代码

function checkLogin(){
    $.ajax({
        type:'get',
        url:'login',
        success:function(body){
            //表示登录成功,此时啥都不需要做
        },
        error:function(body){
            //表示未登录,返回403的情况会触发这个逻辑
            //跳转到登录页面
            location.assign("login.html");
        }
    })
}

③在博客列表页/详情页/编辑页中先引入这个JS文件,然后调用该checkLogin()方法

(1)博客列表页blog_list.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_list.css">
</head>
<body>
    <!-- 导航栏. nav 是 导航 这个词的缩写 -->
    <div class="nav">
        <!-- logo -->
        <img src="image/logo2.jpg" alt="">
        <div class="title">我的博客系统</div>
        <!-- 只是一个空白, 用来把后面的链接挤过去 -->
        <!-- 这是一个简单粗暴的写法~~ -->
        <div class="spacer"></div>
        <a href="blog_list.html">主页</a>
        <a href="blog_edit.html">写博客</a>
        <!-- 这里的地址回头再说 -->
        <a href="">注销</a>
    </div>

    <!-- 页面的主体部分 -->
    <div class="container">
        <!-- 左侧信息 -->
        <div class="container-left">
            <!-- 这个 div 表示整个用户信息的区域 -->
            <div class="card">
                <!-- 用户的头像 -->
                <img src="image/kun.jpg" alt="">
                <!-- 用户名 -->
                <h3>hlizoo</h3>
                <!-- github 地址 -->
                <a href="https://www.github.com">github 地址</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">
                <div class="title">我的第一篇博客博客博客博客</div>
                <div class="date">2023-05-11 20:00:00</div>
                <div class="desc">
                    从今天起, 我要认真敲代码. Lorem ipsum dolor sit amet consectetur, adipisicing elit. Debitis repellendus voluptatum, reiciendis rem consectetur incidunt aspernatur eveniet excepturi magni quis sint, provident est at et pariatur dolorem aliquid fugit voluptatem.
                </div>
                <a href="blog_detail.html?blogId=1">查看全文 &gt;&gt; </a>
            </div> -->
            </div>
        </div>
    </div>

      <!-- 引入 ajax 的依赖 -->
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
      <!-- 引入 editor.md 的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    <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>
      <!-- 引入 app.js,判断是否登录 -->
    <script src="js/app.js"></script>

      <script>
        //编写JS代码
        //构造ajax请求向服务器获取到博客列表数据,然后接收服务器返回的响应并展示到页面上
        function getBlogs(){
            $.ajax({
                type:'get',
                url:'blog',
                success:function(body){
                    //在这里根据响应的内容,构造出html片段,展示到页面上
                    //由于在服务器响应中已经设置了Content-Type为application/json
                    //此时jQuery就能够自动把此处响应的内容解析成js对象数组
                    let container = document.querySelector('.container-right');
                    for(let blog of body){
                         //这个for循环就相当于Java里的for-each循环
                        //根据当前这个blog,构造出一个html片段
                        //构造的格式按上面注释部分来
                        //构造blog的div标签
                        let blogDiv = document.createElement('div');
                        blogDiv.className = 'blog';
                        //第一步:构造标题
                        let titleDiv = document.createElement('div');
                        titleDiv.className = 'title'; 
                        titleDiv.innerHTML = blog.title;
                        blogDiv.appendChild(titleDiv);
                        //第二步:构造发布时间信息
                        let dateDiv = document.createElement('div');
                        dateDiv.className = 'date'; 
                        dateDiv.innerHTML = blog.postTime;
                        blogDiv.appendChild(dateDiv);
                        //第三步:构造内容摘要信息
                        let descDiv = document.createElement('div');
                        descDiv.className = 'desc'; 
                        descDiv.innerHTML = blog.content;
                        blogDiv.appendChild(descDiv);
                        //第四步:构造“查看全文”点击链接按钮
                        let a = document.createElement("a");
                        a.href = 'blog_detail.html?blogId=' + blog.blogId;
                        a.innerHTML = '查看全文 &gt;&gt;';
                        blogDiv.appendChild(a);
                        //最后一步:将拼好的blogDiv添加到container
                        container.appendChild(blogDiv);
                    }
                }
            })
        }
        getBlogs();
        
        checkLogin();
      </script>
</body>
</html>

(2)博客详情页blog_detail.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">
</head>
<body>
    <!-- 导航栏. nav 是 导航 这个词的缩写 -->
    <div class="nav">
        <!-- logo -->
        <img src="image/logo2.jpg" alt="">
        <div class="title">我的博客系统</div>
        <!-- 只是一个空白, 用来把后面的链接挤过去 -->
        <!-- 这是一个简单粗暴的写法~~ -->
        <div class="spacer"></div>
        <a href="blog_list.html">主页</a>
        <a href="blog_edit.html">写博客</a>
        <!-- 这里的地址回头再说 -->
        <a href="">注销</a>
    </div>
    <!-- 页面的主体部分 -->
    <div class="container">
        <!-- 左侧信息 -->
        <div class="container-left">
            <!-- 这个 div 表示整个用户信息的区域 -->
            <div class="card">
                <!-- 用户的头像 -->
                <img src="image/kun.jpg" alt="">
                <!-- 用户名 -->
                <h3>hlizoo</h3>
                <!-- github 地址 -->
                <a href="https://www.github.com">github 地址</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">
            <h3></h3>
            <div class="date"></div>
            <div id="content">
            </div>
        </div>
    </div>

      <!-- 引入 ajax 的依赖 -->
      <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
      <!-- 引入 editor.md 的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    <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>
        <!-- 引入 app.js,判断是否登录 -->
    <script src="js/app.js"></script>
      
    <script>
        function getBlog(){
            $.ajax({
                type:'get',
                url:'blog' + location.search,
                success:function(body){
                    //根据服务器返回的响应内容,构造页面
                    //第一步:构造标题
                    let h3 = document.querySelector('.container-right h3');
                    h3.innerHTML = body.title;
                    //第二步:构造时间
                    let dateDiv = document.querySelector('.container-right .date');
                    dateDiv.innerHTML = body.postTime;
                    //第三步:构造内容
                    //格式:editormd.markdownToHTML('content',{markdown:markdown});
                    editormd.markdownToHTML('content',{markdown:body.content});
                }
            });
        }
        getBlog(); 

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

(3)博客编辑页blog_edit.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_edit.css">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
    <!-- 引入 editor.md 的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    <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>
    <!-- 导航栏. nav 是 导航 这个词的缩写 -->
    <div class="nav">
        <!-- logo -->
        <img src="image/logo2.jpg" alt="">
        <div class="title">我的博客系统</div>
        <!-- 只是一个空白, 用来把后面的链接挤过去 -->
        <!-- 这是一个简单粗暴的写法~~ -->
        <div class="spacer"></div>
        <a href="blog_list.html">主页</a>
        <a href="blog_edit.html">写博客</a>
        <!-- 这里的地址回头再说 -->
        <a href="">注销</a>
    </div>

    <!-- 博客编辑页的版心 -->
    <div class="blog-edit-container">
        <form action="">
            <!-- 标题编辑区 -->
            <div class="title">
                <input type="text" id="title-input">
                <input type="submit" id="submit">
            </div>
            <!-- 博客编辑器 -->
            <!-- 把 md 编辑器放到这个 div 中 -->
            <div id="editor">

            </div>
        </form>
    </div>

    <!-- 引入 app.js,判断是否登录 -->
    <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/"
        });

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

④效果展示

💚当你未登录时想访问博客编辑页面,就会自动跳转到博客登录页;具体效果要自行运行代码

(6)实现功能五

1.设计前后端交互接口

2.编写后端代码

🌟在api包下创建一个新的文件,取名为UserServlet


package api;
import com.fasterxml.jackson.databind.ObjectMapper;
import dao.Blog;
import dao.BlogDao;
import dao.User;
import dao.UserDao;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;

@WebServlet("/user")
public class UserServlet extends HttpServlet {
    //定义ObjectMapper对象,这是jackson库,我们用它来解析或者组织JSON格式数据
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //先获取一下blogId的value值,根据能否获取判断是博客列表页还是博客详情页
        String blogId = req.getParameter("blogId");

        if (blogId==null){
            //如果blogId为空,说明query string为空,此时就是博客列表页
            HttpSession session = req.getSession(false);
            if (session==null){
                //session为空,说明没有创建会话
                User user = new User();
                String respJson = objectMapper.writeValueAsString(user);
                resp.setContentType("application/json charset=utf8");
                resp.getWriter().write(respJson);
                return;
            }
            //因为前面LoginServlet的session保存了user对象,这里我们直接获取即可
            User user = (User) session.getAttribute("user");
            if (user==null){
                //user为空,说明没有该用户
                user = new User();
                String respJson = objectMapper.writeValueAsString(user);
                resp.setContentType("application/json charset=utf8");
                resp.getWriter().write(respJson);
                return;
            }
            //代码来到这,说明前两步都正确了,此时说明对话存在且拿到了一个user对象
            String respJson = objectMapper.writeValueAsString(user);
            resp.setContentType("application/json charset=utf8");
            resp.getWriter().write(respJson);

        }else {
            //如果blogId不为空,说明query string不为空,此时就是博客详情页
            //此时需要根据blogId去数据库查询对应的博客
            BlogDao blogDao = new BlogDao();
            Blog blog = blogDao.getblog(Integer.parseInt(blogId));
            if (blog==null){
                //表示没有查询到该博客,此时返回一个空的user对象
                User user = new User();
                String respJson = objectMapper.writeValueAsString(user);
                resp.setContentType("application/json charset=utf8");
                resp.getWriter().write(respJson);
                return;
            }
            UserDao userDao = new UserDao();
            //根据博客的userId来查询该博客对应的用户信息,比如谁写的
            User user = userDao.getUserById(blog.getUserId());
            if (user==null){
                //表示没有查询到该用户,此时返回一个空的user对象
                user = new User();
                String respJson = objectMapper.writeValueAsString(user);
                resp.setContentType("application/json charset=utf8");
                resp.getWriter().write(respJson);
                return;
            }
            //代码来到这,说明根据blogId已经查询到对应的用户了
            String respJson = objectMapper.writeValueAsString(user);
            resp.setContentType("application/json charset=utf8");
            resp.getWriter().write(respJson);
        }
    }
}
3.编写前端代码

①使用VsCode打开博客列表页blog_list.html这个前端代码,添加一个新的方法,取名为getUser

<!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_list.css">
</head>
<body>
    <!-- 导航栏. nav 是 导航 这个词的缩写 -->
    <div class="nav">
        <!-- logo -->
        <img src="image/logo2.jpg" alt="">
        <div class="title">我的博客系统</div>
        <!-- 只是一个空白, 用来把后面的链接挤过去 -->
        <!-- 这是一个简单粗暴的写法~~ -->
        <div class="spacer"></div>
        <a href="blog_list.html">主页</a>
        <a href="blog_edit.html">写博客</a>
        <!-- 这里的地址回头再说 -->
        <a href="">注销</a>
    </div>

    <!-- 页面的主体部分 -->
    <div class="container">
        <!-- 左侧信息 -->
        <div class="container-left">
            <!-- 这个 div 表示整个用户信息的区域 -->
            <div class="card">
                <!-- 用户的头像 -->
                <img src="image/kun.jpg" alt="">
                <!-- 用户名 -->
                <h3>hlizoo</h3>
                <!-- github 地址 -->
                <a href="https://www.github.com">github 地址</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">
                <div class="title">我的第一篇博客博客博客博客</div>
                <div class="date">2023-05-11 20:00:00</div>
                <div class="desc">
                    从今天起, 我要认真敲代码. Lorem ipsum dolor sit amet consectetur, adipisicing elit. Debitis repellendus voluptatum, reiciendis rem consectetur incidunt aspernatur eveniet excepturi magni quis sint, provident est at et pariatur dolorem aliquid fugit voluptatem.
                </div>
                <a href="blog_detail.html?blogId=1">查看全文 &gt;&gt; </a>
            </div> -->
            </div>
        </div>
    </div>

      <!-- 引入 ajax 的依赖 -->
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
      <!-- 引入 editor.md 的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    <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>
      <!-- 引入 app.js,判断是否登录 -->
    <script src="js/app.js"></script>

      <script>
        //编写JS代码
        //构造ajax请求向服务器获取到博客列表数据,然后接收服务器返回的响应并展示到页面上
        function getBlogs(){
            $.ajax({
                type:'get',
                url:'blog',
                success:function(body){
                    //在这里根据响应的内容,构造出html片段,展示到页面上
                    //由于在服务器响应中已经设置了Content-Type为application/json
                    //此时jQuery就能够自动把此处响应的内容解析成js对象数组
                    let container = document.querySelector('.container-right');
                    for(let blog of body){
                         //这个for循环就相当于Java里的for-each循环
                        //根据当前这个blog,构造出一个html片段
                        //构造的格式按上面注释部分来
                        //构造blog的div标签
                        let blogDiv = document.createElement('div');
                        blogDiv.className = 'blog';
                        //第一步:构造标题
                        let titleDiv = document.createElement('div');
                        titleDiv.className = 'title'; 
                        titleDiv.innerHTML = blog.title;
                        blogDiv.appendChild(titleDiv);
                        //第二步:构造发布时间信息
                        let dateDiv = document.createElement('div');
                        dateDiv.className = 'date'; 
                        dateDiv.innerHTML = blog.postTime;
                        blogDiv.appendChild(dateDiv);
                        //第三步:构造内容摘要信息
                        let descDiv = document.createElement('div');
                        descDiv.className = 'desc'; 
                        descDiv.innerHTML = blog.content;
                        blogDiv.appendChild(descDiv);
                        //第四步:构造“查看全文”点击链接按钮
                        let a = document.createElement("a");
                        a.href = 'blog_detail.html?blogId=' + blog.blogId;
                        a.innerHTML = '查看全文 &gt;&gt;';
                        blogDiv.appendChild(a);
                        //最后一步:将拼好的blogDiv添加到container
                        container.appendChild(blogDiv);
                    }
                }
            })
        }
        getBlogs();

        checkLogin();

        function getUser(){
            $.ajax({
                type:'get',
                url:'user',
                success:function(body){
                    //body就是解析后的user对象
                    //此时我们构造的格式按照上述代码-左侧信息那部分
                    //构造用户名
                    let h3 = document.querySelector(".card h3");
                    h3.innerHTML = body.username;
                }
            })
        }
        getUser();
      </script>
</body>
</html>

②使用VsCode打开博客详情页blog_detail.html这个前端代码,添加一个新的方法,取名为getUser

<!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>
    <!-- 导航栏. nav 是 导航 这个词的缩写 -->
    <div class="nav">
        <!-- logo -->
        <img src="image/logo2.jpg" alt="">
        <div class="title">我的博客系统</div>
        <!-- 只是一个空白, 用来把后面的链接挤过去 -->
        <!-- 这是一个简单粗暴的写法~~ -->
        <div class="spacer"></div>
        <a href="blog_list.html">主页</a>
        <a href="blog_edit.html">写博客</a>
        <!-- 这里的地址回头再说 -->
        <a href="">注销</a>
    </div>
    <!-- 页面的主体部分 -->
    <div class="container">
        <!-- 左侧信息 -->
        <div class="container-left">
            <!-- 这个 div 表示整个用户信息的区域 -->
            <div class="card">
                <!-- 用户的头像 -->
                <img src="image/kun.jpg" alt="">
                <!-- 用户名 -->
                <h3>hlizoo</h3>
                <!-- github 地址 -->
                <a href="https://www.github.com">github 地址</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">
            <h3></h3>
            <div class="date"></div>
            <div id="content">
            </div>
        </div>
    </div>

      <!-- 引入 ajax 的依赖 -->
      <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
      <!-- 引入 editor.md 的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    <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>
        <!-- 引入 app.js,判断是否登录 -->
    <script src="js/app.js"></script>
      
    <script>
        function getBlog(){
            $.ajax({
                type:'get',
                url:'blog' + location.search,
                success:function(body){
                    //根据服务器返回的响应内容,构造页面
                    //第一步:构造标题
                    let h3 = document.querySelector('.container-right h3');
                    h3.innerHTML = body.title;
                    //第二步:构造时间
                    let dateDiv = document.querySelector('.container-right .date');
                    dateDiv.innerHTML = body.postTime;
                    //第三步:构造内容
                    //格式:editormd.markdownToHTML('content',{markdown:markdown});
                    editormd.markdownToHTML('content',{markdown:body.content});
                }
            });
        }
        getBlog(); 

        checkLogin();

        function getUser(){
            $.ajax({
                type:'get',
                url:'user' + location.search,
                success:function(body){
                    //body就是解析后的user对象
                    //此时我们构造的格式按照上述代码-左侧信息那部分
                    //构造用户名
                    let h3 = document.querySelector(".card h3");
                    h3.innerHTML = body.username;
                }
            })
        }
        getUser();
    </script>
</body>
</html> 

③效果展示-博客列表页


④效果展示-博客详情页

(7)实现功能六

💗思路与步骤:

1.设计前后端交互接口

2.编写后端代码
package api;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@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.sendRedirect("login.html");
            return;
        }
        //之前在LoginServlet中当用户登录成功时,我们会在sessin中保存user对象
        //此时就可以直接删除会话中的user
        session.removeAttribute("user");
        resp.sendRedirect("login.html");
    }
}
3.编写前端代码

①可以看到页面的右上角有一个“注销”


②使用VsCode打开blog_list.html、blog_edit.html、blog_detail.html这几个前端代码,找到下图所示部分


③这几个代码分别修改<a href>的部分,很简单,只需要加个logout


④代码分析

(8)实现功能七

💗思路与步骤:

这里的过程本质上和登录类似

核心都是通过form表单,把页面中用户输入的内容,给提交到服务器这边

服务器调用insert方法插入一条数据将内容保存到数据库中

1.设计前后端交互接口

2.编写后端代码

💗在BlogServlet文件中添加doPost方法的代码


package api;
import com.fasterxml.jackson.databind.ObjectMapper;
import dao.Blog;
import dao.BlogDao;
import dao.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;

//因为上述前后端交互接口中我们已经约定了方法是GET,路径是blog,因此这里我们得遵守约定
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    //定义ObjectMapper对象,这是jackson库,我们用它来解析或者组织JSON格式数据
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //先获取一下blogId的value值,根据能否获取判断是博客列表页还是博客详情页
        String blogId = req.getParameter("blogId");
        BlogDao blogDao = new BlogDao();

        if (blogId==null){
            //如果blogId为空,说明query string为空,此时就是博客列表页
            //此处的工作:查询数据库,获取到数据库中的所有博客数据之后,构造成要求的json格式并返回
            List<Blog> blogs = blogDao.getBlogs();
            //此处就会把博客列表blogs构造成json格式
            String respJson = objectMapper.writeValueAsString(blogs);
            //写到响应并返回
            resp.setContentType("application/json;charset=utf8");
            resp.getWriter().write(respJson);
        }else {
            //如果blogId不为空,说明query string不为空,此时就是博客详情页
            //根据不同的blogId查看到不同的blog对象和数据
            //注意:这里是单个博客,而不是所有博客
            Blog blog = blogDao.getblog(Integer.parseInt(blogId));
            if (blog==null){
                //如果blog为空,也就是blogId不存在没有这篇博客
                //就返回一个blogId为0的blog对象,因为blogId不可能为0,此时再由前端来进行判定
                blog=new Blog();
            }
            //将指定的博客数据构造成json格式
            String respJson = objectMapper.writeValueAsString(blog);
            //写到响应并返回
            resp.setContentType("application/json;charset=utf8");
            resp.getWriter().write(respJson);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //第一步:先去获取登录的用户
        //这里其实不用判断session和user是否为空;因为在博客编辑页已经做了登录的检查,如果你没有登录是进不去博客编辑页的
        HttpSession session = req.getSession(false);
        User user = (User) session.getAttribute("user");
        //第二步:获取请求中传递过来的内容(内容包括题目和内容)
        req.setCharacterEncoding("utf8");
        String title = req.getParameter("title");
        String content = req.getParameter("content");
        if (title==null || content==null || "".equals(title) || "".equals(content)){
            resp.setContentType("text/html ; charset=utf8");
            resp.getWriter().write("标题或者正文内容为空");
            return;
        }
        //第三步:构造Blog对象,添加到数据库中保存
        //至于构造时间,因为在BlogDao中的insert方法中已经使用了SQL中自带的now()方法获取当前时间,这里我们可以不用自己设置
        Blog blog = new Blog();
        blog.setTitle(title);
        blog.setContent(content);
        blog.setUserId(user.getUserId());
        //将博客插入到数据库中
        BlogDao blogDao = new BlogDao();
        blogDao.insert(blog);
        //第四步:跳转到博客列表页
        resp.sendRedirect("blog_list.html");
    }
}
3.编写前端代码

①主要工作就是把form表单给补齐


②使用VsCode打开blog_edit.html代码,找到下图所示部分


③修改代码

    <!-- 博客编辑页的版心 -->
    <div class="blog-edit-container">
        <form action="blog" method="post">
            <!-- 标题编辑区 -->
            <div class="title">
                <input type="text" id="title-input" name="title">
                <input type="submit" id="submit">
            </div>
            <!-- 博客编辑器 -->
            <!-- 把 md 编辑器放到这个 div 中 -->
            <div id="editor">
                <textarea name="content" style="display:none;"></textarea>
            </div>
        </form>
    </div>

④代码分析


⑤效果展示

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值