• 服务器版博客系统、前后端交互3


    八、显示对应用户信息

    每一次,左侧都是同样的登录内容

    登录 ‘zhangsan’,但是当下用户信息,显示的是还是 “小吴的博客”
    实际登录的用户,是 “zhangsan”

    登录的用户 和 文章作者,可能是一个人,也可能不是一个人!!

    在博客列表页,期望显示的用户信息,就是当前登录的用户

    1、针对博客列表页 blog_list.html

    修改 .left 内容:将 h3 中的内容 删除,

    在 common.js 添加:在登录成功时调用

    function changeUserName(username) {
        let h3 = document.querySelector('.card>h3');
        h3.innerHTML = username;
    }
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    这里其实前面已经处理过了!!! (在检测用户登录状态的时候)
    注意,当前虽然已经让用户名正常了,但是用户的头像,github,文章的统计,这些都是没有变化的,当前在设计数据库的时候,就没考虑这些~~


    2、针对博客详情页

    2.1、前端 区分列表页,详情页

    当前看到的是,博客详情页,用户名,也是成了 zhangsan 了!!! 因为在 common.js 中,同时调用了
    此处就需要处理一下,让博客列表页,和详情页,能够做出一些区分~~

    希望 列表页 和 详情页 这里分别显示不同的内容~~

    比如针对当前这个 blogld 为 6 的博客来说,这个博客的作者是 “lisi” ,此处显示的用户名,也就应该是 lisi~~

    让服务器,提供一个新的接口,这个接口可以让客户端指定 blogld,获取到指定 blogld 的作者信息!!

    修改 .left 内容:将 h3 内容的 “小吴的博客” 删除,

    请求:

    GET /authorlnfo?blogld=6
    
    • 1

    响应:

    {
    	userld: 2,
    	username: 'lisi',
    }
    
    • 1
    • 2
    • 3
    • 4

    修改:

    在这里插入图片描述

    common.js 的 getUserInfo 获取登录状态方法中添加参数 pageName

    common.js 完整代码:

    这个逻辑是页面加载的时候,获取下用户的身份信息,判定是否登录

    // 这个文件里放一些页面公共的代码
    
    // 加上一个逻辑, 通过 GET /login 这个接口来获取下当前的登录状态
    function getUserInfo(pageName) {
        $.ajax({
            type: 'get',
            url: 'login',
            success: function(body) {
                // 判定此处的 body 是不是一个有效的 user 对象(userId 是否非 0)
                if (body.userId && body.userId > 0) {
                    // 登录成功!
                    // 不做处理!
                    console.log("当前用户登录成功!用户名: " + body.username);
                    
                    // 根据当前用户登录的情况, 把当前用户名设置到界面上
                    if (pageName == 'blog_list.html') {
                        changeUserName(body.username);
                    }
                } else {
                    // 登录失败!
                    // 弹窗提示让用户 让前端页面, 跳转到 login.html
                    alert("当前您尚未登录! 请登录后再访问博客列表!");
                    location.assign('blog_login.html');
                }
            },
            error: function() {
                alert("当前您尚未登录! 请登录后再访问博客列表!");
                location.assign('blog_login.html');
            }
        });
    }
    
    // getUserInfo();
    
    function changeUserName(username) {
        let h3 = document.querySelector('.card>h3');
        h3.innerHTML = username;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    2.2、服务器代码 AuthorServlet

    创建 AuthorServlet 类

    package controller;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import model.Blog;
    import model.BlogDao;
    import model.User;
    import model.UserDao;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @WebServlet("/authorInfo")
    public class AuthorServlet extends HttpServlet {
        private ObjectMapper objectMapper = new ObjectMapper();
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.setContentType("application/json; charset=utf8");
            // 通过这个方法, 来获取到指定的博客的作者信息.
            String param = req.getParameter("blogId");
            if (param == null || "".equals(param)) {
                // 参数缺少了
                resp.getWriter().write("{ \"ok\": false, \"reason\": \"参数缺失\" }");
                return;
            }
    
            // 根据当前 blogId 在数据库中进行查找, 找到对应的 Blog 对象, 再进一步的根据 blog 对象, 找到作者信息.
            BlogDao blogDao = new BlogDao();
            Blog blog = blogDao.selectOne(Integer.parseInt(param));
            if (blog == null) {
                resp.getWriter().write("{ \"ok\": false, \"reason\": \"要查询的博客不存在\" }");
                return;
            }
            // 根据 blog 对象, 查询到用户对象
            UserDao userDao = new UserDao();
            User author = userDao.selectById(blog.getUserId());
            if (author == null) {
                resp.getWriter().write("{ \"ok\": false, \"reason\": \"要查询的用户不存在\" }");
                return;
            }
    
            // 把 author 返回到浏览器这边
            // 注意要把密码给干掉!
            author.setPassword("");
            resp.getWriter().write(objectMapper.writeValueAsString(author));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    验证:

    在这里插入图片描述


    2.3、前端代码 blog_detail.html

    调整顺序,编写 getAuthorInfo 方法获取作者信息

    
    <script src="js/common.js">script>
    
    <script>
        function getBlogDetail() {
            $.ajax({
                type: 'get',
                // location.search 拿到了形如 '?blogId=5' 这样的一段内容
                url: 'blog' + location.search,
                success: function(body) { // body 中就是一个 js 对象
                    // 根据 body 中的内容来构造页面
                    // 1. 构造博客标题
                    let h3 = document.querySelector('.blog-content>h3');
                    h3.innerHTML = body.title;
                    // 2. 构造博客发布时间
                    let dateDiv = document.querySelector('.date');
                    dateDiv.innerHTML = body.postTime;
                    // 3. 构造博客正文
                    // let contentDiv = document.querySelector('#content');
                    // contentDiv.innerHTML = body.content;
                    // 如果直接把 content 设为 innerHTML, 此时展示在界面上的内容, 是原始的 markdown 字符串
                    // 咱们需要的是渲染后的, 带有格式的效果
    
                    // 第一个参数对应 id=content 的 html 标签. 渲染后得到的 html 片段就会被放到这个 标签下.
                    editormd.markdownToHTML('content', {
                        markdown: body.content
                    });
                }
            });
        }
        
        getBlogDetail();
    
        
        // 判定用户登录状态
        getUserInfo('blog_detail.html');
        
        // 从服务器获取一下当前博客的作者信息, 并显示到界面上. 
        // 参数 user 就是刚才从服务器拿到的当前登录用户的信息
        function getAuthorInfo() {
            $.ajax({
                type: 'get',
                url: 'authorInfo' + location.search,
                success: function(body) {
                    // 此处的 body, 就是服务器返回的 User 对象, 是文章的作者信息
                    if (body.username) {
                        // 如果响应中的 username 存在, 就把这个值设置到页面上. 
                        changeUserName(body.username);
                    } else {
                        console.log("获取作者信息失败! " + body.reason);
                    }
                }
            });
        }
        
        getAuthorInfo(); // 
        
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    2.4、——验证

    访问列表页,登录 “zhangsan”,点击最上面一篇博客作者为 “lisi” 的博客,跳转到详情页,此时看到,左侧的用户名就是 “lisi”,登录的是 “zhangsan” 的账户,但是是详情页按照文章作者来设置的

    在这里插入图片描述

    blogId 为 6 的 userId 为 2

    mysql> select * from blog;
    +--------+-----------------------+--------------------------------------------------------+--------+---------------------+
    | blogId | title                 | content                                                | userId | postTime            |
    +--------+-----------------------+--------------------------------------------------------+--------+---------------------+
    |      1 | 这是第一篇博客        | 从今天开始, 我要认真学 Java                            |      1 | 2022-05-27 02:51:36 |
    |      2 | 这是第二篇博客        | 从昨天开始, 我要认真学 Java                            |      1 | 2022-05-27 02:51:36 |
    |      3 | 这是第三篇博客        | 从前天开始, 我要认真学 Java                            |      1 | 2022-05-27 02:51:36 |
    |      4 | 这是第一篇博客        | 从今天开始, 我要认真学 C++                             |      2 | 2022-05-27 02:51:36 |
    |      5 | 这是第二篇博客        | 从昨天开始, 我要认真学 C++                             |      2 | 2022-05-27 02:51:39 |
    |      6 | 这是第三篇博客        | # 一级标题
     ### 三级标题
     > 这是引用内容               |      2 | 2022-05-27 20:48:10 |
    +--------+-----------------------+--------------------------------------------------------+--------+---------------------+
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    查看 userId 为 2 的作者,是 lisi

    mysql> select * from user;
    +--------+----------+----------+
    | userId | username | password |
    +--------+----------+----------+
    |      1 | zhangsan | 123      |
    |      2 | lisi     | 123      |
    +--------+----------+----------+
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    上述功能,就实现了把用户信息,显示到页面上 (左侧卡片中)

    1. 对于博客列表页,要显示登录用户的信息,登录用户的信息,在检测用户是否登录的接口中,就已经拿到了。只需要把拿到的用户信息,显示到界面上即可 (只是微调了前端代码,不涉及后端代码的修改)
    2. 对于博客详情页,要显示文章的作者信息,就需要提供一个新的 api,让客户端传一个博客 id 过去,然后在服务器这里查询当前的用户信息,查到之后返回给页面

    通过上述的开发,就能有一个感受:
    页面和服务器之间的交互,不一定只有一次,而且大概率是很多次交互~~

    • 像博客列表页:这里涉及到了两次交互
      • 从服务器拿到博客列表数据
      • 从服务器拿到当前的登录用户信息
    • 像博客详情页:涉及到了三次交互
      • 从服务器拿到博客的详细内容
      • 从服务器拿到了当前登录用户信息
      • 从服务器拿到了当前文章的作者信息

    九、实现 “注销” 功能

    退出当前登录的状态~~
    在导航栏中安排一个 “注销” 按钮

    当用户点击注销之后,就会在服务器上取消登录状态,并且能够跳转到登录页面
    当下注销按钮这里啥都没有,点上去之后,也没反应~~

    希望在点击之后,能够给服务器发送一个 HTTP 请求,从而触发注销动作 (这个动作具体就是把会话中的信息给删掉就行了!!)


    1、约定交互接口

    请求:

    GET /logout
    
    • 1

    响应:

    HTTP/1.1 302
    Location: login.html
    
    • 1
    • 2

    2、服务器代码 LogoutServlet

    创建 LogoutServlet 类

    用户有一个 session ,同时 session 有一个 user 属性,两者同时具备,才叫登录状态

    注销,只要破坏掉上面的任意一个条件就行了~~
    此处选择的是破坏第二个条件,把 user 属性从 session 中删了

    loginServlet 类中,我们写重写了 doGet 方法,下图检测登录状态的代码,要求同时具备 session 对象和 user 属性

    user 属性被删除,后续读取的时候得到的就是 null
    因此就会给客户端返回一个无效 User 对象,客户端就明白,就认为是未登录状态!

    在这里插入图片描述

    代码:

    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 LogoutServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // 先找到当前用户的会话
            HttpSession session = req.getSession(false);
            if (session == null) {
                // 用户没有登录!! 谈不上注销!
                resp.getWriter().write("当前用户尚未登录,无法注销!");
                return;
            }
            // 然后把这个用户的会话中的信息给删掉就行了!!
            session.removeAttribute("user");
            resp.sendRedirect("blog_login.html");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    3、修改客户端代码

    • 博客列表页,博客详情页,博客编辑页,这里的导航栏中的注销按钮href 属性都做出修改,改成 “logout” 这个路径。注意,不是 “/logout”
    • 登录页,是没有注销按钮的!!

    在这里插入图片描述

    修改完,访问页面,登录后,点击页面导航栏上的 “注销”,即跳转到登录页面

    1、博客列表页 2、博客详情页 3、登录功能 4、检测用户登录状态 5、显示用户信息 6、注销

    核心逻辑都是一样的!!!

    1. 约定前后端交互接口
    2. 实现服务器代码
      • 写 controller 层,写 servlet 来实现 api
      • 写 model 层,通过 jdbc 来操作数据库
    3. 实现客户端代码
      • ajax / form / a标签跳转

    十、实现发布博客功能

    在博客编辑页中,当用户输入了博客标题,和正文之后,
    点击发布,此时就会把博客数据提交到服务器,由服务器存储到数据库中

    1、约定交互接口

    请求: 此处的内容都是需要 urlencoded 的,此处浏览器会自动进行编码

    POST /blog
    Content-Type: application/x-www-form-urlencoded
    
    title=这是标题&content=这是正文……
    
    • 1
    • 2
    • 3
    • 4

    响应:

    HTTP/1.1 302
    Location: blog_list.html
    
    • 1
    • 2

    2、服务器代码 BlogServlet

    接下来实现服务器代码,BlogServlet 里面添加一个 doPost 方法,来处理上述 post 请求~~

    核心操作,就是读取请求中的标题和正文,构造 Blog 对象,并插入数据库

    	// 发布博客
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            HttpSession session = req.getSession(false);
            if (session == null) {
                // 当前用户未登录,不能发布博客!
                resp.setContentType("text/html; charset=utf8");
                resp.getWriter().write("当前用户未登录,不能发布博客!");
                return;
            }
            User user = (User) session.getAttribute("user");
            if (user == null) {
                // 当前用户未登录,不能发布博客!
                resp.setContentType("text/html; charset=utf8");
                resp.getWriter().write("当前用户未登录,不能发布博客!");
                return;
            }
    
            // 一定要先指定好请求按照哪种编码来解析
            req.setCharacterEncoding("utf8");
            // 先从请求中, 取出参数 (博客的标题和正文)
            String title = req.getParameter("title");
            String content = req.getParameter("content");
            if (title == null || "".equals(title) || content == null || "".equals(content)) {
                // 直接告诉客户端, 请求参数不对
                resp.setContentType("text/html; charset=utf8");
                resp.getWriter().write("提交博客失败! 缺少必要的参数!");
                return;
            }
    
            // 构造 Blog 对象, 把当前的信息填进去, 并插入数据库中
            // 此处要给 Blog 设置的属性, 主要是 title, content, userId (作者信息)
            // postTime 和 blogId 都不需要手动指定, 都是插入数据库的时候自动生成的.
            Blog blog = new Blog();
            blog.setTitle(title);
            blog.setContent(content);
            // 前面判断了已登录,当前作者 id 就是当前提交这个博客的用户的身份信息!!
            blog.setUserId(user.getUserId());
    
            BlogDao blogDao = new BlogDao();
            blogDao.insert(blog);
    
            // 重定向到 博客列表页!
            resp.sendRedirect("blog_list.html");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    3、修改客户端代码 blog_edit.html

    首先,这里就需要整一个 form 表单,把这里 blog-edit-container 的内容给套上~~

    在这里插入图片描述

    1、加了个 form 表单,把之前的输入框啥的包裹起来了~

    2、给标题的输入框,加上了 name 属性

    3、发布按钮,改成了 input 标签

    4、创建了一个隐藏的 textarea 为了后续的 form 提交

    5、下面设置了一个标志位,使当前的输入框的内容能够自动保存到 textarea 中 saveHTMLToTextarea: true,

    6、同样改成了 input,要对选择器进行调整,保证提交按钮的样式不丢失,发布按钮 id 设为 submit

    代码:

    <!-- 包裹整个博客编辑页内容的顶级容器 -->
    <div class="blog-edit-container">
        <form action="blog" method="post">
            <div class="title">
                <input type="text" placeholder="在此处输入标题" name="title">
                <!-- <button>发布文章</button> -->
                <input type="submit" value="发布文章" id="submit">
            </div>
            <!-- 放置 md 编辑器 -->
            <div id="editor">
                <!-- 为了进行 form 的提交, 此处搞一个 textarea 多行编辑框, 借助这个编辑框来实现表单的提交 -->
                <!-- 可以设置 editor.md, 让编辑器把 markdown 内容也同步的保存到这个隐藏的 textarea 中, 从而可以进行 form 提交 -->
                <textarea name="content" style="display: none"></textarea>
            </div>
        </form>
    </div>
    
    <script>
        // 初始化编辑器
        let editor = editormd("editor", {
            // 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉. 
            width: "100%",
            // 设定编辑器高度
            height: "calc(100% - 50px)", /* 减 titile 的高度 */
            // 编辑器中的初始内容
            markdown: "# 在这里写下一篇博客",
            // 指定 editor.md 依赖的插件路径
            path: "editor.md/lib/",
            // 此处要加上一个重要的选项, 然后 editor.md 就会自动把用户在编辑器输入的内容同步保存到 隐藏的 textarea 中了!
            saveHTMLToTextarea: true,
        });
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    样式: blog_edit.css

    .blog-edit-container .title #submit {
        width: 100px;
        height: 40px;
        border-radius: 10px;
        
        color: white;
        background-color: orange;
        border: none;
        outline: none;
        font-size: 18px; /* 调整按钮文字大小 */
    }
    
    .blog-edit-container .title #submit:active {
        background-color: #666;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    ——验证 修改

    在这里插入图片描述

    改动了代码之后,果然就出问题了!!
    代码出问题,都是很正常的情况,不出问题,才是不科学的

    #editor 设置了一个这样的高度:100% - 50px

    100% 是相对于父元素的高度,之前 #editor 的父亲是 container,
    但是包上一层 form 之后,此时 #editor 的父亲就是 form 了,而 form 自身没有高度,这个 100% 没撑起来~~

    在这里插入图片描述

    **修改: ** 再设置 id,让样式不影响按钮

    <input type="text" placeholder="在此处输入标题" name="title" id="title">
    
    • 1
    .blog-edit-container .title #title {
        width: 895px;
        height: 40px;
        border-radius: 10px;
        border: none;
        outline: none;
        font-size: 22px;
        line-height: 40px;
        padding-left: 10px;
    
        background-color: rgba(255, 255, 255, 0.8);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    当前博客发布这个功能,之所以比较复杂,主要是用了 form 表单来构造 HTTP 请求,就需要改标签结构,进一步的又要改样式…
    但是这里如果不用 form 表单,也能实现类似的效果,基于 ajax 也能构造出 HTTP 请求来提交标题和正文~~


    十一、实现删除博客功能

    约定,只有自己能删除自己的博客,不能删除别人的博客!!! (此处咱们暂时不考虑管理员)

    1、界面上的处理 blog_detail.html

    博客详情页这里,就去进行判定,判定看当前这个博客的作者,是否就是登录的用户,

    • 如果是,就在导航栏里显示一个 “删除按钮”
    • 如果不是,就不显示删除按钮

    在博客详情页这块,其实既从服务器获取了当前用户的登录信息,又获取到了博客的作者信息。主要看这两个接口返回的用户信息是不是一样的?

    这里大家一定要注意理解!!! 这两个函数,调用之后,就给服务器发送了两个 HTTP 请求,然后,这两个请求对应的响应,谁先回来,不确定!!
    虽然函数有先后,但是两个 ajax 请求是并发的关系

    第一个 ajax 发出去之后,不等响应回来,就已经发了第二个 ajax,这正是 ajax 名字的含义
    asynchronize

    如果这两个响应到达的顺序不确定,就不好进行判定了,必须手动的调整让这两个方法,按照一定的顺序来进行发送~~

    在这里插入图片描述

    代码:

    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="css/common.css">
        <link rel="stylesheet" href="css/blog_detail.css">
    
        
        <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="nav">
            <img src="image/picture.jpg" alt="">
            <span>我的博客系统span>
    
            
            <div class="spacer">div>
    
            <a href="blog_list.html">主页a>
            <a href="blog_edit.html">写博客a>
            
            <a href="logout">注销a>
        div>
    
        
        <div class="container">
            
            <div class="left">
                
                <div class="card">
                    <img src="image/picture.jpg" alt="">
                    <h3>h3>
                    <a href="#">github 地址a>
                    <div class="counter">
                        <span>文章span>
                        <span>分类span>
                    div>
                    <div class="counter">
                        <span>2span>
                        <span>1span>
                    div>
                div>
            div>
    
            
            <div class="right">
                
                <div class="blog-content">
                    
                    <h3>h3>
                    
                    <div class="date">div>
                    
                    <div id="content" style="opacity: 73%;">
    
                    div>
                div>
            div>
        div>
    
        
        
        <script>
            function getBlogDetail() {
                $.ajax({
                    type: 'get',
                    // location.search 拿到了形如 '?blogId=5' 这样的一段内容
                    url: 'blog' + location.search,
                    success: function(body) { // body 中就是一个 js 对象
                        // 根据 body 中的内容来构造页面
                        // 1. 构造博客标题
                        let h3 = document.querySelector('.blog-content>h3');
                        h3.innerHTML = body.title;
                        // 2. 构造博客发布时间
                        let dateDiv = document.querySelector('.date');
                        dateDiv.innerHTML = body.postTime;
                        // 3. 构造博客正文
                        // let contentDiv = document.querySelector('#content');
                        // contentDiv.innerHTML = body.content;
                        // 如果直接把 content 设为 innerHTML, 此时展示在界面上的内容, 是原始的 markdown 字符串
                        // 咱们需要的是渲染后的, 带有格式的效果
    
                        // 第一个参数对应 id=content 的 html 标签. 渲染后得到的 html 片段就会被放到这个 标签下.
                        editormd.markdownToHTML('content', {
                            markdown: body.content
                        });
                    }
                });
            }
    
            getBlogDetail();
    
    
            // 加上一个逻辑, 通过 GET /login 这个接口来获取下当前的登录状态~
            function getUserInfo(pageName) {
                $.ajax({
                    type: 'get',
                    url: 'login',
                    success: function(body) {
                        // 判定此处的 body 是不是一个有效的 user 对象(userId 是否非 0)
                        if (body.userId && body.userId > 0) {
                            // 登录成功!
                            // 不做处理!
                            console.log("当前用户登录成功! 用户名: " + body.username);
    
                            // 在 getUesrInfo 的回到函数汇总,来调用获取作者信息
                            getAuthorInfo(body); //
                        } else {
                            // 登录失败!
                            // 让前端页面, 跳转到 login.html
                            alert("当前您尚未登录! 请登录后再访问博客列表!");
                            location.assign('blog_login.html');
                        }
                    },
                    error: function() {
                        alert("当前您尚未登录! 请登录后再访问博客列表!");
                        location.assign('blog_login.html');
                    }
                });
            }
    
            // 判定用户登录状态
            getUserInfo('blog_detail.html');
    
            // 从服务器获取一下当前博客的作者信息, 并显示到界面上.
            // 参数 user 就是刚才从服务器拿到的当前登录用户的信息
            function getAuthorInfo(user) {
                $.ajax({
                    type: 'get',
                    url: 'authorInfo' + location.search,
                    success: function(body) {
                        // 此处的 body, 就是服务器返回的 User 对象, 是文章的作者信息
                        if (body.username) {
                            // 如果响应中的 username 存在, 就把这个值设置到页面上.
                            changeUserName(body.username);
    
                            if (body.username == user.username) {
                                // 作者和登录的用户是同一个人,显示 "删除按钮"
                                let navDiv = document.querySelector('.nav'); // 导航栏
                                let a = document.createElement('a'); // 加入 a 标签
                                a.innerHTML = '删除';
                                // 希望点击删除 构造一个形如 blogDelete?blogId=6 的请求
                                a.href = 'blogDelete' + location.search;
                                navDiv.appendChild(a);
                            }
                        } else {
                            console.log("获取作者信息失败! " + body.reason);
                        }
                    }
                });
            }
    
            function changeUserName(username) {
                let h3 = document.querySelector('.card>h3');
                h3.innerHTML = username;
            }
    
        script>
    
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170

    在这里插入图片描述

    这是关键代码,要在第一个 ajax 的回调中
    执行第二个 ajax,才能保证两个 ajax 之间获取数据的顺序先后~~

    当前咱们只是两个 ajax 还好,即使是在回调里嵌套调用 ajax。也勉强能接受~~
    但是如果咱们要进行的操作有十个八个的,如果一直是前一个 ajax 回调里面调用下一个 ajax 代码写起来就会非常之丑陋!!!
    回调地狱

    js ES6 引入了 Promise 就能解决这个问题~~
    ES7 引入 async 和 await,能够更佳优雅的解决上述问题了~~

    通过这些语法,能够简化代码,让代码看起来没那么复杂,没那么丑陋,执行的效果仍然是一样的


    2、约定交互接口

    请求:

    GET /blogDelete?blogld=7
    
    • 1

    响应:

    直接跳转到博客列表页即可


    3、服务器代码 BlogDeleteServlet

    创建 BlogDeleteServlet 类

    服务器处理,用户点击删除按钮,触发一个 HTTP 请求, HTTP 请求就会让服务器删除指定的博客,服务器收到请求之后,就会把这个博客从数据库里给删除掉~~

    package controller;
    
    import model.Blog;
    import model.BlogDao;
    import model.User;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    
    @WebServlet("/blogDelete")
    public class BlogDeleteServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // 1. 检查当前用户是否登录
            HttpSession session = req.getSession(false);
            if (session == null) {
                resp.setContentType("text/html; charset=utf8");
                resp.getWriter().write("当前尚未登录, 不能删除!");
                return;
            }
            User user = (User) session.getAttribute("user");
            if (user == null) {
                resp.setContentType("text/html; charset=utf8");
                resp.getWriter().write("当前尚未登录, 不能删除!");
                return;
            }
    
            // 2. 获取到参数中的 blogId
            String blogId = req.getParameter("blogId");
            if (blogId == null || "".equals(blogId)) {
                resp.setContentType("text/html; charset=utf8");
                resp.getWriter().write("当当前 blogId 参数不对!");
                return;
            }
    
            // 3. 获取要删除的博客信息
            BlogDao blogDao = new BlogDao();
            Blog blog = blogDao.selectOne(Integer.parseInt(blogId));
            if (blog == null) {
                resp.setContentType("text/html; charset=utf8");
                resp.getWriter().write("当前要删除的博客不存在!");
                return;
            }
    
            // 4. 再次校验, 当前的用户是否就是博客的作者
            if (user.getUserId() != blog.getUserId()) {
                // 这一点在前端这里其实也处理过~~ 但是此处还是再校验一次, 不是坏事!!!
                resp.setContentType("text/html; charset=utf8");
                resp.getWriter().write("当前登录的用户不是作者, 没有权限删除!");
                return;
            }
    
            // 5. 确认无误, 开始删除
            blogDao.delete(Integer.parseInt(blogId));
    
            // 6. 重定向到博客列表页
            resp.sendRedirect("blog_list.html");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    4、——验证

    登录 “lisi” 的账号,点击这篇博客,看到作者是 “lisi”,点击右上角的 ”删除“,即删除博客,跳转到博客列表页,查看数据库,此博客已被删除

    在这里插入图片描述


  • 相关阅读:
    Pytest系列(31) - config.cache 使用
    【C语言】自定义类型:联合和枚举
    内网渗透之内网信息收集(六)
    测试八股文-Selenium
    2、React中的JSX使用
    深度学习-神经网络原理2
    Mysql的数据类型
    C++基础——对于C语言缺点的补充(2)
    任务系统之API子任务
    网络资源的源码如何查看?电脑PC推荐安装的软件(pdf编辑工具、浏览器、杀毒软件、办公软件、压缩工具等等)
  • 原文地址:https://blog.csdn.net/qq_56884023/article/details/126797785