小小博客项目(servlet实战演练)(下)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 小小博客项目(servlet实战演练)(下)

二、Controller控制层:用来处理我们前端发来的请求。

事实上,在项目中我们如果想要实现一个功能,有以下三步

1、约定前后端交互接口

2、编写服务器端代码(其实主要编写的就是我们这里控制层的代码)

3、编写客户端代码(即视图层,我们的前端)


我们大致说以下我们这个项目中所用到的交互接口(详细的可以看我们表白墙的那一篇博客)

🏀前后端交互接口举例说明

🍑博客列表获取(博客列表页中)


image.png


接下来我们对Controller层的这几个类做一下解析


image.png

🏀BlogServlet代码

首先看我们的BlogServlet代码(与博客列表页、博客详情页、博客编辑页相关联)


image.png

BlogServlet代码

package controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import mode.Blog;
import mode.BlogDao;
import mode.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;
import java.util.List;
// 这个类可以获取博客列表以及指定博客的博客详情
// 同时这个类也处理新博客的发布,前端的博客编辑页通过from表单,把title和content发给我们,方法是post
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取博客列表,这个是由我们的blog_home.html发出的get请求
        // 通过jackson使得我们返回的响应的json格式的
        // 告诉服务器如何解析请求,用户输入的可能是中文
        req.setCharacterEncoding("utf8");
        ObjectMapper objectMapper = new ObjectMapper();
        String blogId = req.getParameter("blogId");
        if (blogId == null) { // 说明此时前端是要我们返回博客列表
            // 因为我们子啊BlogDao类中定义的方法是静态的,所以这里我们不用实例化对象就能调用
            List<Blog> blogs = BlogDao.getList();
            // 把blogs转成json数组的格式
            String respJson = objectMapper.writeValueAsString(blogs);
            // 告诉浏览器以json格式来读取响应数据,如果没写这行,浏览器会把他当作是一个普通的字符串来处理
            resp.setContentType("application/json; charset=utf-8");
            resp.getWriter().write(respJson);
        }
        else {  // 说明前端此时是要我们返回指定blogId的博客详情
            Blog blog = BlogDao.getDetail(Integer.parseInt(blogId));
            String respJson = objectMapper.writeValueAsString(blog);
            // 告诉浏览器以json格式来读取响应数据,如果没写这行,浏览器会把他当作是一个普通的字符串来处理
            resp.setContentType("application/json; charset=utf-8");
            resp.getWriter().write(respJson);
        }
    }
    @Override
    // 发表新的博客,新增博客
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 告诉服务器如何解析请求,用户输入的可能是中文
        req.setCharacterEncoding("utf8");
        // 接收前端提交是数据
        String title = req.getParameter("title");
        String content = req.getParameter("content");
        // 我们还需要一个author信息,当前发布该文章的作者就当前的登录用户
        HttpSession session = req.getSession(false);
        User user = (User) session.getAttribute("user");
        int authorId = user.getUserId(); // 因为当前在博客编辑页面,用户一点是登陆过的,所以不用判断当前用户是否登录
        // 构建新的博客对象
        Blog blog = new Blog();
        blog.setTitle(title);
        blog.setContent(content);
        blog.setUserId(authorId);
        // 在数据库中添加刚新建的博客
        BlogDao.insertBlog(blog);
        // 新增完成后,跳转到博客列表页
        resp.sendRedirect("blog_home.html");
    }
}

对应的模板层的代码代码在上面,下面我们列出对应视图层的代码

相关联的视图层代码

博客列表页blog_home.html:

<!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>
  <link rel="stylesheet" href="common.css">
  <link rel="stylesheet" href="bologlist.css">
</head>
<body>
  <!-- 导航栏 -->
  <div class="navigation">
    <img src="image/大头贴.png">
    <span>我的博客系统</span>
    <!-- 占位置用的 -->
    <span class="space"></span>
    <a href="blog_home.html">主页</a>
    <a href="write_blog.html">写博客</a>
    <a href="logout">退出登录</a>
  </div>
  <!-- 这里是页面的版心 -->
  <div class="container">
    <!-- 左侧用户信息区 -->
    <div class="left">
      <!-- 表示整个用户信息区域 -->
      <div class="card">
        <img src="image/博客头像男.jpg">
        <h3>是小鱼儿哈</h3>
        <a href="https://blog.csdn.net/weixin_61061381?type=blog">CSDN地址</a>
        <div class="counter">
          <span>文章</span>
          <span>分类</span>
        </div>
        <div class="counter">
          <span>67</span>
          <span>7</span>
        </div>
      </div>
    </div>
    <!-- 右侧博客列表页 -->
    <div class="right">
    </div>
  </div>
  <!-- 我们的js代码一般放在我们页面后面,来进行事件给构造,因为在调用回调函数时候,前提是你已经有了页面内容,然后对页面内容进行操作 -->
   <!-- 响要使用ajax构造请求,我们要有引入jquery -->
   <script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
   <script>
     // 通过该函数,从后端获取到当前登录的用户是谁,并把它显示在博客列表页上
     function getUser() {
      $.ajax({
        type: 'get',
        url: 'user',
        success: function(body) {
          // 替换条原来的用户信息
          let userDiv = document.querySelector('.card h3');
          // 引入css,因为我们css就是按原来的类选择器来设置样式的
           // 但其实这一步也没什么必要,因为我们只是获取到原来的作者标签,并不是新建的标签,在获取到了后该标签所对应的className本来就是原来的名字,不用担心原来的css样式失效
          userDiv.className = '.card h3';
          userDiv.innerHTML = body.username;
        },
        error: function() {
           // 强行跳转到登录页面. 
           location.assign('blog_login.html');
        }
      });
     }
     // 你要明白一个html文件,在加载的过程中,是可以同时执行多个函数、多个ajax同时发送请求的
     // 通过这个函数, 来从服务器获取到博客列表的数据
     function getBlogList() {
         $.ajax({
             type: 'get',
             url: 'blog',  // servlet path, 在页面加载的过程中就会调用主机ip + contentPath + 这个servlet path(就@WebServlet后的地址)
             //dataType: 'json',
             success: function(body) {
                 // 根据返回的 json 数据, 来构造出页面内容, div.blog
                 // jquery ajax 会自动的把响应得到的 body 按照响应的 Content-Type 进行转换格式. 
                 // 如果响应的 Content-Type 是 json, 此时就会自动把 body 转成 js 的对象
                 let rightDiv = document.querySelector('.right');
                 for (let blog of body) {
                     // 新建博客结点
                     let blogDiv = document.createElement('div');
                     blogDiv.className = 'blog'; // 引入CSS属性
                     // 创建博客标题
                     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 = '查看全文 >>';
                     // a标签跳转的过程就相当于是发了一个get请求,这里我们在跳转的url地址后加上要传递的参数,即QueryString,等下我们在博客详情页也会用到这个参数
                     a.href = 'blog_detail.html?blogId=' + blog.blogId;
                     blogDiv.appendChild(a);
                     // 把 blogDiv 加入外层元素
                     rightDiv.appendChild(blogDiv);  // 把构建好的一篇博客挂到博客列表上
                 }
             },
             error: function() {
              alert("获取博客列表失败!")
             }
         });
     }
     function checkLogin() {
      $.ajax({
        type: 'get',
        url: 'login',
        success: function(body) {
          // 如果用户已经登录就什么也不做
        },
        error: function() {
          //alert("当前登录已过期,请重新登录!")
          // 403 就会触发 error
            // 强行跳转到登录页面. 
            location.assign('blog_login.html');
        }
      });
     }
     getBlogList(); // 不用忘了调用函数
     checkLogin();
     getUser();
    </script>
</body>
</html>

博客详情页blog_detail代码

<!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>
  <link rel="stylesheet" href="common.css">
  <link rel="stylesheet" href="blogdetail.css">
   <!-- 引入 editor.md 的依赖 -->
   <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
   <script src="js/jquery.min.js"></script>
   <script src="editor.md/lib/marked.min.js"></script>
   <script src="editor.md/lib/prettify.min.js"></script>
   <script src="editor.md/editormd.js"></script>
</head>
<body>
  <!-- 导航栏 -->
  <div class="navigation">
    <img src="image/大头贴.png">
    <span>我的博客系统</span>
    <!-- 占位置用的 -->
    <span class="space"></span>
    <a href="blog_home.html">主页</a>
    <a href="write_blog.html">写博客</a>
    <a href="logout">退出登录</a>
    <!-- 如果这篇博客是用户自己写的,这里会出现一个删除该博客的按钮 -->
  </div>
  <!-- 这里是页面的版心 -->
  <div class="container">
    <!-- 左侧用户信息区 -->
    <div class="left">
      <!-- 表示整个用户信息区域 -->
      <div class="card">
        <img src="image/博客头像男.jpg">
        <h3>是小鱼儿哈</h3>
        <a href="https://blog.csdn.net/weixin_61061381?type=blog">CSDN地址</a>
        <div class="counter">
          <span>文章</span>
          <span>分类</span>
        </div>
        <div class="counter">
          <span>67</span>
          <span>7</span>
        </div>
      </div>
    </div>
    <!-- 右侧内容区 -->
    <div class="right">
      <!-- 标题 -->
      <h2></h2>
      <!-- 日期 -->
      <div class="date">
      </div> 
       <!-- 博客正文,注意我们这里是id名为content,而不是类名,id选择器#开头 -->
       <div id="content" style="opacity: 80%">  
      </div>
    </div>  
  </div>
    <!-- 引入jquery -->
    <script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
    <script>
      // 通过这个函数,借助blogId,来从后端获取到这篇博客的作者,并把他显示在博客详情页上
      function getAuthor() {
        $.ajax({
          type: 'get',
          url: 'user' + location.search, //JS中可以通过location.search设置或获取网页地址跟在问号后面的部
          // 后端在查询失败的时候不要返回200,避免前端触发ajax中的sucess回调函数
          success: function(body) {
            let authorDiv = document.querySelector('.card h3');
            // 但其实这一步也没什么必要,因为我们只是获取到原来的作者标签,并不是新建的标签,在获取到了后该标签所对应的className本来就是原来的名字,不用担心原来的css样式失效
            authorDiv.className = '.card h3';
            authorDiv.innerHTML = body.username;
            // 接下来我们根据后端返回的body,来确定是否在导航栏中显示删除该博客这个按键(只有当前博客的作者和登录的用户名一样才能删,只能删自己的)
            // 所以我们就要在对应的后端代码中判断当前要删除的博客是否是自己的博客,在user/locatin.search所对应的后端servlet代码写相应的逻辑
            if (body.isYourBlog == 1) {
              // 新增一个a标签
              // alert("进入添加删除博客按钮的那个if语句了呀!"); 可以用了测试是否进入了该条件语句
              let DeleteA = document.createElement('a');
              DeleteA.href = "delete" + location.search; //JS中可以通过location.search设置或获取网页地址跟在问号后面的部分,即queryString
              DeleteA.innerHTML = "删除该博客";
              // 注意,在JS中,单引号和双引号是不同的,下面的代码如果你用双引号,你就获取不到该组件
              let navDiv = document.querySelector('.navigation'); 
              // 此时navDiv就代码的类名是class=avigation这个组件,这次赋值后,该组件的名字并没有变
              // 要想改变该组件的名字,还要通过 navDiv.className 来修改,让他被新的css选择器来修饰
              navDiv.appendChild(DeleteA);  // 把我们新增的a标签添加到导航栏中
              // // 在导航栏中加个按钮, 用来删除文章. 
              // let deleteA = document.createElement('a');
              // // location.search 就是当前页面 url 的 query string, 也就是
              // // ?blogId=1 这样的结果. 
              // deleteA.href = 'blogDelete' + location.search;
              // deleteA.innerHTML = '删除';
              // let navDiv = document.querySelector('.nav');
              // navDiv.appendChild(deleteA);
            }
          },
          error: function() {
            // 强行跳转到登录页面
            location.assign('blog_login.html');
          }
        });
      }
      // 通过这个函数, 来获取到博客详情
      function getBlogDetail() {
        $.ajax({
          type: 'get',
          url: 'blog' + location.search, // servlet path,和获取博客列表一个地址,但获取博客列表不带blogId,显示博客详情带blogId
          // 后端在查询失败的时候不要返回200,避免前端触发ajax中的sucess回调函数
          success: function(body) {
            // 根据返回的数据来构造页面
            // rightDiv是我们博客详情,我们具体的博客内容要挂在rightDiv这个结点上
            let rightDiv = document.querySelector('.right'); // 获取我们上面的右侧页面对象,接下来我们就可以对右侧页面对象进行操作来给我们页面添加元素
            rightDiv.className = 'right'; // 引入css
            // 博客标题构建与添加
            // let titleDiv = document.createComment('div');
            // titleDiv.className = 'h2';
            // titleDiv.innerHTML = body.title;
            // rightDiv.appendChild(titleDiv); // 把标题结点挂到博客详情上
            // // 创建该篇博客的发布日期,并把日期添加到博客详情rightDiv上
            // let dateDiv = document.createComment('div');
            // dateDiv.className = 'date';
            // dateDiv.innerHTML = body.dateTime;
            // rightDiv.appendChild(dateDiv);
            // 博客标题
            let titleDiv = document.querySelector('.right h2');
            titleDiv.innerHTML = body.title;
            // 发布日期
            let dateDiv = document.querySelector('.right .date');
            dateDiv.innerHTML = body.postTime;
            // 创建内容
            // 刚才这里是直接把正文内容设置到 innerHTML 中了, 没有渲染的过程的. 
            // let divContent = document.querySelector('#content');
            // divContent.innerHTML = body.content;
            // 靠谱的做法, 应该是先使用 editor.md 进行渲染. 
            // [重要] 这个方法就是 editor.md 提供的一个方法把 markdown 字符串转成格式化的效果. 
            // 第一个参数是一个 div 的 id, 表示要把渲染结果放到哪个 div 中. 
            // 第二个参数是一个 js 对象, 把正文内容传入即可. (还支持很多别的参数属性, 此处暂时不涉及)
            editormd.markdownToHTML('content', {
              markdown: body.content
            });            
          },
          error: function() {
            alert("获取博客详情失败!");
          }
        });
      }
      function checkLogin() {
        $.ajax({
          type: 'get',
          url: 'login',
          success: function(body) {
            // 如果用户已经登录就什么也不做
          },
          error: function() {
          //alert("当前登录已过期,请重新登录!")
          // 403 就会触发 error
            // 强行跳转到登录页面. 
            location.assign('blog_login.html');
          }
        });
      }
      getBlogDetail();
      checkLogin();
      getAuthor();
    </script>
</body>
</html>

🏀DeleteBlogServlet代码

对应了删除操作,处理了前端的博客详情页(blog_detail.html中)发来的get请求。

当然了在这个类中也少不了对模型层中代码的使用(Blog和BlogDao)

package controller;
import mode.Blog;
import mode.BlogDao;
import mode.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("/delete")
public class DeleteBlogServlet extends HttpServlet {
    // 因为我们是通过a标签来跳转到这里的,发的请求是get
    // 前端在给我们传HTTP的get请求时,带上了当前博客详情页中,当前所访问的blogId
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 告诉浏览器读取响应数据的格式
        resp.setContentType("text/html; charset=utf8");
        HttpSession session = req.getSession(false);
        if (session == null) {
            resp.setStatus(403);
            System.out.println("当前你还未登录,无删除权限!");
            resp.getWriter().write("当前你还未登录,无删除权限");
            return;
        }
        User user = (User) session.getAttribute("user");
        if (user == null) {
            resp.setStatus(403);
            System.out.println("当前你还未登录,无删除权限!");
            resp.getWriter().write("当前你还未登录,无删除权限");
            return;
        }
        int blogId = Integer.parseInt(req.getParameter("blogId"));
        Blog blog = BlogDao.getDetail(blogId);
        if (blog == null) {
            resp.setStatus(403);
            System.out.println("没有你要删除的博客,删除失败!");
            resp.getWriter().write("没有你要删除的博客,删除失败");
        }
        else {
            // 其实我们这里判断是否能删除没必要,因为只有在能删除的情况下,前端才会显示删除按键,跳到我们这里
            if (blog.getUserId() == user.getUserId()) {
                // 此时说明通过blogId而获取的当前的博客作者id和当前登录用户的id相同,可以删除
                BlogDao.deleteBlog(blogId);
                // 删除成功后,自动跳转到博客列表页
                resp.sendRedirect("blog_home.html");
            }
            resp.setStatus(403);
            System.out.println("你要删除的博客不是你自己所写的博客删除失败!");
            resp.getWriter().write("你要删除的博客不是你自己所写的博客, 删除失败");
        }
    }
}

相关联的视图层代码

博客详情页(blog_detail.html)]

<!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>
  <link rel="stylesheet" href="common.css">
  <link rel="stylesheet" href="blogdetail.css">
   <!-- 引入 editor.md 的依赖 -->
   <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
   <script src="js/jquery.min.js"></script>
   <script src="editor.md/lib/marked.min.js"></script>
   <script src="editor.md/lib/prettify.min.js"></script>
   <script src="editor.md/editormd.js"></script>
</head>
<body>
  <!-- 导航栏 -->
  <div class="navigation">
    <img src="image/大头贴.png">
    <span>我的博客系统</span>
    <!-- 占位置用的 -->
    <span class="space"></span>
    <a href="blog_home.html">主页</a>
    <a href="write_blog.html">写博客</a>
    <a href="logout">退出登录</a>
    <!-- 如果这篇博客是用户自己写的,这里会出现一个删除该博客的按钮 -->
  </div>
  <!-- 这里是页面的版心 -->
  <div class="container">
    <!-- 左侧用户信息区 -->
    <div class="left">
      <!-- 表示整个用户信息区域 -->
      <div class="card">
        <img src="image/博客头像男.jpg">
        <h3>是小鱼儿哈</h3>
        <a href="https://blog.csdn.net/weixin_61061381?type=blog">CSDN地址</a>
        <div class="counter">
          <span>文章</span>
          <span>分类</span>
        </div>
        <div class="counter">
          <span>67</span>
          <span>7</span>
        </div>
      </div>
    </div>
    <!-- 右侧内容区 -->
    <div class="right">
      <!-- 标题 -->
      <h2></h2>
      <!-- 日期 -->
      <div class="date">
      </div> 
       <!-- 博客正文,注意我们这里是id名为content,而不是类名,id选择器#开头 -->
       <div id="content" style="opacity: 80%">  
      </div>
    </div>  
  </div>
    <!-- 引入jquery -->
    <script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
    <script>
      // 通过这个函数,借助blogId,来从后端获取到这篇博客的作者,并把他显示在博客详情页上
      function getAuthor() {
        $.ajax({
          type: 'get',
          url: 'user' + location.search, //JS中可以通过location.search设置或获取网页地址跟在问号后面的部
          // 后端在查询失败的时候不要返回200,避免前端触发ajax中的sucess回调函数
          success: function(body) {
            let authorDiv = document.querySelector('.card h3');
            // 但其实这一步也没什么必要,因为我们只是获取到原来的作者标签,并不是新建的标签,在获取到了后该标签所对应的className本来就是原来的名字,不用担心原来的css样式失效
            authorDiv.className = '.card h3';
            authorDiv.innerHTML = body.username;
            // 接下来我们根据后端返回的body,来确定是否在导航栏中显示删除该博客这个按键(只有当前博客的作者和登录的用户名一样才能删,只能删自己的)
            // 所以我们就要在对应的后端代码中判断当前要删除的博客是否是自己的博客,在user/locatin.search所对应的后端servlet代码写相应的逻辑
            if (body.isYourBlog == 1) {
              // 新增一个a标签
              // alert("进入添加删除博客按钮的那个if语句了呀!"); 可以用了测试是否进入了该条件语句
              let DeleteA = document.createElement('a');
              DeleteA.href = "delete" + location.search; //JS中可以通过location.search设置或获取网页地址跟在问号后面的部分,即queryString
              DeleteA.innerHTML = "删除该博客";
              // 注意,在JS中,单引号和双引号是不同的,下面的代码如果你用双引号,你就获取不到该组件
              let navDiv = document.querySelector('.navigation'); 
              // 此时navDiv就代码的类名是class=avigation这个组件,这次赋值后,该组件的名字并没有变
              // 要想改变该组件的名字,还要通过 navDiv.className 来修改,让他被新的css选择器来修饰
              navDiv.appendChild(DeleteA);  // 把我们新增的a标签添加到导航栏中
              // // 在导航栏中加个按钮, 用来删除文章. 
              // let deleteA = document.createElement('a');
              // // location.search 就是当前页面 url 的 query string, 也就是
              // // ?blogId=1 这样的结果. 
              // deleteA.href = 'blogDelete' + location.search;
              // deleteA.innerHTML = '删除';
              // let navDiv = document.querySelector('.nav');
              // navDiv.appendChild(deleteA);
            }
          },
          error: function() {
            // 强行跳转到登录页面
            location.assign('blog_login.html');
          }
        });
      }
      // 通过这个函数, 来获取到博客详情
      function getBlogDetail() {
        $.ajax({
          type: 'get',
          url: 'blog' + location.search, // servlet path,和获取博客列表一个地址,但获取博客列表不带blogId,显示博客详情带blogId
          // 后端在查询失败的时候不要返回200,避免前端触发ajax中的sucess回调函数
          success: function(body) {
            // 根据返回的数据来构造页面
            // rightDiv是我们博客详情,我们具体的博客内容要挂在rightDiv这个结点上
            let rightDiv = document.querySelector('.right'); // 获取我们上面的右侧页面对象,接下来我们就可以对右侧页面对象进行操作来给我们页面添加元素
            rightDiv.className = 'right'; // 引入css
            // 博客标题构建与添加
            // let titleDiv = document.createComment('div');
            // titleDiv.className = 'h2';
            // titleDiv.innerHTML = body.title;
            // rightDiv.appendChild(titleDiv); // 把标题结点挂到博客详情上
            // // 创建该篇博客的发布日期,并把日期添加到博客详情rightDiv上
            // let dateDiv = document.createComment('div');
            // dateDiv.className = 'date';
            // dateDiv.innerHTML = body.dateTime;
            // rightDiv.appendChild(dateDiv);
            // 博客标题
            let titleDiv = document.querySelector('.right h2');
            titleDiv.innerHTML = body.title;
            // 发布日期
            let dateDiv = document.querySelector('.right .date');
            dateDiv.innerHTML = body.postTime;
            // 创建内容
            // 刚才这里是直接把正文内容设置到 innerHTML 中了, 没有渲染的过程的. 
            // let divContent = document.querySelector('#content');
            // divContent.innerHTML = body.content;
            // 靠谱的做法, 应该是先使用 editor.md 进行渲染. 
            // [重要] 这个方法就是 editor.md 提供的一个方法把 markdown 字符串转成格式化的效果. 
            // 第一个参数是一个 div 的 id, 表示要把渲染结果放到哪个 div 中. 
            // 第二个参数是一个 js 对象, 把正文内容传入即可. (还支持很多别的参数属性, 此处暂时不涉及)
            editormd.markdownToHTML('content', {
              markdown: body.content
            });            
          },
          error: function() {
            alert("获取博客详情失败!");
          }
        });
      }
      function checkLogin() {
        $.ajax({
          type: 'get',
          url: 'login',
          success: function(body) {
            // 如果用户已经登录就什么也不做
          },
          error: function() {
          //alert("当前登录已过期,请重新登录!")
          // 403 就会触发 error
            // 强行跳转到登录页面. 
            location.assign('blog_login.html');
          }
        });
      }
      getBlogDetail();
      checkLogin();
      getAuthor();
    </script>
</body>
</html>

🏀UserServlet代码


900dbafec8eb4f7388640815f8a5f7dc.pngUserServlet代码

package controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import mode.Blog;
import mode.BlogDao;
import mode.User;
import mode.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;
import java.sql.Struct;
@WebServlet("/user")
public class UserServlet extends HttpServlet {
    public static boolean isYourBlog = false;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 返回当前的用户
        // String user
        // 如果前端带的请求中没有带blogId,说明是列表页的
        String blogId = req.getParameter("blogId");
        // 处理博客列表页
        if (blogId == null) {
            HttpSession session = req.getSession(false);
            if (session == null) {
                resp.setContentType("text/html; charset=utf-8");
                //resp.getWriter().write("当前用户未登录请重新登录");
                resp.setStatus(403); // 返回给前端403,让前端在error回调函数中强制跳转到登录页面
                return;
            }
            User user = (User) session.getAttribute("user");
            // 这里的判断是否是多次一举呢? 我们再登录的servlet代码中,再创建会话的时候,已经把当前登录的user对象给存在session的value中的键值对里了
            // 因为我们接下来的退出登录操作其实就是把session中的user的对象给删除了,但session本身并没有删除,因为req中没有直接提供删session的方法
            // 这样的话,就是即使session存在,但用户还未登录,此时的user为空
            if (user == null) {
                resp.setContentType("text/html; charset=utf-8");
                resp.setStatus(403);  // 表示服务器拒绝你的访问,意思的为登录的用户禁止访问该界面
            }
            // 为了能用ObjectMapper,我们要在web.xml中引用jackson这个第三方库
            ObjectMapper objectMapper = new ObjectMapper();
            resp.setContentType("application/json; charset=utf-8");
            // 把我们后端要返回的数据转换json格式
            String retJson = objectMapper.writeValueAsString(user);
            resp.getWriter().write(retJson);
        }
        // 处理博客详情页,获取到了blogId,根据他获取到当前的博客作者,并返回数据给前端
        else {
            // 告诉浏览器以json格式来读取响应数据,如果没写这行,浏览器会把他当作是一个普通的字符串来处理
            resp.setContentType("application/json; charset=utf-8");
            // 获取该blogId,所对应的博客详情
            Blog blog = BlogDao.getDetail(Integer.parseInt(blogId));
            if (blog == null) {
                resp.setStatus(403); // 后端在查询失败的时候不要返回200,避免前端触发ajax中的sucess回调函数
                resp.setContentType("text/html; charset=utf-8");
                resp.getWriter().write("当前的blogId有误!!");
            }
            else {
                int userId = blog.getUserId();  // 这个userId是我们当前这篇博客的作者Id
                // 这个userId不仅在博客表里有,在用户表里也有,所以我们就可以借助这个userId,找到该篇博客所对应的作者名,也就是你通过userId在user表中找到用户名
                User author = UserDao.selectById(userId);
                if (author == null) {
                    resp.setStatus(403);
                }
                HttpSession session = req.getSession(false);
                if (session == null) {
                    resp.setStatus(403);
                }
                User user = (User) session.getAttribute("user");
                if (user == null) {
                    resp.setStatus(403);
                }
                // 当前user.getUserId()的值,就是当前登录用户的id值,如果这两个值一样就说明当前我们查看博客详情的这篇博客的作者id和当前登录用户的id
                // 是一个人,就可以进行删除操作,所以我们给这个类添加一个属性,来表示可以删除
                if (author.getUserId() == user.getUserId()) {
                    author.setIsYourBlog(1);  // 接下来我们前端还要依赖这个属性来操作——是否显示删除按钮
                }
                ObjectMapper objectMapper = new ObjectMapper();
                // 把我们后端要返回的数据转换json格式
                String retJson = objectMapper.writeValueAsString(author);
                System.out.println(retJson);
                resp.getWriter().write(retJson);
            }
        }
    }
}


与之相关的视图层的代码

博客列表页(blog_home.html)

<!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>
  <link rel="stylesheet" href="common.css">
  <link rel="stylesheet" href="bologlist.css">
</head>
<body>
  <!-- 导航栏 -->
  <div class="navigation">
    <img src="image/大头贴.png">
    <span>我的博客系统</span>
    <!-- 占位置用的 -->
    <span class="space"></span>
    <a href="blog_home.html">主页</a>
    <a href="write_blog.html">写博客</a>
    <a href="logout">退出登录</a>
  </div>
  <!-- 这里是页面的版心 -->
  <div class="container">
    <!-- 左侧用户信息区 -->
    <div class="left">
      <!-- 表示整个用户信息区域 -->
      <div class="card">
        <img src="image/博客头像男.jpg">
        <h3>是小鱼儿哈</h3>
        <a href="https://blog.csdn.net/weixin_61061381?type=blog">CSDN地址</a>
        <div class="counter">
          <span>文章</span>
          <span>分类</span>
        </div>
        <div class="counter">
          <span>67</span>
          <span>7</span>
        </div>
      </div>
    </div>
    <!-- 右侧博客列表页 -->
    <div class="right">
    </div>
  </div>
  <!-- 我们的js代码一般放在我们页面后面,来进行事件给构造,因为在调用回调函数时候,前提是你已经有了页面内容,然后对页面内容进行操作 -->
   <!-- 响要使用ajax构造请求,我们要有引入jquery -->
   <script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
   <script>
     // 通过该函数,从后端获取到当前登录的用户是谁,并把它显示在博客列表页上
     function getUser() {
      $.ajax({
        type: 'get',
        url: 'user',
        success: function(body) {
          // 替换条原来的用户信息
          let userDiv = document.querySelector('.card h3');
          // 引入css,因为我们css就是按原来的类选择器来设置样式的
           // 但其实这一步也没什么必要,因为我们只是获取到原来的作者标签,并不是新建的标签,在获取到了后该标签所对应的className本来就是原来的名字,不用担心原来的css样式失效
          userDiv.className = '.card h3';
          userDiv.innerHTML = body.username;
        },
        error: function() {
           // 强行跳转到登录页面. 
           location.assign('blog_login.html');
        }
      });
     }
     // 你要明白一个html文件,在加载的过程中,是可以同时执行多个函数、多个ajax同时发送请求的
     // 通过这个函数, 来从服务器获取到博客列表的数据
     function getBlogList() {
         $.ajax({
             type: 'get',
             url: 'blog',  // servlet path, 在页面加载的过程中就会调用主机ip + contentPath + 这个servlet path(就@WebServlet后的地址)
             //dataType: 'json',
             success: function(body) {
                 // 根据返回的 json 数据, 来构造出页面内容, div.blog
                 // jquery ajax 会自动的把响应得到的 body 按照响应的 Content-Type 进行转换格式. 
                 // 如果响应的 Content-Type 是 json, 此时就会自动把 body 转成 js 的对象
                 let rightDiv = document.querySelector('.right');
                 for (let blog of body) {
                     // 新建博客结点
                     let blogDiv = document.createElement('div');
                     blogDiv.className = 'blog'; // 引入CSS属性
                     // 创建博客标题
                     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 = '查看全文 >>';
                     // a标签跳转的过程就相当于是发了一个get请求,这里我们在跳转的url地址后加上要传递的参数,即QueryString,等下我们在博客详情页也会用到这个参数
                     a.href = 'blog_detail.html?blogId=' + blog.blogId;
                     blogDiv.appendChild(a);
                     // 把 blogDiv 加入外层元素
                     rightDiv.appendChild(blogDiv);  // 把构建好的一篇博客挂到博客列表上
                 }
             },
             error: function() {
              alert("获取博客列表失败!")
             }
         });
     }
     function checkLogin() {
      $.ajax({
        type: 'get',
        url: 'login',
        success: function(body) {
          // 如果用户已经登录就什么也不做
        },
        error: function() {
          //alert("当前登录已过期,请重新登录!")
          // 403 就会触发 error
            // 强行跳转到登录页面. 
            location.assign('blog_login.html');
        }
      });
     }
     getBlogList(); // 不用忘了调用函数
     checkLogin();
     getUser();
    </script>
</body>
</html>

博客详情页(blog_detail.html)

<!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>
  <link rel="stylesheet" href="common.css">
  <link rel="stylesheet" href="blogdetail.css">
   <!-- 引入 editor.md 的依赖 -->
   <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
   <script src="js/jquery.min.js"></script>
   <script src="editor.md/lib/marked.min.js"></script>
   <script src="editor.md/lib/prettify.min.js"></script>
   <script src="editor.md/editormd.js"></script>
</head>
<body>
  <!-- 导航栏 -->
  <div class="navigation">
    <img src="image/大头贴.png">
    <span>我的博客系统</span>
    <!-- 占位置用的 -->
    <span class="space"></span>
    <a href="blog_home.html">主页</a>
    <a href="write_blog.html">写博客</a>
    <a href="logout">退出登录</a>
    <!-- 如果这篇博客是用户自己写的,这里会出现一个删除该博客的按钮 -->
  </div>
  <!-- 这里是页面的版心 -->
  <div class="container">
    <!-- 左侧用户信息区 -->
    <div class="left">
      <!-- 表示整个用户信息区域 -->
      <div class="card">
        <img src="image/博客头像男.jpg">
        <h3>是小鱼儿哈</h3>
        <a href="https://blog.csdn.net/weixin_61061381?type=blog">CSDN地址</a>
        <div class="counter">
          <span>文章</span>
          <span>分类</span>
        </div>
        <div class="counter">
          <span>67</span>
          <span>7</span>
        </div>
      </div>
    </div>
    <!-- 右侧内容区 -->
    <div class="right">
      <!-- 标题 -->
      <h2></h2>
      <!-- 日期 -->
      <div class="date">
      </div> 
       <!-- 博客正文,注意我们这里是id名为content,而不是类名,id选择器#开头 -->
       <div id="content" style="opacity: 80%">  
      </div>
    </div>  
  </div>
    <!-- 引入jquery -->
    <script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
    <script>
      // 通过这个函数,借助blogId,来从后端获取到这篇博客的作者,并把他显示在博客详情页上
      function getAuthor() {
        $.ajax({
          type: 'get',
          url: 'user' + location.search, //JS中可以通过location.search设置或获取网页地址跟在问号后面的部
          // 后端在查询失败的时候不要返回200,避免前端触发ajax中的sucess回调函数
          success: function(body) {
            let authorDiv = document.querySelector('.card h3');
            // 但其实这一步也没什么必要,因为我们只是获取到原来的作者标签,并不是新建的标签,在获取到了后该标签所对应的className本来就是原来的名字,不用担心原来的css样式失效
            authorDiv.className = '.card h3';
            authorDiv.innerHTML = body.username;
            // 接下来我们根据后端返回的body,来确定是否在导航栏中显示删除该博客这个按键(只有当前博客的作者和登录的用户名一样才能删,只能删自己的)
            // 所以我们就要在对应的后端代码中判断当前要删除的博客是否是自己的博客,在user/locatin.search所对应的后端servlet代码写相应的逻辑
            if (body.isYourBlog == 1) {
              // 新增一个a标签
              // alert("进入添加删除博客按钮的那个if语句了呀!"); 可以用了测试是否进入了该条件语句
              let DeleteA = document.createElement('a');
              DeleteA.href = "delete" + location.search; //JS中可以通过location.search设置或获取网页地址跟在问号后面的部分,即queryString
              DeleteA.innerHTML = "删除该博客";
              // 注意,在JS中,单引号和双引号是不同的,下面的代码如果你用双引号,你就获取不到该组件
              let navDiv = document.querySelector('.navigation'); 
              // 此时navDiv就代码的类名是class=avigation这个组件,这次赋值后,该组件的名字并没有变
              // 要想改变该组件的名字,还要通过 navDiv.className 来修改,让他被新的css选择器来修饰
              navDiv.appendChild(DeleteA);  // 把我们新增的a标签添加到导航栏中
              // // 在导航栏中加个按钮, 用来删除文章. 
              // let deleteA = document.createElement('a');
              // // location.search 就是当前页面 url 的 query string, 也就是
              // // ?blogId=1 这样的结果. 
              // deleteA.href = 'blogDelete' + location.search;
              // deleteA.innerHTML = '删除';
              // let navDiv = document.querySelector('.nav');
              // navDiv.appendChild(deleteA);
            }
          },
          error: function() {
            // 强行跳转到登录页面
            location.assign('blog_login.html');
          }
        });
      }
      // 通过这个函数, 来获取到博客详情
      function getBlogDetail() {
        $.ajax({
          type: 'get',
          url: 'blog' + location.search, // servlet path,和获取博客列表一个地址,但获取博客列表不带blogId,显示博客详情带blogId
          // 后端在查询失败的时候不要返回200,避免前端触发ajax中的sucess回调函数
          success: function(body) {
            // 根据返回的数据来构造页面
            // rightDiv是我们博客详情,我们具体的博客内容要挂在rightDiv这个结点上
            let rightDiv = document.querySelector('.right'); // 获取我们上面的右侧页面对象,接下来我们就可以对右侧页面对象进行操作来给我们页面添加元素
            rightDiv.className = 'right'; // 引入css
            // 博客标题构建与添加
            // let titleDiv = document.createComment('div');
            // titleDiv.className = 'h2';
            // titleDiv.innerHTML = body.title;
            // rightDiv.appendChild(titleDiv); // 把标题结点挂到博客详情上
            // // 创建该篇博客的发布日期,并把日期添加到博客详情rightDiv上
            // let dateDiv = document.createComment('div');
            // dateDiv.className = 'date';
            // dateDiv.innerHTML = body.dateTime;
            // rightDiv.appendChild(dateDiv);
            // 博客标题
            let titleDiv = document.querySelector('.right h2');
            titleDiv.innerHTML = body.title;
            // 发布日期
            let dateDiv = document.querySelector('.right .date');
            dateDiv.innerHTML = body.postTime;
            // 创建内容
            // 刚才这里是直接把正文内容设置到 innerHTML 中了, 没有渲染的过程的. 
            // let divContent = document.querySelector('#content');
            // divContent.innerHTML = body.content;
            // 靠谱的做法, 应该是先使用 editor.md 进行渲染. 
            // [重要] 这个方法就是 editor.md 提供的一个方法把 markdown 字符串转成格式化的效果. 
            // 第一个参数是一个 div 的 id, 表示要把渲染结果放到哪个 div 中. 
            // 第二个参数是一个 js 对象, 把正文内容传入即可. (还支持很多别的参数属性, 此处暂时不涉及)
            editormd.markdownToHTML('content', {
              markdown: body.content
            });            
          },
          error: function() {
            alert("获取博客详情失败!");
          }
        });
      }
      function checkLogin() {
        $.ajax({
          type: 'get',
          url: 'login',
          success: function(body) {
            // 如果用户已经登录就什么也不做
          },
          error: function() {
          //alert("当前登录已过期,请重新登录!")
          // 403 就会触发 error
            // 强行跳转到登录页面. 
            location.assign('blog_login.html');
          }
        });
      }
      getBlogDetail();
      checkLogin();
      getAuthor();
    </script>
</body>
</html>

🏀LoginServlet代码

用来处理前端登录页(blog_login.html)发来的发来的post请求


此外这个类还额外处理了一个验证当前用户是否已经登录的get请求

(前端的博客列表页blog.home、博客详情页blog_detail.html和博客编辑页都发送一个这样的验证用户是否登录的get请求)

用到了模型层的User、UserDao

package controller;
import mode.User;
import mode.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
    // 处理用户提交登录登陆信息,进行302跳转,跳转到博客列表页,即博客首页
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=utf-8");
        // 告诉服务器如何解析请求,用户输入的密码可能是中文
        req.setCharacterEncoding("utf8");
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if (username == null || password == null) {
            resp.getWriter().write("你输入的账号或密码错误");
            return;
        }
        User user = UserDao.selectByName(username);
        if (user == null || !user.getPassword().equals(password)) {
            System.out.println("这是因为浏览器默认按GDK来解读我们HTTP请求发过来的参数吗");
            resp.getWriter().write("你输入的账号或密码错误");
        }
        else {
            // tomcat把会话存到 内存中,服务器一重启,会话就消失了
            // 登录之后,构造会话
            HttpSession session = req.getSession(true); // 参数为true说明如果当前没有会话,就新建立一个会话
            // 把刚才获取到的user对象给存到session里,方便后续使用
            session.setAttribute("user", user);
            // 返回一个重定向报文,跳转到博客列表页
            resp.sendRedirect("blog_home.html");
        }
    }
    @Override
    // 检查用户是否登录,没登陆强转返回登录页面
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 看能不能从请求中取到session,也接是看看请求中有没有cookie,是否存了sessionId
        HttpSession session = req.getSession(false);
        if (session == null) {
            // resp.sendRedirect("blog_login.html"); 为啥在后端就直接跳转不了,的确302了,但就是跳转不了
            // 当前用户还为登录,可以在这里跳转,也可以设置设置一个状态值,返回给前端,让前端跳转
            resp.setStatus(403);
            //resp.sendRedirect("blog_login.html"); // 强转跳转到登录页面
        }
        else {
            // 不做任何处理
            // resp.setStatus(200);
        }
    }
}


相关联的视图层的代码

博客登录页(blog_login.html)

<!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>
  <link rel="stylesheet" href="common.css">
  <link rel="stylesheet" href="bloglogin.css">
</head>
<body>
  <!-- 导航栏 -->
  <div class="navigation">
    <img src="image/大头贴.png">
    <span>我的博客系统</span>
    <!-- 占位置用的 -->
    <span class="space"></span>
    <a href="blog_home.html">主页</a>
    <a href="write_blog.html">写博客</a>
  </div>
   <!-- 这个container_login就作为我们登录的版心 -->
  <div class="container_login">
    <form action="login" method="post">
      <div class="dialog">
        <h3 >登录</h3>
        <div class="row"> 
          <span>用户名</span>
          <!-- 必须要用name属性,我们后端就是通过这些name属性拿到用户提交的信息的 -->
          <!-- 如果没有name属性,前端给后端发的请求中没没有用户信息  -->
          <input type="text" id="username" name="username"> 
        </div>
        <div class="row">
          <span>密码</span>
          <input type="password" id="password" name="password">
        </div>
        <div class="row">
        <input type="submit" id="submit" value="登录">
        </div>
        <div class="row">
          <div id="reg">
            <a href="blog_reg.html">注册</a>
          </div>
        </div>
      </div>
    </form>
  </div>
</body>
</html>


博客列表页(blog_home.html)

<!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>
  <link rel="stylesheet" href="common.css">
  <link rel="stylesheet" href="bologlist.css">
</head>
<body>
  <!-- 导航栏 -->
  <div class="navigation">
    <img src="image/大头贴.png">
    <span>我的博客系统</span>
    <!-- 占位置用的 -->
    <span class="space"></span>
    <a href="blog_home.html">主页</a>
    <a href="write_blog.html">写博客</a>
    <a href="logout">退出登录</a>
  </div>
  <!-- 这里是页面的版心 -->
  <div class="container">
    <!-- 左侧用户信息区 -->
    <div class="left">
      <!-- 表示整个用户信息区域 -->
      <div class="card">
        <img src="image/博客头像男.jpg">
        <h3>是小鱼儿哈</h3>
        <a href="https://blog.csdn.net/weixin_61061381?type=blog">CSDN地址</a>
        <div class="counter">
          <span>文章</span>
          <span>分类</span>
        </div>
        <div class="counter">
          <span>67</span>
          <span>7</span>
        </div>
      </div>
    </div>
    <!-- 右侧博客列表页 -->
    <div class="right">
    </div>
  </div>
  <!-- 我们的js代码一般放在我们页面后面,来进行事件给构造,因为在调用回调函数时候,前提是你已经有了页面内容,然后对页面内容进行操作 -->
   <!-- 响要使用ajax构造请求,我们要有引入jquery -->
   <script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
   <script>
     // 通过该函数,从后端获取到当前登录的用户是谁,并把它显示在博客列表页上
     function getUser() {
      $.ajax({
        type: 'get',
        url: 'user',
        success: function(body) {
          // 替换条原来的用户信息
          let userDiv = document.querySelector('.card h3');
          // 引入css,因为我们css就是按原来的类选择器来设置样式的
           // 但其实这一步也没什么必要,因为我们只是获取到原来的作者标签,并不是新建的标签,在获取到了后该标签所对应的className本来就是原来的名字,不用担心原来的css样式失效
          userDiv.className = '.card h3';
          userDiv.innerHTML = body.username;
        },
        error: function() {
           // 强行跳转到登录页面. 
           location.assign('blog_login.html');
        }
      });
     }
     // 你要明白一个html文件,在加载的过程中,是可以同时执行多个函数、多个ajax同时发送请求的
     // 通过这个函数, 来从服务器获取到博客列表的数据
     function getBlogList() {
         $.ajax({
             type: 'get',
             url: 'blog',  // servlet path, 在页面加载的过程中就会调用主机ip + contentPath + 这个servlet path(就@WebServlet后的地址)
             //dataType: 'json',
             success: function(body) {
                 // 根据返回的 json 数据, 来构造出页面内容, div.blog
                 // jquery ajax 会自动的把响应得到的 body 按照响应的 Content-Type 进行转换格式. 
                 // 如果响应的 Content-Type 是 json, 此时就会自动把 body 转成 js 的对象
                 let rightDiv = document.querySelector('.right');
                 for (let blog of body) {
                     // 新建博客结点
                     let blogDiv = document.createElement('div');
                     blogDiv.className = 'blog'; // 引入CSS属性
                     // 创建博客标题
                     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 = '查看全文 >>';
                     // a标签跳转的过程就相当于是发了一个get请求,这里我们在跳转的url地址后加上要传递的参数,即QueryString,等下我们在博客详情页也会用到这个参数
                     a.href = 'blog_detail.html?blogId=' + blog.blogId;
                     blogDiv.appendChild(a);
                     // 把 blogDiv 加入外层元素
                     rightDiv.appendChild(blogDiv);  // 把构建好的一篇博客挂到博客列表上
                 }
             },
             error: function() {
              alert("获取博客列表失败!")
             }
         });
     }
     function checkLogin() {
      $.ajax({
        type: 'get',
        url: 'login',
        success: function(body) {
          // 如果用户已经登录就什么也不做
        },
        error: function() {
          //alert("当前登录已过期,请重新登录!")
          // 403 就会触发 error
            // 强行跳转到登录页面. 
            location.assign('blog_login.html');
        }
      });
     }
     getBlogList(); // 不用忘了调用函数
     checkLogin();
     getUser();
    </script>
</body>
</html>


博客详情页(blog_detail.html)

<!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>
  <link rel="stylesheet" href="common.css">
  <link rel="stylesheet" href="blogdetail.css">
   <!-- 引入 editor.md 的依赖 -->
   <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
   <script src="js/jquery.min.js"></script>
   <script src="editor.md/lib/marked.min.js"></script>
   <script src="editor.md/lib/prettify.min.js"></script>
   <script src="editor.md/editormd.js"></script>
</head>
<body>
  <!-- 导航栏 -->
  <div class="navigation">
    <img src="image/大头贴.png">
    <span>我的博客系统</span>
    <!-- 占位置用的 -->
    <span class="space"></span>
    <a href="blog_home.html">主页</a>
    <a href="write_blog.html">写博客</a>
    <a href="logout">退出登录</a>
    <!-- 如果这篇博客是用户自己写的,这里会出现一个删除该博客的按钮 -->
  </div>
  <!-- 这里是页面的版心 -->
  <div class="container">
    <!-- 左侧用户信息区 -->
    <div class="left">
      <!-- 表示整个用户信息区域 -->
      <div class="card">
        <img src="image/博客头像男.jpg">
        <h3>是小鱼儿哈</h3>
        <a href="https://blog.csdn.net/weixin_61061381?type=blog">CSDN地址</a>
        <div class="counter">
          <span>文章</span>
          <span>分类</span>
        </div>
        <div class="counter">
          <span>67</span>
          <span>7</span>
        </div>
      </div>
    </div>
    <!-- 右侧内容区 -->
    <div class="right">
      <!-- 标题 -->
      <h2></h2>
      <!-- 日期 -->
      <div class="date">
      </div> 
       <!-- 博客正文,注意我们这里是id名为content,而不是类名,id选择器#开头 -->
       <div id="content" style="opacity: 80%">  
      </div>
    </div>  
  </div>
    <!-- 引入jquery -->
    <script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
    <script>
      // 通过这个函数,借助blogId,来从后端获取到这篇博客的作者,并把他显示在博客详情页上
      function getAuthor() {
        $.ajax({
          type: 'get',
          url: 'user' + location.search, //JS中可以通过location.search设置或获取网页地址跟在问号后面的部
          // 后端在查询失败的时候不要返回200,避免前端触发ajax中的sucess回调函数
          success: function(body) {
            let authorDiv = document.querySelector('.card h3');
            // 但其实这一步也没什么必要,因为我们只是获取到原来的作者标签,并不是新建的标签,在获取到了后该标签所对应的className本来就是原来的名字,不用担心原来的css样式失效
            authorDiv.className = '.card h3';
            authorDiv.innerHTML = body.username;
            // 接下来我们根据后端返回的body,来确定是否在导航栏中显示删除该博客这个按键(只有当前博客的作者和登录的用户名一样才能删,只能删自己的)
            // 所以我们就要在对应的后端代码中判断当前要删除的博客是否是自己的博客,在user/locatin.search所对应的后端servlet代码写相应的逻辑
            if (body.isYourBlog == 1) {
              // 新增一个a标签
              // alert("进入添加删除博客按钮的那个if语句了呀!"); 可以用了测试是否进入了该条件语句
              let DeleteA = document.createElement('a');
              DeleteA.href = "delete" + location.search; //JS中可以通过location.search设置或获取网页地址跟在问号后面的部分,即queryString
              DeleteA.innerHTML = "删除该博客";
              // 注意,在JS中,单引号和双引号是不同的,下面的代码如果你用双引号,你就获取不到该组件
              let navDiv = document.querySelector('.navigation'); 
              // 此时navDiv就代码的类名是class=avigation这个组件,这次赋值后,该组件的名字并没有变
              // 要想改变该组件的名字,还要通过 navDiv.className 来修改,让他被新的css选择器来修饰
              navDiv.appendChild(DeleteA);  // 把我们新增的a标签添加到导航栏中
              // // 在导航栏中加个按钮, 用来删除文章. 
              // let deleteA = document.createElement('a');
              // // location.search 就是当前页面 url 的 query string, 也就是
              // // ?blogId=1 这样的结果. 
              // deleteA.href = 'blogDelete' + location.search;
              // deleteA.innerHTML = '删除';
              // let navDiv = document.querySelector('.nav');
              // navDiv.appendChild(deleteA);
            }
          },
          error: function() {
            // 强行跳转到登录页面
            location.assign('blog_login.html');
          }
        });
      }
      // 通过这个函数, 来获取到博客详情
      function getBlogDetail() {
        $.ajax({
          type: 'get',
          url: 'blog' + location.search, // servlet path,和获取博客列表一个地址,但获取博客列表不带blogId,显示博客详情带blogId
          // 后端在查询失败的时候不要返回200,避免前端触发ajax中的sucess回调函数
          success: function(body) {
            // 根据返回的数据来构造页面
            // rightDiv是我们博客详情,我们具体的博客内容要挂在rightDiv这个结点上
            let rightDiv = document.querySelector('.right'); // 获取我们上面的右侧页面对象,接下来我们就可以对右侧页面对象进行操作来给我们页面添加元素
            rightDiv.className = 'right'; // 引入css
            // 博客标题构建与添加
            // let titleDiv = document.createComment('div');
            // titleDiv.className = 'h2';
            // titleDiv.innerHTML = body.title;
            // rightDiv.appendChild(titleDiv); // 把标题结点挂到博客详情上
            // // 创建该篇博客的发布日期,并把日期添加到博客详情rightDiv上
            // let dateDiv = document.createComment('div');
            // dateDiv.className = 'date';
            // dateDiv.innerHTML = body.dateTime;
            // rightDiv.appendChild(dateDiv);
            // 博客标题
            let titleDiv = document.querySelector('.right h2');
            titleDiv.innerHTML = body.title;
            // 发布日期
            let dateDiv = document.querySelector('.right .date');
            dateDiv.innerHTML = body.postTime;
            // 创建内容
            // 刚才这里是直接把正文内容设置到 innerHTML 中了, 没有渲染的过程的. 
            // let divContent = document.querySelector('#content');
            // divContent.innerHTML = body.content;
            // 靠谱的做法, 应该是先使用 editor.md 进行渲染. 
            // [重要] 这个方法就是 editor.md 提供的一个方法把 markdown 字符串转成格式化的效果. 
            // 第一个参数是一个 div 的 id, 表示要把渲染结果放到哪个 div 中. 
            // 第二个参数是一个 js 对象, 把正文内容传入即可. (还支持很多别的参数属性, 此处暂时不涉及)
            editormd.markdownToHTML('content', {
              markdown: body.content
            });            
          },
          error: function() {
            alert("获取博客详情失败!");
          }
        });
      }
      function checkLogin() {
        $.ajax({
          type: 'get',
          url: 'login',
          success: function(body) {
            // 如果用户已经登录就什么也不做
          },
          error: function() {
          //alert("当前登录已过期,请重新登录!")
          // 403 就会触发 error
            // 强行跳转到登录页面. 
            location.assign('blog_login.html');
          }
        });
      }
      getBlogDetail();
      checkLogin();
      getAuthor();
    </script>
</body>
</html>

🏀LoginOutServlet代码

用来应对视图层发来的注销登录的请求,当然也用到了模型层(User、UserDao)

package controller;
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 LoginOutServlet extends HttpServlet {
    // 我们是通过a标签跳转到这里来的,所以这里是get方法
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取当前的session,因为只有在用户以及登录的情况下,才会出现这个退出登录的按键,所以这里我们不考虑session为空
        // 因为在req类中没有直接提供删除session的操作,所以为了简便我们这里就不删session
        // 我们这里的退出登录只是把session里的user键值对中的值给删除了,但session还在,sessionId还在,也就是请求中的cookie还在
        // 这也是为啥我们在判断当前用户是否登录是还要专门判断session在的user的值还在不在(user是否为空),如果不再说明我们已经通过这种方式,退出了登录
        HttpSession session = req.getSession(false);
        session.setAttribute("user", null);
        // 重定向报文,强制跳转到登录页面,既然以及退出登录了,肯定要重新登录呀!!
        resp.sendRedirect("blog_login.html");
    }
}


🏀RegServlet代码

用来注册用户,当然和视图层(注册页)和模型层(User、UserDao)也有联系

package controller;
import mode.User;
import mode.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("/reg")
public class RegServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        String username = req.getParameter("username");
        String password1 = req.getParameter("password1");
        String password2 = req.getParameter("password2");
        if (username == null | password1 == null | password2 == null) {
            resp.setStatus(403);
            resp.setContentType("text/html; charset=utf-8");
            resp.getWriter().write("你的输入不能为空!请重新输入!!");
            // resp.sendRedirect("blog_reg.html");
            // 因为我们是用from表单提交的,前端跳转后,我们无法给用户提示信息
            // 在有跳转的情况下,我们的提示信息显示不出来,所以我们干脆就不跳转,让用户手动跳转,重新注册
        }
        // 密码不一致也不行
        else if (!password1.equals(password2)) { // 注意因为这里是字符所以我们,要用equals
            resp.setStatus(403);
            resp.setContentType("text/html; charset=utf-8");
            // 下面是返回了一个js弹窗
            //resp.getWriter().write("<script language='javascript'>alert('两次输入的密码不一致!请重新输入!!')</script>");
            resp.getWriter().write("两次输入的密码不一致!请重新输入!!");
            //resp.sendRedirect("blog_reg.html");
        } else {
            // 如果程序走到这里说明,说明用户的提交没什么问题,我们可以开始给用户注册了
            User user = new User();
            user.setUsername(username);
            user.setPassword(password1);
            int ret = UserDao.insertUser(user);
            if (ret == 0) {
                // 注册成功,跳转到登录页面
                System.out.println("注册成功!");
                resp.sendRedirect("blog_login.html");
            }
            else {
                // 注册失败,新增用户失败,当前用户名已经被注册过了
                resp.setStatus(403);
                System.out.println("注册失败!");
                resp.setContentType("text/html; charset=utf-8");
                resp.getWriter().write("当前用户已存在,请重新输入!!");
                //resp.sendRedirect("blog_reg.html");
            }
        }
    }
}

三、视图层(view)总代码展示

🍑博客登录页

<!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>
  <link rel="stylesheet" href="common.css">
  <link rel="stylesheet" href="bloglogin.css">
</head>
<body>
  <!-- 导航栏 -->
  <div class="navigation">
    <img src="image/大头贴.png">
    <span>我的博客系统</span>
    <!-- 占位置用的 -->
    <span class="space"></span>
    <a href="blog_home.html">主页</a>
    <a href="write_blog.html">写博客</a>
  </div>
   <!-- 这个container_login就作为我们登录的版心 -->
  <div class="container_login">
    <form action="login" method="post">
      <div class="dialog">
        <h3 >登录</h3>
        <div class="row"> 
          <span>用户名</span>
          <!-- 必须要用name属性,我们后端就是通过这些name属性拿到用户提交的信息的 -->
          <!-- 如果没有name属性,前端给后端发的请求中没没有用户信息  -->
          <input type="text" id="username" name="username"> 
        </div>
        <div class="row">
          <span>密码</span>
          <input type="password" id="password" name="password">
        </div>
        <div class="row">
        <input type="submit" id="submit" value="登录">
        </div>
        <div class="row">
          <div id="reg">
            <a href="blog_reg.html">注册</a>
          </div>
        </div>
      </div>
    </form>
  </div>
</body>
</html>

🍑博客注册页

<!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>
  <link rel="stylesheet" href="common.css">
  <link rel="stylesheet" href="blogreg.css">
</head>
<body>
  <!-- 导航栏 -->
  <div class="navigation">
    <img src="image/大头贴.png">
    <span>我的博客系统</span>
    <!-- 占位置用的 -->
    <span class="space"></span>
    <!-- 既然是注册,还是不要显示这两个了 -->
    <a href="blog_login.html">登录</a>
    <!-- <a href="blog_home.html">主页</a>
    <a href="write_blog.html">写博客</a> -->
  </div>
   <!-- 这个container_login就作为我们登录的版心 -->
  <div class="container_reg">
    <form action="reg" method="post">
      <div class="dialog">
        <h3 >注册</h3>
        <div class="row"> 
          <span>用户名</span>
          <!-- 必须要用name属性,我们后端就是通过这些name属性拿到用户提交的信息的 -->
          <!-- 如果没有name属性,前端给后端发的请求中没没有用户信息  -->
          <input type="text" id="username" name="username"> 
        </div>
        <div class="row">
          <span>密码</span>
          <input type="password" id="password1" name="password1">
        </div>
        <div class="row">
            <span>确认密码</span>
            <input type="password" id="password2" name="password2">
          </div>
        <div class="row">
        <input type="submit" id="submit" value="注册">
        </div>
      </div>
    </form>
  </div>
</body>
</html>


🍑博客列表页

<!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>
  <link rel="stylesheet" href="common.css">
  <link rel="stylesheet" href="bologlist.css">
</head>
<body>
  <!-- 导航栏 -->
  <div class="navigation">
    <img src="image/大头贴.png">
    <span>我的博客系统</span>
    <!-- 占位置用的 -->
    <span class="space"></span>
    <a href="blog_home.html">主页</a>
    <a href="write_blog.html">写博客</a>
    <a href="logout">退出登录</a>
  </div>
  <!-- 这里是页面的版心 -->
  <div class="container">
    <!-- 左侧用户信息区 -->
    <div class="left">
      <!-- 表示整个用户信息区域 -->
      <div class="card">
        <img src="image/博客头像男.jpg">
        <h3>是小鱼儿哈</h3>
        <a href="https://blog.csdn.net/weixin_61061381?type=blog">CSDN地址</a>
        <div class="counter">
          <span>文章</span>
          <span>分类</span>
        </div>
        <div class="counter">
          <span>67</span>
          <span>7</span>
        </div>
      </div>
    </div>
    <!-- 右侧博客列表页 -->
    <div class="right">
    </div>
  </div>
  <!-- 我们的js代码一般放在我们页面后面,来进行事件给构造,因为在调用回调函数时候,前提是你已经有了页面内容,然后对页面内容进行操作 -->
   <!-- 响要使用ajax构造请求,我们要有引入jquery -->
   <script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
   <script>
     // 通过该函数,从后端获取到当前登录的用户是谁,并把它显示在博客列表页上
     function getUser() {
      $.ajax({
        type: 'get',
        url: 'user',
        success: function(body) {
          // 替换条原来的用户信息
          let userDiv = document.querySelector('.card h3');
          // 引入css,因为我们css就是按原来的类选择器来设置样式的
           // 但其实这一步也没什么必要,因为我们只是获取到原来的作者标签,并不是新建的标签,在获取到了后该标签所对应的className本来就是原来的名字,不用担心原来的css样式失效
          userDiv.className = '.card h3';
          userDiv.innerHTML = body.username;
        },
        error: function() {
           // 强行跳转到登录页面. 
           location.assign('blog_login.html');
        }
      });
     }
     // 你要明白一个html文件,在加载的过程中,是可以同时执行多个函数、多个ajax同时发送请求的
     // 通过这个函数, 来从服务器获取到博客列表的数据
     function getBlogList() {
         $.ajax({
             type: 'get',
             url: 'blog',  // servlet path, 在页面加载的过程中就会调用主机ip + contentPath + 这个servlet path(就@WebServlet后的地址)
             //dataType: 'json',
             success: function(body) {
                 // 根据返回的 json 数据, 来构造出页面内容, div.blog
                 // jquery ajax 会自动的把响应得到的 body 按照响应的 Content-Type 进行转换格式. 
                 // 如果响应的 Content-Type 是 json, 此时就会自动把 body 转成 js 的对象
                 let rightDiv = document.querySelector('.right');
                 for (let blog of body) {
                     // 新建博客结点
                     let blogDiv = document.createElement('div');
                     blogDiv.className = 'blog'; // 引入CSS属性
                     // 创建博客标题
                     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 = '查看全文 >>';
                     // a标签跳转的过程就相当于是发了一个get请求,这里我们在跳转的url地址后加上要传递的参数,即QueryString,等下我们在博客详情页也会用到这个参数
                     a.href = 'blog_detail.html?blogId=' + blog.blogId;
                     blogDiv.appendChild(a);
                     // 把 blogDiv 加入外层元素
                     rightDiv.appendChild(blogDiv);  // 把构建好的一篇博客挂到博客列表上
                 }
             },
             error: function() {
              alert("获取博客列表失败!")
             }
         });
     }
     function checkLogin() {
      $.ajax({
        type: 'get',
        url: 'login',
        success: function(body) {
          // 如果用户已经登录就什么也不做
        },
        error: function() {
          //alert("当前登录已过期,请重新登录!")
          // 403 就会触发 error
            // 强行跳转到登录页面. 
            location.assign('blog_login.html');
        }
      });
     }
     getBlogList(); // 不用忘了调用函数
     checkLogin();
     getUser();
    </script>
</body>
</html>

🍑博客详情页

<!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>
  <link rel="stylesheet" href="common.css">
  <link rel="stylesheet" href="blogdetail.css">
   <!-- 引入 editor.md 的依赖 -->
   <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
   <script src="js/jquery.min.js"></script>
   <script src="editor.md/lib/marked.min.js"></script>
   <script src="editor.md/lib/prettify.min.js"></script>
   <script src="editor.md/editormd.js"></script>
</head>
<body>
  <!-- 导航栏 -->
  <div class="navigation">
    <img src="image/大头贴.png">
    <span>我的博客系统</span>
    <!-- 占位置用的 -->
    <span class="space"></span>
    <a href="blog_home.html">主页</a>
    <a href="write_blog.html">写博客</a>
    <a href="logout">退出登录</a>
    <!-- 如果这篇博客是用户自己写的,这里会出现一个删除该博客的按钮 -->
  </div>
  <!-- 这里是页面的版心 -->
  <div class="container">
    <!-- 左侧用户信息区 -->
    <div class="left">
      <!-- 表示整个用户信息区域 -->
      <div class="card">
        <img src="image/博客头像男.jpg">
        <h3>是小鱼儿哈</h3>
        <a href="https://blog.csdn.net/weixin_61061381?type=blog">CSDN地址</a>
        <div class="counter">
          <span>文章</span>
          <span>分类</span>
        </div>
        <div class="counter">
          <span>67</span>
          <span>7</span>
        </div>
      </div>
    </div>
    <!-- 右侧内容区 -->
    <div class="right">
      <!-- 标题 -->
      <h2></h2>
      <!-- 日期 -->
      <div class="date">
      </div> 
       <!-- 博客正文,注意我们这里是id名为content,而不是类名,id选择器#开头 -->
       <div id="content" style="opacity: 80%">  
      </div>
    </div>  
  </div>
    <!-- 引入jquery -->
    <script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
    <script>
      // 通过这个函数,借助blogId,来从后端获取到这篇博客的作者,并把他显示在博客详情页上
      function getAuthor() {
        $.ajax({
          type: 'get',
          url: 'user' + location.search, //JS中可以通过location.search设置或获取网页地址跟在问号后面的部
          // 后端在查询失败的时候不要返回200,避免前端触发ajax中的sucess回调函数
          success: function(body) {
            let authorDiv = document.querySelector('.card h3');
            // 但其实这一步也没什么必要,因为我们只是获取到原来的作者标签,并不是新建的标签,在获取到了后该标签所对应的className本来就是原来的名字,不用担心原来的css样式失效
            authorDiv.className = '.card h3';
            authorDiv.innerHTML = body.username;
            // 接下来我们根据后端返回的body,来确定是否在导航栏中显示删除该博客这个按键(只有当前博客的作者和登录的用户名一样才能删,只能删自己的)
            // 所以我们就要在对应的后端代码中判断当前要删除的博客是否是自己的博客,在user/locatin.search所对应的后端servlet代码写相应的逻辑
            if (body.isYourBlog == 1) {
              // 新增一个a标签
              // alert("进入添加删除博客按钮的那个if语句了呀!"); 可以用了测试是否进入了该条件语句
              let DeleteA = document.createElement('a');
              DeleteA.href = "delete" + location.search; //JS中可以通过location.search设置或获取网页地址跟在问号后面的部分,即queryString
              DeleteA.innerHTML = "删除该博客";
              // 注意,在JS中,单引号和双引号是不同的,下面的代码如果你用双引号,你就获取不到该组件
              let navDiv = document.querySelector('.navigation'); 
              // 此时navDiv就代码的类名是class=avigation这个组件,这次赋值后,该组件的名字并没有变
              // 要想改变该组件的名字,还要通过 navDiv.className 来修改,让他被新的css选择器来修饰
              navDiv.appendChild(DeleteA);  // 把我们新增的a标签添加到导航栏中
              // // 在导航栏中加个按钮, 用来删除文章. 
              // let deleteA = document.createElement('a');
              // // location.search 就是当前页面 url 的 query string, 也就是
              // // ?blogId=1 这样的结果. 
              // deleteA.href = 'blogDelete' + location.search;
              // deleteA.innerHTML = '删除';
              // let navDiv = document.querySelector('.nav');
              // navDiv.appendChild(deleteA);
            }
          },
          error: function() {
            // 强行跳转到登录页面
            location.assign('blog_login.html');
          }
        });
      }
      // 通过这个函数, 来获取到博客详情
      function getBlogDetail() {
        $.ajax({
          type: 'get',
          url: 'blog' + location.search, // servlet path,和获取博客列表一个地址,但获取博客列表不带blogId,显示博客详情带blogId
          // 后端在查询失败的时候不要返回200,避免前端触发ajax中的sucess回调函数
          success: function(body) {
            // 根据返回的数据来构造页面
            // rightDiv是我们博客详情,我们具体的博客内容要挂在rightDiv这个结点上
            let rightDiv = document.querySelector('.right'); // 获取我们上面的右侧页面对象,接下来我们就可以对右侧页面对象进行操作来给我们页面添加元素
            rightDiv.className = 'right'; // 引入css
            // 博客标题构建与添加
            // let titleDiv = document.createComment('div');
            // titleDiv.className = 'h2';
            // titleDiv.innerHTML = body.title;
            // rightDiv.appendChild(titleDiv); // 把标题结点挂到博客详情上
            // // 创建该篇博客的发布日期,并把日期添加到博客详情rightDiv上
            // let dateDiv = document.createComment('div');
            // dateDiv.className = 'date';
            // dateDiv.innerHTML = body.dateTime;
            // rightDiv.appendChild(dateDiv);
            // 博客标题
            let titleDiv = document.querySelector('.right h2');
            titleDiv.innerHTML = body.title;
            // 发布日期
            let dateDiv = document.querySelector('.right .date');
            dateDiv.innerHTML = body.postTime;
            // 创建内容
            // 刚才这里是直接把正文内容设置到 innerHTML 中了, 没有渲染的过程的. 
            // let divContent = document.querySelector('#content');
            // divContent.innerHTML = body.content;
            // 靠谱的做法, 应该是先使用 editor.md 进行渲染. 
            // [重要] 这个方法就是 editor.md 提供的一个方法把 markdown 字符串转成格式化的效果. 
            // 第一个参数是一个 div 的 id, 表示要把渲染结果放到哪个 div 中. 
            // 第二个参数是一个 js 对象, 把正文内容传入即可. (还支持很多别的参数属性, 此处暂时不涉及)
            editormd.markdownToHTML('content', {
              markdown: body.content
            });            
          },
          error: function() {
            alert("获取博客详情失败!");
          }
        });
      }
      function checkLogin() {
        $.ajax({
          type: 'get',
          url: 'login',
          success: function(body) {
            // 如果用户已经登录就什么也不做
          },
          error: function() {
          //alert("当前登录已过期,请重新登录!")
          // 403 就会触发 error
            // 强行跳转到登录页面. 
            location.assign('blog_login.html');
          }
        });
      }
      getBlogDetail();
      checkLogin();
      getAuthor();
    </script>
</body>
</html>

🍑博客编辑页

<!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>
  <link rel="stylesheet" href="common.css">
  <link rel="stylesheet" href="writeblog.css">
   <!-- 引入 editor.md 的依赖 -->
   <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
   <script src="js/jquery.min.js"></script>
   <script src="editor.md/lib/marked.min.js"></script>
   <script src="editor.md/lib/prettify.min.js"></script>
   <script src="editor.md/editormd.js"></script>
</head>
<body>
  <!-- 导航栏 -->
  <div class="navigation">
    <img src="image/大头贴.png">
    <span>我的博客系统</span>
    <!-- 占位置用的 -->
    <span class="space"></span>
    <a href="blog_home.html">主页</a>
    <a href="write_blog.html">写博客</a>
    <a href="logout">退出登录</a>
  </div>
  <div class="blog-edit-container"> 
    <!-- 内容分为两个部分,标题区和编辑区 -->
    <form action="blog" method="post" style="height: 100%">
       <!-- 标题区 -->
      <div class="title">
      <!-- 这里我们用from表单提交数据,后端要想获得前端提交的数据,from表单中要加上name,后端就是通过这个key来获取到value的 -->
        <input type="text" id="title" placeholder="请输入文章标题", name="title">
        <input type="submit" id="submit" value="发布文章">
      </div>
      <!-- 编辑区 -->
      <div id="editor">
        <!-- 需要在这里加上一个隐藏的 textarea -->
        <!-- 属于editor.md 这个库的要求 -->
        <textarea name="content" style="display: none;"></textarea>
      </div>
    </form>
    <script>
      // 初始化编辑器
      var editor = editormd("editor", {
          // 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉. 
          width: "100%",
          // 设定编辑器高度,这个100%是相对于父元素的高度,此时editor的父元素的from, 但form没高度,所以我们要把from的高度设置一下
          height: "calc(100% - 50px)",
          // 编辑器中的初始内容
          markdown: "# 在这里写下一篇博客",
          // 指定 editor.md 依赖的插件路径
          path: "editor.md/lib/",
          // 加上这个属性,效果就是把编辑器里的内容给自动保存到textarea里
          saveHTMLToTextarea:true,
      });
      // 检查当前用户是否登录,如果未登录就强制跳转到登录界面
      function checkLogin() {
        $.ajax({
        type: 'get',
        url: 'login',
        success: function(body) {
          // 如果用户已经登录就什么也不做
        },
        error: function() {
          //alert("当前登录已过期,请重新登录!")
          // 403 就会触发 error
            // 强行跳转到登录页面. 
            location.assign('blog_login.html');
        }
        });
      }
      // 别忘了函数调用
      checkLogin() // 这样函数在页面加载的时候就会调用
    </script>
  </div>
</body>
</html>

🔔视图层对应的css代码

还有他们各自对应的css代码,注意css代码和html代码在同一个目录下

共同的样式

common.css

/* 导航栏的样式 */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
html, body {
  height: 100%;
  background-image: url(image/未来风\(1\).jpg);
  background-repeat: no-repeat;
  background-position: center center;
  background-size: cover;
}
.navigation {
  width: 100%;
  height: 50px;
  background-color: rgba(50, 50, 50, 0.4);
  color: rgb(255, 255, 255);
  display: flex;
  /* 实现元素垂直居中 */
  align-items: center;
}
.navigation img {
  height: 40px;
  width: 40px;
  border-radius: 50%;
  margin-left: 30px;
  margin-right: 10px;
}
.navigation .space {
  width: 68%;
}
.navigation a {
  /* 去掉下划线 */
  text-decoration: none;
  color: rgb(255, 255, 255);
  justify-content: space-between;
  padding: 0 10px;
  display: flex;
}
.container {
  width: 1000px;
  height: calc(100% - 50px);
  /* 要弄成弹性布局,要不然右块内容就另起一行了 */
  display: flex;
  /* 水平居中 */
  margin: 0 auto;
  /* 左右两块中间留出间距 */
  justify-content: space-between;
  /* 想一下为啥justify-content: center不能让两块居中; */
}
.container .left {
  height: 100%;
  width: 200px;
}
.container .right {
  height: 100%;
  width: 795px;
  background-color: rgba(255, 255, 255, 0.7);
  border-radius: 10px;
  /* 处理异常问题 */
  overflow:auto;
}
.card {
  /* card的宽度默认是200px,与父类相同 */
  background-color: rgba(255, 255, 255, 0.7);
  /* 注意棱角 */
  border-radius: 10px;
  /* 通过内边距使得头像水平居中,200 = 140 + 2 * 30 */
  padding: 30px;
}
/* 用户头像 */
.card img {
  width: 140px;
  height: 140px;
  border-radius: 70px;
}
.card h3 {
  text-align: center;
  margin: 5px;
}
.card a{
  /* 行级元素变成块级元素,因为行级元素无法指定大小 */
  display: block;
  /* 去掉下划线 */
  text-decoration: none;
  /* 文本居中 */
  text-align: center;
  color: rgba(50, 50, 50, 0.4);
  padding: 5px;
}
.card .counter {
  display: flex;
  /* 使得他们左右进行分离排列 */
  justify-content: space-around;
  margin: 5px;
}

博客登录页所对应的css代码

/* .dialog {
  width: 400px;
  height: 400px;
  display: flex;
  justify-content: center;
  align-items: center;
  注意不能之间用弹性布局,我们弹性布局用在父子之间,写在父元素中
} */
.container_login {
  width: 100%;
  /* 要注意减去导航栏的高度 */
  height: calc(100% - 50px); 
  display: flex;
  align-items: center;
  justify-content: center;
}
.container_login .dialog {
  width: 400px;
  height: 400px;
  background-color: rgba(255, 255, 255, 0.6);
  /* 边框圆角 */
  border-radius: 10px;
}
.dialog h3 {
  margin: 30px;
  text-align: center;
}
.dialog .row {
  width: 100%;
  height: 50px;
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 20px;
}
.dialog .row span {
  display: block;
  width: 100px;
  height: 40px;
  font-weight: 700;
  /* 文本水平居中 */
  text-align: center;
  padding-top: 10px;
  padding-bottom: 10px;
}
/* 输入框格式设置 */
.dialog .row #username, #password {
  display: block;
  width: 200px;
  height: 40px;
  border-radius: 10px;
  /* 设置左内边距,使得输入的数据与边框隔开 */
  padding-left: 10px;
  /* 去掉边框线,和轮廓线 */
  border: none;
  outline: none;
  /* 设置字体大小,和输入时字体的位置 */
  font-size: 22px;
  /* 文本垂直居中 */
  line-height: 40px;
}
/*登录提交框*/
.row #submit {
  width: 300px;
  height: 40px;
  margin-left: 50px;
  margin-right: 30px;
  background-color: rgb(0, 189, 189);
  font-size: large;
  /*设置边框中的文本位置*/
  text-align: center; 
  padding-top: 5px;
  padding-bottom: 5px;
}
/*注册框*/
.row #reg {
  width: 300px;
  height: 40px;
  margin-left: 50px;
  margin-right: 30px;
  background-color: rgb(0, 189, 126);
  font-size: large;
  /*设置边框中的文本位置*/
  text-align: center; 
  padding-top: 5px;
  padding-bottom: 5px;
}
/*去掉下划线
.navigation a {
  text-decoration: none;
  color: rgb(255, 255, 255);
  justify-content: space-between;
  padding: 0 10px;
  display: flex;
} * /

博客注册页对应的css

.container_reg {
  width: 100%;
  /* 要注意减去导航栏的高度 */
  height: calc(100% - 50px);
  display: flex;
  align-items: center;
  justify-content: center;
}
.container_reg .dialog {
  width: 400px;
  height: 400px;
  background-color: rgba(255, 255, 255, 0.6);
  /* 边框圆角 */
  border-radius: 10px;
}
.dialog h3 {
  margin: 30px;
  text-align: center;
}
.dialog .row {
  width: 100%;
  height: 50px;
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 20px;
}
.dialog .row span {
  display: block;
  width: 100px;
  height: 40px;
  font-weight: 700;
  /* 文本水平居中 */
  text-align: center;
  padding-top: 10px;
  padding-bottom: 10px;
}
.dialog .row #username, #password1, #password2 {
  display: block;
  width: 200px;
  height: 40px;
  border-radius: 10px;
  /* 设置左内边距,使得输入的数据与边框隔开 */
  padding-left: 10px;
  /* 去掉边框线,和轮廓线 */
  border: none;
  outline: none;
  /* 设置字体大小,和输入时字体的位置 */
  font-size: 22px;
  /* 文本垂直居中 */
  line-height: 40px;
}
/*注册提交框*/
.dialog .row #submit {
  width: 300px;
  height: 40px;
  margin-left: 50px;
  margin-right: 30px;
  background-color: rgb(0, 189, 189);
  font-size: large;
  /*设置边框中的文本位置*/
  text-align: center;
  padding-top: 5px;
  padding-bottom: 5px;
}

博客列表页对应的css

/* 博客主页中博客概述、博客列表中的页面个数 */
.blog {
  width: 100%;
  /* blog的宽度和父元素right是一样的,
  而高度如果不设定的话,就取决于内容高度的总和
  所以我们在这里不设置高度 */
  padding: 10px;
  /* 当我们要注意设置每一篇博客的间距 */
}
.blog .title {
  /* 设置字体大小和粗细 */
  font-size: 18px;
  font-weight: 600;
  /* 居中 */
  text-align: center;
  padding-top: 10px;
  padding-bottom: 5px;
}
.blog .date {
  padding: 5px;
  color: darkgoldenrod;
  text-align: center;
}
.blog .desc, p{
  /* 首行缩进, 注意在博客详情页的每一段落也要首行缩进*/
  text-indent: 2em;
}
.blog a {
  width: 140px;
  height: 40px;
  text-decoration: none;
  color: #000;
  /* 行级元素无法改变高度和宽度 */
  display: block;
  font-size: 15px;
  font-weight: 300px;
  /* 文本内容水平居中 */
  text-align: center;
  /* 文本框垂直居中 */
  line-height: 40px;
  /* 边框的上下边距5px,水平居中 */
  margin: 5px auto;
  display: flex;
  /* 边框线条粗细2px,颜色黑色,边框线条:实线 */
  border: 2px black solid;
  /* 当用户点击文本框:查看全文时,产生渐变效果 */
  transition: all 1.5s;
}
.blog a:hover {
  /* 设置渐变效果的颜色 */
  background-color: orange;
}

博客详情页对应的css

/* 博客详情页的格式 */
p {
  text-indent: 2em;
  /* 给自然段和自然段之间设置间距 */
  padding: 10px;
}
.right h2 {
  padding: 15px;
  text-align: center;
}
.right .date {
  text-align: center;
  color: orange;
  /* 水平居中 */
  padding-top: 0x;
  padding-bottom: 15px;
}

博客编辑页对应的css

.blog-edit-container {
  width: 1000px;
  height: calc(100% - 50px);
  /* 水平居中 */
  margin: 0 auto;
}
.blog-edit-container .title {
  width: 100%;
  height: 50px;
  /* 垂直居中 */
  align-self: center;
  display: flex;
  /* 使子元素输入框和按钮靠两边放置,中间留个缝 */
  justify-content: space-between;
}
.title #title {
  width: 790px;
  height: 40px;
  border-radius: 10px;
  display: block;
  /* 去掉边框和轮廓线条 */
  border: none;
  outline: none;
  padding-left: 10px;
  font-size: large;
  background-color: rgba(255, 255, 255, 0.75);
  align-items: center;
}
.title #submit {
  width: 200px;
  height: 40px;
  background-color: rgba(255, 165, 0, 0.7);
  border-radius: 10px;
  font-size: large;
  font-weight: 600;
  display: block;
  /* 去掉边框和轮廓线条 */
  border: none;
  outline: none;
}
.blog-edit-container #editor {
  border-radius: 10px;
  background-color: rgba(255, 255, 255, 0.7);
  /* 设置半透明 */
  opacity: 85%;
}



相关文章
|
6月前
|
Java 应用服务中间件 程序员
如何利用Idea创建一个Servlet项目(新手向)(下)
如何利用Idea创建一个Servlet项目(新手向)(下)
135 0
|
6月前
|
Java 应用服务中间件 API
如何利用Idea创建一个Servlet项目(新手向)(上)
如何利用Idea创建一个Servlet项目(新手向)
322 0
|
6月前
新闻发布项目——Servlet类(doRegServlet )
新闻发布项目——Servlet类(doRegServlet )
|
5月前
|
Java 应用服务中间件 Maven
IDEA创建一个Servlet项目(tomcat10)
IDEA创建一个Servlet项目(tomcat10)
244 1
|
3月前
|
监控 前端开发 Java
揭秘Web开发神器:Servlet、过滤器、拦截器、监听器如何联手打造无敌博客系统,让你的用户欲罢不能!
【8月更文挑战第24天】在Java Web开发中,Servlet、过滤器(Filter)、拦截器(Interceptor,特指Spring MVC中的)及监听器(Listener)协同工作,实现复杂应用逻辑。以博客系统为例,Servlet处理文章详情请求,过滤器(如LoginFilter)检查登录状态并重定向,Spring MVC拦截器(如LoggingInterceptor)提供细粒度控制(如日志记录),监听器(如SessionListener)监控会话生命周期事件。这些组件共同构建出高效、有序的Web应用程序。
37 0
|
6月前
新闻发布项目——Servlet类(doNewsModifyServlet )
新闻发布项目——Servlet类(doNewsModifyServlet )
|
6月前
|
SQL JSON 前端开发
基于 Servlet 的博客系统
基于 Servlet 的博客系统
|
6月前
|
Java
springboot项目出现Exception in thread “main“ java.lang.NoClassDefFoundError: javax/servlet/Filter
springboot项目出现Exception in thread “main“ java.lang.NoClassDefFoundError: javax/servlet/Filter
230 0
|
3月前
|
缓存 安全 Java
Java服务器端技术:Servlet与JSP的集成与扩展
Java服务器端技术:Servlet与JSP的集成与扩展
32 3
|
3月前
|
存储 缓存 前端开发
Servlet与JSP在Java Web应用中的性能调优策略
Servlet与JSP在Java Web应用中的性能调优策略
31 1
下一篇
无影云桌面