博客系统(使用前后端分离)

news2024/11/18 11:20:26

博客系统

  • 前言
  • 一.准备工作
    • 1.1 准备好前端文件
    • 1.2 设计数据库
    • 1.3 编写基本的数据库代码
    • 1.4 封装好数据库的连接操作
    • 1.5 根据设计的表创建实体类
    • 1.6 根据实体类,提供一些简单的增删改查操作
  • 二.博客要实现的功能
    • 2.1 博客列表页功能
    • 2.2 博客详情页
    • 2.3 博客登录页
    • 2.4 页面强制登录功能
    • 2.5 显示用户功能
    • 2.6 退出登录功能
    • 2.7 发布博客功能

前言

这篇博客相当于是,根据前面的所学的知识,来做一个综合练习

一.准备工作

1.1 准备好前端文件

登录页:
在这里插入图片描述
列表详情页:在这里插入图片描述
博客详情页
在这里插入图片描述
博客编辑页
在这里插入图片描述

1.2 设计数据库

因为是博客管理系统,我们涉及数据库的话,这个两个表的结构如下:
在这里插入图片描述

1.3 编写基本的数据库代码

列出数据库的基本代码.

-- 这个文件主要用来写建库建表语句.
-- 一般都建议大家, 在建表的时候把建表 sql 保留下来. 以备后续部署其他机器的时候就方便了.

create database if not exists java_blog_system;
use java_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)
);

-- 构造测试数据
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', '123');

1.4 封装好数据库的连接操作

我们会对数据库有基本的操作.

package model;

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

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

/**
 * @author <a href="mailto:1065043594@qq.com">ChenJiaYi</a>
 * @CreateDate 2023/7/7
 * @ProjectDetails [项目简述]
 */
public class DBUtil {
    private static DataSource dataSource = new MysqlDataSource();

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

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

1.5 根据设计的表创建实体类

Blog表 =>Blog类对应的Blog的一个对象,就对应表中的一个记录
User表 => User类对应的User的一个对象,就对应表中的一个记录
实体类有哪些属性都跟表中的东西一一对应的
实体类如下:
Blog类

package model;

/**
 * @author <a href="mailto:1065043594@qq.com">ChenJiaYi</a>
 * @CreateDate 2023/7/7
 * @ProjectDetails [项目简述]
 */

import java.sql.Timestamp;
import java.text.SimpleDateFormat;

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

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

    public int getUserId() {
        return userId;
    }

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

User类

package model;
/**
 * @author <a href="mailto:1065043594@qq.com">ChenJiaYi</a>
 * @CreateDate 2023/7/7
 * @ProjectDetails [项目简述]
 */
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;
    }
}

1.6 根据实体类,提供一些简单的增删改查操作

首先我们对blog博客类的操作

  1. 新增一个博客.
  2. 根据博客 id 来查询指定博客 (博客详情页中)
  3. 直接查询出数据库中所有的博客列表 (用于博客列表页)
  4. 删除指定博客
package model;

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.getPostTimestamp());
            statement.setInt(4, blog.getUserId());
            // 3. 执行 sql
            statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } 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. 遍历结果集合. 由于 blogId 在 blog 表中是唯一的. (主键)
            //    此时的查询结果, 要么是没有查到任何数据, 要么只有一条记录!!
            //    此处可以不使用 while, 直接 if 判定即可.
            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 throwables) {
            throwables.printStackTrace();
        } 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 order by postTime desc";
            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 == null) {
                    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 throwables) {
            throwables.printStackTrace();
        } finally {
            DBUtil.close(connection, statement, resultSet);
        }
        return blogs;
    }

    // 4. 删除指定博客
    public void delete(int blogId) {
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            // 1. 和数据库建立连接.
            connection = DBUtil.getConnection();
            // 2. 构造 SQL
            String sql = "delete from blog where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1, blogId);
            // 3. 执行 SQL
            statement.executeUpdate();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            // 4. 关闭
            DBUtil.close(connection, statement, null);
        }
    }
}

二.博客要实现的功能

这里所实现的功能基本我们思路都是这样

1.实际上就是借助ajax给服务器发送一个请求
2.服务器查找数据库获取到博客列表数据,返回给浏览器
3.浏览器再根据数据构造页面内容

基本步骤如下:
1)约定前后端交互接口
2)编写后端代码
3)编写前端代码

2.1 博客列表页功能

这里列表页的逻辑如下.
在这里插入图片描述
我们具体的步骤如下:

  • 约定前后端接口
    我们在约定前后端接口之前,我们先回答几个问题?
    前端要发什么样子的请求?
    后端要返回什么样子的响应?
    大概就是如下所示:
    在这里插入图片描述

  • 编写后端代码
    后端代码入下:
    在这里插入图片描述

  • 编写前端代码

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

2.2 博客详情页

具体的逻辑如下:
点击”查看全文”按钮,就能够跳转到博客详情页中.
跳转过去之后,在博客详情页中发起一个ajax,从服务器获取当前博客的具体内容.
再显示出来

1)约定前后端接口
在这里插入图片描述
2) 编写后端代码

			BlogDao blogDao = new BlogDao();
        	String blogId = req.getParameter("blogId");
        	private ObjectMapper objectMapper = new ObjectMapper();


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

3)编写前端代码

$.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. 更新博客正文
            // 此处不应该直接把博客正文填充到这个标签里~~
            // let contentDiv=document.querySelector('#content');
            // contentDiv.innerHTML=body.content;
            editormd.markdownToHTML('content', { markdown: body.content });
        }
    })

在 JavaScript 中,location 是用于获取或设置窗口的 URL,并可以用来对 URL 进行解析和操作。
location.search 属性用于获取 URL 的查询字符串部分,也就是问号 (?) 后的内容。

2.3 博客登录页

具体思路:
在这里插入图片描述
1.在此处输入用户名和密码
2.点击登录就会触发一个http请求
3.服务器验证结果,判定是否登录成功
4.如果成功就跳转到博客列表页

1) 约定前后端交互接口

在这里插入图片描述

2) 编写后端代码

private ObjectMapper objectMapper = new ObjectMapper();

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置请求的编码. 告诉 servlet 按照啥格式来理解请求
        req.setCharacterEncoding("utf8");
        // 设置响应的编码. 告诉 servlet 按照啥格式来构造响应
        // 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>登录失败! 缺少 username 或者 password 字段</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(true);
        session.setAttribute("user", user);
        // 4. 进行重定向. 跳转到博客列表页
        resp.sendRedirect("blog_list.html");
    }

3)编写前端代码
这是里是通过from表单构造请求.
在这里插入图片描述

2.4 页面强制登录功能

当用户访问 博客列表页 和 博客详情页 时, 如果用户当前尚未登陆, 就自动跳转到登陆页面.
在页面加载的时候,专门发起一个新的ajax.
举个例子
以博客列表页为例子,会先发送一个请求获取博客列表,再发一个ajax获取用户的登录状态.
如果用户已经登录相安无事,如果未登录,就跳转登录页
具体思路:
首先判断登录状态

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

然后如果有这种状态就直接可以访问
如果没有就强制登录即可

1)约定前后端交互接口

在这里插入图片描述

2)编写后端代码

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

3)编写前端代码

 function checkLogin() {
            $.ajax({
                type: 'get',
                url: 'login',
                success: function(body) {
                    if (body.userId && body.userId > 0) {
                        // 登录成功!!
                        console.log("当前用户已经登录!!");
                    } else {
                        // 当前未登录
                        // 强制跳转到登录页. 
                        location.assign('login.html');
                    }
                }
            });
        }

        checkLogin();

这里的前端代码,要在每一个页面中都要检查.

2.5 显示用户功能

在这里插入图片描述
具体思路:

  1. 如果是博客列表页,此处显示登录用户的信息
  2. 如果是博客详情页,此时显示的是该文章的作者.

这里的工作就是根据实际的用户,来生成响应的用户

1)约定前后端交互接口
博客列表页
在这里插入图片描述
博客详情页
在这里插入图片描述

2)编写后端代码
博客列表的后端代码
这里是利用之前的的强制登录的一个逻辑

@WebServlet("/login")
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);
    }

博客详情页的后端代吗

@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;
        }
        // 根据 blogId 查询 Blog 对象
        BlogDao blogDao = new BlogDao();
        Blog blog = blogDao.selectById(Integer.parseInt(blogId));
        if (blog == null) {
            // 博客不存在.
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("没有找到指定博客: blogId = " + blogId);
            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);
    }
}

3)编写前端代码
博客列表页

function checkLogin() {
            $.ajax({
                type: 'get',
                url: 'login',
                success: function(body) {
                    if (body.userId && body.userId > 0) {
                        // 登录成功!!
                        console.log("当前用户已经登录!!");
                        //加上一个功能,把当前用户界面加载到界面上
                        let h3 = document.querySelector('.container-left .card h3');
                        h3.innerHTML = body.username;
                    } else {
                        // 当前未登录
                        // 强制跳转到登录页. 
                        location.assign('login.html');
                    }
                }
            });
        }

        checkLogin();

博客详情页

      // 函数定义
         function getAuthor() {
            $.ajax({
                type: 'get',
                url: 'author' + location.search,
                success: function(body) {
                    // 把 username 设置到界面上
                    let h3 = document.querySelector('.container-left .card h3');
                    h3.innerHTML = body.username;
                }
            });
        }

        // 函数调用
        getAuthor();

2.6 退出登录功能

基本思路:
思路:
首先判断登录状态

  1. 看是否能查到http Session对象
  2. 看session 对象里有没有user
  3. 实现退出登录
    要么把HttpSession干掉
    要么把user干掉.

这里的请求就是a标签了,就不是ajax了
我们这里选择把user干掉
1) 约定前后端交互接口
在这里插入图片描述

2) 编写后端代码

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

3)编写前端代码
在这里插入图片描述

2.7 发布博客功能

具体思路
在这里插入图片描述
1.点击发布文章就会向服务器发布一个请求
2.服务器做出相关操作
3.浏览器跳转到指定页面

我们这里使用from构造请求.
在这里插入图片描述

1)约定前后端交互接口
在这里插入图片描述

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

3)编写前端代码

 <div class="blog-edit-container">
    <form action="blog" method="post">
        <!-- 标题编辑区 -->
        <div class="title">
            <input type="text" id="title-input" name="title">
            <input type="submit" id="submit" value="发布文章">
        </div>
        <!-- 博客编辑器 -->
        <!-- 把 md 编辑器放到这个 div 中 -->
        <div id="editor">
            <textarea name="content" style="display: none;"></textarea>

        </div>
    </form>

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

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

相关文章

涵子来信——AI的无限未来——谈谈想法

大家好&#xff1a; 这一次&#xff0c;我想要跟大家讲一讲我对AI的看法和未来的展望&#xff0c;谈谈我的想法。 AI&#xff08;Artificial Intelligence&#xff0c;中文人工智能&#xff09;&#xff0c;是我们生活中处处都可以见到的&#xff0c;小到一个语音助手&#x…

ylb-接口13实名认证

总览&#xff1a; 在api模块下的service包&#xff0c;创建一个充值接口RechargeService&#xff0c;并创建一个&#xff08;根据userID查询它的充值记录&#xff09;方法&#xff1a; package com.bjpowernode.api.service;import com.bjpowernode.api.model.RechargeRecord…

迪赛智慧数——柱状图(多色柱状图):旅行灵感来源

效果图 涉足旅行就是一次睿智的选择&#xff0c;心系未来、永不停步&#xff0c;让精神和思维得到滋养&#xff0c;更加懂得珍惜和感恩&#xff0c;这是旅行给予生活的灵感。西方一位哲人也说过&#xff0c;“生命的意义在于尝试&#xff0c;体验不同的可能”&#xff0c;旅行能…

基于springboot+Redis的前后端分离项目(九)-【黑马点评】

&#x1f381;&#x1f381;资源文件分享 链接&#xff1a;https://pan.baidu.com/s/1189u6u4icQYHg_9_7ovWmA?pwdeh11 提取码&#xff1a;eh11 附近的商户、用户签到、UV统计 &#xff08;一&#xff09;附近的商户1.附近商户-GEO数据结构的基本用法2.附近商户-导入店铺数据到…

Python实现登陆界面+生日界面

文章目录 1. 需求分析1.1 功能分析1.2 性能分析 2. 技术原理3. 详细设计3.1 登录界面3.2 注册界面3.3 修改密码3.4 注销账户3.5 生日界面 4. 功能实现4.1 登陆界面4.2 注册界面4.3 修改密码4.4 注销账户4.5 生日界面 1. 需求分析 1.1 功能分析 ① 登录界面实现用户的登录、注…

消费者行为分析VR情景模拟演练系统

VR虚拟现实技术是一种先进的技术&#xff0c;利用VR开展消费者行为分析课程是一种创新的教育方式&#xff0c;它可以提高学生的学习兴趣和效果&#xff0c;同时也可以为企业提供更好的人才培训和发展机会。 1.帮助学生更好地理解和应用心理学概念&#xff1a;VR技术可以让学生…

【Vite搭建Vue3项目】如何使用自定义的svg

Vite搭建Vue3项目如何使用自定义的svg 1. 准备一份svg图标集放入到自己想放的目录2. 下载对应的插件并进行配置3. 测试使用 绪论&#xff1a;当用 vite 构建 vue3 项目的时候&#xff0c;咱可以使用 Element-plus 为我们提供的图标&#xff0c;但是它是一个个标签&#xff0c;当…

HTTP1.1 wireshark分析

本地springboot启动一个简单的服务&#xff0c;然后请求测试 tcpdump -i lo0 -nnvv -w tmp.cap tcpdump 本地回环网卡 http1.1 HTTP/1.0 每进行一次通信&#xff0c;都需要经历建立连接、传输数据和断开连接三个阶段。当一个页面引用了较多的外部文件时&#xff0c;这个建立…

两种异步日志方案的介绍

文章目录 一、日志写入逻辑1.1 相关接口函数1.2 写入逻辑 二、log4cpp 日志框架2.1 下载和编译2.2 日志级别2.3 日志格式2.4 日志输出2.5 日志回滚 三、muduo 异步日志库3.1 异步日志机制3.2 双缓冲机制3.3 前端日志写入3.4 后端日志落盘3.5 coredump 查找未落盘的日志3.6 总结…

复习第六课 C语言-排序,初识指针

目录 【1】冒泡排序&#xff08;从小到大&#xff09; 【2】选择排序 【3】二维数组 【4】指针 【5】指针修饰 【6】大小端 【7】初见二级指针 练习&#xff1a; 【1】冒泡排序&#xff08;从小到大&#xff09; #include <stdio.h> //数组哪里的\0?自己和字符串…

论文阅读-2:基于深度学习的大尺度遥感图像建筑物分割研究

一、该网络中采用了上下文信息捕获模块。通过扩大感受野&#xff0c;在保留细节信息的同时&#xff0c;在中心部分进行多尺度特征的融合&#xff0c;缓解了传统算法中细节信息丢失的问题&#xff1b;通过自适应地融合局部语义特征&#xff0c;该网络在空间特征和通道特征之间建…

SSH框架简介篇

文章目录 概述目录结构 strutsSpringHibernate总结 概述 SSH框架&#xff08;Struts Spring Hibernate&#xff09;是一种广泛应用的Java企业级开发框架组合&#xff0c;它将Struts、Spring和Hibernate三个优秀的框架有机地结合在一起&#xff0c;提供了一套完整的解决方案&…

cmake 函数相关

目录 cmake函数和宏基础 demo cmake函数和宏的参数处理 cmake函数和宏的基本使用 demo cmake函数和宏使用变量 demo demo cmake函数和宏需要注意的地方 demo cmake函数和宏的关键字参数 demo 使用第二种形式cmake_parse_arguments() demo 关键字list demo singl…

GDB 调试代码

目录 一、其他调试代码的工具 二、GDB调试 1、调试准备 2、开始调试 3、调试命令 1.运行程序 2.退出gdb 3.传参 4.查看代码 5.设置或删除断点及相关操作 6.继续运行 7.运行中打印某些值及其类型 8.自动的打印某些值和信息及其相关操作 9.单步调试 10.设置变量的…

http-server 的安装与使用

文章目录 问题背景http-server简介安装nodejs安装http-server开启http服务http-server参数 问题背景 打开一个文档默认使用file协议打开&#xff0c;不能发送ajax请求&#xff0c;只能使用http协议才能请求资源&#xff0c;所以此时我们需要在本地建立一个http服务&#xff0c…

基于java的智能停车场管理系统

背景 智能停车场管理系统的主要使用者分为管理员和用户&#xff0c;实现功能包括管理员&#xff1a;个人中心、用户管理、车位信息管理、车位租用管理、车位退租管理、违规举报管理、论坛交流、系统管理&#xff0c;用户&#xff1a;个人中心、车位租用管理、车位退租管理、违…

MySQL每日一练——MySQL多表查询进阶挑战

目录 1、首先创建表 t_dept: t_emp: 2、插入数据 t_dept表&#xff1a; t_tmp表: 3、修改表 4、按条件查找 1、首先创建表 t_dept: CREATE TABLE t_dept (id INT(11) NOT NULL AUTO_INCREMENT,deptName VARCHAR(30) DEFAULT NULL,address VARCHAR(40) DEFAULT NULL,P…

为什么单片机可以直接烧录程序的原因是什么?

单片机&#xff08;Microcontroller&#xff09;可以直接烧录程序的原因主要有以下几点&#xff1a; 集成性&#xff1a;单片机是一种高度集成的芯片&#xff0c;内部包含了处理器核心&#xff08;CPU&#xff09;、存储器&#xff08;如闪存、EEPROM、RAM等&#xff09;、输入…

JavaScript 使用URL跳转传递数组对象数据类型的方法

文章目录 首先了解一下正常传递基本数据类型JavaScript 跳转页面方法JavaScript 路由传递参数JavaScript 路由接收参数传递对象、数组效果&#xff1a; 在前端有的时候会需要用链接进行传递参数&#xff0c;基本数据类型的传递还是比较简单的&#xff0c;但是如果要传递引用数据…

AWS 解决方案架构师「免费考」

周五晚&#xff0c;AWS 推出了的训练营活动&#xff0c;这对于正在准备 Cloud Practitioner 的我来说&#xff0c;简直不要太开心。官方文章原文链接《限定&#xff01;直冲「云」霄训练营开营啦》。 PART-01 训练营简介 看到推送后第一时间点了进去&#xff0c;活动的情况简…