【JavaEE初阶】博客系统后端

news2024/11/21 1:40:43

文章目录

  • 一. 创建项目 引入依赖
  • 二. 设计数据库
  • 三. 编写数据库代码
  • 四. 创建实体类
  • 五. 封装数据库的增删查改
  • 六. 具体功能书写
    • 1. 博客列表页
    • 2. 博客详情页
    • 3. 博客登录页
    • 4. 检测登录状态
    • 5. 实现显示用户信息的功能
    • 6. 退出登录状态
    • 7. 发布博客

一. 创建项目 引入依赖

创建blog_system项目.将之前写的博客系统前端代码复制到webapp目录下.
在这里插入图片描述
pom.xml中引入Servlet mysql jackson三个依赖:
pom.xml:

<?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>
    </properties>

    <dependencies>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>


        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.14.2</version>
        </dependency>

    </dependencies>
</project>

二. 设计数据库

结合之前的需求,在当前博客系统中,主要涉及到两个实体.即 用户博客.
经过分析我们可以得到,用户博客之间是一对多的关系.即一个用户可以拥有多篇博客.
所以我们可以创建两张表,来表示用户和博客.
blog(blogId,title,content,postTime,userId)
user(userId,userName,password)

三. 编写数据库代码

把一些基本的数据库操作,先封装好.
在这里插入图片描述
需要手动粘贴至MySQL

-- 这个文件主要用来写 建库建表 语句

create database if not exists javaee_blog_system;
use javaee_blog_system;

-- 删除旧表,重新建表
drop table if exists user;
drop table if exists blog;

-- 创建表
create table blog(
    blogId int primary key auto_increment,
    title varchar(128),
    content varchar(4096),
    postTime datetime,
    userId int
);

create table user(
    userId int primary key auto_increment,
    username varchar(20) unique,-- 要求用户名和别人的不重复
    password varchar(20)
);

接下来是封装数据库的连接操作:
DBUtil:

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

import javax.sql.DataSource;
import java.sql.*;

public class DBUtil {
    private static DataSource dataSource = new MysqlDataSource();

    static {
        ((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/javaee_blog_system?characterEncoding=utf8&useSSL=false");
        ((MysqlDataSource)dataSource).setUser("root");
        ((MysqlDataSource)dataSource).setPassword("0828");
    }

    public static Connection getConnection() throws SQLException {
        return dataSource.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();
            }
        }
    }
}

四. 创建实体类

实体类就是和表中的记录对应的类:
blog表 =>Blog类对应Blog的一个对象,就对应表中的一条记录.
user表 =>User类对应User的一个对象,就对应表中的一个记录.
实体类有哪些属性,都是和当前表中的列是密切相关的.
User:


public class User {
  private int userId;
  private String username;

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

    private String password;
}

Blog:

import java.sql.Timestamp;

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 Timestamp getPostTime() {
        return postTime;
    }

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

    public int getUserId() {
        return userId;
    }

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

五. 封装数据库的增删查改

针对博客表,创建BlogDao;(Data Access Object)
针对用户表,创建UserDao.
上述Dao用来提供一些方法.来进行增删查改.
BlogDao:

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

//通过这个类,封装针对 博客表 的基本操作
//修改:删除+新增(不考虑)
public class BlogDao {
    //1.新增博客
    public void add(Blog blog){
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            //1.和数据库建立连接
            connection = DBUtil.getConnection();
            //2.构造SQL
            String sql = "insert into blog values(null,?,?,?,?)";
            statement = connection.prepareStatement(sql);
            statement.setString(1,blog.getTitle());
            statement.setString(2, blog.getContent());
            statement.setTimestamp(3,blog.getPostTime());
            statement.setInt(4,blog.getUserId());
            //3.执行SQL
            statement.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            DBUtil.close(connection,statement,null);
        }
    }

    //2.根据博客id来查询指定博客(博客详情页)
    public Blog selectById(int blogId){
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            //1.与数据库建立连接
            connection = DBUtil.getConnection();
            //2.构造sql
            String sql = "select * from blog where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,blogId);
            //3.执行sql
            resultSet = statement.executeQuery();
            //4.遍历结果集合
            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"));
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            //5.释放资源
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }

    //3.查询数据库中所有的博客列表(用于博客列表页)
    public List<Blog> selectAll(){
        List<Blog> blogs = new ArrayList<>();
        
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            //1.与数据库建立连接
            connection = DBUtil.getConnection();
            //2.构造sql语句
            String sql = "select * from blog";
            statement = connection.prepareStatement(sql);
            //3.执行sql
            resultSet = statement.executeQuery();
            //4.遍历结果集合
            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() >= 100){
                    content = content.substring(0,100) +"...";
                }
                blog.setContent(content);
                blog.setPostTime(resultSet.getTimestamp("postTime"));
                blog.setUserId(resultSet.getInt("userId"));
                blogs.add(blog);
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return blogs;
    }

    //4.删除指定博客
    public void delect(int blogId){
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            //1.与数据库建立连接
            connection = DBUtil.getConnection();
            //2.构造sql语句
            String sql = "delete from blog where bolgId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,blogId);
            //3.执行sql
            statement.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            //4.关闭资源
            DBUtil.close(connection,statement,null);
        }
    }
}

UserDao:

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

//针对用户表提供的基本操作
public class UserDao {
    //1.根据 userId 来查用户信息
    public User selectById(int userId){
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            //1.与数据库建立连接
            connection = DBUtil.getConnection();
            //2.构造sql
            String sql = "select * from user where userId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,userId);
            //3.执行SQL
            resultSet = statement.executeQuery();
            //4.遍历结果集合
            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 {
            //5.释放资源
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }
    //2.根据username来查找用户信息.
    public User selectByUsername(int username){
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            //1.与数据库建立连接
            connection = DBUtil.getConnection();
            //2.构造sql
            String sql = "select * from user where userId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,username);
            //3.执行SQL
            resultSet = statement.executeQuery();
            //4.遍历结果集合
            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 {
            //5.释放资源
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }
}

六. 具体功能书写

1. 博客列表页

在这里插入图片描述
当前博客列表页上的数据都是写死的.正确的做法,应该是通过数据库读取数据显示到页面上.
此处就需要打通前后端交互的操作.
让博客列表页,在加载的时候,通过ajax给服务器发一个请求服务器查数据库获取到博客列表数据,返回给浏览器,浏览器再根据数据构造页面内容.这样的交互过程,也称为“前后端分离"
前端只向后端请求数据,而不请求具体的页面,后端也仅仅是返回数据.这样的设定的目的就是为了前端和后端更加解耦,由浏览器进行具体的页面渲染.减少了服务器的工作量.

上述进行实现博客列表页的基本思路

接下来需要:

  1. 约定前后端交互接口
  2. 开发后端代码
  3. 开发前端代码
  1. 约定前后端交互接口
    请求:GET /blog
    响应:使用json格式的数据来组织
[
	{
		blogId:1,
		title:"这是第一篇博客",
		content:"从今天开始,我要认真学习",
		postTime:"2023-08-01 09:40:45",
		userId:1
	}
	{
		blogId:1,
		title:"这是第一篇博客",
		content:"从今天开始,我要认真学习",
		postTime:"2023-08-01 09:40:45",
		userId:1
	}
	{
		blogId:1,
		title:"这是第一篇博客",
		content:"从今天开始,我要认真学习",
		postTime:"2023-08-01 09:40:45",
		userId:1
	}
]
  1. 开发后端代码
    BlogServlet:
package api;

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

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

public class BlogServlet extends HttpServlet {

    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        BlogDao blogDao = new BlogDao();
        List<Blog> blogs = blogDao.selectAll();
        //需要把blogs转成符合要求的 json 格式字符串
        String respJson = objectMapper.writeValueAsString(blogs);
        resp.setContentType("application/json;charset = utf8");
        resp.getWriter().write(respJson);
    }
}

在这里插入图片描述
此处注意文件所在位置.model是管理数据/操作数据的部分.

  1. 开发前端代码
    在博客列表页加载过程中,触发ajax,访问服务器中的数据.再把拿到的数据构造到页面中.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客列表页</title>
    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/blog_list.css">
</head>
<body>
    <!-- 导航栏 -->
    <div class="nav">
        <img src="image/logo2.jpg" alt="">
        <span class="title">我的博客系统</span>
        <!-- 用于占位,将a标签挤到右侧去 -->
        <div class="spacer"></div>
        <a href="#">主页</a>
        <a href="#">写博客</a>
        <a href="#">注销</a>
    </div>
    <!-- 页面主体部分 -->
    <div class="container">
        <!-- 左侧信息 -->
        <div class="container-left">
            <!-- 使用.card表示用户信息 -->
            <div class="card">
                <!-- 用户头像 -->
                <img src="image/touxiang.jpg" alt="">
                <!-- 用户名 -->
                <h3>xxxflower</h3>
                <a href="#">github地址</a>
                <div class="counter">
                    <span>文章</span>
                    <span>分类</span>
                </div>
                <div class="counter">
                    <span>2</span>
                    <span>1</span>
                </div>
            </div>
        </div>
        <!-- 右侧信息 -->
        <div class="container-right">
        </div>
    </div>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.3/jquery.min.js"></script>
    <script>
        // 在页面加载时, 向服务器发起请求, 获取博客列表数据
        function getBlogs() {
            $.ajax({
                type: 'get',
                url: 'blog',
                success: function(body) {
                    // 响应的正文 是一个 json 字符串, 此处已经被 jquery 自动解析成 js 对象数组了. 
                    // 直接 for 循环遍历即可. 
                    let containerRight = document.querySelector('.container-right');
                    for (let blog of body) {
                        // 构造页面内容, 参考之前写好的 html 代码
                        // 构造整个博客 div
                        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;';
                        // 期望点击之后能跳转到博客详情页. 为了让博客详情页知道是点了哪个博客, 把 blogId 给传过去
                        a.href = 'blog_detail.html?blogId=' + blog.blogId;
                        blogDiv.appendChild(a);

                        // 把 blogDiv 加到父元素中
                        containerRight.appendChild(blogDiv);
                    }
                }
            });
        }

        // 要记得调用
        getBlogs();
        </script>
</body>
</html>

效果如下图所示:
在这里插入图片描述
但是我们发现这里的时间显示有问题:
我们需要使用SimpleDateFormat类.注意该类使用时的书写格式!
在这里插入图片描述
在这里插入图片描述

2. 博客详情页

关于博客详情页,点击查看全文按钮,就能跳转到博客详情页中.跳转过去之后,在博客详情页中发起一个ajax,从服务器获取到当前的博客的具体内容.再进行显示.

分为以下几个步骤:

  1. 约定前后端交互接口
  2. 实现后端代码
  3. 实现前端代码
  1. 约定前后端交互接口
    请求:GET/blog?blogId = 1
    响应:
    HTTP/1.1 200 OK
    {
    blogId:1,
    title:“这是第一篇博客”,
    content:“从今天开始我要认真学习”,
    postTime:“2023-08-01 17:00:00”,
    userId:1
    }

  2. 实现后端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客详情页</title>
    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/blog_detail.css">
</head>
<body>
     <!-- 导航栏 -->
     <div class="nav">
        <img src="image/logo2.jpg" alt="">
        <span class="title">我的博客系统</span>
        <!-- 用于占位,将a标签挤到右侧去 -->
        <div class="spacer"></div>
        <a href="#">主页</a>
        <a href="#">写博客</a>
        <a href="#">注销</a>
    </div>
    <!-- 页面主体部分 -->
    <div class="container">
        <!-- 左侧信息 -->
        <div class="container-left">
            <!-- 使用.card表示用户信息 -->
            <div class="card">
                <!-- 用户头像 -->
                <img src="image/touxiang.jpg" alt="">
                <!-- 用户名 -->
                <h3>xxxflower</h3>
                <a href="#">github地址</a>
                <div class="counter">
                    <span>文章</span>
                    <span>分类</span>
                </div>
                <div class="counter">
                    <span>2</span>
                    <span>1</span>
                </div>
            </div>
        </div>
        <!-- 右侧信息 -->
        <div class="container-right">
            <!-- 博客标题 -->
            <h3 class="title"></h3>
            <!-- 博客发布时间 -->
            <div class="date"></div>
            <!-- 博客正文, 为了配合 editormd 进行格式转换, 此处务必改成 id  -->
            <div id="content">
                
            </div>
        </div>
    </div>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.3/jquery.min.js"></script>
    <!-- 要保证这几个 js 的加载在 jquery 之后. editor.md 依赖了 jquery -->
    <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>
        $.ajax({
            type: 'get',
            url: 'blog' + location.search,
            success: function(body) {
                // 处理响应结果, 此处的 body 就是表示一个博客的 js 对象. 
                // 1. 更新标题
                let titleDiv = document.querySelector('.container-right .title');
                titleDiv.innerHTML = body.title;
                // 2. 更新日期
                let dateDiv = document.querySelector('.date');
                dateDiv.innerHTML = body.postTime;
                // 3. 更新博客正文
                // 此处不应该直接把博客正文填充到这个标签里
                editormd.markdownToHTML('content', { markdown: body.content });
            }
        })
    </script>
</body>
</html>
  1. 开发后端代码
package api;

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 {
        BlogDao blogDao = new BlogDao();
        String blogId = req.getParameter("blogId");
        if(blogId == null){
            List<Blog> blogs = blogDao.selectAll();
            //需要把blogs转成符合要求的 json 格式字符串
            String respJson = objectMapper.writeValueAsString(blogs);
            resp.setContentType("application/json;charset = utf8");
            resp.getWriter().write(respJson);
        }else{
            // queryString 存在, 说明本次请求获取的是指定 id 的博客.
            Blog blog = blogDao.selectById(Integer.parseInt(blogId));
            if (blog == null) {
                System.out.println("当前 blogId = " + blogId + " 对应的博客不存在!");
            }
            String respJson = objectMapper.writeValueAsString(blog);
            resp.setContentType("application/json; charset=utf8");
            resp.getWriter().write(respJson);
        }
    }
}

在这里插入图片描述
这是因为浏览器自身也有缓存机制.
浏览器从服务器这边获取页面,这个操作是通过网络传输完成的,速度是比较慢的.浏览器就会把页面给缓存到本地(客户端电脑的硬盘上).后续再访问同一个页面,就直接读缓存.但是这样做有一些问题,即客户端命中缓存之后就不一定能及时感知到变化.
解决方法:强制刷新.(无视缓存,100%重新访问服务器)Ctrl + f5
在这里插入图片描述

3. 博客登录页

在这里插入图片描述
在此处输入用户名和密码,点击登录,就会触发一个http请求.
服务器验证用户名和密码,然后就可以根据结果,判定是否登录成功.

分为以下几个步骤:

  1. 约定前后端交互接口
  2. 实现前端代码
  3. 实现后端代码
  1. 约定前后端交互接口
    请求:
    POST/login
    username = zhangsan&password = 123
    响应:
    HTTP/1.1 302
    Location:blog_list.html

注意:
此处的响应应是from表单(本身就会触发页面跳转),响应是302才能够进行页面跳转.
如果是ajax请求(本身不会触发),响应是302,此时是无法进行跳转的.

  1. 实现前端代码
    往页面上加入from表单,使得点击登录操作能够触发请求.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客登录页</title>
    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/login.css">
</head>
<body>
     <!-- 导航栏 -->
     <div class="nav">
        <img src="image/logo2.jpg" alt="">
        <span class="title">我的博客系统</span>
        <!-- 用于占位,将a标签挤到右侧去 -->
        <div class="spacer"></div>
        <a href="#">主页</a>
        <a href="#">写博客</a>
    </div>
    <!-- 正文部分 -->
    <!-- 贯穿整个页面的容器 -->
    <div class="login-container">
        <!-- 垂直水平登录的对话框 -->
        <div class="login-dialog">
            <form action="login" method="post">
            <h3>登录</h3>
            <div class="row">
                <span>用户名:</span>
                <input type="text" id="username" placeholder="手机号/邮箱号" name="username">
            </div>
            <div class="row">
                <span>密码:</span>
                <input type="password" id="password" placeholder="请输入密码" name="password">
            </div>
            <div class="row">
                <input type="submit" id="submit" value="登录">
            </div>
            </form>
        </div>
    </div>
</body>
</html>

在这里插入图片描述
注意此处对应的关系:
在这里插入图片描述
3. 修改后端代码

package api;

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 {
        //设置请求的编码,使用 utf8 理解请求
        req.setCharacterEncoding("utf8");
        //设置响应的编码,使用 utf8 构造响应
        //resp.setCharacterEncoding("utf8");
        resp.setContentType("text/html;charset=utf8");
        //1.读取参数中的用户名和密码
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if(username == null ||"".equals(username) || password == null ||"".equals(password)){
            //登录失败
            String html = "<h3>登录失败!用户名/密码为空!</h3>";
            resp.getWriter().write(html);
            return;
        }
        //2.读取数据库,查看用户名是否存在.密码是否匹配
        UserDao userDao = new UserDao();
        User user =userDao.selectByUsername(username);
        if(user == null){
            //用户不存在
            String html = "<h3>登录失败!用户名或密码错误</h3>";
            resp.getWriter().write(html);
            return;
        }
        if(!password.equals(user.getPassword())){
            String html = "<h3>登录失败!用户名或密码错误</h3>";
            resp.getWriter().write(html);
            return;
        }
        //3.用户名密码验证通过,登录成功,接下来就创建会话,使用该会话保存用户信息
        HttpSession session = req.getSession();
        session.setAttribute("user",user);
        //4.重定向,跳转到博客列表页
        resp.sendRedirect("blog_list.html");
    }
}

在这里插入图片描述

4. 检测登录状态

实现让页面强制要求登录,
当用户访问博客列表页/详情页/编辑页,要求用户必须是已经登录的状态.如果用户还没登录,就会强制跳转到登录页面.

实现思路:
在页面加载的时候,专门发起一个新的 ajax . (一个页面里可以发N个ajax请求)以博客列表页为例,先会发一个请求获取博客列表,再发个ajax获取用户的登录状态,如果用户已登录,相安无事.如果未登录,则页面跳转到登录页.

  1. 约定前后端交互接口
  2. 实现前端代码
  3. 实现后端代码
  1. 约定前后端交互接口
    请求:GET/login
    响应:
    HTTP/1.1 200 OK
    {
    userId:1,
    username:‘xxxflower’
    }

响应就把当前登录的用户信息返回回来了,如果未登录,就返回一个userld0user 对象

  1. 实现后端代码
@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 user = new User();
            String respJson = objectMapper.writeValueAsString(user);
            resp.getWriter().write(respJson);
            return;
        }
        User user = (User) session.getAttribute("user");
        if (user == null) {
            user = new User();
            String respJson = objectMapper.writeValueAsString(user);
            resp.getWriter().write(respJson);
            return;
        }
        // 确实成功取出了 user 对象, 就直接返回即可.
        String respJson = objectMapper.writeValueAsString(user);
        resp.getWriter().write(respJson);
    }
  1. 开发后端代码:
function checkLogin() {
            $.ajax({
                type: 'get',
                url: 'login',
                success: function(body) {
                    if (body.userId && body.userId > 0) {
                        // 登录成功!!
                        console.log("当前用户已经登录!!");
                        h3.innerHTML = body.username;
                    } else {
                        // 当前未登录
                        // 强制跳转到登录页. 
                        location.assign('login.html');
                    }
                }
            });
        }
        checkLogin();

将此代码粘贴至博客列表页
则效果如下:
在这里插入图片描述

5. 实现显示用户信息的功能

在这里插入图片描述
此处我们是写死的,我们希望这个地方可以动态生成.

  1. 如果是博客列表页,此处显示登陆用户的信息
  2. 如果此处是博客详情页,此时显示的是该文章的作者
  1. 约定前后端接口
    博客列表页:(复用监测登录状态的接口)
    请求:
    GET /login
    响应:
    HTTP/1.1 200 OK
    {
    userId:1,
    username:‘huang’,
    password:‘234’
    }
    由于是在监测登录的接口中直接写,所以后端代码不变,只需要微调前端代码即可
    在这里插入图片描述
    登录可以看到:
    在这里插入图片描述
    博客详情页:
  2. 前后端交互接口
    请求:
    GET/author?blogId = 1
    响应:
    HTTP/1.1 200 OK
    {
    userId:1,
    username:‘huang’,
    password:‘234’
    }
  3. 后端代码:
package api;

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("/author")
public class AuthorServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String blogId = req.getParameter("blogId");
        if(blogId == null){
            resp.setContentType("text/html;charset = utf8");
            resp.getWriter().write("参数非法,缺少blogId!");
            return;
        }
        //根据 blog 查询Blog对象
        BlogDao blogDao = new BlogDao();
        Blog blog = blogDao.selectById(Integer.parseInt("blogId"));
        if(blog == null){
            resp.setContentType("text/html;charset = utf8");
            resp.getWriter().write("没有找到指定博客");
            return;
        }
        //根据BLog中UserId 获取到对应的用户信息
        UserDao userDao = new UserDao();
        User author = userDao.selectById(blog.getUserId());
        String respJson = objectMapper.writeValueAsString(author);
        resp.setContentType("application/json;charset=utf8");
        resp.getWriter().write(respJson);
    }
}

  1. 前端代码:
function getAuthor(){
            $.ajax({
                type:'get',
                url:'author' + location.search,
                success: function(body){
                    //将username设置到页面上
                    let h3 = document.querySelector('.container-left .card h3');
                    h3.innerHTML = body.userId;
                }
            })
        }
        getAuthor();

在这里插入图片描述

6. 退出登录状态

判断登录状态:

  1. 看是否能查到http session对象
  2. session对象里面有没有user

在这里插入图片描述

实现退出登录:

  1. 处理HttpSession
    getSession能够创建/获取会话,但是没有删除会话的方法.但是我们可以通过设置过期时间来达到类似效果
  2. 处理user(推荐) 可以通过removeAttribute来处理
  1. 约定前后端接口
    请求:
    GET/logout
    响应:
    HTTP/1.1 302
    Location:login.html

  2. 实现后端代码:

package api;

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("/logout")
public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession httpSession = req.getSession(false);
        if(httpSession == null){
            //未登录,直接出错
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前未登录!");
            return;
        }
        httpSession.removeAttribute("user");
        resp.sendRedirect("login.html");
    }
}

  1. 实现前端代码
    修改博客列表页 ,博客编辑页的前端代码:
    在这里插入图片描述
    在这里插入图片描述

7. 发布博客

  1. 约定前后端交互接口
    使用form表单:页面中更多了form标签,同时让form里面能够感知到博客的内容.
    请求:POST/blog
    title=标题&content=正文
    响应:HTTP/1.1 302
    Location:blog_list.html
  2. 编写服务器代码
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 发布博客
        // 读取请求, 构造 Blog 对象, 插入数据库中即可!!
        HttpSession httpSession = req.getSession(false);
        if (httpSession == null) {
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前未登录, 无法发布博客!");
            return;
        }
        User user = (User) httpSession.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());
        // 发布时间, 在 java 中生成 / 数据库中生成 都行
        blog.setPostTime(new Timestamp(System.currentTimeMillis()));
        // 插入数据库
        BlogDao blogDao = new BlogDao();
        blogDao.add(blog);

        // 跳转到博客列表页
        resp.sendRedirect("blog_list.html");

    }
  1. 客户端代码:
    <!-- 编辑区的容器 -->
    <div class="blog-edit-container">
        <form action="blog" method="post" style="height: 100%;">
            <!-- 博客标题编辑区 -->
            <div class="title">
                <input type="text" id="title" placeholder="输入文章标题" name="title">
                <input type="submit" id="submit" value="发布文章">
            </div>
            <!-- 博客编辑器, 这里用 id 是为了和 markdown 编辑器对接, 而设置的 -->
            <div id="editor">
                <textarea name="content" style="display:none"></textarea>
            </div>
        </form>
    </div>

在这里插入图片描述
后续点击submit就能自动提交
display:none是隐藏元素.
在这里插入图片描述
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt编写自定义控件:自定义表头实现左右两端上部分圆角

如上图&#xff0c;左上角和右上角凸出来了。设置表格圆角和表头圆角和QHeaderView::section圆角都不管用。解决此问题需要重写QHeaderView的paintSection()函数&#xff1a; class CustomHeaderView : public QHeaderView { public:explicit CustomHeaderView(Qt::Orientati…

使用toad库进行机器学习评分卡全流程

1 加载数据 导入模块 import pandas as pd from sklearn.metrics import roc_auc_score,roc_curve,auc from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression import numpy as np import math import xgboost as xgb …

Layui实现OA会议系统之会议管理模块总合

目录 一、项目背景 二、项目概述 1. 概述 2. 环境搭建 3. 工具类引用 4. 功能设计 4.1 会议发布 4.2 我的会议 4.3 会议审批 4.4 会议通知 4.5 待开会议 4.6 历史会议 4.7 所有会议 5. 性能优点 5.1 兼容性好 5.2 可维护性和可扩展性 5.3 轻量灵活 5.4 模块化设计…

C++ 第六弹 STL

目录 1.什么是stl 2.六大组件-容器-序列式容器-C98 string 3.六大组件-容器-序列式容器-C98 vector 4.六大组件-容器-序列式容器-C98 list 5.六大组件-容器-序列式容器-C98 deque 6.六大组件-容器-序列式容器-C11 array 7.六大组件-容器-序列式容器-C11 forward_list 8…

Kaggle狗图像分类实战

文章目录 Kaggle狗图像分类实战d2l安装问题python语法学习os.path.joind2l 数据加载streamlit Kaggle狗图像分类实战 d2l安装问题 d2l安装失败&#xff0c;报错如上图 去下面的网站下载到该项目文件目录下再pip install即可 Python d2l项目安装包(第三方库)下载详情页面 - …

若依打印sql

官方issue 自动生成的代码&#xff0c;sql日志怎么没有打印 在ruoyi-admin中的application.yml配置如下。 # 日志配置&#xff0c;默认 logging:level:com.ruoyi: debugorg.springframework: warn#添加配置com.ying: debug输出sql

java 分支控制语句

在程序中&#xff0c;程序运行的流程控制决定程序是如何执行的。 顺序控制 介绍&#xff1a; 程序从上到下的逐行的执行&#xff0c;中间没有任何判断和跳转。 使用&#xff1a;java中定义变量时&#xff0c;采用合法的前向引用。如&#xff1a; public class Test{int num…

成都链安:7月区块链安全事件爆发式增长,导致损失超4.11亿美元

7月&#xff0c;各类安全事件数量及造成的损失较6月爆发式增长。7月发生较典型安全事件超36起&#xff0c;各类安全事件造成的损失总金额约4.11亿美元&#xff08;虚拟货币案件涉案金额除外&#xff09;&#xff0c;较6月上涨约321%。Rug Pull导致损失约2065万美元&#xff0c;…

【linux基础(三)】Linux基本指令(下)

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到开通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; Linux基本指令 1. 前言2. 取头…

HTML 是什么?它的全称是什么?

聚沙成塔每天进步一点点 专栏简介HTML是什么&#xff1f;HTML的全称是什么&#xff1f;写在最后 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对We…

VMware ESXI虚拟网络和物理网络的对接

探讨VMware ESXI虚拟网络和物理网络的对接 前提&#xff1a; 在上篇搭建了ESXI 6.7&#xff0c;那和VMware Workstation一样想要创建虚拟机前提就必须先创建网络。没有网络连最基本的通信都没有&#xff0c;那肯定不行。所以我们解析要研究一下ESXI的网络。 物理连接模式&am…

Linux Day05

一、库文件生成与使用 1.1库文件 头文件是方法的声明&#xff0c;不是方法的实现 方法的实现是在库&#xff0c;库是预先编译好的方法的集合即.o文件 Linux上的库分为静态库(libxxx.a)和共享库(libxxx.so) 库文件常存放在/lib或者/usr/lib 库对应的头文件一般放在/usr/inc…

【188】Java8利用AVL树实现Map

AVL树又被叫做平衡二叉搜索树、平衡二叉树。AVL是其发明者的首字母缩写。 这篇文章中&#xff0c;AVLTreeMap 类集成了 java.util.Map 接口&#xff0c;并利用 AVL 树结构实现了 Map 接口的所有方法。本文还给出了测试代码。 为什么要发明AVL树&#xff1f; 当我按照从小到大…

【雕爷学编程】MicroPython动手做(33)——物联网之天气预报3

天气&#xff08;自然现象&#xff09; 是指某一个地区距离地表较近的大气层在短时间内的具体状态。而天气现象则是指发生在大气中的各种自然现象&#xff0c;即某瞬时内大气中各种气象要素&#xff08;如气温、气压、湿度、风、云、雾、雨、闪、雪、霜、雷、雹、霾等&#xff…

将自己的网站免费发布到互联网上【无需公网IP】

将自己的网站免费发布到互联网上【无需公网IP】 文章目录 将自己的网站免费发布到互联网上【无需公网IP】将本地搭建的网站发布到互联网步骤 ↓1. 注册并安装cpolar客户端1.1 windows系统1.2 linux系统&#xff08;支持一键自动安装脚本&#xff09;2. 登录cpolar web UI管理界…

Gradio-YOLOv5-YOLOv7 搭建Web GUI

目录 0 相关资料&#xff1a;1 Gradio介绍2 环境搭建3 GradioYOLOv54 GradioYOLOv75 源码解释 0 相关资料&#xff1a; Gradio-YOLOv5-Det&#xff1a;https://gitee.com/CV_Lab/gradio_yolov5_det 【手把手带你实战YOLOv5-入门篇】YOLOv5 Gradio搭建Web GUI: https://www.bi…

一次某某云上的redis读超时排查经历

性能排查&#xff0c;服务监控方面的知识往往涉及量广且比较零散&#xff0c;如何较为系统化的分析和解决问题&#xff0c;建立其对性能排查&#xff0c;性能优化的思路&#xff0c;我将在这个系列里给出我的答案。 问题背景 最近一两天线上老是偶现的redis读超时报警&#xf…

ChatGPT在工作中的七种用途

1. 用 ChatGPT 替代谷歌搜索引擎 工作时&#xff0c;你一天会访问几次搜索引擎&#xff1f;有了 ChatGPT&#xff0c;使用搜索引擎的频率可能大大下降。 据报道&#xff0c;谷歌这样的搜索引擎巨头&#xff0c;实际上很担心用户最终会把自己的搜索工具换成 ChatGPT。该公司针对…

KiCad各层简述

KiCad各层简述 KiCAD在Pcbnew中总计提供了32个铜层供导线走线&#xff08;可覆铜&#xff09;&#xff0c;12个固定技术层&#xff08;按照正反面分为6对&#xff09;&#xff0c;2个独立技术层&#xff0c;4个辅助层。在KiCad里Pcbnew的层描述中&#xff0c;F.代表电路板上层&…

机器学习笔记之优化算法(八)简单认识Wolfe Condition的收敛性证明

机器学习笔记之优化算法——简单认识Wolfe Condition收敛性证明 引言回顾&#xff1a; Wolfe \text{Wolfe} Wolfe准则准备工作推导条件介绍推导结论介绍 关于 Wolfe \text{Wolfe} Wolfe准则收敛性证明的推导过程 引言 上一节介绍了非精确搜索方法—— Wolfe \text{Wolfe} Wolf…