博客系统(前后端分离)

作者:~小明学编程 

文章专栏:JavaEE

格言:热爱编程的,终将被编程所厚爱。
在这里插入图片描述

目录

项目需求分析与技术栈

前端页面构成

功能需求

技术栈

数据库的设计

博客表

用户表

连接数据库

博客用户类

博客类Blog

BlogDao操作博客表

User用户类

UserDao操作用户表

实现博客列表

约定前后端交互接口

服务器代码

客户端代码

实现博客详情

约定前后端的交互接口

实现服务器代码

实现客户端

实现登录

约定前后端的交互

实现服务器

实现客户端

实现强制登录验证

实现服务端代码

实现客户端

实现显示用户信息

约定前后端的交互接口

实现服务端的代码

实现客户端代码

实现注销登录

约定前后端接口

实现服务端代码

实现发布博客

约定前后端交互接口

实现服务端代码

实现客户端代码

实现删除博客

约定前后端交互接口

实现服务端代码

实现客户端代码


今天给大家介绍我的第一个项目博客系统,里面包含了我的实现逻辑,前端的代码和数据库的存储等,下面详细给大家介绍。

项目需求分析与技术栈

此项目是简单的模仿CSDN这样的博客系统的主要功能,其中我们有四个页面分别是:

前端页面构成

1.登录页面。

2.博客列表页。

 3.博客详情页。

4.博客编辑页。

功能需求

我们要分别针对上面四个页面实现相应的功能。

1.针对登录模块我们需要在数据中中创建相应的表来存储我们的用户信息,登录的时候以便校验用户的信息是否正确,同时我们如果未登录的话我们需要从当前的页面直接跳转到登录的页面。

2.然后就是博客列表页,我们需要在博客列表页中展示我们当前的用户和所有用户的所有文章同时每篇文章都只是截取前面一段的内容。

3.博客详情中我们需要展示我们当前的用户信息,和文章的全部内容,同时能够删除当前用户的文章。

4.博客编辑页我们需要能够编辑博客并且上传文章到我们的数据库中。

技术栈

1.我们该博客系统的服务器是基于Servlet来实现的,在IDEA中我们引用了small Tomcat的第三方工具来帮助我们打包部署程序到服务器中。

2.在存储数据方面我们用到了mySQL,这里我们采用的是JDBC来实现交互。

3.前端的代码是拿别人做好的,主要就是改改请求和交互。

数据库的设计

--创建数据库
create database if not exists blog_system;

use blog_system;

--创建一个博客表 
create table blog (
    blogId int primary key auto_increment, -- 博客ID,用自增主键
    title varchar(1024), -- 文章标题
    content mediumtext,  -- 文章内容
    userId int, -- 作者id
    postTime datetime -- 发布时间
);

-- 创建一个用户表
create table user (
    userId int primary key auto_increment,
    username varchar(128) unique, -- 用户名不能重复
    password varchar(128) -- 用户密码
);
-- 测试用例
insert into user values(null,'aaa','123');
insert into user values(null,'bbb','456');

首先我们需要创建一个数据库,用于存储我们的信息,接着我们创建两个表。

博客表

其中博客表中主要就是存储我们的博客信息,其中我们定义了自增主键博客ID作为唯一标识,然后就是我们文章的标题,然后是文章内容,因为文章的内容较大我们用mediumtext类型的变量来存储,接着就是作者的ID和发布时间。

用户表

用户表中我们的用户ID作为自增主键,然后就是我们的用户名和用户密码,其中我们规定用户名不能重复。

连接数据库

//与数据库建立连接
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 = "0217";

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

这里我们创建一个DBUtil的类来封装我们相关的连接数据库的方法,这里就不详细介绍了,想要了解的话可以查看博主前面的文章JDBC编程。

博客用户类

博客类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 String getPostTime() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return simpleDateFormat.format(postTime);
    }

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

这里博客类的创建是对应我们的数据库中的博客表的,我们将相关的成员封装成一个类方便传输。

BlogDao操作博客表

//封装博客表的基本操作
public class BlogDao {
    // 1.向博客表里面插入一个博客。
    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());//获取用户Id
            //3.执行sql
            statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //4.关闭连接
            DBUtil.close(connection,statement,null);
        }
    }
    // 2.获取博客表里面的所有博客的信息。(博客列表页)
    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 e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return blogs;
    }
    // 3.能够根据博客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();
            //此时查询结果最多一条
            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 e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return null;

    }
    // 4.从博客表里面,根据博客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 e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection,statement,null);
        }
    }

}

这里我们再次封装一个类用于操作我们的博客表,便于我们从数据库中查询或者删除数据等的操作。

代码虽然比较的繁琐但是都是基于JDBC那一套,有很多重复的代码,其中:

  1. inert()方法就是向我们的博客表中插入一个blog的对象数据。
  2. selectAll()的方法是返回我们当前博客表中的所有的信息。
  3. selectOne()是根据我们的博客ID来返回我们相应的blog对象。
  4. delete()方法用于我们删除博客。

这里全是JDBC的内容这里就不再给大家赘述了。

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

该类就是对应我们用户表的一条信息,包括我们的用户ID用户名和用户密码。

UserDao操作用户表

//针对用户表的基本操作
public class UserDao {
    //1.根据用户名查询 User 对象用于登录
    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();
            if (resultSet.next()) {
                User user = new User();
                user.setUserId(resultSet.getInt("userId"));
                user.setUsername(resultSet.getString("username"));
                user.setPassword(resultSet.getString("password"));
                return user;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }
    //2.根据用户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();
            if (resultSet.next()) {
                User user = new User();
                user.setUserId(resultSet.getInt("userId"));
                user.setUsername(resultSet.getString("username"));
                user.setPassword(resultSet.getString("password"));
                return user;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }

}

这里我们也是连接数据库通过JDBC来操作数据库中的用户表。

  1. selectByName()方法:根据我们的用户名来查询到我们的从用户表中查询到该行的所有信息并且通过用户对象来返回。
  2. selectById()方法:根据我们的用户ID来查询到我们的从用户表中查询到该行的所有信息并且通过用户对象来返回。

现在我们的准备工作已经完毕,下面就要来实现我们的前后端的逻辑了。

实现博客列表

约定前后端交互接口

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

这里我们首先是要约定好前后端的交互,我们前端会发出一个get类型的请求,地址是blog。

然后我们后端会给出响应,这个时候我们将会返回所有的的博客信息,同时我们返回数据的格式是JSON格式的数据。

服务器代码

@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();
        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);
            Blog blog = blogDao.selectOne(blogId);
            String respJson = objectMapper.writeValueAsString(blog);
            resp.setContentType("application/json; charset=utf8");
            resp.getWriter().write(respJson);
        }
}

针对前端的格式我们这里重写了get方法,下面就来详细的介绍这段代码。

  1. 首先我们前面已经创建了一个jsckson的依赖,接下来我们要借助这个第三方的工具来所提供的类讲我们的对象转换成JSON格式的数据,关于jsckson我们前面已经讲过了,这里不再赘述。
  2. 首先我们要创建一个blogDao的对象然后后面方便操作数据库中的表,接着我们通过getParameter()方法来获取我们的blogId。
  3. 然后就是判断param是否为空,如果为空的话就说明我们的请求中不带相应的参数我们就要返回blog表中的所有数据,因为是博客列表页,当param不为空的时候就说明我们查询指定的数据这个时候我们就要调用selectOne的方法了。
  4. 设置我们返回数据的格式然后将JSON数据进行返回。

客户端代码

这里我们主要就是通过ajax给服务端发送请求,然后将得到的响应填入到我们的页面中。

    <script>
        //加载页面的时候通过ajax给博客
        function getBlogList() {
            $.ajax({
                type: 'get',
                url: 'blog',
                success: function(body) {
                    //获取到的body就是一个js对象数组
                    //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>

实现博客详情

约定前后端的交互接口

[请求]
GET /blog?blogId=1
[响应]
[
    {
        blogId: 1,
        title: "第一篇博客",
        content: "博客正文",
        userId: 1,
        postTime: "2023-01-13 12:00:00"
    }

]

这里我们在请求中加入了一个参数blogId也就是查找我们指定的博客,服务器就会根据响应返回对应的博客。

实现服务器代码

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //从数据库中查询到列表,将其转换成JSON格式
        BlogDao blogDao = new BlogDao();
        String param = req.getParameter("blogId");
//        System.out.println(param);
        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);
            Blog blog = blogDao.selectOne(blogId);
            String respJson = objectMapper.writeValueAsString(blog);
            resp.setContentType("application/json; charset=utf8");
            resp.getWriter().write(respJson);
        }


    }

服务器的代码同上,我们一个方法就实现了两个不同的功能,根据不同的请求去实现不同的功能。

实现客户端

        function getBlogDetail() {
            $.ajax({
                type: 'get',
                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.构造发布正文
                    //此时展示的内容是原始的内容,现在我们需要markdown渲染后的
                    //就需要引入依赖
                    // let conTent = document.querySelector('#content');
                    // conTent.innerHTML = body.content;
                    editormd.markdownToHTML('content',{
                        markdown: body.content
                    });
                }
            });
        }
        getBlogDetail();

这里我们需要注意的是,我们要引入editor.md的相关依赖才能将markdown的数据正确的显示出来。

实现登录

约定前后端的交互

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

这里我们采用post请求我们不希望我们的账号密码直接通过query string发给服务端,我们将其放在body里面。

接着服务器收到响应然后核验密码,密码正确的话就跳转到列表页。

实现服务器

@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");
        //处理登录异常情况
        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");
    }
}
  1. 我们要从请求的额表单中获取我们的用户名和密码。
  2. 对用户名和密码进行一番验证检测用户密码是否正确。
  3. 创建会话,并且将用户的信息储存在会话中便于后面访问其它的页面。
  4. 登录成功之后然后跳转到博客列表的页面。

实现客户端

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

这里我们通过from表单来发送请求。

实现强制登录验证

当用户访问非登录界面的时候我们要给其加上一个验证,验证其是否已经登录过了,如果没有的话那么就强行跳转到登录界面。

实现服务端代码

    //检测当前的登录状态
    @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 = new User();
            resp.getWriter().write(objectMapper.writeValueAsString(user));
            return;
        }
        //已经登录
        user.setPassword("");
        resp.getWriter().write(objectMapper.writeValueAsString(user));
    }
  1. 首先我们会尝试获取session。
  2. 当session不存在的时候我们直接返回一个空的对象。
  3. 如果存在session但是里面没有对象我们已经返回一个空的对象。
  4. 如果对象也存在那么我们需要将密码给定义为空的字符串然后写回给前端。

实现客户端

//判断当前的登录状态
function getUserInfo(pageName) {
    $.ajax({
        type: 'get',
        url: 'login',
        success: function(body) {
            //判断body是否有效
            if (body.userId && body.userId > 0) {
                //登录成功
                console.log(body.username+"登录成功!");
                //根据当前用户登录情况,把当前用户名设置到界面上
                if (pageName == 'blog_list.html') {
                    changeUserName(body.username);
                }
                
            } else {
                //登录失败
                alert("您尚未登录");
                location.assign('blog_login.html');
            }
        },
        error: function() {
            alert("你当前未登录!");
            location.assign('blog_login.html');
        }
    });
}

我们首先会判断当前的对象里面有没有内容没有的话那就跳转到我们的登录页面。

实现显示用户信息

我们现在想要显示我们用户的用户名,用于我们区分当前的用户:

 就是下面这个aaa就是我们当前的用户名。

当我们访问主页的时候显示的是当前用户的信息,如果我们访问博客详情页的话就是显示的当前博客作者的用户名。

约定前后端的交互接口

在博客列表页的时候我们要获取我们当前登录用户的用户名。

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

在博客详情页我们就获取当前作者的用户名。

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

实现服务端的代码

@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对象
        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,然后从数据库中根据博客ID找到我们的对应用户,再从用户中找到我们的用户名,当然这个过程我们也可以用多表查询来解决,之后我们再以JSON的数据格式来返回我们的用户对象,不过返回之前最好把我们的用户密码给处理一下。

实现客户端代码

    </script>
    <!-- 检测登录状态 -->
    <script src="js/comment.js"></script>
    <script>
        //针对博客列表页传入参数
        getUserInfo('blog_list.html');
    </script>

在博客列表的页面中我们会检测登录状态,然后进入下面这个文件:

//判断当前的登录状态
function getUserInfo(pageName) {
    $.ajax({
        type: 'get',
        url: 'login',
        success: function(body) {
            //判断body是否有效
            if (body.userId && body.userId > 0) {
                //登录成功
                console.log(body.username+"登录成功!");
                //根据当前用户登录情况,把当前用户名设置到界面上
                if (pageName == 'blog_list.html') {
                    changeUserName(body.username);
                }
                
            } else {
                //登录失败
                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;
}

当判断出我们当前的页面是博客列表页的时候就是直接调用changeUserName()这个方法,将我们的h3标签给更改了。

当我们跳转到博客详情页的时候:

 这里我们会附带一个blogId也就是我们服务器刚刚接收到的那个ID,向服务器发送请求。

        //从服务器获取一下当前博客的信息,显示到界面上
        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;
        }

得到响应之后然后把数据写入我们的前端页面中去。

实现注销登录

约定前后端接口

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

我们会给后端服务器发一个请求找到服务器,然后服务器接受请求,删除我们当前的会话,返回登录界面。

实现服务端代码

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

}

实现发布博客

约定前后端交互接口

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

实现服务端代码

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

    }

我们服务器接受请求然后将我们正文中的信息填入进去,接着将数据插入到数据库中,最后跳转页面到博客列表页。

实现客户端代码

    <!-- 包裹整个博客编辑页 -->
    
    <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来辅助 -->
                <textarea name="content" style="display: none;"></textarea>
            </div>
        </form>

    </div>

我们这里通过 form 表单来构造一个 post 请求,通过submit来发布文章。

实现删除博客

约定前后端交互接口

[请求]
    GET /user?blogId=1
[响应]
{
    userId: 1,
    username: test,
    isYourBlog: 1, // 1 表示当前博客就是登陆者的博客. 0 表示当前博客不是登陆者的博客.
}

约定前后端的交互接口,前端发送请求同时发送指定的博客ID后端接到请求从数据库中找到数据然后将数据给删除。

实现服务端代码

@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.获取到博客Id
        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");
    }
}
  1. 首先为了保险期间我们做一些必要的验证。
  2. 通过博客ID找到我们对应的博客。
  3. 校验一下当前用户是否是作者,是否有权力删除博客。
  4. 通过数据库操作删除博客的数据。
  5. 删除完毕时候重定向到我们的列表页。

实现客户端代码

        //从服务器获取一下当前博客的信息,显示到界面上
        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);
                    }
                }
            });
        }

这里面值得注意的是我们发送请求的时候要带上我们的博客ID,这样我们后端的处理请求的时候才能找到相应的数据。

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
以下是一个简单的前后端分离博客系统Java 代码示例,使用 Spring Boot 框架和 MySQL 数据库。 前端代码可以使用 Vue.js 或 React 等框架开发,这里不再给出示例。 ### 后端代码 #### 实体类 ```java @Entity @Table(name = "articles") public class Article { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String title; private String content; @Column(name = "created_at") private Date createdAt; @Column(name = "updated_at") private Date updatedAt; // 省略 getter 和 setter 方法 } ``` #### 数据访问层 ```java @Repository public interface ArticleRepository extends JpaRepository<Article, Long> { } ``` #### 服务层 ```java @Service public class ArticleService { @Autowired private ArticleRepository articleRepository; public List<Article> getAllArticles() { return articleRepository.findAll(); } public Article getArticleById(Long id) { return articleRepository.findById(id) .orElseThrow(() -> new ResourceNotFoundException("Article", "id", id)); } public Article createArticle(Article article) { return articleRepository.save(article); } public Article updateArticle(Long id, Article articleDetails) { Article article = getArticleById(id); article.setTitle(articleDetails.getTitle()); article.setContent(articleDetails.getContent()); article.setUpdatedAt(new Date()); return articleRepository.save(article); } public void deleteArticle(Long id) { Article article = getArticleById(id); articleRepository.delete(article); } } ``` #### 控制器层 ```java @RestController @RequestMapping("/api/articles") public class ArticleController { @Autowired private ArticleService articleService; @GetMapping("") public List<Article> getAllArticles() { return articleService.getAllArticles(); } @GetMapping("/{id}") public Article getArticleById(@PathVariable(value = "id") Long id) { return articleService.getArticleById(id); } @PostMapping("") public Article createArticle(@Valid @RequestBody Article article) { return articleService.createArticle(article); } @PutMapping("/{id}") public Article updateArticle(@PathVariable(value = "id") Long id, @Valid @RequestBody Article articleDetails) { return articleService.updateArticle(id, articleDetails); } @DeleteMapping("/{id}") public ResponseEntity<?> deleteArticle(@PathVariable(value = "id") Long id) { articleService.deleteArticle(id); return ResponseEntity.ok().build(); } } ``` #### 异常处理 ```java @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(ResourceNotFoundException.class) public ResponseEntity<?> resourceNotFoundException(ResourceNotFoundException ex) { ErrorResponse errorResponse = new ErrorResponse(HttpStatus.NOT_FOUND.value(), ex.getMessage()); return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); } @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<?> validationException(MethodArgumentNotValidException ex) { ErrorResponse errorResponse = new ErrorResponse(HttpStatus.BAD_REQUEST.value(), "Validation failed"); List<String> errors = ex.getBindingResult().getFieldErrors().stream() .map(error -> error.getField() + ": " + error.getDefaultMessage()) .collect(Collectors.toList()); errorResponse.setErrors(errors); return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); } @ExceptionHandler(Exception.class) public ResponseEntity<?> defaultExceptionHandler(Exception ex) { ErrorResponse errorResponse = new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Internal server error"); return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR); } } ``` #### 异常类 ```java public class ResourceNotFoundException extends RuntimeException { private String resourceName; private String fieldName; private Object fieldValue; public ResourceNotFoundException(String resourceName, String fieldName, Object fieldValue) { super(String.format("%s not found with %s : '%s'", resourceName, fieldName, fieldValue)); this.resourceName = resourceName; this.fieldName = fieldName; this.fieldValue = fieldValue; } public String getResourceName() { return resourceName; } public String getFieldName() { return fieldName; } public Object getFieldValue() { return fieldValue; } } ``` #### 错误响应类 ```java public class ErrorResponse { private int status; private String message; private List<String> errors; public ErrorResponse(int status, String message) { this.status = status; this.message = message; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public List<String> getErrors() { return errors; } public void setErrors(List<String> errors) { this.errors = errors; } } ``` ### 配置文件 #### application.properties ```properties spring.datasource.url=jdbc:mysql://localhost:3306/blog?useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2B8 spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true ``` ### 总结 以上是一个简单的前后端分离博客系统Java 代码示例,该示例使用了 Spring Boot 框架、MySQL 数据库和 JPA 数据访问层。该示例中,实体类、数据访问层、服务层和控制器层分别处理了数据的持久化、业务逻辑和 RESTful API 接口的实现,并使用了全局异常处理来处理异常情况。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值