博客系统 - (servlet总结,前后端分离)

前面我们学习了前端的博客系统,然后我们现在又学了后端的 servlet 技术,基本就可以写一个可以自己增删查改的网页了

所谓的前后端分离,通俗的说,就是这种方式下的服务器端不关注页面的内容,而只是给网页端提供数据,然后网页端通过 ajax 的方式和服务器之间交互数据,网页拿到数据之后再根据数据的内容渲染到页面上..

渲染就是相当于给你一个模板,然后里面的内容可以通过后端数据随意更改.

一. 准备工作

先准备好servlet:servlet准备工作

之前讲过的,我这里就简单点写了

1.创建Maven项目

在这里插入图片描述

2. 引入依赖. (servlet,jackson,mysql)

去中央仓库搜索下载(版本别搞错了):https://mvnrepository.com/

<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.13.0</version>
        </dependency>

在这里插入图片描述
引入后记得刷新一下:
在这里插入图片描述

3.创建必要的目录

在这里插入图片描述
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>

在这里插入图片描述

4. 编写代码

在 java目录里新建一个HelloServlet

在这里插入图片描述

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;

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        resp.getWriter().write("hello");
    }
}

注意路径

5/6. 打包部署程序

我们下载的 smart tomcat,就不用频繁的去打包部署了
在这里插入图片描述

7. 在浏览器中验证

点击小三角运行:
在这里插入图片描述

到这里准备工作算是做完了,接下来就可以执行接下来的步骤了

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

1.创建数据库/表结构(数据库设计)

这里就要根据你的前端页面来设计了,之前搞得什么页面,需求是什么就设计什么,我们实现的博客系统,需要用户表和博客表,我们就设计这俩

建议大家现在记事本上写好了在直接拷贝到数据库上,这样不容易出错
在这里插入图片描述

db.sql

-- 编写建库建表的 sql

create database if not exists blog_system;

use blog_system;

-- 创建一个博客表.
drop table if exists blog;
create table blog (
                      blogId int primary key auto_increment,
                      title varchar(1024),
                      content mediumtext,
                      userId int,         -- 文章作者的 id
                      postTime datetime   -- 发布时间
);


-- 给博客插点数据,后面方便我们进行测试
insert into blog values(null,'第一篇博客','今天开始学Java了!',1,now());
insert into blog values(null,'第二篇博客','昨天开始学Java!',1,now());
insert into blog values(null,'第三篇博客','明天开始学Java',1,now());
insert into blog values(null,'第一篇博客','今天开始学C++',2,now());
insert into blog values(null,'第二篇博客','明天开始学C++',2,now());

-- 创建一个用户表
drop table if exists user;
create table user (
                      userId int primary key auto_increment,
                      username varchar(128) unique,    -- 后续会使用用户名进行登录, 一般用于登录的用户名都是不能重复的.
                      password varchar(128)
);


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

在这里插入图片描述

2. 封装数据库操作

2.1 先创建 DBUtil 封装数据库连接的操作

我们相关都操作放到各自的包里面,这样让我们后面写起来不容易混乱!!
在这里插入图片描述

这里就先用之前学过的 JDBC 来连接数据库

package model;

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

// 使用这个类和数据库建立连接.
public class DBUtil {
    
    
    private static final String URL = "jdbc:mysql://127.0.0.1:3306/blog_system?characterEncoding=utf8&useSSL=false";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "root";

    private static volatile DataSource dataSource = null;

    private static DataSource getDataSource() {
    
    
        if (dataSource == null) {
    
    
            synchronized (DBUtil.class) {
    
    
                if (dataSource == null) {
    
    
                    dataSource = new MysqlDataSource();
                    ((MysqlDataSource)dataSource).setUrl(URL);
                    ((MysqlDataSource)dataSource).setUser(USERNAME);
                    ((MysqlDataSource)dataSource).setPassword(PASSWORD);
                }
            }
        }
        return dataSource;
    }

    public static Connection getConnection() throws SQLException {
    
    
        return getDataSource().getConnection();
    }

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

2.2 创建实体类

使用实体类来表示数据库中的一条记录,根据数据库创建的表,我们需要创建 Blog 类和 User 类
在这里插入图片描述

这样写方便我们观察:
在这里插入图片描述

Blog

package model;

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

//每一个 Blog 对象 对应到 blog 表中的一条记录,所以我们要对照着表结构来设置
public class Blog {
    
    
    private int blogId;
    private String title;
    private String content;
    private int userId;
    private Timestamp postTime;

    public int getBlogId() {
    
    
        return blogId;
    }

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

    public String getTitle() {
    
    
        return title;
    }

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

    public String getContent() {
    
    
        return content;
    }

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

    public int getUserId() {
    
    
        return userId;
    }

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

    //    public Timestamp getPostTime() {
    
    
//        return postTime;
//    }

    // 把这里的 getter 方法给改了, 不是返回一个时间戳对象, 而是返回一个 String (格式化好的时间)
    public String getPostTime() {
    
    
        // 使用 SimpleDateFormat 来完成时间戳到格式化日期时间的转换.
        // 这个转换过程, 需要在构造方法中指定要转换的格式, 然后调用 format 来进行转换
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return simpleDateFormat.format(postTime);
    }


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

User

package model;

//每个 User对象,表示 user 表中的一条记录
public class User {
    
    
//先初始化,后面判断是否是登录状态
    private int userId = 0;
    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;
    }
}

2.3 封装针对数据的增删改查

我们把提供了增删改查这样的类,称为 DAO

BlogDao

package model;

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

//这个类用于去封装博客表的基本操作
public class BlogDao {
    
    
    // 一. 往博客列表里插入一个博客.
    public void insert(Blog blog){
    
    
        //JDBC 的基本代码
        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
            statement.executeUpdate();
        } catch (SQLException throwables) {
    
    
            throwables.printStackTrace();
        }finally {
    
    
            // 4. 关闭资源
            DBUtil.close(connection,statement,null);
        }
    }

    //二. 能够获取到博客表中的所有博客的信息(用于博客列表页,注意此处的获取不一定就是 里面的所有内容)
    public List<Blog> selectAll(){
    
    
        List<Blog> blogs = new ArrayList<>();
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
    
    
            connection = DBUtil.getConnection();
            //我们想要让最新的排在博客列表最上面
            String sql = "select * from blog order by postTime desc";
            statement = connection.prepareStatement(sql);
            resultSet = statement.executeQuery();
            while(resultSet.next()){
    
    
                Blog blog = new Blog();
                blog.setBlogId(resultSet.getInt("blogId"));
                blog.setTitle(resultSet.getString("title"));
                //这里我们要针对长的内容进行截取
                String content = resultSet.getString("content");
                //自己设定长度
                if(content.length() > 50){
    
    
                    content = content.substring(0,50)+"...";
                }
                blog.setContent(content);
                blog.setUserId(resultSet.getShort("userId"));
                blog.setPostTime(resultSet.getTimestamp("postTime"));
                blogs.add(blog);
            }
        } catch (SQLException throwables) {
    
    
            throwables.printStackTrace();
        }finally {
    
    
            DBUtil.close(connection,statement,resultSet);
        }
        return blogs;
    }

    //三. 能够根据博客 id 获取到指定的博客内容(用于在博客详情页)
    public Blog selectOne(int blogId){
    
    
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
    
    
            connection = DBUtil.getConnection();
            String sql = "select * from blog where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,blogId);
            resultSet = statement.executeQuery();
            // 这里根据主键来查询的,查到的要么是 0,要么是 1..
            if(resultSet.next()){
    
    
                Blog blog = new Blog();
                blog.setBlogId(resultSet.getInt("blogId"));
                blog.setTitle(resultSet.getString("title"));
                blog.setContent(resultSet.getString("content"));
                blog.setUserId(resultSet.getShort("userId"));
                blog.setPostTime(resultSet.getTimestamp("postTime"));
                return blog;
            }
        } catch (SQLException throwables) {
    
    
            throwables.printStackTrace();
        }finally {
    
    
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }

    //四. 从博客列表中,根据博客 id 删除博客
    public void delete(int blogId){
    
    
        Connection connection = null;
        PreparedStatement statement = null;
        try {
    
    
            connection = DBUtil.getConnection();
            String sql = "delete from blog where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,blogId);
            statement.executeUpdate();
        } catch (SQLException throwables) {
    
    
            throwables.printStackTrace();
        }finally {
    
    
            DBUtil.close(connection,statement,null);
        }
    }


    //五. 修改
    public void update(Blog blog) {
    
    
        Connection connection = null;
        PreparedStatement statement = null;
        try {
    
    
            // 1. 建立连接
            connection = DBUtil.getConnection();
            // 2. 拼装 SQL 语句
            String sql = "update blog set content = ? ,title = ? where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setString(1, blog.getContent());
            statement.setString(2, blog.getTitle());

            statement.setInt(3, blog.getBlogId());
            // 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);
        }
    }

    //6. 计算个人文章的总数
    public static Integer selectTotal(int userId){
    
    
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
    
    
            connection = DBUtil.getConnection();
            String sql = "select count(userId) from blog where userId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,userId);
            resultSet = statement.executeQuery();
            if(resultSet.next()){
    
    

            }
            return resultSet.getInt(1);

        } catch (SQLException throwables) {
    
    
            throwables.printStackTrace();
        }finally {
    
    
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }

    public static void main(String[] args) {
    
    

        Integer ret1 = selectTotal(2);
        System.out.println(ret1);
        BlogDao blogDao = new BlogDao();
        Blog blog = new Blog();
        blog.setUserId(1);
        blog.setBlogId(1);
        blog.setTitle("hahah");
        blog.setContent("fff");
        blogDao.update(blog);
    }
}


UserDao

package model;

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

//提供了针对用户表的基本操作
public class UserDao {
    
    

    public void insert(User user){
    
    
        Connection connection = null;
        PreparedStatement statement = null;
        try {
    
    
            connection = DBUtil.getConnection();
            String sql = "insert into user values(null,?,?)";
            statement = connection.prepareStatement(sql);
            statement.setString(1, user.getUsername());
            statement.setString(2, user.getPassword());
            statement.executeUpdate();
        } catch (SQLException throwables) {
    
    
            throwables.printStackTrace();
        }finally {
    
    
            DBUtil.close(connection,statement,null);
        }
    }

    //主要实现
    // 1. 根据用户名来查找用户信息
      //这个会在登录逻辑中使用
    public User selectByName(String username) {
    
    
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
    
    
            connection = DBUtil.getConnection();
            String sql = "select * from user where username = ?";
            statement = connection.prepareStatement(sql);
            statement.setString(1, username);
            resultSet = statement.executeQuery();
            // 此处 username 使用 unique 约束, 要么能查到一个, 要么一个都查不到.
            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 throwables) {
    
    
            throwables.printStackTrace();
        } finally {
    
    
            DBUtil.close(connection, statement, resultSet);
        }
        return null;
    }

    // 2. 根据用户 id 来找用户信息.
    //    博客详情页, 就可以根据用户 id 来查询作者的名字, 把作者名字显示出来.
    public User selectById(int userId) {
    
    
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
    
    
            connection = DBUtil.getConnection();
            String sql = "select * from user where userId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1, userId);
            resultSet = statement.executeQuery();
            // 此处 username 使用 unique 约束, 要么能查到一个, 要么一个都查不到.
            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 throwables) {
    
    
            throwables.printStackTrace();
        } finally {
    
    
            DBUtil.close(connection, statement, resultSet);
        }
        return null;
    }
}

到这里 数据库操作都给准备好了
下面就是实现服务器的代码了

三. 约定前后端交互接口

我们先把之前写的前端博客系统给拷贝到 webapp 里面
在这里插入图片描述

博客系统:链接

1. 实现博客列表

1.1约定前后端交互接口

[请求]
GET /blog
[响应] [
   {
        blogId: 1,
        title: "第一篇博客",
        content: "博客正文",
        userId: 1,
        postTime: "2022-05-27 12:00:00"
   },
   {
        blogId: 2,
        title: "第二篇博客",
        content: "博客正文",
        userId: 1,
        postTime: "2022-05-27 12:10:00"
   },
    ...
]

我们约定, 浏览器给服务器发送一个 GET /blog 这样的 HTTP 请求, 服务器给浏览器返回了一个 JSON 格式的数据.

1.2 实现服务器代码

package controller;

import model.Blog;
import model.BlogDao;
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;

//通过这个类,来处理 /blog 路径对应的请求
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    
    
    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.selectAll();
        //把blogs 对象转成 JSON 格式
        String respJson = objectMapper.writeValueAsString(blogs);
        resp.setContentType("application/json;charset=utf8");
        resp.getWriter().write(respJson);
    }
}





根据我们之前插入的数据,写完可以用 Postman验证一下
在这里插入图片描述

1.3 编写客户端代码

<!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>
<!-- 这是导航栏 -->
<div class="nav">
    <img src="image/head.jpg" alt="">
    <span>我的博客系统</span>
    <!-- 空白元素, 用来占位置 -->
    <div class="spacer"></div>
    <a href="blog_list.html">主页</a>
    <a href="blog_edit.html">写博客</a>
    <a href="logout">注销</a>
</div>
<!-- 这里的 .container 作为页面的版心 -->
<div class="container">
    <!-- 左侧个人信息 -->
    <div class="left">
        <!-- 表示整个用户信息区域. -->
        <div class="card">
            <img src="image/gamegirl.jpg" alt="">
            <h3></h3>
            <a href="#">github 地址</a>
            <div class="counter">
                <span>文章</span>
                <span>分类</span>
            </div>
            <div class="counter">
                <span>999</span>
                <span>999</span>
            </div>
        </div>
    </div>
    <!-- 右侧内容详情 -->
    <div class="right">
        <!-- .blog 就对应一个博客 -->
        <!-- <div class="blog">
            <div class="title">
                我的第一篇博客
            </div>
            <div class="date">
                2022-05-05 20:52:00
            </div>
            <div class="desc">
                从今天起, 我要认真敲代码. Lorem ipsum dolor sit amet consectetur adipisicing elit. Nulla alias tenetur ut velit ex voluptatibus consequatur quam exercitationem, assumenda ea blanditiis repudiandae? Repellendus tenetur nostrum asperiores molestias doloremque cupiditate maiores.
            </div>
            <a href="#">查看全文 &gt;&gt; </a>
        </div> -->
    </div>
</div>
</div>

<script src="js/jquery.min.js"></script>
<script>
    // 在页面加载的时候, 通过 ajax 给服务器发送数据, 获取到博客列表信息, 并且显示在界面上.
    function getBlogList() {
    
    
        $.ajax({
    
    
            type: 'get',
            url: 'blog',
            success: function(body) {
    
    
                // 获取到的 body 就是一个 js 对象数组, 每个元素就是一个 js 对象, 根据这个对象构造 div
                // 1. 先把 .right 里原有的内容给清空
                let rightDiv = document.querySelector('.right');
                rightDiv.innerHTML = '';
                // 2. 遍历 body, 构造出一个个的 blogDiv
                for (let blog of body) {
    
    
                    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.innerHTML = '查看全文 &gt;&gt;';
                    // 此处希望点击之后能够跳转到 博客详情页 !!
                    // 这个跳转过程需要告知服务器要访问的是哪个博客的详情页.
                    a.href = 'blog_detail.html?blogId=' + blog.blogId;
                    blogDiv.appendChild(a);

                    // 把 blogDiv 挂到 dom 树上!
                    rightDiv.appendChild(blogDiv);
                }
            },
            error: function() {
    
    
                alert("获取博客列表失败!");
            }
        });
    }

    getBlogList();
</script>
<!--&lt;!&ndash; 在这里引入上述的 js 文件, 就可以执行到里面的代码, 也就进行了登录状态的监测了 &ndash;&gt;-->
<!--<script src="js/common.js"></script>-->
<!--<script>-->
<!--    // 针对博客列表页, 调用的时候传入参数-->
<!--    getUserInfo('blog_list.html');-->
<!--</script>-->
</body>
</html>


效果:
在这里插入图片描述

2. 实现博客详情

目前点击博客列表页的 “查看全文” , 能进入博客详情页, 但是这个博客详情页是写死的内容. 我们期望能够根据当前的 博客 id 从服务器动态获取博客内容.

2.1 约定前后端交互接口

[请求]
GET /blog?blogId=1 

[响应] {
HTTP/1.1 200OK
Content-Type:application/json;
{
    blogId: 1,
    title: "第一篇博客",
    content: "博客正文",
    userId: 1,
    postTime: "2022-05-27 12:00:00"
},

相比于博客列表页, 博客详情页的请求中多了一个 blogId 参数, 响应中只获取到一个博客的内容.

2.1 实现服务器代码

获取详情页跟获取列表基本相同,所以我们只需要改一下代码判断是否带参数 blogId来返回列表还是详情页

package controller;

import model.Blog;
import model.BlogDao;
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;

//通过这个类,来处理 /blog 路径对应的请求
@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();
        // 先尝试获取到 req 中的 blogId 参数. 如果该参数存在, 说明是要请求博客详情
        // 如果该参数不存在, 说明是要请求博客的列表.
        String param = req.getParameter("blogId");
        if (param == null) {
    
    
            // 不存在参数, 获取博客列表
            List<Blog> blogs = blogDao.selectAll();
            // 把 blogs 对象转成 JSON 格式.
            String respJson = objectMapper.writeValueAsString(blogs);
            resp.getWriter().write(respJson);
        } else {
    
    
            // 存在参数, 获取博客详情
            int blogId = Integer.parseInt(param);
            Blog blog = blogDao.selectOne(blogId);
            String respJson = objectMapper.writeValueAsString(blog);
            resp.getWriter().write(respJson);
        }
    }
}


当然,我们也可以分开写好理解一些:

package controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import model.Blog;
import model.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;

@WebServlet("/detail")
public class BlogDetailServlert extends HttpServlet {
    
    
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        resp.setContentType("application/json;charset=utf8");
        String blogId = req.getParameter("blogId");
        BlogDao blogDao = new BlogDao();
        Blog blog = blogDao.selectOne(Integer.parseInt(blogId));
        resp.getWriter().write(objectMapper.writeValueAsString(blog));
    }
}

通过Postman验证一下
在这里插入图片描述

实现客户端代码

在这里插入图片描述

<!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="js/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="image/head.jpg" alt="">
    <span>我的博客系统</span>
    <!-- 空白元素, 用来占位置 -->
    <div class="spacer"></div>
    <a href="blog_list.html">主页</a>
    <a href="blog_edit.html">写博客</a>
    <a href="logout">注销</a>
</div>
<!-- 这里的 .container 作为页面的版心 -->
<div class="container">
    <!-- 左侧个人信息 -->
    <div class="left">
        <!-- 表示整个用户信息区域. -->
        <div class="card">
            <img src="image/gamegirl.jpg" alt="">
            <h3></h3>
            <a href="https://gitee.com/big-white-rice">github 地址</a>
            <div class="counter">
                <span>文章</span>
                <span>分类</span>
            </div>
            <div class="counter">
                <span>999</span>
                <span>999</span>
            </div>
        </div>
    </div>
    <!-- 右侧内容详情 -->
    <div class="right">
        <!-- 使用这个 div 来包裹整个博客的内容详情 -->
        <div class="blog-content">
            <!-- 博客标题 -->
            <h3></h3>
            <!-- 博客的时间 -->
            <div class="date"></div>
            <!-- 博客的正文内容 -->
            <div id="content" style="opacity: 80%">

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

<script>
    function getBlogDetail() {
      
      
        $.ajax({
      
      
            type: 'get',
            // location.search 拿到了形如 '?blogId=5' 这样的一段内容
            url: 'blog' + location.search,
            success: function(body) {
      
      
                // 根据 body 中的内容来构造页面
                // 1. 构造博客标题
                let h3 = document.querySelector(".blog-content>h3");
                h3.innerHTML = body.title;
                // 2. 构造博客发布时间
                let dateDiv = document.querySelector('.date');
                dateDiv.innerHTML = body.postTime;
                // 3. 构造博客正文
                // 如果直接把 content 设为 innerHTML, 此时展示在界面上的内容, 是原始的 markdown 字符串
                // 咱们需要的是渲染后的, 带有格式的效果
                // let content = document.querySelector('#content');
                // content.innerHTML = body.content;

                // 第一个参数对应 id=content 的 html 标签. 渲染后得到的 html 片段就会被放到这个 标签下.
                editormd.markdownToHTML('content', {
      
      
                    markdown: body.content
                });
            }
        });
    }

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

在这里插入图片描述

注意:当我们在进行程序验证的时候,要牢记一件事情,浏览器的缓存可能会影响到结果,你可能修改了之后再去同一个浏览器访问的时候,他并没有发生改变,因为浏览器可能把你上次访问的页面给保存到本地了,下次在尝试访问的时候访问的就是本地的内容,所以这个时候我们要按下: Ctrl + F5强制刷新一下…

3. 实现登录(注册)

1: 登陆页面提供一个 form 表单, 通过 form 的方式把用户名密码提交给服务器.
2: 服务器端验证用户名密码是否正确.
3: 如果密码正确, 则在服务器端创建 Session , 并把 sessionId 通过 Cookie 返回给浏览器.

3.1 约定前后端交互接口

[请求]
POST /login
Content-Type: application/x-www-form-urlencoded
username=test&password=123
[响应]
HTTP/1.1 302 
Location: blog_list.html

当登录成功以后,就会自动跳转到主页(博客列表页(blog_list.html))

3.2 实现服务器代码

package controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import model.User;
import model.UserDao;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

 
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    
    

    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        req.setCharacterEncoding("utf8");
        resp.setCharacterEncoding("utf8");
        //1. 获取到请求中的参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        System.out.println("username="+username+",password="+password);
        if(username == null || "".equals(username) || password == null || "".equals(password)){
    
    
            //请求的内容缺失,肯定是登录失败!!
          resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前的用户名或密码为空!!");
            return;
        }
        //2. 和数据库中的内容进行比较
        UserDao userDao = new UserDao();
        User user = userDao.selectByName(username);
        if(user == null || !user.getPassword().equals(password)){
    
    
            //请求的内容缺失,肯定是登录失败!!
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前的用户名或密码错误!!");
            return;
        }
        //3. 如果比较通过,就创建会话
        HttpSession session = req.getSession(true);
        //把刚才的用户信息,存储到会话中
        session.setAttribute("user",user);

        //4. 返回一个重定向报文,跳转到博客列表
        resp.sendRedirect("blog_list.html");
    }
}

注意:
在这里插入图片描述

3.3 实现客户端代码

<!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_login.css">
</head>
<body>
<!-- 这是导航栏 -->
<div class="nav">
    <img src="image/head.jpg" alt="">
    <span>我的博客系统</span>
    <!-- 空白元素, 用来占位置 -->
    <div class="spacer"></div>
    <a href="blog_list.html">主页</a>
    <a href="blog_edit.html">写博客</a>
    <!-- 注销按钮没必要在登录页面展示 -->
    <!-- <a href="#">注销</a> -->
</div>
<div class="login-container">
    <form action="login" method="post">
        <div class="login-dialog">
            <h3>登录</h3>
            <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">
<!--                 <button>提交</button> -->
                <input type="submit" id="submit" value="提交">
            </div>
        </div>
    </form>
</div>
</body>
</html>

先来一个错的:
在这里插入图片描述

在这里插入图片描述

是对的话(我们数据库里有这个用户)他就会直接跳回列表页
在这里插入图片描述

注册:

package controller;

import model.User;
import model.UserDao;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/register")
public class RegisterServlet extends HttpServlet {
    
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        req.setCharacterEncoding("utf8");
        resp.setCharacterEncoding("utf8");
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if(username == null || "".equals(username) || password == null || "".equals(password)){
    
    
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("用户名或密码不能为空!!!");
            return;
        }
        UserDao userDao = new UserDao();
        User user1 = userDao.selectByName(username);
        if(user1 != null){
    
    
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("用户名已被占用,请更改用户名!!!");
            return;
        }
        User user = new User();
        user.setUsername(username);
        user.setPassword(password);
        userDao.insert(user);
        HttpSession session = req.getSession(true);
        session.setAttribute("user",user);
        resp.sendRedirect("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_register.css">
</head>
<body>
    <div class="nav">
        <img src="image/wallhaven-72keg9.jpg" alt="">
        <span>我的博客系统</span>
        <div class="spacer"></div>
        <a href="blog_list.html">主页</a>
        <a href="blog_edit.html">写博客</a>
        <a href="blog_login.html">登录</a>
    </div>
    <div class="login-container">
        <form action="register" method="post">
            <div class="login-dialog">
                <h3>注册</h3>
                <div class="row">
                    <span>用户名</span>
                    <input type="text" id="username" name="username" placeholder="输入用户名">
                </div>
                <div class="row">
                    <span>密码</span>
                    <input type="password" id="password" name="password" placeholder="输入密码">
                </div>
                <div class="row">
                    <!--                 <button>提交</button> -->
                    <input type="submit" id="submit" value="注册">
                </div>
            </div>
        </form>
    </div>
</body>
</html>

实现强制要求登录的
当用户访问 博客列表页 和 博客详情页 时, 如果用户当前尚未登陆, 就自动跳转到登陆页面.

这个可以在博客列表页/详情页,加载的时候,通过 ajax 访问一下服务器,获取到当前的登录状态,看看能不能获取到,如果获取到了,就说明当前雀氏是已经登陆了,此时就可以留在这个页面,如果没有获取到,说明是未登录的,就会跳转到登录页面让你登录…

约定前后端交互接口

[请求]
GET/login

[响应]
HTTP/1.1 200OK
Content-Type:application/json

{
    
    
    userId: 1,
    username: 'zhangsan',
}

登陆了,就直接返回当前登录的用户信息
如果未登录,则直接返回一个userId 为 0 的对象

在这里插入图片描述

添加后的LoginServlet

package controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import model.User;
import model.UserDao;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    
    

    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        req.setCharacterEncoding("utf8");
        resp.setCharacterEncoding("utf8");
        //1. 获取到请求中的参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        System.out.println("username="+username+",password="+password);
        if(username == null || "".equals(username) || password == null || "".equals(password)){
    
    
            //请求的内容缺失,肯定是登录失败!!
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前的用户名或密码为空!!");
            return;
        }
        //2. 和数据库中的内容进行比较
        UserDao userDao = new UserDao();
        User user = userDao.selectByName(username);
        if(user == null || !user.getPassword().equals(password)){
    
    
            //请求的内容缺失,肯定是登录失败!!
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前的用户名或密码错误!!");
            return;
        }
        //3. 如果比较通过,就创建会话
        HttpSession session = req.getSession(true);
        //把刚才的用户信息,存储到会话中
        session.setAttribute("user",user);

        //4. 返回一个重定向报文,跳转到博客列表
        resp.sendRedirect("blog_list.html");
    }

    //这个方法用来让前端检测当前的登录状态
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        resp.setContentType("application/json;charset=utf8");
        HttpSession session = req.getSession(false);
        if(session == null){
    
    
            //检测下这个会话是否存在,不存在说明未登录
            User user = new User();
            resp.getWriter().write(objectMapper.writeValueAsString(user));
            return;

        }
        User user = (User) session.getAttribute("user");
        if(user == null){
    
    
            //虽然有会话,但是会话里没有 user 对象,也视为未登录
            user = new User();
            resp.getWriter().write(objectMapper.writeValueAsString(user));
            return;
        }
        //已经登录的状态!!!
        //注意,此处不要把密码给返回到前端
        user.setPassword("");
        resp.getWriter().write(objectMapper.writeValueAsString(user));
    }

}

添加后的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>
<!-- 这是导航栏 -->
<div class="nav">
    <img src="image/head.jpg" alt="">
    <span>我的博客系统</span>
    <!-- 空白元素, 用来占位置 -->
    <div class="spacer"></div>
    <a href="blog_list.html">主页</a>
    <a href="blog_edit.html">写博客</a>
    <a href="logout">注销</a>
</div>
<!-- 这里的 .container 作为页面的版心 -->
<div class="container">
    <!-- 左侧个人信息 -->
    <div class="left">
        <!-- 表示整个用户信息区域. -->
        <div class="card">
            <img src="image/gamegirl.jpg" alt="">
            <h3></h3>
            <a href="#">github 地址</a>
            <div class="counter">
                <span>文章</span>
                <span>分类</span>
            </div>
            <div class="counter">
                <span>999</span>
                <span>999</span>
            </div>
        </div>
    </div>
    <!-- 右侧内容详情 -->
    <div class="right">
        <!-- .blog 就对应一个博客 -->
        <!-- <div class="blog">
            <div class="title">
                我的第一篇博客
            </div>
            <div class="date">
                2022-05-05 20:52:00
            </div>
            <div class="desc">
                从今天起, 我要认真敲代码. Lorem ipsum dolor sit amet consectetur adipisicing elit. Nulla alias tenetur ut velit ex voluptatibus consequatur quam exercitationem, assumenda ea blanditiis repudiandae? Repellendus tenetur nostrum asperiores molestias doloremque cupiditate maiores.
            </div>
            <a href="#">查看全文 &gt;&gt; </a>
        </div> -->
    </div>
</div>
</div>

<script src="js/jquery.min.js"></script>
<script>
    // 在页面加载的时候, 通过 ajax 给服务器发送数据, 获取到博客列表信息, 并且显示在界面上.
    function getBlogList() {
    
    
        $.ajax({
    
    
            type: 'get',
            url: 'blog',
            success: function(body) {
    
    
                // 获取到的 body 就是一个 js 对象数组, 每个元素就是一个 js 对象, 根据这个对象构造 div
                // 1. 先把 .right 里原有的内容给清空
                let rightDiv = document.querySelector('.right');
                rightDiv.innerHTML = '';
                // 2. 遍历 body, 构造出一个个的 blogDiv
                for (let blog of body) {
    
    
                    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.innerHTML = '查看全文 &gt;&gt;';
                    // 此处希望点击之后能够跳转到 博客详情页 !!
                    // 这个跳转过程需要告知服务器要访问的是哪个博客的详情页.
                    a.href = 'blog_detail.html?blogId=' + blog.blogId;
                    blogDiv.appendChild(a);

                    // 把 blogDiv 挂到 dom 树上!
                    rightDiv.appendChild(blogDiv);
                }
            },
            error: function() {
    
    
                alert("获取博客列表失败!");
            }
        });
    }

    getBlogList();

    // 加上一个逻辑, 通过 GET /login 这个接口来获取下当前的登录状态~
    function getUserInfo(pageName) {
    
    
        $.ajax({
    
    
            type: 'get',
            url: 'login',
            success: function(body) {
    
    
                // 判定此处的 body 是不是一个有效的 user 对象(userId 是否非 0)
                if (body.userId && body.userId > 0) {
    
    
                    // 登录成功!
                    // 不做处理!
                    console.log("当前用户登录成功! 用户名: " + body.username);

                    // 根据当前用户登录的情况, 把当前用户名设置到界面上
                    if (pageName == 'blog_list.html') {
    
    
                        changeUserName(body.username);
                    }
                } else {
    
    
                    // 登录失败!
                    // 让前端页面, 跳转到 login.html
                    alert("当前您尚未登录! 请登录后再访问博客列表!");
                    location.assign('blog_login.html');
                }
            },
            error: function() {
    
    
                alert("当前您尚未登录! 请登录后再访问博客列表!");
                location.assign('blog_login.html');
            }
        });
    }

    // 针对博客列表页, 调用的时候传入参数
    getUserInfo('blog_list.html');
</script>
</body>
</html>

这样我们未登录去访问列表页的时候就会出现让你登录了才行:
在这里插入图片描述
详情页也可以有这样的功能,就可以把这段逻辑放入公共样式,这样的可以引用了
在这里插入图片描述

4. 实现显示用户信息

我们期望这个信息可以随着用户登陆而发生改变.

如果当前页面是博客列表页, 则显示当前登陆用户的信息.
如果当前页面是博客详情页, 则显示该博客的作者用户信息.

4.1 约定前后端交互接口

在博客列表页,获取登录的用户的用户信息

[请求]
GET /user
[响应] {
 userId: 1,
 username: test
}

在博客详情页, 获取当前文章作者的用户信息

[请求]
GET /user?blogId=1
[响应] {
 userId: 1,
 username: test
}

注意:

针对博客详情页的时候,用户名并不能正确的显示谁是谁的,只能显示当前登录的名字,因为我们用的公共样式,所以,此处我们要进行处理一下,比如我访问的是 lisi的文章,用户名应该显示 lisi,这个时候我们就需要提供一个新的接口,这个接口可以让客户端指定 blogId ,获取指定 blogId 的作者信息!!
在这里插入图片描述

4.2 实现服务器代码

这里主要针对详情页的代码

package controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import model.Blog;
import model.BlogDao;
import model.User;
import model.UserDao;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/authorInfo")
public class AuthorServlet extends HttpServlet {
    
    
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        resp.setContentType("application/json;charset=utf8");
        //通过这个方法,来获取到指定的博客的作者信息
        String param = req.getParameter("blogId");
        if(param == null || "".equals(param)){
    
    
            //参数缺少了
            resp.getWriter().write("{\"ok\": false,\"reason\":\"参数缺失!\" }");
            return;
        }

        //根据当前blogId 在数据库中进行查找,找到对应的 Blog 对象,再进一步的根据 blog 对象,找到作者信息.
        BlogDao blogDao = new BlogDao();
        Blog blog = blogDao.selectOne(Integer.parseInt(param));
        if(blog == null){
    
    
            resp.getWriter().write("{\"ok\": false,\"reason\":\"要查询的博客不存在!!\" }");
            return;
        }

        // 根据 blog 对象,查询到用户对象
        UserDao userDao = new UserDao();
        User author = userDao.selectById(blog.getUserId());
        if(author == null){
    
    
            resp.getWriter().write("{\"ok\": false,\"reason\":\"要查询的用户不存在!!\" }");
            return;
        }


        //把 author 返回到浏览器这边
        //注意要把密码干掉
        author.setPassword("");
        resp.getWriter().write(objectMapper.writeValueAsString(author));
    }
}

这个就是在详情页的时候,要显示作者信息,让客户端传一个博客 id 过去,服务器在里面找,在返回数据

4.3 实现客户端代码

修改上述blog_list.html和blog_detail.html代码 实现随登录过后返回的是用户名
在这里插入图片描述

common.js
// 这个文件里放一些页面公共的代码

// 加上一个逻辑, 通过 GET /login 这个接口来获取下当前的登录状态~
function getUserInfo(pageName) {
    
    
    $.ajax({
    
    
        type: 'get',
        url: 'login',
        success: function(body) {
    
    
            // 判定此处的 body 是不是一个有效的 user 对象(userId 是否非 0)
            if (body.userId && body.userId > 0) {
    
    
                // 登录成功!
                // 不做处理!
                console.log("当前用户登录成功! 用户名: " + body.username);

                // 根据当前用户登录的情况, 把当前用户名设置到界面上
                if (pageName == 'blog_list.html') {
    
    
                    changeUserName(body.username);
                }
            } else {
    
    
                // 登录失败!
                // 让前端页面, 跳转到 login.html
                alert("当前您尚未登录! 请登录后再访问博客列表!");
                location.assign('blog_login.html');
            }
        },
        error: function() {
    
    
            alert("当前您尚未登录! 请登录后再访问博客列表!");
            location.assign('blog_login.html');
        }
    });
}

function changeUserName(username) {
    
    
    let h3 = document.querySelector('.card>h3');
    h3.innerHTML = username;
}

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>
<!-- 这是导航栏 -->
<div class="nav">
    <img src="image/head.jpg" alt="">
    <span>我的博客系统</span>
    <!-- 空白元素, 用来占位置 -->
    <div class="spacer"></div>
    <a href="blog_list.html">主页</a>
    <a href="blog_edit.html">写博客</a>
    <a href="logout">注销</a>
</div>
<!-- 这里的 .container 作为页面的版心 -->
<div class="container">
    <!-- 左侧个人信息 -->
    <div class="left">
        <!-- 表示整个用户信息区域. -->
        <div class="card">
            <img src="image/gamegirl.jpg" alt="">
            <h3></h3>
            <a href="#">github 地址</a>
            <div class="counter">
                <span>文章</span>
                <span>分类</span>
            </div>
            <div class="counter">
                <span>999</span>
                <span>999</span>
            </div>
        </div>
    </div>
    <!-- 右侧内容详情 -->
    <div class="right">
        <!-- .blog 就对应一个博客 -->
        <!-- <div class="blog">
            <div class="title">
                我的第一篇博客
            </div>
            <div class="date">
                2022-05-05 20:52:00
            </div>
            <div class="desc">
                从今天起, 我要认真敲代码. Lorem ipsum dolor sit amet consectetur adipisicing elit. Nulla alias tenetur ut velit ex voluptatibus consequatur quam exercitationem, assumenda ea blanditiis repudiandae? Repellendus tenetur nostrum asperiores molestias doloremque cupiditate maiores.
            </div>
            <a href="#">查看全文 &gt;&gt; </a>
        </div> -->
    </div>
</div>
</div>

<script src="js/jquery.min.js"></script>
<script>
    // 在页面加载的时候, 通过 ajax 给服务器发送数据, 获取到博客列表信息, 并且显示在界面上.
    function getBlogList() {
      
      
        $.ajax({
      
      
            type: 'get',
            url: 'blog',
            success: function(body) {
      
      
                // 获取到的 body 就是一个 js 对象数组, 每个元素就是一个 js 对象, 根据这个对象构造 div
                // 1. 先把 .right 里原有的内容给清空
                let rightDiv = document.querySelector('.right');
                rightDiv.innerHTML = '';
                // 2. 遍历 body, 构造出一个个的 blogDiv
                for (let blog of body) {
      
      
                    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.innerHTML = '查看全文 &gt;&gt;';
                    // 此处希望点击之后能够跳转到 博客详情页 !!
                    // 这个跳转过程需要告知服务器要访问的是哪个博客的详情页.
                    a.href = 'blog_detail.html?blogId=' + blog.blogId;
                    blogDiv.appendChild(a);

                    // 把 blogDiv 挂到 dom 树上!
                    rightDiv.appendChild(blogDiv);
                }
            },
            error: function() {
      
      
                alert("获取博客列表失败!");
            }
        });
    }

    getBlogList();
</script>
    <!-- 在这里引入上述的 js 文件, 就可以执行到里面的代码, 也就进行了登录状态的监测了 -->
    <script src="js/common.js"></script>
<script>
    // 针对博客列表页, 调用的时候传入参数
    getUserInfo('blog_list.html');
</script>
</body>
</html>

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">

    <!-- 引入 editor.md 的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    <script src="js/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="image/head.jpg" alt="">
    <span>我的博客系统</span>
    <!-- 空白元素, 用来占位置 -->
    <div class="spacer"></div>
    <a href="blog_list.html">主页</a>
    <a href="blog_edit.html">写博客</a>
    <a href="logout">注销</a>
</div>
<!-- 这里的 .container 作为页面的版心 -->
<div class="container">
    <!-- 左侧个人信息 -->
    <div class="left">
        <!-- 表示整个用户信息区域. -->
        <div class="card">
            <img src="image/gamegirl.jpg" alt="">
            <h3></h3>
            <a href="https://gitee.com/big-white-rice">github 地址</a>
            <div class="counter">
                <span>文章</span>
                <span>分类</span>
            </div>
            <div class="counter">
                <span>999</span>
                <span>999</span>
            </div>
        </div>
    </div>
    <!-- 右侧内容详情 -->
    <div class="right">
        <!-- 使用这个 div 来包裹整个博客的内容详情 -->
        <div class="blog-content">
            <!-- 博客标题 -->
            <h3></h3>
            <!-- 博客的时间 -->
            <div class="date"></div>
            <!-- 博客的正文内容 -->
            <div id="content" style="opacity: 80%">

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

<script>
    function getBlogDetail() {
      
      
        $.ajax({
      
      
            type: 'get',
            // location.search 拿到了形如 '?blogId=5' 这样的一段内容
            url: 'blog' + location.search,
            success: function(body) {
      
      
                // 根据 body 中的内容来构造页面
                // 1. 构造博客标题
                let h3 = document.querySelector(".blog-content>h3");
                h3.innerHTML = body.title;
                // 2. 构造博客发布时间
                let dateDiv = document.querySelector('.date');
                dateDiv.innerHTML = body.postTime;
                // 3. 构造博客正文
                // 如果直接把 content 设为 innerHTML, 此时展示在界面上的内容, 是原始的 markdown 字符串
                // 咱们需要的是渲染后的, 带有格式的效果
                // let content = document.querySelector('#content');
                // content.innerHTML = body.content;

                // 第一个参数对应 id=content 的 html 标签. 渲染后得到的 html 片段就会被放到这个 标签下.
                editormd.markdownToHTML('content', {
      
      
                    markdown: body.content
                });
            }
        });
    }

    getBlogDetail();


    // 加上一个逻辑, 通过 GET /login 这个接口来获取下当前的登录状态~
    function getUserInfo(pageName) {
      
      
        $.ajax({
      
      
            type: 'get',
            url: 'login',
            success: function(body) {
      
      
                // 判定此处的 body 是不是一个有效的 user 对象(userId 是否非 0)
                if (body.userId && body.userId > 0) {
      
      
                    // 登录成功!
                    // 不做处理!
                    console.log("当前用户登录成功! 用户名: " + body.username);

                    // 在 getUserInfo 的回调函数中, 来调用获取作者信息
                    getAuthorInfo(body);
                } else {
      
      
                    // 登录失败!
                    // 让前端页面, 跳转到 login.html
                    alert("当前您尚未登录! 请登录后再访问博客列表!");
                    location.assign('blog_login.html');
                }
            },
            error: function() {
      
      
                alert("当前您尚未登录! 请登录后再访问博客列表!");
                location.assign('blog_login.html');
            }
        });
    }

    // 判定用户的登录状态
    getUserInfo("blog_detail.html");

    // 从服务器获取一下当前博客的作者信息, 并显示到界面上.
    // 参数 user 就是刚才从服务器拿到的当前登录用户的信息
    function getAuthorInfo(user) {
      
      
        $.ajax({
      
      
            type: 'get',
            url: 'authorInfo' + location.search,
            success: function(body) {
      
      
                // 此处的 body, 就是服务器返回的 User 对象, 是文章的作者信息
                if (body.username) {
      
      
                    // 如果响应中的 username 存在, 就把这个值设置到页面上.
                    changeUserName(body.username);

                    if (body.username == user.username) {
      
      
                        // 作者和登录的用户是一个人, 则显示 "删除按钮"
                        let navDiv = document.querySelector('.nav');
                        let a = document.createElement('a');
                        a.innerHTML = '删除';
                        // 期望点击删除, 构造一个形如 blogDelete?blogId=6 这样的请求
                        a.href = 'blogDelete' + location.search;
                        navDiv.appendChild(a);
                    }
                } else {
      
      
                    console.log("获取作者信息失败! " + body.reason);
                }
            }
        });
    }

    function changeUserName(username) {
      
      
        let h3 = document.querySelector('.card>h3');
        h3.innerHTML = username;
    }
</script>
</body>
</html>

在这里插入图片描述在这里插入图片描述

总结:
1:对于博客列表页,要显示登录用户信息,登录用户信息在检测用户是否登录的接口中,就已经拿到了,只需要把拿到的用户信息,显示到界面上即可(只需要动前端,后端不必修改)
2:对于博客详情页,要显示出文章作者的信息,需要提供一个新的 api ,让客户端传一个博客 id 过去,然后再服务器这里查询到用户信息,查到之后,返回给页面

页面和服务器之间的交互,不一定只有一次,大概率是会有多次的
我们这里写的博客列表页涉及到两次交互:
1:从服务器拿到博客列表数据
2:从服务器拿到当前的登录用户信息
博客详情页涉及到三次交互:
1:从服务器拿到博客的详细内容
2:从服务器拿到了当前的登录用户信息
3:从服务器拿到了当前文章的作者信息
我们以后可能还会遇到不止两次三次的,有些可能复杂的十几次也是正常的…

5. 实现注销登录

就是退出登录状态
在导航栏安排一个"注销按钮",当用户点击注销之后,就会在服务器上取消登录状态,并且跳回到登录页面,希望在点击之后,能够给服务器发送一个 HTTP 请求,从而触发注销动作(其实就是把会话中的信息给删除掉)

5.1 约定前后端接口

[请求]
GET /logout
[响应]
HTTP/1.1 302
Location: login.html

在这里插入图片描述

5.2 实现服务器代码

从 session 中删除掉保存的 User 对象.
响应重定向到 login.html 页面.

package controller;

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.getWriter().write("当前用户尚未登录! 无法注销");
            return;
        }
        //然后把这个用户的会话中的信息给删掉就行了!!
        session.removeAttribute("user");
        resp.sendRedirect("blog_login.html");
    }
}

5.3 实现客户端代码

只需要把注销按钮的href属性(blog_list.html,blog_detail.html,blog_edit.html)改了就行 ,注意不是 /logout
在这里插入图片描述

6. 实现发布博客

在博客编辑页中,当用户输入博客标题,和正文之后,点击发布,这个时候就会把博客数据提交到服务器,由服务器存储到数据中

6.1 约定前后端接口

[请求]
POST /blog
Content-Type: application/x-www-form-urlencoded
title=标题&content=正文...
[响应]
HTTP/1.1 302
Location: blog_list.html

6.2 实现服务器代码

在 BlogServlet 里面添加 一个 doPost 方法,来处理 post 请求
核心的操作就是读取请求中的标题和正文,构造 Blog 对象,并插入数据库

package controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import model.Blog;
import model.BlogDao;
import model.User;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;

// 通过这个类, 来处理 /blog 路径对应的请求
@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();
        // 先尝试获取到 req 中的 blogId 参数. 如果该参数存在, 说明是要请求博客详情
        // 如果该参数不存在, 说明是要请求博客的列表.
        String param = req.getParameter("blogId");
        if (param == null) {
    
    
            // 不存在参数, 获取博客列表
            List<Blog> blogs = blogDao.selectAll();
            // 把 blogs 对象转成 JSON 格式.
            String respJson = objectMapper.writeValueAsString(blogs);
            resp.getWriter().write(respJson);
        } else {
    
    
            // 存在参数, 获取博客详情
            int blogId = Integer.parseInt(param);
            Blog blog = blogDao.selectOne(blogId);
            String respJson = objectMapper.writeValueAsString(blog);
            resp.getWriter().write(respJson);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        HttpSession session = req.getSession(false);
        if (session == null) {
    
    
            // 当前用户未登录, 不能提交博客!
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前用户未登录, 不能提交博客!");
            return;
        }
        User user = (User) session.getAttribute("user");
        if (user == null) {
    
    
            // 当前用户未登录, 不能提交博客!
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前用户未登录, 不能提交博客!");
            return;
        }
        // 一定要先指定好请求按照哪种编码来解析
        req.setCharacterEncoding("utf8");
        // 先从请求中, 取出参数(博客的标题和正文)
        String title = req.getParameter("title");
        String content = req.getParameter("content");
        if (title == null || "".equals(title) || content == null || "".equals(content)) {
    
    
            // 直接告诉客户端, 请求参数不对
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("提交博客失败! 缺少必要的参数!");
            return;
        }
        // 构造 Blog 对象, 把当前的信息填进去, 并插入数据库中
        // 此处要给 Blog 设置的属性, 主要是 title, content, userId (作者信息)
        // postTime 和 blogId 都不需要手动指定, 都是插入数据库的时候自动生成的.
        Blog blog = new Blog();
        blog.setTitle(title);
        blog.setContent(content);
        // 作者 id 就是当前提交这个博客的用户的身份信息!!
        blog.setUserId(user.getUserId());
        BlogDao blogDao = new BlogDao();
        blogDao.insert(blog);
        // 重定向到, 博客列表页!
        resp.sendRedirect("blog_list.html");
    }
}

6.3 实现客户端代码

就是需要一个form表单,把这里的内容嵌套上
在这里插入图片描述

<!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="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="image/head.jpg" alt="">
        <span>我的博客系统</span>
        <!-- 空白元素, 用来占位置 -->
        <div class="spacer"></div>
        <a href="blog_list.html">主页</a>
        <a href="blog_edit.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" placeholder="在此处输入标题" name="title" id="title">
<!--                <button>发布文章</button>-->
                <input type="submit" value="发布文章" id="submit">
            </div>

            <!-- 放置 md 编辑器 -->
            <div id="editor">
                <!-- 为了进行 form 的提交,此处搞一个 textarea 多行编辑器,借助这个编辑框来实现表单的提交 -->
                <!-- 可以设置 editor.md,让编辑器把 markdown 内容也同步的保存到这个隐藏的 textarea 中,从而进行 form 提交 -->
                <textarea name="content" style="display: none"></textarea>
            </div>
        </form>

    </div>

    <script>
        //初始化编辑器
        let editor = editormd("editor",{
      
      
            //这里的尺寸必须在这里设置,设置样式会被 editormd 自动覆盖掉
            width: "100%",
            //设定编辑器高度
            height: "calc(100% - 50px)",
            //编辑器中的初始内容
            markdown: "# 在这里写下一篇博客",
            //指定 editor.md 依赖的插件路径
            path: "editor.md/lib/",
            // 此处要加上一个重要的选项,然后 editor.md 就会自动把用户在编辑器输入的内容同步保存到 隐藏的 textarea 中了!
            saveHTMLToTextarea: true,
        });
    </script>
<!--     <script src="js/common.js"></script>-->
</body>
</html>

在这里插入图片描述在这里插入图片描述

7. 实现删除博客

界面上的处理:进入用户详情页时, 如果当前登陆用户正是文章作者, 则在导航栏中显示 “删除” 按钮, 用户点击时则删除该文章. 不是就不显示删除按钮
服务器处理:用户点击删除按钮,触发一个HTTP 请求,HTTP 请求就会让服务器删除指定博客,服务器收到请求后,就会把这个博客从数据库里删除掉…
需要实现两件事:
判定当前博客详情页中是否要显示 “删除” 按钮
实现删除逻辑.
在这里插入图片描述

7.1 约定前后端交互接口

[请求]
GET /blogDelete?blogId=1
[响应] 
直接跳转到 博客列表页 即可

7.2 实现服务器代码

package controller;

import model.Blog;
import model.BlogDao;
import model.User;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/blogDelete")
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.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前尚未登录, 不能删除!");
            return;
        }
        User user = (User) session.getAttribute("user");
        if (user == null) {
    
    
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前尚未登录, 不能删除!");
            return;
        }

        // 2. 获取到参数中的 blogId
        String blogId = req.getParameter("blogId");
        if (blogId == null || "".equals(blogId)) {
    
    
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前 blogId 参数不对!");
            return;
        }

        // 3. 获取要删除的博客信息.
        BlogDao blogDao = new BlogDao();
        Blog blog = blogDao.selectOne(Integer.parseInt(blogId));
        if (blog == null) {
    
    
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前要删除的博客不存在!");
            return;
        }

        // 4. 再次校验, 当前的用户是否就是博客的作者
        if (user.getUserId() != blog.getUserId()) {
    
    
            // 这一点在前端这里其实也处理过~~ 但是此处还是再校验一次, 不是坏事!!!
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前登录的用户不是作者, 没有权限删除!");
            return;
        }

        // 5. 确认无误, 开始删除
        blogDao.delete(Integer.parseInt(blogId));

        // 6. 重定向到博客列表页
        resp.sendRedirect("blog_list.html");
    }
}

7.3 实现客户端代码

修改 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">

    <!-- 引入 editor.md 的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    <script src="js/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="image/head.jpg" alt="">
    <span>我的博客系统</span>
    <!-- 空白元素, 用来占位置 -->
    <div class="spacer"></div>
    <a href="blog_list.html">主页</a>
    <a href="blog_edit.html">写博客</a>
    <a href="logout">注销</a>
</div>
<!-- 这里的 .container 作为页面的版心 -->
<div class="container">
    <!-- 左侧个人信息 -->
    <div class="left">
        <!-- 表示整个用户信息区域. -->
        <div class="card">
            <img src="image/gamegirl.jpg" alt="">
            <h3></h3>
            <a href="https://gitee.com/big-white-rice">github 地址</a>
            <div class="counter">
                <span>文章</span>
                <span>分类</span>
            </div>
            <div class="counter">
                <span>999</span>
                <span>999</span>
            </div>
        </div>
    </div>
    <!-- 右侧内容详情 -->
    <div class="right">
        <!-- 使用这个 div 来包裹整个博客的内容详情 -->
        <div class="blog-content">
            <!-- 博客标题 -->
            <h3></h3>
            <!-- 博客的时间 -->
            <div class="date"></div>
            <!-- 博客的正文内容 -->
            <div id="content" style="opacity: 80%">

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

<script>
    function getBlogDetail() {
      
      
        $.ajax({
      
      
            type: 'get',
            // location.search 拿到了形如 '?blogId=5' 这样的一段内容
            url: 'blog' + location.search,
            success: function(body) {
      
      
                // 根据 body 中的内容来构造页面
                // 1. 构造博客标题
                let h3 = document.querySelector(".blog-content>h3");
                h3.innerHTML = body.title;
                // 2. 构造博客发布时间
                let dateDiv = document.querySelector('.date');
                dateDiv.innerHTML = body.postTime;
                // 3. 构造博客正文
                // 如果直接把 content 设为 innerHTML, 此时展示在界面上的内容, 是原始的 markdown 字符串
                // 咱们需要的是渲染后的, 带有格式的效果
                // let content = document.querySelector('#content');
                // content.innerHTML = body.content;

                // 第一个参数对应 id=content 的 html 标签. 渲染后得到的 html 片段就会被放到这个 标签下.
                editormd.markdownToHTML('content', {
      
      
                    markdown: body.content
                });
            }
        });
    }

    getBlogDetail();


    // 加上一个逻辑, 通过 GET /login 这个接口来获取下当前的登录状态~
    function getUserInfo(pageName) {
      
      
        $.ajax({
      
      
            type: 'get',
            url: 'login',
            success: function(body) {
      
      
                // 判定此处的 body 是不是一个有效的 user 对象(userId 是否非 0)
                if (body.userId && body.userId > 0) {
      
      
                    // 登录成功!
                    // 不做处理!
                    console.log("当前用户登录成功! 用户名: " + body.username);

                    // 在 getUserInfo 的回调函数中, 来调用获取作者信息
                    getAuthorInfo(body);
                } else {
      
      
                    // 登录失败!
                    // 让前端页面, 跳转到 login.html
                    alert("当前您尚未登录! 请登录后再访问博客列表!");
                    location.assign('blog_login.html');
                }
            },
            error: function() {
      
      
                alert("当前您尚未登录! 请登录后再访问博客列表!");
                location.assign('blog_login.html');
            }
        });
    }

    // 判定用户的登录状态
    getUserInfo("blog_detail.html");

    // 从服务器获取一下当前博客的作者信息, 并显示到界面上.
    // 参数 user 就是刚才从服务器拿到的当前登录用户的信息
    function getAuthorInfo(user) {
      
      
        $.ajax({
      
      
            type: 'get',
            url: 'authorInfo' + location.search,
            success: function(body) {
      
      
                // 此处的 body, 就是服务器返回的 User 对象, 是文章的作者信息
                if (body.username) {
      
      
                    // 如果响应中的 username 存在, 就把这个值设置到页面上.
                    changeUserName(body.username);

                    if (body.username == user.username) {
      
      
                        // 作者和登录的用户是一个人, 则显示 "删除按钮"
                        let navDiv = document.querySelector('.nav');
                        let a = document.createElement('a');
                        a.innerHTML = '删除';
                        // 期望点击删除, 构造一个形如 blogDelete?blogId=6 这样的请求
                        a.href = 'blogDelete' + location.search;
                        navDiv.appendChild(a);
                    }
                } else {
      
      
                    console.log("获取作者信息失败! " + body.reason);
                }
            }
        });
    }

    function changeUserName(username) {
      
      
        let h3 = document.querySelector('.card>h3');
        h3.innerHTML = username;
    }
</script>
</body>
</html>

在这里插入图片描述

8. 实现用户博客总数显示

8.1 服务器端代码

package controller;

import model.Blog;
import model.BlogDao;
import model.User;
import model.UserDao;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/num")
public class TotalServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    

        HttpSession session = req.getSession(false);
        if (session == null) {
    
    
            // 当前用户未登录
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前用户未登录");
            return;
        }
        User user = (User) session.getAttribute("user");
        if (user == null) {
    
    
            // 当前用户未登录
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前用户未登录");
            return;
        }

        resp.setContentType("text/html;charset=utf8");
        String blogId = req.getParameter("blogId");
        BlogDao blogDao = new BlogDao();
        if(blogId == null){
    
    
            resp.getWriter().write(blogDao.selectTotal(user.getUserId())+"");
        }else {
    
    
            Blog blog = blogDao.selectOne(Integer.parseInt(blogId));
            UserDao userDao = new UserDao();
            User author = userDao.selectById(blog.getUserId());
            resp.getWriter().write(blogDao.selectTotal(author.getUserId())+"");
        }
    }
}

8.2 实现客户端代码

在博客列表页和详情页加上:
在这里插入图片描述

<script>
    $.ajax({
      
      
        type: 'get',
        url: 'num'+location.search,
        success: function (data){
      
      
            changUserNum(data);
        }

    });
    function changUserNum(num){
      
      
        let total = document.querySelector('.total');
        total.innerHTML = num;
    }
</script>

9. 实现修改功能

9.1 实现服务器端代码

package controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import model.Blog;
import model.BlogDao;
import model.User;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/update")
public class BlogUpdateServlet extends HttpServlet {
    
    
    private ObjectMapper objectMapper = new ObjectMapper();
    private int BlogId = 0;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 1. 检查当前用户是否登录
        resp.setContentType("application/json;charset=utf-8");
        HttpSession session = req.getSession(false);
        if (session == null) {
    
    
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前尚未登录, 不能修改!");
            return;
        }
        User user = (User) session.getAttribute("user");
        if (user == null) {
    
    
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前尚未登录, 不能修改!");
            return;
        }

        // 2. 获取到参数中的 blogId
        String blogId = req.getParameter("blogId");
        if (blogId == null || "".equals(blogId)) {
    
    
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前 blogId 参数不对!");
            return;
        }

        // 3. 获取要修改的博客信息.
        BlogDao blogDao = new BlogDao();
        BlogId = Integer.parseInt(blogId);
        Blog blog = blogDao.selectOne(Integer.parseInt(blogId));
        if (blog == null) {
    
    
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前要修改的博客不存在!");
            return;
        }else{
    
    
            resp.setContentType("application/json;charset=utf-8");
            resp.getWriter().write(objectMapper.writeValueAsString(blog));
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 1. 检查当前用户是否登录
        req.setCharacterEncoding("utf-8");
        resp.setContentType("application/json;charset=utf8");
        HttpSession session = req.getSession(false);
        if (session == null) {
    
    
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前尚未登录, 不能修改!");
            return;
        }
        User user = (User) session.getAttribute("user");
        if (user == null) {
    
    
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前尚未登录, 不能修改!");
            return;
        }

        String title = req.getParameter("title");
        String content = req.getParameter("content");
        if(title == null || "".equals(title) || content == null || "".equals(content)){
    
    
            resp.getWriter().write("<script>alert('有内容为空')</script>");
            return;
        }
        int blogId = BlogId;

        BlogDao blogDao = new BlogDao();
        Blog blog = new Blog();
        blog.setTitle(title);
        blog.setContent(content);
        blog.setBlogId(blogId);
        blogDao.update(blog);
        resp.sendRedirect("blog_list.html");
    }
}

9.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="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="image/head.jpg" alt="">
    <span>我的博客系统</span>
    <!-- 空白元素, 用来占位置 -->
    <div class="spacer"></div>
    <a href="blog_list.html">主页</a>
    <a href="blog_edit.html">写博客</a>
    <a href="logout">注销</a>
</div>
<!-- 包裹整个博客编辑页内容的顶级容器 -->
<div class="blog-edit-container">
    <form action="update" method="post" style="height: 100%">
        <div class="title">
            <input type="text" placeholder="在此处输入标题" name="title" id="title">
            <!--                <button>发布文章</button>-->
            <input type="submit" value="发布文章" id="submit">
        </div>

        <!-- 放置 md 编辑器 -->
        <div id="editor">
            <!-- 为了进行 form 的提交,此处搞一个 textarea 多行编辑器,借助这个编辑框来实现表单的提交 -->
            <!-- 可以设置 editor.md,让编辑器把 markdown 内容也同步的保存到这个隐藏的 textarea 中,从而进行 form 提交 -->
            <textarea class="content" name="content" style="display: none"></textarea>
        </div>
    </form>

</div>


<script>
    //初始化编辑器
    let editor = editormd("editor",{
      
      
        //这里的尺寸必须在这里设置,设置样式会被editormd 自动覆盖掉
        width: "100%",
        //设定编辑器高度
        height: "calc(100% - 50px)",
        //编辑器中的初始内容
        //markdown: "# 在这里写下一篇博客",
        //指定 editor.md 依赖的插件路径
        path: "editor.md/lib/",
        // 此处要加上一个重要的选项,然后 editor.md 就会自动把用户在编辑器输入的内容同步保存到 隐藏的 textarea 中了!
        saveHTMLToTextarea: true,
    });
</script>
<script>
    function getEditUpdate(){
      
      
        $.ajax({
      
      
            type: 'get',
            url: 'update' + location.search,
            success: function(body){
      
      
                let title = document.querySelector('.title>#title')
                title.innerHTML = body.title;
                let content = document.querySelector('.content')
                content.innerHTML = body.content;
            }

        });
    }
    getEditUpdate();


</script>
<!--     <script src="js/common.js"></script>-->
</body>
</html>

文件地址

猜你喜欢

转载自blog.csdn.net/chenbaifan/article/details/125016116