博客系统详解

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

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

博客系统详解_第1张图片

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

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

Maven Repository: Search/Browse/Explore (mvnrepository.com)https://mvnrepository.com/博客系统详解_第2张图片③创建必要的目录

在main的目录下创建webapp这个目录文件,之后再webapp这个目录下创建WEB-INF这个目录文件,在WEB-INF这个目录下创建文件web.xml;并且对其进行配置固定内容如下:

博客系统详解_第3张图片




    Archetype Created Web Application

④编写代码

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

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)

 ⑦在浏览器中验证

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

博客系统详解_第4张图片

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');

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

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

博客系统详解_第5张图片

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 selectAll() throws SQLException {
        Listblogs=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博客列表页的展示

①需求:

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

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

博客系统详解_第6张图片

③编写服务器代码:

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 blogs=blogDao.selectAll();
        //将blogs转成json格式
        String respJson=objectMapper.writeValueAsString(blogs);
        resp.setContentType("application/json;charset=utf8");
        resp.getWriter().write(respJson);
    }
}

④编写客户端代码:

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

!DOCTYPE html>


    
    
    
    博客列表
    
    


    
    
    
    

知名博主

博客网址
文章 分类
2 1

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

值得注意的是:

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

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

博客系统详解_第7张图片

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

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

4.2博客详情页的展示

①需求:

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

②前后端的交互接口:

博客系统详解_第8张图片

注意:这里我们是通过某个博客的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 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.





    
    
    
    博客详情页
    
    link
    
    
    
    
    
    


      
      
      
      

知名博主

博客网址
文章 分类
2 1

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

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

博客系统详解_第9张图片

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

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

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

展示结果: 

博客系统详解_第10张图片

4.3登录功能

①需求:

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

②实现前后端交互:

博客系统详解_第11张图片

很显然,我们这里决定基于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");
    }
}

④客户端代码:




    
    
    
    登录页面
    
    


    
    
    

结果展示:

博客系统详解_第12张图片

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

4.4检测用户登录状态

①需求:

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

②实现前后端交互接口:

博客系统详解_第13张图片

③服务器代码:

我们就直接在上述代码中直接添加一个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();

引入:

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

博客系统详解_第14张图片

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

博客系统详解_第15张图片 而当我们登录后就不会出现这种情况。

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;

}

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

博客系统详解_第16张图片

博客详情页:

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

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

②前后端交互接口:

博客系统详解_第18张图片

③后端服务器代码:

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

结果展示:

博客系统详解_第19张图片

我们就可以看到第五篇博客的作者是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。我们可以看我们检测登录状态的代码,会看到两者必须同时具备;

博客系统详解_第20张图片

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

 ④客户端代码:

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

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

博客系统详解_第21张图片

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

4.7实现发布博客功能

①需求:

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

②前后端交互接口:

博客系统详解_第22张图片

③服务器代码:(在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");
    }
}

④客户端代码:




    
    
    
    博客编辑页
    
    

    
    
    
    
    
    


    
    
    
    

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

博客系统详解_第23张图片

注意!!

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

4.8实现删除博客功能

①需求:

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

②前后端交互:

博客系统详解_第24张图片

 

③服务器代码:

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

④客户端代码:





    
    
    
    博客详情页
    
    
    
    
    
    
    
    


      
      
      
      

博客网址
文章 分类
2 1

结果如下:

博客系统详解_第25张图片

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

注意!!!:

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

博客系统详解_第26张图片 回调函数部分如下:

博客系统详解_第27张图片

5.小结

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

感谢观看嘿嘿嘿~ 

你可能感兴趣的:(JavaEE,服务器,运维,servlet,java)