博客系统详解

博客系统的前端代码,我放在码云的,这里就不给大家重复展示了。

https://gitee.com/zhang-qinyang1/class102.git

目录

1.设计一个简单网站的基本思路

2.准备工作

3.编写数据库的代码

3.1数据库设计

3.2数据库封装

4.开发服务器controller和客户端的功能

4.1博客列表页的展示

4.2博客详情页的展示

4.3登录功能

4.4检测用户登录状态

4.5能够正确显示用户信息

4.6实现注销博客功能

4.7实现发布博客功能

4.8实现删除博客功能

5.小结


1.设计一个简单网站的基本思路

(1)设计前后端交互的接口
(2)实现服务器部分
(3)实现客户端部分
我们主要是采取客户端渲染来实现前后端分离

2.准备工作

这里的准备工作实际上就是一个servlet使用前所需要的导入工作

①创建Maven项目

这里我给博客系统命名为blog_system

②引入依赖(servlet,Jackon,mysql)

这里是在中央仓库去找你需要的

Maven Repository: Search/Browse/Explore (mvnrepository.com)https://mvnrepository.com/③创建必要的目录

在main的目录下创建webapp这个目录文件,之后再webapp这个目录下创建WEB-INF这个目录文件,在WEB-INF这个目录下创建文件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>

④编写代码

我们先试着随便输出一个来测试以上环境是否配置无误

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("/test")
public class TestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf8");
        resp.setContentType("text/html;charset=utf8");
        resp.getWriter().write("这是用来测试环境的servlet");
    }
}

⑤⑥部署打包(用smart Tomcat)

 ⑦在浏览器中验证

这里的时候需要注意一级路径与二级路径分别是什么

3.编写数据库的代码

数据库这里的操作大多是JDBC,因此大家首先需要对其熟悉,才能够看懂后续代码操作。

3.1数据库设计

数据库设计在这里的话就是指的创建数据库/数据表的结构
对于我们博客的功能来说,我们需要发布博客,获取博客,以及用户登录。所以,建表的时候抓住实体,我们就很容易想到建博客表和用户表来进行操作。

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,--对于blog而言,往往是较长的,所以我们这里用mediumtext来表示
  userId int,--文章作者id
  postTime datetime--发布的时间
);
--创建一个user表
drop table if exists user;
create table user(
userId int primary key auto_increment,
username varchar(1024) unique,
password varchar(124)
);
insert into user values(null,'zhangsan','123');
insert into user values(null,'lisi','123');

然后将它插入到数据库中去,显示如下:

插入的时候注意一些细节,比如去掉注释这些,不然的话就会报错

3.2数据库封装

(1)先创建DBUtil封装数据库连接的操作

package model;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
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/my_blog?characterEncoding=utf8&useSSL=false";
    private static final String USERNAME = "root";
    //数据库密码
    private static final String PASSWORD = "123456";
    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 (Connection) getDataSource().getConnection();
    }
    public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet) throws SQLException {
        if (resultSet!=null){
            resultSet.close();
        }
        if(statement!=null){
            statement.close();
        }
        if (connection!=null){
            connection.close();
        }
    }
}

(2)创建实体类,使用实体来表示数据库中的一条内容,此处主要创建的是Blog类以及User类

这里只展示Blog类

package model;
import java.sql.Timestamp;
//每个Blog对象,对应blog表中的一条记录
public class Blog {
    //因为都是private,所以这里要重写get,set方法
    private int BlogId;
    private String title;
    private String content;
    private int UserId;
    private Timestamp postTime;

    public void setBlogId(int blogId) {
        BlogId = blogId;
    }

    public int getBlogId() {
        return 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) {
        UserId = userId;
    }

    public Timestamp getPostTime() {
        return postTime;
    }

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

(3)封装针对数据的增删查改,把提供增删查改的这样的类,称为DAO。这里也只展示BlogDao

package model;
import com.mysql.jdbc.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
// 这个类用于去封装博客表的基本操作
public class BlogDao {
    // 1. 向博客列表中插入一个博客
    public void insert(Blog blog) throws SQLException {
        // JDBC 基本代码
        Connection connection=null;
        PreparedStatement statement=null;
        try {
            // ①和数据库建立连接.
            connection = DBUtil.getConnection();
            // ② 构造 SQL 语句
            String sql = "insert into blog values(null, ?, ?, ?, now())";
            statement=connection.prepareStatement(sql);
            //第一个参数表示的是第几个?的位置
            statement.setString(1, blog.getTitle());
            statement.setString(2, blog.getContent());
            statement.setInt(3,blog.getUserId());
            // ③执行 SQL
          statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // ④ 关闭连接, 释放资源
            DBUtil.close(connection,statement,null);
        }
    }
    // 2. 能够获取到博客表中的所有博客的信息 (用于在博客列表页, 此处每篇博客不一定会获取到完整的正文)
    public List<Blog> selectAll() throws SQLException {
        List<Blog>blogs=new ArrayList<>();
        Connection connection=null;
        PreparedStatement statement=null;
        ResultSet resultSet=null;
        try {
            connection=DBUtil.getConnection();
            String sql="select * from blog";
            statement=connection.prepareStatement(sql);
            resultSet=statement.executeQuery();
            //遍历整个集合
            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 {
            DBUtil.close(connection, statement,resultSet );
        }
        return blogs;
    }

    // 3. 能够根据博客 id 获取到指定的博客内容 (用于在博客详情页)
    public Blog selectOne(int blogId) throws SQLException {
        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();
            //由于id是唯一的,要么是0条,要么是唯一的,是主键作为查询条件的,所以直接用if
            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.getInt("userId"));
                blog.setPostTime(resultSet.getTimestamp("postTime"));
                return blog;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }
    // 4. 从博客表中, 根据博客 id 删除博客.
    public void delete(int blogId) throws SQLException {
        Connection connection=null;
        PreparedStatement statement=null;
        try {
            connection=DBUtil.getConnection();
            String sql="delete from blog where blogId=?";
            statement.setInt(1,blogId);
            statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtil.close(connection,statement,null);
        }
    }
    // 注意, 上述操作是 增删查, 没有改
}

4.开发服务器controller和客户端的功能

4.1博客列表页的展示

①需求:

这个页面需要能够展示出数据库中的博客列表

②约定前后端的交互接口:

③编写服务器代码:

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;
import java.util.List;
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    private ObjectMapper objectMapper=new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //从数据库中查询到博客列表,转成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);
    }
}

④编写客户端代码:

在页面加载的时候,让页面通过ajax访问服务器,获取到数据库中的博客数据,并且填到页面中

!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客列表</title>
    <link rel="stylesheet" href="css/blog_list.css">
    <link rel="stylesheet" href="css/common.css">
</head>
<body>
    <!-- 导航栏 -->
    <div class="nav">
        <img src="images/1.jpg" alt="">
        <span >我的博客系统</span>
        <!-- 空白元素,用来起占位效果 -->
        <div class="spacer"></div>
        <a href="blog_list.html">主页</a>
        <a href="blog_edit.html">写博客</a>
        <a href="#">注销</a>
    </div>
    <!-- 这里的.container作为页面的版心 -->
    <div class="container">
        <!-- 左侧个人信息 -->
        <div class="left">
            <!-- 用来表示整个用户信息 -->
         <div class="card">
             <img src="./images/3.webp" alt="">
             <h3>知名博主</h3>
             <a href="">博客网址</a>
             <div class="counter">
                 <span>文章</span>
                 <span>分类</span>
             </div>
             <div class="counter">
                 <span>2</span>
                 <span>1</span>
             </div>
         </div>
        </div>
        <!-- 右侧内容详情 -->
        <div class="right">
        
        </div>
        </div>
    </div>
    <script src="http://code.jquery.com/jquery-2.1.1.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设置一个属性,类名设为blog
                        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);
                        //链接 查看全文(这里用的是a标签)
                        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>
</body>
</html>

展示效果:当我们在数据库中插入一条数据后,他会更新到列表页的最前面

值得注意的是:

a.db.sql中的代码并不会生效,我们需要把其添加到mysql中去

b.可以利用postman来看我们构造的响应body中返回的数据格式与希望的是不是一致的

c.转换时间格式:yyyy-MM-dd HH:mm:ss(分别指的是年月日 小时分钟秒钟) 

d.要想插入的博客是按照时间约后发布越在上面,就不能忘了加上order by desc来进行降序的排列

4.2博客详情页的展示

①需求:

这里用来获取博客的详情页,发送的请求。希望得到的页面能够显示出博客对应的正文内容
而这个正文内容,我们通过ajax来进行获取。在blog_detail.html页面加载的时候,触发ajax请求来访问服务器,获取到博客内容,再次填充到博客详情页里面去。

②前后端的交互接口:

注意:这里我们是通过某个博客的id来确定具体要访问哪个内容的。与博客列表页不同之处有以下三点:

(1)请求中带有参数blogId

(2)结果响应不是json数组,而是单一的对象(因为具体到了某一个)

(3) 响应博客的正文,不用进行截断了

③编写服务器代码:

此处服务器代码几乎和博客列表页一致,唯一不同的点就是有了个blogId来区分是博客列表还是详情,因此我们还是放到同一个方法中来实现

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

@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    private ObjectMapper objectMapper=new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //从数据库中查询到博客列表,转成json格式,直接返回
        //查询数据库

        BlogDao blogDao=new BlogDao();
        // 先尝试获取到 req 中的 blogId 参数. 如果该参数存在, 说明是要请求博客详情页
        // 如果该参数不存在, 说明是要请求博客的列表.
        //因为这里是get,我们直接可以在URL中判断该参数是否存在
        String param=req.getParameter("blogId");
        //当其不存在是则是获取列表页
        if(param==null){
            List<Blog> blogs=blogDao.selectAll();
            //将blogs转成json格式
            String respJson=objectMapper.writeValueAsString(blogs);
            resp.setContentType("application/json;charset=utf8");
            resp.getWriter().write(respJson);
        }else {//存在参数,则是获取博客详情页
            int blogId = Integer.parseInt(param);//包装类转换成Integer
            Blog blog = blogDao.selectOne(blogId);
            //用json对象来进行接收
            String respJson = objectMapper.writeValueAsString(blog);
            resp.setContentType("application/json;charset=utf8");
            resp.getWriter().write(respJson);
        }
    }
}

④编写客户端代码:

前端代码这边,要想构造一个请求获取博客详情,就得知道当前用户的博客id,当然这个id已经在当前的blog_detail.html页面的url中了。在前端的时候,我们可以通过location.search来获取到blogId.


<!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/blog_detail.css">
    link<link rel="stylesheet" href="css/common.css">
    <!-- 引入 editor.md 的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    <script src="http://code.jquery.com/jquery-2.1.1.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="images/1.jpg" alt="">
        <span >我的博客系统</span>
        <!-- 空白元素,用来起占位效果 -->
        <div class="spacer"></div>
        <a href="blog_list.html">主页</a>
        <a href="blog_edit.html">写博客</a>
        <a href="#">注销</a>
    </div>
      <!-- 这里的.container作为页面的版心 -->
      <div class="container">
        <!-- 左侧个人信息 -->
        <div class="left">
            <!-- 用来表示整个用户信息 -->
         <div class="card">
             <img src="./images/3.webp" alt="">
             <h3>知名博主</h3>
             <a href="">博客网址</a>
             <div class="counter">
                 <span>文章</span>
                 <span>分类</span>
             </div>
             <div class="counter">
                 <span>2</span>
                 <span>1</span>
             </div>
         </div>
        </div>
        <!-- 右侧内容详情 -->
        <div class="right">
            <!-- 使用这个div来包括整个博客内容详情 -->
            <div class="blog-content">
                <!-- 博客标题 -->
                <h3></h3>
                <!-- 博客时间 -->
                <div class="date"></div>
                <!-- 正文 -->
                <div id="content"></div>
            </div>
            </div>
    </div>
    
    <script>
        function getBlogDetail(){
            $.ajax({
                type:'get',
                url:'blog'+location.search,
                success:function(body){
                    //根据body内容来构造页面
                    //①title
                    let h3=document.querySelector(".blog-content>h3");
                    h3.innerHTML=body.title;
                    //②postTime
                    let dateDiv=document.querySelector(".date");
                    dateDiv.innerHTML=body.postTime;
                    //③正文
                    // let content=document.querySelector("#content");
                    // content.innerHTML=body.content;
                    //为了展现出来是Markdown的形式,所以引入editor.markdown操作
                    editormd.markdownToHTML('content',{
                        markdown:body.content
                    });

                }
            });

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

注意!!!这里的难点主要有三个问题:

a.弄清楚blogId到底是怎么来的

 b.为什么要引入editor.md?

因为我们编辑的时候用的是Markdown格式的,要想响应的时候也是,就需要引入相应的需求

c.为了防止出现乱码情况,我们一定要设置格式!!!而且要注意整体和局部之间的关系!!!

展示结果: 

4.3登录功能

①需求:

实现登录,当登录之后跳转至博客列表页

②实现前后端交互:

很显然,我们这里决定基于form表单来实现,当然ajax也可以,只是这里用form表单

③服务器代码:

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("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        //①获取到参数中的请求
        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 user=userDao.selectByName(username);
        if(user == null || !user.getPassword().equals(password)) {
            // 用户没有查到或者密码不匹配,登录失败
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("用户名或密码错误");
            return;
        }
        //③如果比较通过,则用会话记录下来,即创建会话
        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_login.css">
</head>
<body>
    <!-- 这是导航栏 -->
    <div class="nav">
        <img src="./images/1.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>

结果展示:

 注意!!!值得注意的是用form表单的时候,不要忘了添加name属性,还有css里面的格式会随着属性的改变可能会不同。并且form表单中提交按钮不能是button,要是type类型的submit。

4.4检测用户登录状态

①需求:

当登录功能完成后,我们要调节一下列表页和详情页,使得这两个页面只有登录了才能访问,要是没有登录就跳转到登录页面

②实现前后端交互接口:

③服务器代码:

我们就直接在上述代码中直接添加一个doGet方法,用来检验这个时候用户的登录状态就好。因此,我们这里的代码也就是直接连着的上面的

//用来检验用户的登录状态
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("application/json;charset=utf8");
        //①获取开始存在的会话,看会话是否存在,会话都不存在的话,那么必然是没有登录
        //因为false是不会创建新的会话的
        HttpSession session=req.getSession(false);
        if (session==null){
            //没有会话
            //创建对象,里面为空,并返回,注意这里返回空,就不用和数据库进行连接,,但是仍然是json格式的,所以用mapper来转换
            User user=new User();
            resp.getWriter().write(objectMapper.writeValueAsString(user));
            return;
        }
        //拿到了会话,但是会话里面的user为空值
        User user= (User) session.getAttribute("user");
        if (user==null){
            //这个时候,仍然是没有登录,那么仍然返回空
            user = new User();
            resp.getWriter().write(objectMapper.writeValueAsString(user));
            return;
        }
        //获取到了session,且里面的user并不为空,那么可以继续访问
        //但是注意不要把密码返回给了前端
        user.setPassword("");
        resp.getWriter().write(objectMapper.writeValueAsString(user));
    }

④客户端代码:

对于一些通用的代码,我们可以放到一个js里面来统一管理,然后其他页面直接调用它就好,这里我们把其命名为common.js。然后把这个js分别引入到列表页和详情页即可。

common.js

// 这个文件里放一些 页面公共的代码
// 加上一个逻辑,通过 GET/login 这个接口来获取下当前的登录状态
function getUserInfo() {
    $.ajax({
        type: 'get',
        url: 'login',
        success: function(body) {
            // 判定此处的 body 是不是一个有效的 user 对象(userId 是否非 0)
            if(body.userId && body.userId >0) {
                // 登录成功
                // 不做处理
                console.log("当前用户登录成功!用户名:" + body.username);
            }else{
                // 登录失败
                // 让前端页面,跳转到 login.html
                alert("当前你尚未登录!请登录后再访问博客列表!");
                location.assign('blog_login.html');
            }
        },
        error: function() {
            alert("当前你尚未登录!请登录后再访问博客列表!");
                location.assign('blog_login.html');
        }
    });
    

}
getUserInfo();

引入:

结果展示:(当我们直接访问列表页时) 

当我们点击确定或者等待一段时间后,就会跳转到登录页面:

 而当我们登录后就不会出现这种情况。

4.5能够正确显示用户信息

由于我们登录的人是可能与发布文章的博主是不同的人,但是我们发现,当我们进去的时候登录的用户是zhangsan,但是写博客的可能不是zhangsan,所以我们要实现能够根据具体情况进行转换。

①需求:

让在列表页显示的用户名称是用户的登陆者,而在详情页具体某篇博客时,显示的是作者本人。

博客列表页:

而我们可以发现针对博客列表页来说,其实前面已经处理过了,我们通过ajax得到了body,此时我们只需要在前端代码中稍作改动,拿到当前改动的这个名字,作为当前用户名即可。

所以我们只需要在common.js里面做改动

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

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

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

}

此时我们在列表页看到的用户名就是我们登录时的用户名了

博客详情页:

根据上面的操作,我们发现,当我们进入任何一篇内容时,用户名都是zhangsan;

 然而实际上第五篇的作者却是wangwu,所以说我们得在列表页与详情页作出区分。而如何区分呢?很显然,我们可以通过blogId来提供新的接口,这个接口可以让客户端指定blogId,从而获取指定blogId的作者信息

②前后端交互接口:

③后端服务器代码:

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.setContentType("application/json;charset=utf8");
            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));
    }
}

④客户端代码: (这里主要是此处的核心代码)

  //从服务器获取一下当前博客的作者信息,并且显示到界面上
        function getAuthorInfo(){
            $.ajax({
                type:'get',
                url:'authorInfo'+location.search,
                success:function(body){
                    // 此处的 body, 就是服务器返回的 User 对象, 是文章的作者信息
                    if (body.username) {
                        // 如果响应中的 username 存在, 就把这个值设置到页面上. 
                        changeUserName(body.username)
                    }else{
                        console.log("获取作者信息失败"+body.reason);
                    }
                }
            });
        }
        getAuthorInfo();

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

结果展示:

我们就可以看到第五篇博客的作者是wangwu了 

我们需要注意的点是,在完成局部功能函数后,一定不要忘记调用它,不然就毫无意义了。

4.6实现注销博客功能

①需求:
退出当前的登录状态,当我们点击注销按钮之后,就会在服务器上取消登录并且能够跳转到登录页面。

②前后端交互接口:

③服务器后端代码:

我们希望点击注销后,能够给服务器发送一个HTTP请求,从而触发注销动作,具体来说就是这个动作把会话中的信息给删掉就行

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("当前用户没有登录,无法注销");
        }
        //然后把这个用户的会话中的信息删掉
        session.removeAttribute("user");
        resp.sendRedirect("blog_login.html");
    }
}

什么叫做登录呢?

用户有一个session,同时session有一个user属性,两个同时具备,才叫做登录状态。注销,只需要破坏上面的任意一个条件就OK啦,此处选择的破坏第二个条件,也就是让session不具备user属性,把user属性从session中删了。为啥不删会话呢?因为没有提供删会话的API。我们可以看我们检测登录状态的代码,会看到两者必须同时具备;

无论删谁,都会返回null,也就是说都会被认为是未登录状态。 

 ④客户端代码:

我们把博客列表页,详情页和编辑页的注销按钮的herf属性作出修改即可

 结果展示:(此时当我们点击注销,就会回到登录页面)

 注意!!!博客登录页不用设置注销按钮的。

4.7实现发布博客功能

①需求:

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

②前后端交互接口:

③服务器代码:(在blogservlet中继续创建写在post请求中的)

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setCharacterEncoding("utf8");
        req.setCharacterEncoding("utf8");
        HttpSession session=req.getSession();
        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.setCharacterEncoding("utf8");
        String title=req.getParameter("title");
        String content=req.getParameter("content");
        if (title==null||"".equals(title)||content==null||"".equals(content)){
            //参数有误
            resp.getWriter().write("提交博客失败,缺少必要的参数");
            return;
        }
        //构造blog对象,把当前的信息填进去,并插入数据库中
        //此处要设置的属性主要是title,content,userId;而postTime和blogId都是系统自动生成的
        Blog blog=new Blog();
        blog.setTitle(title);
        blog.setContent(content);
        blog.setUserId(user.getUserId());//作者id就是当前提交这个博客的用户的身份信息
        BlogDao blogDao=new BlogDao();
        blogDao.insert(blog);
        //新增成功后重定向到列表页
        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_edit.css">

    <!-- 引入 editor.md 的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    <script src="jquery.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="./images/1.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">
                <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>
</body>
</html>

 结果显示:(当我们写一篇新的文章提交后,会自动跳转到博客列表,并且刷新当前内容)

注意!!

form表单的添加与修改,以及为了延续Markdown使用的textarea的调节。还有别忘了在将button修改后应该注意对应在css中同样进行修改,不然样式就会有所改变。 

4.8实现删除博客功能

①需求:

我们这里的删除博客,不考虑管理员删除,只考虑我们自己删除。因此,在博客详情页上我们就要进行判定,判定当前这个博客的作者,是否就是登录的用户,如果是,就在导航栏显示一个“删除按钮”,如果哦不是,就不显示删除按钮。而在博客详情页这里,其实既从服务器获取了当前用户的登录信息,又获取到了博客的作者信息,我们主要看这两个接口返回的用户信息是不是一样的。

②前后端交互:

 

③服务器代码:

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();
        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("当前blog参数不对");
            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");
    }
}

④客户端代码:


<!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/blog_detail.css">
    <link rel="stylesheet" href="css/common.css">
    <!-- 引入 editor.md 的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    <script src="http://code.jquery.com/jquery-2.1.1.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="images/1.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="./images/3.webp" alt="">
             <h3></h3>
             <a href="">博客网址</a>
             <div class="counter">
                 <span>文章</span>
                 <span>分类</span>
             </div>
             <div class="counter">
                 <span>2</span>
                 <span>1</span>
             </div>
         </div>
        </div>
        <!-- 右侧内容详情 -->
        <div class="right">
            <!-- 使用这个div来包括整个博客内容详情 -->
            <div class="blog-content">
                <!-- 博客标题 -->
                <h3></h3>
                <!-- 博客时间 -->
                <div class="date"></div>
                <!-- 正文 -->
                <div id="content"></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>

结果如下:

当我们删了我是新文章后跳转到列表就没有这篇文章了。

注意!!!:

a.调用顺序,因为ajax是并发执行的,我们不知道谁会先执行完,我们就决定使用回调的方式来完成

 回调函数部分如下:

5.小结

这只是一个很简单的网站,可能很多地方没有考虑全面,仅供参考。详细代码,可以在页首查看我的码云地址。

感谢观看嘿嘿嘿~ 

  • 13
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

张洋洋~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值