java实现web项目博客管理系统

博客系统

前端页面基于html+css+js

后端实现基于servlet +themthefe

1.需求分析

软件开发之前,先需求分析。明白需要实现哪些需求

博客系统要实现的功能:

1.能够展示当前的博客列表
博客列表页,展示了很多篇博客.每一篇博客包含标题,包含发布时间,博客的摘要
点击标题,就跳转到博客正文页
2博客正文页
显示该博客的详细内容.
正文里包含标题,发布时间,完整的正文(不实现展示图片)

3.博客编辑页
显示一个编辑框,用户可以在里面编辑博客内容.并提交给服务器.
提交成功之后,博客后台就会保存这个博客的内容接下来就能在列表页中看到这个博客了.
4.删除博客功能.

在博客详情页这里加上一个"删除"按钮.

5.注册&登陆功能
要求,登陆成功,才能发布博客~
如果没有登陆,只能查看博客,不能发布博客~

java实现web项目博客管理系统_第1张图片

2.数据库设计操作

分析需要的表和每个表的字段

需要的是blog表和user表

博客表blog表

编号 blogid 标题title 正文 content 发布时间postTime 作者id userid

用户表 user

编号userid 用户名username 密码 password

create database if not exists  RocketBlog;
use RocketBlog;

drop table if exists blog;
create table blog(
    blogId int primary key auto_increment,
    title varchar (512),
    content text,
    userId int,
    postTime datetime
);
drop table if exists user;
create table user(
    userId int primary  key auto_increment,
    username varchar (50),
    password varchar (50)
);

封装数据库的操作

在编码之前先补充目录 引入以来 需要引入servlet mysql thymelef(页面渲染)等依赖


<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.0modelVersion>

    <groupId>org.examplegroupId>
    <artifactId>rocket_blogartifactId>
    <version>1.0-SNAPSHOTversion>

    <dependencies>
        
        <dependency>
            <groupId>javax.servletgroupId>
            <artifactId>javax.servlet-apiartifactId>
            <version>3.1.0version>
            <scope>providedscope>
        dependency>
        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.49version>
        dependency>
        
        <dependency>
            <groupId>org.thymeleafgroupId>
            <artifactId>thymeleafartifactId>
            <version>3.0.12.RELEASEversion>
        dependency>


    dependencies>

    <packaging>warpackaging>
    <build>
        <finalName>rocket_blogfinalName>
    build>

    <properties>
        <encoding>UTF-8encoding>
        <maven.compiler.source>1.8maven.compiler.source>
        <maven.compiler.target>1.8maven.compiler.target>
    properties>



project>
DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Applicationdisplay-name>
web-app>

使用jdbc进行连接 -》 使用单例(代码中只有一个实例)模式 -》

package dao;
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;
public class DBUtil {
    private static final String URL = "jdbc:mysql://127.0.0.1:3306/RocketBlog?characterEncoding=utf8&useSSL=false";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "wangxp184219";

    private static DataSource dataSource = null;

    // 在这个代码中还存在一个非常严重的 BUG~(多线程)
    public static DataSource getDataSource() {
        // 看一下 dataSource 当前是否已经持有一个实例了.
        // 如果没有持有, 就创建一个新的.
        // 如果持有了, 就不必创建新的, 直接返回之前的.
        if (dataSource == null) {
            //datasource=new MysqlDataSource();
            //((MysqlDataSource)datasource).setUrl(URL);  向下转型
            MysqlDataSource mysqlDataSource = new MysqlDataSource();
            mysqlDataSource.setUrl(URL);
            mysqlDataSource.setUser(USERNAME);
            mysqlDataSource.setPassword(PASSWORD);
            dataSource = mysqlDataSource;
        }
        return dataSource;
    }

    public static Connection getConnection() {
        try {
            return getDataSource().getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

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

创建实体 类-》

package dao;

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 "dao.User{" +
                "userId=" + userId +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
package dao;
import java.sql.Date;
import java.sql.Timestamp;
public class Blog {
    private int blogId;
    private String title;
    private String content;
    private int userId;
    // java.sql.Date 这个类型只能表示日期, 没有时分秒
    private Timestamp postTime;
    public int getBlogId() {
        return blogId;
    }
    public void setBlogId(int blogId) {
        this.blogId = blogId;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public int getUserId() {
        return userId;
    }
    public void setUserId(int userId) {
        this.userId = userId;
    }
    public Timestamp getPostTime() {
        return postTime;
    }
    public void setPostTime(Timestamp postTime) {
        this.postTime = postTime;
    }    @Override
    public String toString() {
        return "dao.Blog{" +
                "blogId=" + blogId +
                ", title='" + title + '\'' +
                ", content='" + content + '\'' +
                ", userId=" + userId +
                ", postTime=" + postTime +
                '}';
    }
}

针对数据库进行增删改查 dao data access object 数据访问对象

package dao;
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 {
    // 往数据库中新增一个 博客
    public void insert(Blog blog) {
        // 1. 和数据库服务器建立连接.
        Connection connection = DBUtil.getConnection();
        // 2. 拼装 SQL
        String sql = "insert into blog values(null, ?, ?, ?, now())";
        PreparedStatement statement = null;
        try {
            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) {
            e.printStackTrace();
        } finally {
            // 4. 收尾工作.
            DBUtil.close(connection, statement, null);
        }
    }
    // 从数据库删除博客
    public void delete(int blogId) {
        // 1. 和数据库服务器建立连接.
        Connection connection = DBUtil.getConnection();
        // 2. 拼装 SQL
        String sql = "delete from blog where blogId = ?";
        PreparedStatement statement = null;
        try {
            statement = connection.prepareStatement(sql);
            statement.setInt(1, blogId);
            // 3. 执行 SQL
            statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection, statement, null);
        }
    }
    // 从数据库中查找出所有的博客.
    // 实现博客列表页的时候, 就需要这个方法.
    // 如果博客数目少, 都无所谓. 如果博客数目很多, 应该要支持 "分页查询"
    // limit offset
    public List<Blog> selectAll() {
        List<Blog> blogs = new ArrayList<Blog>();
        // 1. 和数据库服务器建立连接.
        Connection connection = DBUtil.getConnection();
        // 2. 拼装 SQL
        String sql = "select * from blog order by blogId desc";
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            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() > 40) {   //这里显示简介  一部分正文
                    content = content.substring(0, 40) + "...";
                }
                blog.setContent(content);
                blog.setUserId(resultSet.getInt("userId"));
                blog.setPostTime(resultSet.getTimestamp("postTime"));
                blogs.add(blog);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection, statement, resultSet);
        }
        return blogs;
    }
    // 从数据库查询出指定的一篇博客.
    // 实现博客详情页的时候, 需要这个方法.
    public Blog selectOne(int blogId) {
        // 1. 和数据库建立连接.
        Connection connection = DBUtil.getConnection();
        // 2. 拼装 SQL
        String sql = "select * from blog where blogId = ?";
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            statement = connection.prepareStatement(sql);
            statement.setInt(1, blogId);
            // 3. 执行 SQL
            resultSet = statement.executeQuery();
            // 4. 遍历结果集. 要么是 0 个记录, 要么只有 1 条记录.
            if (resultSet.next()) {
                Blog blog = new Blog();
                blog.setBlogId(resultSet.getInt("blogId"));
                blog.setTitle(resultSet.getString("title"));
                blog.setContent(resultSet.getString("content"));
                blog.setUserId(resultSet.getInt("userId"));
                blog.setPostTime(resultSet.getTimestamp("postTime"));
                return blog;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection, statement, resultSet);
        }
        return null;
    }
    public static void main(String[] args) {
        // 通过这里的代码对上面的数据库操作进行验证.
        // 1. 验证插入.
        // 此处的测试, 只是一个简单的 "冒烟测试"
        Blog blog = new Blog();
        blog.setTitle("这是第三篇博客");
        blog.setContent("这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文这是博客正文");
        blog.setUserId(1);
        BlogDao blogDao = new BlogDao();
        blogDao.insert(blog);

        // 2. 验证查找.
//        dao.BlogDao blogDao = new dao.BlogDao();
//        List blogs = blogDao.selectAll();
//        System.out.println(blogs);
//        dao.Blog blog = blogDao.selectOne(1);
//        System.out.println(blog);

        // 3. 验证删除.
//        dao.BlogDao blogDao = new dao.BlogDao();
//        blogDao.delete(1);
    }
}
package dao;

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

public class UserDao {
    // 注册的时候, 需要新增用户到数据库中.
    // 期望 username 不能重复. (username 是用于登陆的用户名)
    // 注册的时候(insert 之前), 先判定一下该 username 是否已经存在.
    // 如果存在, 就直接不执行后续的 insert 操作.
    // username 的唯一性不一定非得通过 数据库的约束 来完成.
    // 也可以通过用户代码来完成.
    public void insert(User user) {
        // 1. 和数据库建立连接.
        Connection connection = DBUtil.getConnection();
        // 2. 拼装 SQL
        String sql = "insert into user values(null, ?, ?)";
        PreparedStatement statement = null;
        try {
            statement = connection.prepareStatement(sql);
            statement.setString(1, user.getUsername());
            statement.setString(2, user.getPassword());
            // 3. 执行 SQL
            statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 4. 关闭连接.
            DBUtil.close(connection, statement, null);
        }
    }

    // 登陆的时候, 需要根据用户名获取到密码
    public User selectByName(String username) {
        // 1. 和数据库建立连接.
        Connection connection = DBUtil.getConnection();
        // 2. 拼装 SQL
        String sql = "select * from user where username = ?";
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            statement = connection.prepareStatement(sql);
            statement.setString(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) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection, statement, resultSet);
        }
        return null;
    }

    // 根据用户 id 来查用户信息
    // 显示博客作者的时候需要用到.
    public User selectById(int userId) {
        // 1. 和数据库建立连接.
        Connection connection = DBUtil.getConnection();
        // 2. 拼装 SQL
        String sql = "select * from user where userId = ?";
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            statement = connection.prepareStatement(sql);
            statement.setInt(1, userId);
            // 3. 执行 SQL
            resultSet = statement.executeQuery();
            // 4. 遍历结果集. 预期结果只是有 0 个或者 1 个.
            if (resultSet.next()) {
                User user = new User();
                user.setUserId(resultSet.getInt("userId"));
                user.setUsername(resultSet.getString("username"));
                user.setPassword(resultSet.getString("password"));
                return user;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection, statement, resultSet);
        }
        return null;
    }

    public static void main(String[] args) {
        UserDao userDao = new UserDao();
        // 1. 测试 插入用户 功能
//        dao.User user = new dao.User();
//        user.setUsername("zhangsan");
//        user.setPassword("123");
//        userDao.insert(user);
        // 2. 测试根据名字查找.
//        dao.User user = userDao.selectByName("zhangsan");
//        System.out.println(user);

        // 3. 测试根据 id 查找.
//        dao.User user = userDao.selectById(1);
//        System.out.println(user);
    }
}

3.业务实现

1.实现博客列表

罗列出当前数据库中的博客 动态页面 由代码生成

虽然页面是动态生成的,但是直接在代码中通过字符串拼接的方式构造出HTML,是不科学的!!!不科学主要是因为这样做特别麻烦,尤其是当HTML 比较复杂的时候,代码会非常混乱,也难以排查错误~~
更推荐的方式是使用模板来代替原始的字符串拼接~ 生成HTML,也可以基于一个"模板"完成.

Thymeleaf 来进行渲染

如果这个渲染是在服务器这边完成,就称为“服务器渲染" 此处就是这样使用
这个渲染工作也可能是在浏览器这边完成.就称为"客户端渲染"(前后端分离) 更常用

package api;
import dao.Blog;
import dao.BlogDao;
import dao.User;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.List;
@WebServlet("/blogList")
public class BlogListServlet extends HttpServlet {
    @Override
    public void init() throws ServletException {
    }
    private User checkLogin(HttpServletRequest req) {
        HttpSession session = req.getSession(false);
        if (session == null) {
            return null;
        }
        User user = (User) session.getAttribute("user");
        return user;
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 0. 从 req 里面读取一下当前用户信息. 判定用户是否登陆.
        User user = checkLogin(req);
        boolean isLogin = false;
        if (user != null) {
            isLogin = true;
        }
        // 1. 先从数据库查询出都有哪些博客.
        BlogDao blogDao = new BlogDao();
        List<Blog> blogList = blogDao.selectAll();
       /* for (Blog blog : blogs) {
            html.append("
"); // 标题(点击标题能够跳转到博客详情页) // 要想满足 点击 就能跳转, 标题是一个 a 标签. // 期望构造出一个形如 "blogContent?blogId=1" URL html.append(String.format("%s", "blogContent?blogId=" + blog.getBlogId() , blog.getTitle())); // 发布时间 html.append(String.format("
%s
", blog.getPostTime())); // 博客摘要 html.append(String.format("
%s
", blog.getContent().substring(0, 100) + "...")); html.append("
"); }*/
// 2. 构造博客页面. // 1) 通过 Thymeleaf 进行渲染. 渲染的时候需要定义 "数据集合" 这样的概念. // WebContext 功能就是把要替换的数据给收集起来, 统一的传给模板引擎. WebContext webContext = new WebContext(req, resp, getServletContext()); // 2) setVariable 可以设置多个键值对. 完全取决于模板代码怎么写. // 模板里的每个 ${ } 里面的内容都需要在 webContext 设定进去. webContext.setVariable("blogs", blogList); webContext.setVariable("isLogin", isLogin); webContext.setVariable("user", user); // 3) 进行渲染. TemplateEngine engine = (TemplateEngine) getServletContext().getAttribute("engine"); String html = engine.process("blog_list", webContext); System.out.println("模板渲染的内容: " + html); resp.setContentType("text/html; charset=utf-8"); resp.getWriter().write(html); } }

thymeleaf 用法

先引入依赖

实现一个模板 (缺失一些关键信息)

Thymeleaf提供的“特殊符号”对这些关键信息进行“占位"后面就可以借助Thymeleaf 库,把数据库中获取到的标题,正文等信息,替换到占位符这里.

DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客列表页title>
    <style>
        /* 清除浏览器默认样式 */
        * {
            margin: 0;
            padding: 0;
        }
        body {
            background-color: #f3f3f3;
        }
        .container {
            width: 800px;
            margin: 0 auto;
            background-color: #fff;
        }
        .title {
            display: block;
            text-align: center;
            color: #000;
            font-size: 20px;
            font-weight: 700;
            text-decoration: none;
            padding: 10px 0;
        }
        .post-time {
            text-align: center;
            color: #999aaa;
        }
        .desc {
            padding-bottom: 10px;
            text-indent: 20px;
        }
        .nav {
            height: 50px;
            background: #000;
            color: #fff;
            display: flex;
            align-items: center;
        }
        .nav-title {
            padding-left: 10px;
        }
        .nav-button {
            color: #fff;
            text-decoration: none;
            padding: 0 10px;
        }
        .spacer {
            width: 55%;
        }
    style>
head>

<body>
<div class="nav">
    
    
    <h3 class="nav-title">我的博客系统h3>
    <div class="spacer">div>
    <a href="login.html" class="nav-button" th:if="${!isLogin}">登陆a>
    <a href="register.html" class="nav-button" th:if="${!isLogin}">注册a>
    <div th:if="${isLogin}" th:text="${'欢迎' + user.username}">div>
    <a href="blog_insert.html" class="nav-button" th:if="${isLogin}">写博客a>
    <a href="logout" class="nav-button" th:if="${isLogin}">注销a>
div>

<div class="container">
    
    <div class="blog" th:each="blog : ${blogs}"> 
        <a th:href="${'blogContent?blogId=' + blog.blogId}" class="title" th:text="${blog.title}">我是标题a>
        <div class="post-time" th:text="${blog.postTime}">2021-07-11 17:30:00div>
        <div class="desc" th:text="${blog.content}">我是正文的摘要...div>
    div>
div>
body>

html>

里 面 的 内 容 表 示 是 T h y m e l e a f 从 J a v a 代 码 中 获 取 到 的 变 量   这 里 的 t h : e a c h 会 循 环 遍 历 {}里面的内容表示是Thymeleaf 从Java代码中获取到的变量~ 这里的th:each会循环遍历 ThymeleafJava th:each{blogs}每次取出一个blog元素就会同时创建出一个

此处的 blog的类型就是代码中的 Blog这里确实是有title,postTime, content字段的~~
Thymeleaf判定当前这几个字段是不是public.如果是,直接取.如果不是,看看有没有public 的getter方法.如果有,也能调用getter方法来取到属性的值.
如果两个都不是, Thymeleaf 就报错了~~

在java代码中渲染模板

首先要初始化thymeleaf

package config;
//import com.sun.xml.internal.ws.api.policy.PolicyResolver;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener    //实现 监听器
public class ThymeleafConfig implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // 使用这个!!!
        // 这个方法就会在当前 webapp 的 ServletContext 初始化之后
        // 立即执行. Thymeleaf 的初始化就在这里调用即可!!!
        // 初始化 Thymeleaf
        // 1) 创建一个 engine(引擎), 负责把 Java 中的数据替换到模板中.
        TemplateEngine engine = new TemplateEngine();
        // 2) 创建一个 resolver 对象(解析器), 负责找到 html 模板在哪, 并加载到内存中.
        //    供 engine 对象来使用.
        ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(sce.getServletContext());
        // 3) 给 resolver 设置一些属性, 让它能够找到 html 模板.
        resolver.setCharacterEncoding("utf-8");
        //    Prefix 表示 "前缀", 设定了满足啥样条件的文件被加载到内存中作为 HTML 模板
        resolver.setPrefix("/WEB-INF/template/");
        //    Suffix 表示 "后缀"
        resolver.setSuffix(".html");
        // 4) 把 resolver 和 engine 关联起来.
        engine.setTemplateResolver(resolver);

        // 把初始化好的 engine 对象交给 ServletContext 来保管.
        ServletContext context = sce.getServletContext();
        context.setAttribute("engine", engine);
    }
    @Override
    public void contextDestroyed(ServletContextEvent sce) {  //销毁

    }
}

2.实现博客详情页

DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客内容页title>
    <style>
        /* 清除浏览器默认样式 */
        * {
            margin: 0;
            padding: 0;
        }
        body {
            background-color: #f3f3f3;
        }
        .container {
            width: 800px;
            margin: 0 auto;
            background-color: #fff;
        }
        .title {
            text-align: center;
            padding: 10px 0;
            font-size: 36px;
        }
        .author {
            text-align: center;
            padding: 10px 0;
            color: #999aaa;
        }
        .post-time {
            text-align: center;
            color: #999aaa;
        }
        .content {
            padding: 10px 0;
            text-indent: 20px;
        }
        .nav {
            height: 50px;
            background: #000;
            color: #fff;
            display: flex;
            align-items: center;
        }
        .nav-title {
            padding-left: 10px;
        }
        .nav-button {
            color: #fff;
            text-decoration: none;
            padding: 0 10px;
        }
        .spacer {
            width: 55%;
        }
    style>
head>

<body>
<div class="nav">
    
    
    <h3 class="nav-title">我的博客系统h3>
    <div class="spacer">div>
    <a class="nav-button" th:href="${'blogDelete?blogId=' + blog.blogId}">删除a>
div>

<div class="container">
    <h3 th:text="${blog.title}" class="title">这是博客标题h3>
    <div class="author" th:text="${username}">小王div>
    <div class="post-time" th:text="${blog.postTime}">2021-07-11 12:00:00div>
    <div class="content" th:text="${blog.content}">
        这是博客正文
        这是博客正文
        这是博客正文
        这是博客正文
        这是博客正文
    div>
div>
body>

html>

servlet实现 该页面是通过url跳转过来是一个get请求

package api;

import dao.Blog;
import dao.BlogDao;
import dao.User;
import dao.UserDao;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;

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("/blogContent")
public class BlogContentServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=utf-8");
        // 1. 从 req 中读取出 blogId
        String blogId = req.getParameter("blogId");
        if (blogId == null || "".equals(blogId)) {
            // /rocket_blog/blogContent => blogId = null
            // /rocket_blog/blogContent?blogId=   blogId = ""
            // resp.getWriter().write("

blogId 不存在

");
resp.sendError(404, "blogId 参数错误!"); return; } // 2. 根据 blogId 在数据库中查询出博客的详细内容. BlogDao blogDao = new BlogDao(); Blog blog = blogDao.selectOne(Integer.parseInt(blogId)); if (blog == null) { resp.sendError(404, "blogId 指定的文章不存在!"); return; } // 3. 根据 blog.userId 找到对应的 dao.User 对象 UserDao userDao = new UserDao(); User user = userDao.selectById(blog.getUserId()); // 4. 根据详细内容, 渲染到模板中. TemplateEngine engine = (TemplateEngine) getServletContext().getAttribute("engine"); WebContext webContext = new WebContext(req, resp, getServletContext()); webContext.setVariable("blog", blog); webContext.setVariable("username", user.getUsername()); String html = engine.process("blog_content", webContext); // 5. 把渲染好的结果写回客户端. resp.getWriter().write(html); } }

在BlogListServlet中已经初始化过一份TemplateEngine 了.发现在 BlogContentServlet中也需要使用这个engine对象.
同一个engine需要在多个Servlet之间共享~
就可以使用ServletContext
把初始化 engine 的工作交给ServletContext, 也把engine实例交给ServletContext管理.
此时,任意Servlet都能拿到同一个ServletContext对象,也就可以拿到同一个engine 对象了~

借助ServletContext来初始化engine 在servletcontext初始化完毕之后初始化engine

通过ServletContext持有engine的引用使其他servlet获取引用

初始化的代码借助"监听器"(Listener)d)使用Thymeleaf来渲染模板~
搞一个WebContext对象.(数据集合)
可以把想要往模板中替换的关键数据,放到WebContext中.

3.博客登陆页面

1.登录页面(两个输入框 用户名 密码 提交按钮)

2.登录页面是from表单 点击提交 Servlet进行处理

3.读取用户名 密码在数据库中寻找是否匹配

4.登陆成功 创建session 把当前登陆成功的用户信息保存到session 以备后续访问服务器可以识别身份

接下来用户去访问博客编辑页的时候,此时服务器就知道该用户是已经登陆的了.就可以进行编辑.否则直接重定向到登陆页面.
实现了登陆页面之后,发现在当前的博客列表页中,没有体现出当前用户到底是登陆了还是没登陆~~
此处更好的做法是搞一个“导航栏",如果用户未登录,显示未登录,并且显示登陆/注册按钮
如果用户已经登陆,显示"欢迎xxx "

该页面就可以设置静态页面

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登陆页面title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        html,
        body {
            height: 100%;
        }
        .container {
            width: 800px;
            margin: 0 auto;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100%;
            flex-direction: column;
        }
        input {
            display: block;
            margin-bottom: 10px;
            width: 315px;
            height: 35px;
            font-size: 20px;
            text-indent: 10px;
        }
        .button {
            color: #fff;
            background-color: orange;
            border: none;
            border-radius: 5px;
        }
        h3 {
            padding: 10px 0;
        }
    style>
head>
<body>
<div class="container">
    <h3>请登录博客系统h3>
    <form action="login" method="POST">
        <input type="text" name="username">
        <input type="password" name="password">
        <input type="submit" value="提交" class="button">
    form>
div>
body>
html>

loginServlet

package api;

import dao.User;
import dao.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 {
        resp.setCharacterEncoding("utf-8");
        req.setCharacterEncoding("utf-8");
        // 1. 从 req 读取用户提交的 username 和 password
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if (username == null || "".equals(username)
                || password == null || "".equals(password)) {
            resp.sendError(404, "用户名或密码不能为空");
            return;
        }
        // 2. 从数据库中查找指定用户名的用户信息.
        UserDao userDao = new UserDao();
        User user = userDao.selectByName(username);
        if (user == null) {
            resp.sendError(404, "用户名或者密码错误");
            return;
        }
        if (!password.equals(user.getPassword())) {
            // 密码不匹配
            resp.sendError(404, "用户名或者密码错误");
            return;
        }
        // 3. 登陆成功! 创建会话.
        HttpSession session = req.getSession(true);
        session.setAttribute("user", user);
        // 4. 直接把用户页面重定向到博客列表页.
        resp.sendRedirect("blogList");
    }
}

4.注销页面

package api;
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 {
        // 把当前用户的 session 中的 user 这个数据给删掉.
        HttpSession session = req.getSession(false);
        if (session == null) {
            resp.sendError(404, "当前尚未登陆, 不能注销");
            return;
        }
        session.removeAttribute("user");
        resp.sendRedirect("blogList");
    }
}

5.注册页面

实现汪册贝面.
1.register.html静态页面.内容和login.html基本差不多.

2.实现一个Servlet处理注册请求.
a)读取提交的用户名和密码.
b)根据用户名查找数据库,该用户名是否存在.c)如果用户名不存在,就插入数据库即可

DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>注册页面title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        html,
        body {
            height: 100%;
        }
        .container {
            width: 800px;
            margin: 0 auto;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100%;
            flex-direction: column;
        }
        input {
            display: block;
            margin-bottom: 10px;
            width: 315px;
            height: 35px;
            font-size: 20px;
            text-indent: 10px;
        }
        .button {
            color: #fff;
            background-color: orange;
            border: none;
            border-radius: 5px;
        }
        h3 {
            padding: 10px 0;
        }
    style>
head>
<body>
<div class="container">
    <h3>请注册博客系统h3>
    <form action="register" method="POST">
        <input type="text" name="username">
        <input type="password" name="password">
        <input type="submit" value="注册" class="button">
    form>
div>
body>
html>
package api;

import dao.User;
import dao.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("/register")
public class RegisterServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 如果不做任何限定, 此时服务器这边读取 Parameter 的时候默认不是按照 utf-8 的方式来理解字符编码的.
        req.setCharacterEncoding("utf-8");
        // 1. 先读取用户提交的用户名和密码
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if (username == null || "".equals(username)
                || password == null || "".equals(password)) {
            resp.sendError(404, "提交的用户名或者密码为空");
            return;
        }
        // 2. 查询数据库, 看看 username 是否存在
        UserDao userDao = new UserDao();
        User existsUser = userDao.selectByName(username);
        if (existsUser != null) {
            // 用户已经存在! 提示注册失败!
            resp.sendError(404, "用户名已经存在, 注册失败!");
            return;
        }
        // 3. 构造 dao.User 对象, 插入到数据库中.
        User newUser = new User();
        newUser.setUsername(username);
        newUser.setPassword(password);
        userDao.insert(newUser);
        // 4. 返回一个结果.
        resp.setContentType("text/html; charset=utf-8");
        resp.getWriter().write("

注册成功!

"
); } }

6.博客编辑页

1.先创建一个博客编辑页面.

这个页面中就包含两个输入框:文章标题,文章内容
还有一个提交按钮.
点击提交按钮,就给服务器发送一个POST请求.

2.实现一个Servlet,来处理这个POST请求.
直接把读取到的数据插入到数据库中即可.处理请求之前需要先判定用户是否已经登陆.如果未登录,则不能进行插入数据库~~

DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客编辑页title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        .title {
            display: block;
            width: 600px;
            height: 50px;
            margin: 10px auto;
            font-size: 20px;
            text-indent: 10px;
        }
        textarea {
            display: block;
            width: 600px;
            margin: 10px auto;
            font-size: 16px;
            padding: 10px;
            box-sizing: border-box;
        }
        .button {
            display: block;
            width: 200px;
            height: 50px;
            margin: 0 auto;
        }
    style>
head>
<body>
<form action="blogInsert" method="POST">
    
    <input type="text" name="title" class="title">
    
    <textarea name="content" rows="30">textarea>
    <input type="submit" value="发布博客" class="button">
form>
body>

html>
package api;

import dao.Blog;
import dao.BlogDao;
import dao.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("/blogInsert")
public class BlogInsertServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        // 0. 判定用户是否已经登陆
        HttpSession session = req.getSession(false);
        if (session == null) {
            resp.sendError(404, "当前尚未登陆, 不能发布博客!");
            return;
        }
        User user = (User) session.getAttribute("user");
        if (user == null) {
            resp.sendError(404, "当前尚未登陆, 不能发布博客!");
            return;
        }
        // 1. 读取请求中的参数.
        String title = req.getParameter("title");
        String content = req.getParameter("content");
        if (title == null || "".equals(title)
                || content == null || "".equals(content)) {
            resp.sendError(404, "标题或者正文为空");
            return;
        }
        // 2. 根据读到的数据构造 dao.Blog 对象, 并插入数据库
        Blog blog = new Blog();
        blog.setTitle(title);
        blog.setContent(content);
        blog.setUserId(user.getUserId());
        BlogDao blogDao = new BlogDao();
        blogDao.insert(blog);
        // 3. 重定向到博客列表页, 也能直接看到新的博客
        resp.sendRedirect("blogList");
    }
}

7.博客删除

1.删除按钮可以放到博客列表页(每个博客标题旁边带个删除按钮)

2.删除按钮也可以放到博客详情页,在博客最上面加个删除按钮~

package api;

import dao.Blog;
import dao.BlogDao;
import dao.User;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/blogDelete")
public class BlogDeleteServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 从 req 中读取要删除的 blogId
        String blogId = req.getParameter("blogId");
        if (blogId == null || "".equals(blogId)) {
            resp.sendError(404, "blogId 为空!");
            return;
        }
        // 2. 判定用户的登陆状态, 未登陆不能删除.
        HttpSession session = req.getSession(false);
        if (session == null) {
            resp.sendError(404, "当前未登录, 不能删除!");
            return;
        }
        User user = (User) session.getAttribute("user");
        if (user == null) {
            resp.sendError(404, "当前未登录, 不能删除!");
            return;
        }
        // 3. 根据 blogId, 查询 blogId 的作者 id, 看看和当前登陆的用户 id 是否相同
        //    不相同也不能删除.
        BlogDao blogDao = new BlogDao();
        Blog blog = blogDao.selectOne(Integer.parseInt(blogId));
        if (blog == null) {
            resp.sendError(404, "当前博客不存在!");
            return;
        }
        if (blog.getUserId() != user.getUserId()) {
            resp.sendError(403, "不能删除别人的博客!");
            return;
        }
        // 4. 如果用户相同, 从数据库中删除这个博客即可.
        blogDao.delete(Integer.parseInt(blogId));
        // 5. 重定向到博客列表页
        resp.sendRedirect("blogList");
    }
}

欢迎大家来交流指正错误!
源码下载

你可能感兴趣的:(项目,servlet,博客管理,java,tomcat)