Java-博客系统(前后端交互)

目录

前言

博客系统基本情况

1 创建项目,引入依赖

2 数据库设计 

2.1 分析

2.2 建库建表

3 封装数据库 

3.1 在java目录下创建DBUtil类,通过这个类对数据库进行封装

3.2 在java目录下创建实体类(博客类Blog)

3.2 在java目录下创建实体类(用户类User)

3.3  在java目录下创建BlogDao类

3.4 在java目录下创建UserDao类 

4 获取博客列表页

4.1 约定前后端交互接口

4.2 让前端给服务器发起请求

4.3 服务器处理上述请求,返回响应数据

4.4 让前端处理上述响应数据

5 获取博客详情页 

 5.1 约定前后端交互接口

5.2 让前端给服务器发起请求 

5.3 服务器处理上述请求,返回响应数据

5.4 让前端处理上述响应数据

6 实现登录

6.1 分析

 6.2 约定前后端交互接口

6.3 前端发起请求 

6.4 服务器处理响应

7 实现强制登录

7.1 约定前后端交互接口

7.2 让前端给服务器发起请求

7.3 服务器处理上述请求,返回响应

8 实现显示用户信息

8.1 约定前后端交互接口

 8.2 让前端给服务器发起请求

8.3 服务器处理上述请求,返回响应

8.4 让前端处理上述响应数据

9 实现退出功能

9.1 约定前后端交互接口

9.2  让前端给服务器发起请求

9.3 服务器处理上述请求,返回响应

10 实现发布博客

10.1 约定前后端交互接口

10.2 让前端给服务器发起请求

10.3 服务器处理上述请求,返回响应

前言

基于前后端交互实现一个博客系统


博客系统基本情况

这里实现的博客系统主要分为四个页面,博客列表页、博客详情页、博客编辑页、博客登录页,主要实现的功能有:实现博客列表页的展示功能,实现博客详情页的展示功能、登录功能、强制用户登录后查看信息、显示用户信息、实现注销(退出登录)、发布博客、删除博客。


1 创建项目,引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>blog_system</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.15.0</version>
        </dependency>

    </dependencies>

</project>

2 数据库设计 

2.1 分析

创建一个数据库,两张表

1)博客表:blog(blogId,title,content,postTime,userld)

2)用户表:user(userId,username,password)

2.2 建库建表

create database if not exists blog_system charset utf8;

use blog_system;

drop table if exists blog;
drop table if exists user;

create table blog (
    blogId int primary key auto_increment,
    title varchar(1024),
    content varchar(4096),
    postTime datetime,
    userId int
);

create table user(
    userId int primary key auto_increment,
    username varchar(50) unique,
    password varchar(50)
);

-- 插入一些数据,方便后续进行测试
insert into blog values(1, '这是第一篇博客', '#今天开始写代码', now(), 1);
insert into blog values(2, '这是第二篇博客', '#今天开始写代码', now(), 1);
insert into blog values(3, '这是第三篇博客', '#今天开始写代码', now(), 1);

insert into user values(1, 'zhangsan', '123');
insert into user values(2, 'lisi', '456');

3 封装数据库 

由于这个博客系统要给多个用户使用,因此对数据库进行封装

3.1 在java目录下创建DBUtil类,通过这个类对数据库进行封装

public class DBUtil {
    private volatile static DataSource dataSource = null;

    private static DataSource getDataSource() {
        if (dataSource == null) { //这个if表示判断是否加锁
            synchronized (DBUtil.class) {
                if (dataSource == null) { //这个if确保有一个线程把dataSource给new出来
                    dataSource = new MysqlDataSource();
                    ((MysqlDataSource) dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/blog_system?characterEncoding=utf8&useSSl=false");
                    ((MysqlDataSource) dataSource).setUser("root");
                    ((MysqlDataSource) dataSource).setPassword("22222");

                }
            }
        }
        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) {
                throw new RuntimeException(e);
            }

        }
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

1.上述代码使用try、catch而不用throws来处理异常是因为,如果使用throws,一旦哪一个发生了异常,就有可能存在后面的还有需要释放的资源没释放掉的情况。

2.释放顺序和创建的顺序是相反的。

3.如果不判断是否为空直接close,就会对所有的资源close,有些时候ResultSet就没有创建,直接close,就会导致null异常,所以在close之前要判断一下是否为空。

3.2 在java目录下创建实体类(博客类Blog)

//blog对应博客表里的属性
public class Blog {
    private int blogId;
    private String title;
    private String content;
    private Timestamp postTime;
    private int userId;

    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 String getPostTime() {
        //这里修改一下返回值的类型,让最终得到的时间是一个格式化时间
        //使用Java标准库里的SimpleDateFormat类,来完成时间戳到格式化时间的转换
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
        return simpleDateFormat.format(postTime);
    }

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

    public int getUserId() {
        return userId;
    }

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

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

3.2 在java目录下创建实体类(用户类User)

//User对应用户表里的属性
public class User {
    private int userId;
    private String username;
    private String password;

    public int getUserId() {
        return userId;
    }

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

    public String getUsername() {
        return username;
    }

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

    public String getPassword() {
        return password;
    }

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

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

3.3  在java目录下创建BlogDao类

//通过这个类来完成针对blog表的操作
public class BlogDao {
    //1.新增操作
    Connection connection = null;
    PreparedStatement statement = null;
    public void insert(Blog blog) {
        try {
            //1.建立连接
            connection = DBUtil.getConnection();
            //2.构造数据库
            String sql = "insert into blog values(null,?,?,now(),?)";
            statement = connection.prepareStatement(sql);
            statement.setString(1,blog.getTitle());
            statement.setString(2,blog.getContent());
            statement.setInt(3,blog.getUserId());
            //3.执行SQL
            statement.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            DBUtil.close(connection,statement,null);
        }

    }
    //2.查询博客列表
    public List<Blog> getBlogs() {
        List<Blog> blogList = 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"));
                //此处读到正文是整个文章,在博客列表希望显示一笑部分
                //此处需要对content做一个简单的截断
                String content = resultSet.getString("content");
                if (content.length() > 100) {
                    content = content.substring(0,100) + "...";
                }
                blog.setContent(content);
                blog.setPostTime(resultSet.getTimestamp("postTime"));
                blog.setUserId(resultSet.getInt("userId"));
                blogList.add(blog);
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return blogList;
    }
    //3.根据博客id查询指定的博客
    public Blog getBlog(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();
            //此处拿着blogId进行查询,blogId作为主键,是唯一的
            //所以不用循环
            if (resultSet.next()) {
                Blog blog = new Blog();
                blog.setBlogId(resultSet.getInt("blogId"));
                blog.setTitle(resultSet.getString("title"));
                //这个方法是在获取博客详情页的时候调用,所以不需要截断
                blog.setContent(resultSet.getString("content"));
                blog.setPostTime(resultSet.getTimestamp("postTime"));
                blog.setUserId(resultSet.getInt("userId"));
                return blog;
            }
        } catch (SQLException e) {
           throw new RuntimeException(e);
        } 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 = "select * from blog where blogId";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,blogId);
            statement.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            DBUtil.close(connection,statement,null);
        }
    }
}

3.4 在java目录下创建UserDao类 

public class UserDao {
    //1.根据userId来查到对应的用户信息(获取用户信息)
    public User getUserById(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) {
            throw new RuntimeException(e);
        } finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }
    //2.根据username来查到对应的用户信息(登录)
    public User getUserByName(String username) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            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) {
            throw new RuntimeException(e);
        } finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }
}

4 获取博客列表页

在博客列表页加载的时候,通过ajax给服务器发起请求,从服务器(数据库)拿到博客列表页数据并显示到页面上

4.1 约定前后端交互接口

请求

GET / blog

响应

HTTP/1.1  200 OK

Content-Type:application/json

[

     { 

        blogId:1,

        title:"这是标题",

        content:"这是正文",

        postTime:"2024-4-10  9:00:00",

        userId:1

     }

]

4.2 让前端给服务器发起请求

引入第三方库

<script>
        //对这个函数进行封装
        function getBlogs() {
            $.ajax({
                type: 'get',
                url: 'blog',
                success: function (body) {
                    //根据服务器返回的响应,构造页面的片段
                }
            });
        }
        //调用函数
        getBlogs();
</script>

4.3 服务器处理上述请求,返回响应数据

@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //查询数据库,得到博客列表
        //之前已经对BlogDao进行封装了,因此直接调用
        BlogDao blogDao = new BlogDao();
        //得到的是一个List数组,jackson就会把结果转成数组,数组里的每个元素又是一个Blog对象
        //转成数组的每个元素也就是json构成的blog对象
        List<Blog> blogs = blogDao.getBlogs();
        //把博客列表按照json格式返回给浏览器
        String respJson = objectMapper.writeValueAsString(blogs);
        resp.setContentType("application/json;charset=utf8");
        resp.getWriter().write(respJson);
    }
}

4.4 让前端处理上述响应数据

将响应的数据构造成HTML片段,显示到页面上

<script>
        //对这个函数进行封装
        function getBlogs() {
            $.ajax({
                type: 'get',
                url: 'blog',
                success: function (body) {
                    //根据服务器返回的响应,构造页面的片段
                    let containerDiv = document.querySelector('.container-right');
                    for (let i = 0; i < body.length; i++) {
                        //blog就是一个形如{blogId:1,title,"这是标题" ...}
                        let blog = body[i];
                        //构造整个博客
                        let blogDiv = document.createElement('div');
                        blogDiv.className = 'blog';
                        //构建标题
                        let titleDiv = document.createElement('div');
                        titleDiv.className = 'title';
                        titleDiv.innerHTML = blog.title;
                        //构建发布时间
                        let dateDiv = document.createElement('div');
                        dateDiv.className = 'date';
                        dateDiv.innerHTML = blog.postTime;
                        //构建博客的摘要
                        let descDiv = document.createElement('div');
                        descDiv.className = 'desc';
                        descDiv.innerHTML = blog.content;
                        //构造全文查看按钮
                        let a = document.createElement("a");
                        a.innerHTML = '查看全文 &gt;&gt;'
                        //点击不同的博客,跳转到不同的博客详情页
                        a.href = 'blog_detail.html?blogId=' + blog.blogId;
                        //把上面的操作进行组合
                        blogDiv.appendChild(titleDiv);
                        blogDiv.appendChild(dateDiv);
                        blogDiv.appendChild(descDiv);
                        blogDiv.appendChild(a);
                        //把上面blogDiv最终组合到页面上
                        containerDiv.appendChild(blogDiv);
                    }
                }
            });
        }
        //调用函数
        getBlogs();
</script>

此时我们通过访问页面观察

页面展示的时间是一个时间戳,我们期望展示的时间是一个格式化的时间。

这里修改一下返回值的类型,让最终得到的时间是一个格式化时间 

此时我们的获取博客列表页就完成了 

5 获取博客详情页 

我们期望点击博客列表页不同的查看全文,会跳转过去并且URL后面会带有不同的query string

在博客详情页中,通过ajax给服务器发送请求,根据blogId来查询数据库中博客的具体内容在返回

 5.1 约定前后端交互接口

请求

GET / blog?blogId=1

响应

HTTP / 1.1 200 OK

Content-Type:application/json

{

        blogId:1,

        title:"这是第一篇博客",

        content:"这是博客正文",

        postTime:"2024-4-10  9:00:00",

        userId:1

}

5.2 让前端给服务器发起请求 

<script>
        function getBlog() {
            $.ajax({
                type:'get',
                //通过这个location.search方式拿到页面url中的query string
                url:'blog' + location.search,
                success:function(blog) {
                    //这里的blog就是返回一篇博客的内容
                }
            });
        }
        getBlog();
</script>

5.3 服务器处理上述请求,返回响应数据

@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //查询数据库,得到博客列表
        //之前已经对BlogDao进行封装了,因此直接调用
        BlogDao blogDao = new BlogDao();
        String respJson = "";
        String blogId = req.getParameter("blogId");
        if (blogId == null) {
            //请求中没有query string,请求来自博客列表页
            List<Blog> blogs = blogDao.getBlogs();
            //把博客列表按照json格式返回给浏览器
            respJson = objectMapper.writeValueAsString(blogs);
        }else {
            //请求中有query string,请求来自博客详情页
            Blog blog = blogDao.getBlog(Integer.parseInt(blogId));
            respJson = objectMapper.writeValueAsString(blog);
        }
        resp.setContentType("application/json;charset=utf8");
        resp.getWriter().write(respJson);
    }
}

5.4 让前端处理上述响应数据

<script>
        function getBlog() {
            $.ajax({
                type: 'get',
                //通过这个location.search方式拿到页面url中的query string
                url: 'blog' + location.search,
                success: function (blog) {
                    //这里的blog就是返回一篇博客的内容
                    let h3 = document.querySelector('.container-right h3');
                    h3.innerHTML = blog.title;
                    let dataDiv = document.querySelector('.container-right .date');
                    dataDiv.innerHTML = blog.postTime;
                    let contentDiv = document.querySelector('.container-right .content');
                    contentDiv.innerHTML = blog.content;
                }
            });
        }
        getBlog();
</script>

此时我们的博客详情页就完成了,但是还有一个小问题,上述是md原始数据,这是因为content部分的内容是使用markdown写的,直接通过blog.content得到的正文内容是一个简单的文本格式,因此需要引入第三方库(editor.md)来完成渲染。

<!-- 引入 editor.md 的依赖 -->
<link rel="stylesheet" href="editor.md/css/editormd.min.css" />
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="editor.md/lib/marked.min.js"></script>
<script src="editor.md/lib/prettify.min.js"></script>
<script src="editor.md/editormd.js"></script>

<script>
        function getBlog() {
            $.ajax({
                type: 'get',
                //通过这个location.search方式拿到页面url中的query string
                url: 'blog' + location.search,
                success: function (blog) {
                    //这里的blog就是返回一篇博客的内容
                    let h3 = document.querySelector('.container-right h3');
                    h3.innerHTML = blog.title;
                    let dataDiv = document.querySelector('.container-right .date');
                    dataDiv.innerHTML = blog.postTime;
                    //这种设置方式,页面显示的是md原始数据
                    // let contentDiv = document.querySelector('.container-right.content');
                    // contentDiv.innerHTML = blog.content;
                    //在这个方法的第一个参数,必须是一个html标签的id属性
                    editormd.markdownToHTML('content', { markdown: blog.content });
                }
            });
        }
        getBlog();
    </script>

此时我们的博客详情页就彻底完成了 

6 实现登录

6.1 分析

1 在登录页面,在输入框中输入用户名和密码,点击登录,就会给服务器发起HTTP请求(这里我们使用form)

2 服务器在处理登录请求的时候,读取用户名和密码,在数据库中查询,如果匹配就登录成功,创建会话并跳转到博客列表页。

3 由于登录成功后直接进行重定向跳转,这种重定向跳转不需要前端写额外的代码。

 6.2 约定前后端交互接口

请求

POST/login

Content-Type:application/x-www-form-urlencoded

username=zhangsan&password=123

响应

HTTP/1.1 302

Location:blog_list.html

6.3 前端发起请求 

<form action="login" method="post">
    <div class="row">
        <span>用户名</span>
        <input type="text" id="username" name="username">
    </div>
    <div class="row">
        <span>密码</span>
        <input type="password" id="password" name="password">
    </div>
    <div class="row">
        <input type="submit" id="submit" value="登录">
    </div>
</form>

6.4 服务器处理响应

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.读取参数中的用户名和密码
        req.setCharacterEncoding("utf8");
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        //验证参数
        if (username == null || username.length() == 0 || password == null || password.length() == 0) {
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("输入的用户名或密码为空");
            return;
        }
        //2.查询数据库,看用户名密码是否正确
        UserDao userDao = new UserDao();
        User user = userDao.getUserByName(username);
        if (user == null) {
            //用户名不存在
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("输入的用户名或密码不正确");
            return;
        }
        if (!password.equals(user.getPassword())) {
            //密码不正确
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("输入的用户名或密码不正确");
            return;
        }
        //3.创建会话
        HttpSession session = req.getSession(true);
        session.setAttribute("user",user);
        //4.跳转到主页
        resp.sendRedirect("blog_list.html");
    }
}

此时实现登录就完成了 

7 实现强制登录

在博客列表页、详情页、编辑页,判断当前用户是否已经登陆,如果未登录,则强制跳转到登录页

7.1 约定前后端交互接口

请求

GET / login

响应

登陆成功:HTTP/1.1 200

登录失败:HTTP/1.1 404

7.2 让前端给服务器发起请求

//定义新的函数,获取登录状态
function getLoginStatus() {
    $.ajax({
        type: 'get',
        url: 'login',
        success: function (body) {
            //已经登录的状态
            console.log("已经登录了!");
        },
        error: function () {
            //error这里对应的回调函数就是在状态码不为2XX的时候出发
            //服务器返回403的时候,就会触发error部分的逻辑
            //这里强制页面跳转到博客登录页
            //这里不适用302这样的重定向跳转,是因为302这种响应无法被ajax直接处理
            location.assign('login.html');
        }
    });
}
getLoginStatus();

上面没有定义<script></script>标签是因为是在一个script标签里写的。

7.3 服务器处理上述请求,返回响应

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //通过这个方法来反馈当前用户的登录状态
        //直接看会话是否存在
        //此处不仅要求会话存在,还要求会话中要保存user对象
        HttpSession session = req.getSession(false);
        if (session == null) {
            //会话不存在
            resp.setStatus(403);
            return;
        }
        User user = (User) session.getAttribute("user");
        if (user == null) {
            //user不存在
            resp.setStatus(403);
            return;
        }
        //说明两个都存在,直接返回200
        //此处默认就是200,不写也行
        resp.setStatus(200);
    }
}

 

当我们访问博客列表页就会跳转到博客登录页,接下来我们抓包看一下。 

第一请求就是我们访问博客列表数据

第二请求就是获取登录状态,此时返回的响应就是403

我们的目标是让博客列表页、详情页、编辑页都能够有强制登录

因此我们把获取登陆状态的代码,单独提出来,在博客列表页、详情页、编辑页中通过html中的script标签来引用这样的文件内容

现在我们的实现强制登录就完成了

8 实现显示用户信息

在博客列表页显示的是当前登录的用户信息,在博客详情页显示的是当前文章作者的信息,在页面加载的时候,给服务器发起ajax请求,服务器返回对应的用户数据,根据发起请求不同的页面,服务器返回不同的信息。

8.1 约定前后端交互接口

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

请求

GET / userInfo

响应

HTTP/1.1 200 OK

Content-Type:application/json

{

        userId:1,

        username:'zhangsan'

}

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

请求

GET / authroInfo?blogId=1

响应

HTTP/1.1 200 OK

Content-Type:application/json

{

        userId:1,

        username:'zhangsan'

}

 8.2 让前端给服务器发起请求

博客列表页

//获取当前登录的用户信息
function getUserInfo() {
    $.ajax({
        type:'get',
        url:'userInfo',
        success:function(user) {

        }
    });
}
getUserInfo();

博客详情页

//获取作者信息
function getAuthorInfo() {
    $.ajax({
        type:'get',
        url:'authorInfo' + location.search,
        success:function(user) {
                    
        }
    });
}

8.3 服务器处理上述请求,返回响应

博客列表页

@WebServlet("/userInfo")
public class UserInfoServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //从会话中拿到用户的信息然后返回
        HttpSession session = req.getSession(false);
        if (session == null) {
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前用户未登录");
            return;
        }
        User user = (User) session.getAttribute("user");
        if (user == null) {
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前用户未登录");
            return;
        }
        //此时把user对象转成json字符串并返回给浏览器
        resp.setContentType("application/json;charset=utf8");
        //这里需要把密码去掉在返回
        user.setPassword("");
        String respJson = objectMapper.writeValueAsString(user);
        resp.getWriter().write(respJson);
    }
}

博客详情页 

@WebServlet("/authorInfo")
public class AuthorInfoServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.先拿到请求中的blogId
        String blogId = req.getParameter("blogId");
        if (blogId == null) {
            resp.setContentType("text/html:charset=utf8");
            resp.getWriter().write("请求中缺少blogId");
            return;
        }
        //2.在blog表中查询对应的Blog对象
        BlogDao blogDao = new BlogDao();
        Blog blog = blogDao.getBlog(Integer.parseInt(blogId));
        if (blog == null) {
            resp.setContentType("text/html:charset=utf8");
            resp.getWriter().write("blogId没有找到");
            return;
        }
        //3.根据blog对象中的userId,从user表中查到对应的作者
        UserDao userDao = new UserDao();
        User user = userDao.getUserById(blog.getUserId());
        if (user == null) {
            resp.setContentType("text/html:charset=utf8");
            resp.getWriter().write("userId没有找到");
            return;
        }
        //4.把这个user对象返回给浏览器
        user.setPassword("");
        String respJson = objectMapper.writeValueAsString(user);
        resp.setContentType("application/json;charset=utf8");
        resp.getWriter().write(respJson);
    }
}

8.4 让前端处理上述响应数据

博客列表页

//获取当前登录的用户信息
function getUserInfo() {
    $.ajax({
        type: 'get',
        url: 'userInfo',
        success: function (user) {
            //把拿到的响应数据取出其中的uesrname设置到页面的h3标签中
            let h3 = document.querySelector('.card h3');
            h3.innerHTML = user.username;
        }
    });
}
getUserInfo();

通过抓包看一下详情

此时在博客列表页就能获取到用户的信息了

博客详情页

//获取作者信息
function getAuthorInfo() {
    $.ajax({
        type: 'get',
        url: 'authorInfo' + location.search,
        success: function (user) {
            let h3 = document.querySelector('.card h3');
            h3.innerHTML = user.username;
        }
    });
}
getAuthorInfo();

我们使用lisi这个用户登录,当我们点击查看全文就可以获取到当前文章的作者信息了,也就是zhangsan

此时我们的博客详情页就能获取到当前文章的作何信息了

此时我们的实现显示用户信息就完成了

9 实现退出功能

在博客列表页、博客详情页、博客编辑页的导航栏中都有一个"注销"按钮,让用户点击"注销" 的时候,就能触发一个HTTP请求

9.1 约定前后端交互接口

请求

GET / logout

响应  直接重定向到博客登录页

HTTP/1.1 302

Location:login.html

9.2  让前端给服务器发起请求

直接给a标签设置href属性即可

9.3 服务器处理上述请求,返回响应

@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.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前未登录");
            return;
        }
        //把会话中的user属性删除
        session.removeAttribute("user");
        //跳转到博客登录页
        resp.sendRedirect("login.html");
    }
}

我们在博客编辑页点击注销按钮,抓包看一下详情,我们可以看到页面会直接跳转到博客登录页

 此时我们的实现退出功能就完成了

10 实现发布博客

当用户点击提交的时候,构造HTTP请求,把此时页面中的标题和正文都传输到服务器这边,然后服务器把这个数据存入数据库,由于这里需要提交数据,因此使用form来构造请求

10.1 约定前后端交互接口

请求

POST/blog

Content-Type:x-www-form-urlencoded

title=这是标题&content=这是正文

响应

HTTP/1.1 302

Location:blog_list.html

10.2 让前端给服务器发起请求

10.3 服务器处理上述请求,返回响应

@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.读取请求中的参数
        req.setCharacterEncoding("utf8");
        String title = req.getParameter("title");
        String content = req.getParameter("content");
        if (title == null || title.length() == 0 || content == null || content.length() == 0) {
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前传过来的标题或者正文为空,无法新增博客");
            return;
        }
        //2.拿到会话
        HttpSession session = req.getSession(false);
        if (session == null) {
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前用户未登录");
            return;
        }
        //3.从会话中拿到用户的信息
        User user = (User) session.getAttribute("user");
        if (user == null) {
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前用户未登录");
            return;
        }
        //4.构造blog对象
        Blog blog = new Blog();
        blog.setTitle(title);
        blog.setContent(content);
        //我们能访问当前的博客编辑页,说明我们处于登录状态
        //因此我们从会话中拿到当前登录用户的userId,设置进去即可
        blog.setUserId(user.getUserId());
        //5.插入到数据库中
        BlogDao blogDao = new BlogDao();
        blogDao.insert(blog);
        //6.返回302重定向,跳转到博客列表页
        resp.sendRedirect("blog_list.html");
    }
}

此时我们写下博客然后提交,此时我们新写的博客就能再页面展示了

此时我们的实现发布博客功能就完成了


总结:整个过程是基于前后端交互来实现博客系统,主要是前后端交互的过程,以及前后端是怎样交互的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/542695.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vwmare+Ubuntu20.04安装超级保姆级完整教程

强烈建议先完整的看完一遍教程在进行安装以免出现问题&#xff01;&#xff01;&#xff01; 如果遇到error&#xff1a;建议复制error后面的信息然后到浏览器搜索&#xff0c;查找解决方案&#xff0c;其次在进行某个不确定的操作时&#xff0c;建议先保存快照&#xff0c;这样…

uboot操作指令1

文章目录 前言一、信息查询命令1.bdinfo用于查看板子的信息2.printenv 打印环境变量3.version查看uboot版本 二、环境变量操作命令1.setenv修改环境变量2.setenv新建环境变量3.setenv删除环境变量 三、内存操作命令1.md 命令2.nm命令3.mm命令4.mw命令 四、网络操作命令1.ping命…

Zookeeper与kafka

目录 一、zookeeper 1.1.zookeeper概述 1.2.Zookeeper 工作机制 1.3. Zookeeper 特点 1.4.Zookeeper 数据结构 1.5.Zookeeper 应用场景 1.6.Zookeeper 选举机制 第一次启动选举机制 非第一次启动选举机制 选举Leader规则&#xff1a; 1.7.部署 Zookeeper 集群 1.7.…

AI人工智能讲师大模型培训讲师叶梓 大语言模型(LLM)在科学文献摘要领域的应用

大语言模型&#xff08;LLM&#xff09;在科学文献摘要领域的应用是一个前沿且迅速发展的技术趋势。通过结合GitHub上yobibyte的Compressor项目&#xff0c;我们可以深入探讨这一技术方案的潜力和实现方式。 技术背景 随着科学研究的快速发展&#xff0c;每天都有大量的科学文…

matlab学习(三)(4.9-4.15)

一、空域里LSB算法的原理 1.原理&#xff1a; LSB算法通过替换图像像素的最低位来嵌入信息。这些被替换的LSB序列可以是需要加入的水印信息、水印的数字摘要或者由水印生成的伪随机序列。 2.实现步骤&#xff1a; &#xff08;1&#xff09;将图像文件中的所有像素点以RGB形…

服务器数据恢复—ext3文件系统下raid5数据恢复案例

服务器数据恢复环境&故障情况&#xff1a; 某企业光纤存储上有一组由16块硬盘组建的raid5阵列。管理员发现该光纤存储上的卷无法挂载&#xff0c;经过检查发现raid5阵列中有2块硬盘离线&#xff0c;于是联系我们数据恢复中心要求数据恢复工程师到现场恢复服务器存储上的数据…

【可能是全网最丝滑的LangChain教程】七、LCEL表达式语言

系列文章地址 【可能是全网最丝滑的LangChain教程】一、LangChain介绍-CSDN博客 【可能是全网最丝滑的LangChain教程】二、LangChain安装-CSDN博客 【可能是全网最丝滑的LangChain教程】三、快速入门LLM Chain-CSDN博客 【可能是全网最丝滑的LangChain教程】四、快速入门Re…

在js中计算两个时间段重叠的时长问题

文章目录 前言一、过程分析二、实现代码(js)总结 前言 最近遇到一个需求&#xff0c;就是在js中计算两段时间的重叠时长问题&#xff0c;这里记录一下。 一、过程分析 两段时间的重叠问题&#xff0c;一般有3中情况 两段时间完全无重叠&#xff0c;也就是无任何交集两段时间…

软考中级--网络工程师-计算机基础与理论第二节无线基础知识

IEEE802.11 规定了多种 WLAN 通信标准&#xff0c;其中&#xff08; &#xff09;与其他标准采用的频段不同&#xff0c;因而不能兼容。 A IEEE802.11a B IEEE802.11b C IEEE802.11g D IEEE802.11n 试题答案 正确答案&#xff1a; A 答案解析 IEEE 802.11a规定采用5GHz的 ISM频…

007Node.js安装自启动工具supervisor运行js文件

在vscode中&#xff0c;某些运行中的程序修改xx.js文件后&#xff0c;通过CtrlC终止再重新运行。supervisor是自启动工具&#xff0c;会不停的查看你的文件&#xff0c;一旦发现有修改&#xff0c;就立马重新载入运行。 我们可以通过安装supervisor代替node命令运行xx.js。终端…

环境变量与进程优先级

目录 进程的优先级 什么是优先级 为什么要有优先级 linux的优先级特点和查看方式 其他概念 环境变量 命令行参数 环境变量 查看环境变量方法 修改PATH 其他环境变量 进程的优先级 什么是优先级 优先级&#xff1a;指定进程获得某种资源的先后顺序。&#xff08;优先级…

Python数据分析案例40——电商直播间成交金额预测

承接上一篇案例电商直播间提取的特征&#xff0c;进而做一篇机器学习的案例&#xff0c;来预测直播间的成交金额。 Python数据分析案例39——电商直播间评论可视化分析&#xff08;LDA&#xff09; 1. 引言 1.1 直播电商与传统电商的比较 直播电商作为一种新兴的电子商务模式…

c语言中<string.h>的strstr与strtok函数

c语言中string.h的strstr与strtok函数 代码运行结果 代码 #include <stdio.h> #include <string.h>///1.在字符串str1里面,查找第一次出现str2的位置 //char * strstr(const char * str1,const char * str2)///2.sep为分割符,根据分割符来对str进行分割 //char * …

【WEEK7】 【DAY5】JDBC—PreparedStatement Object【English Version】

2024.4.12 Friday Following 【WEEK7】 【DAY4】JDBC—Statement Object【English Version】 Contents 10.3.PreparedStatement Object10.3.1.PreparedStatement can prevent SQL injection, more efficient than statement10.3.2. Insertion10.3.3. Deletion10.3.4. Update10.…

Windows版PHP7.4.9解压直用(免安装-绿色-项目打包直接使用)

安装版和解压版 区别 安装版: 安装方便&#xff0c;下一步------下一步就OK了&#xff0c;但重装系统更换环境又要重新来一遍&#xff0c;会特别麻烦解压版&#xff08;推荐&#xff09;&#xff1a; 这种方式&#xff08;项目打包特别方便&#xff09;能更深了解mysql的配置&…

C 408—《数据结构》易错考点200题(含解析)

目录 Δ前言 一、绪论 1.1 数据结构的基本概念 : 1.2 算法和算法评价 : 二、线性表 2.2 线性表的顺序表示 : 2.3 线性表的链式表示 : 三、栈、队列和数组 3.1 栈 3.2 队列 3.3 栈和队列的应用 3.4 数组和特殊矩阵 四、串 4.2 串的模式匹配 五、树与二叉树 5.1 树的基…

StarUML笔记之从UML图生成C++代码

StarUML笔记之从UML图生成C代码 —— 2024-04-14 文章目录 StarUML笔记之从UML图生成C代码1.Add Diagram2.在TOOLBOX中左键点击Class,松开,然后在中间画面再左键点击&#xff0c;即可出现UML3.修改类图&#xff0c;并添加接口&#xff0c;方法&#xff0c;属性&#xff0c;我…

超干!如何编写完美的Python命令行程序?

这篇文章将教你如何编写完美的 Python 命令行程序&#xff0c;提高团队的生产力&#xff0c;让大家的工作更舒适。 作为 Python 开发者&#xff0c;我们经常要编写命令行程序。比如在我的数据科学项目中&#xff0c;我要从命令行运行脚本来训练模型&#xff0c;以及计算算法的…

分享免费财务软件,比花钱买的还好用!

领取方式&#xff1a; 复制该链接在浏览器打开&#xff1a;网页链接扫码登陆。进入系统&#xff0c;创建账套即可直接使用&#xff0c;如图所示&#xff1a; 功能&#xff1a; 功能1、智能会计凭证&#xff1a;可以自动匹配科目、自动填充相应信息、检测到异常情况&#xff…

世界各国柴油价格22.7统计

数据详情介绍&#xff1a; 统计时间为2022年7月4日。在该月份&#xff0c;全球柴油的平均价格为每升1.43美元。然而&#xff0c;各国间存在明显的价格差异。一般而言&#xff0c;西欧等发达国家的价格基本在每升2美元以上&#xff1b;相反&#xff0c;像伊朗、委内瑞拉、利比亚…