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


    五、博客详情页

    写好博客列表页后,点击 “跳转全文”,正文出现的都是我们测试时写好的内容,

    点击 “跳转全文”:127.0.0.1:8080/blog_system/blog_detail.html?blogId=5

    这是获取博客详情页,发送的请求,此处希望得到的页面,页面里就能显示出当前这个博客的正文内容~~

    这个正文内容就要继续通过 ajax 来进行获取,在 blog_detail.html 页面加载的时候,触发 ajax 请求来访问服务器,获取到博客内容再次填充到博客详情页里面!

    1、约定交互接口

    请求:

    GET /blog?blogId=1
    
    • 1

    响应:

    HTTP/1.1 200 OK
    Content-Type:application/json;
    
    {
    	blogId: 1,
        title: "第一篇博客",
        content: "这个正文",
        userId: 1,
        postTime: '2022-05-25' 13:33:33'
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    和获取博客列表的区别:

    1. 请求里面,带有参数 blogld

    2. 响应结果不是 json 数组了,而只是单一的对象

    3. 响应的博客正文,不再截断了


    2、修改 BlogServlet

    后端代码实现和博客列表页的获取,基本相同,就直接放到一个方法中来实现了。使用 blogld 参数来区分是获取博客列表还是详情~

    获取博客列表请求 GET /blog 不带参数的,

    // 通过这个类, 来处理 /blog 路径对应的请求
    @WebServlet("/blog")
    public class BlogServlet extends HttpServlet {
        private ObjectMapper objectMapper = new ObjectMapper();
    
        // 这个方法用来获取到数据库中的博客列表.
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.setContentType("application/json; charset=utf8");
    
            // 先尝试获取到 req 中的 blogId 参数. 如果该参数存在, 说明是要请求博客详情
            // 如果该参数不存在, 说明是要请求博客的列表.
            BlogDao blogDao = new BlogDao();
            String param = req.getParameter("blogId");
            if (param == null) {
                List<Blog> blogs = blogDao.selectAll();
                // 把 blogs 对象转成 JSON 格式.
                String respJson = objectMapper.writeValueAsString(blogs);
                // 构造响应的时候 这里的这两行代码 顺序不能颠倒.
                // 如果先 write 了 body, 再设置 ContentType,设置的 ContentType就会不生效!!
                // 包括说 即使不是写 body,构造 sendRedirect 这种, 其实也是类似的情况~~
                resp.getWriter().write(respJson);
            } else {
                // 有 blogId,获取博客详情
                int blogId = Integer.parseInt(param);
                Blog blog = blogDao.selectOne(blogId);
                String respJson = objectMapper.writeValueAsString(blog);
                resp.getWriter().write(respJson);
            }
        }
    }
    
    • 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

    Postman 构造请求测试:127.0.0.1:8080/blog_system/blog?blogId=3

    body:根据 blogId 获取的博客详情

    {
        "blogId": 3,
        "title": "这是第三篇博客",
        "content": "从前天开始, 我要认真学 Java",
        "userId": 1,
        "postTime": "2022-51-27 02:51:36"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3、前端代码 blog_detail.html

    1). 修改右侧内容

    修改 blog_detail.html,让这个页面加载的时候,能够调用上述接口,来从服务器获取到博客数据!

    
    <div class="right">
        
        <div class="blog-content">
            
            <h3>h3>
            
            <div class="date">div>
            
            <div id="content">
    
            div>	
        div>
    div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2). 下方添加 ajax

    在前端代码这边,要想构造一个请求获取博客详情,就得知道当前用户点击的博客的 id !!
    这个 id 就已经包含在当前的 blog_detail.html 页面的 url 里面了!!

    问题一: 如何拿到 blogId:location.search

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.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;
                }
            });
        }
        getBlogDetail();
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    问题二: 当前咱们的博客正文,并不是单纯的文本!! 其实是带有一定的格式化的 markdown 数据!!

    如果我们在代码中,就使用上述逻辑,直接把 content 设为 innerHTML 意味着界面最终的展示效果,就是左侧,而我们需要的效果是右侧的格式

    在这里插入图片描述

    既然如此,那应该如何处理? 能够让我们这里的 markdown 文本内容,被渲染成带有特定样式的 html 片段呢? 仍然要使用 editor.md 这个库来完成
    这个库不仅提供了 markdown 编辑器,也提供了渲染功能~~

    完整代码:

    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="#">注销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();
        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

    4、——验证

    访问列表页,点击 “查看全文”,发现此时页面没有变化

    注意: 当在进行程序验证的时候,要时刻牢记,浏览器缓存可能会影响到结果
    之前已经访问过 blog_detail 页面了,因此浏览器就可能把这个页面给保存到本地,下次再尝试访问的时候就直接访问本地内容了~~

    在这里插入图片描述

    加入一条 sql:

    insert into blog values(null, '这是第三篇博客', '# 一级标题\n ### 三级标题\n > 这是引用内容', 2, now());
    
    • 1

    点击全文,博客的页面都是 markdown 格式


    六、登录页

    1、约定交互接口

    请求: 这里的逻辑,可以直接使用 form 表单来进行提交,没必要非得使用 ajax (使用 ajax 也是完全可以的!!)

    如果要使用表单,就需要把按钮改成 input type="submit"

    POST /login
    Content-Type:application/x-www-form-urlencoded
    
    • 1
    • 2

    响应:

    HTTP/1.1 302
    Location:blog_list_html
    
    • 1
    • 2

    当登录成功之后,就自动跳转到,主页 (博客列表页)


    2、前端代码 blog_login.html

    这回先写客户端,把登录页面稍加调整~~

    修改 login-container :

    1. 给 login-dialog 这部分代码套上一层 form 标签!

    2. 给 input 加上 name 属性~

      • 加上的这个属性,就是后续提交数据的键值对的 key ~

        username=zhangsan&password=123

    3. 把 button 按钮换成 input 标签~

    <div class="login-container">
        <form action="login" method="post">
            <div class="login-dialog">
                <h3>登录h3>
                <div class="row">
                    <span>用户名span>
                    <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="submut" value="提交">
                div>
            div>
        form>
    div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    启动运行,提交按钮的样式没有起作用

    在前端页面开发的过程中,html 页面结构 是非常重要的,后续的 CSS 和 JS 很多都依赖了这个页面结构,一旦页面结构发生了调整,就可能导致 css 或者 js 失效~~

    按钮设置 id,调整样式,调整完,如果没有改变,ctrl + f5

    原来的样式:

    .row button {
        width: 300px;
        height: 50px;
        border-radius: 10px;
        color: white;
        background-color: rgba(0, 128, 0);
        border: none;
    
        margin-top: 20px;
    }
    
    /* 鼠标按下未弹起 */
    .row button:active {
        background-color: #666;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    调整:

    .row #submit {
        width: 300px;
        height: 50px;
        border-radius: 10px;
        color: white;
        background-color: rgba(0, 128, 0);
        border: none;
    
        margin-top: 20px;
    }
    
    /* 鼠标按下未弹起 */
    .row #submit:active {
        background-color: #666;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    完整代码:

    
    <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>
        
        
    div>
    
    <div class="login-container">
        <form action="login" method="post">
            <div class="login-dialog">
                <h3>登录h3>
                <div class="row">
                    <span>用户名span>
                    <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>
        form>
    div>
    
    • 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

    3、后端代码 LoginServlet

    此处约定的路径是 /login,这是一个新的路径,就需要使用一个新的 servlet 来处理~

    创建 LoginServlet 类

    username=zhangsan&password=123 请求中的数据是这个格式,因此就需要使用

    String username = req.getParameter("username");
    
    • 1

    使用 getParameter 读取参数的时候,如果参数的值,是纯英文,那么还好,一般没啥问题。如果参数的值是中文,此时直接读取参数,很容易出现乱码!!!

    中文就涉及到字符编码
    前端页面,已经告诉了浏览器,汉字是 utf8

    因此接下来浏览器在输入框输入的汉字,也会使用 utf8 来编码,但是 Servlet 默认并不是按照 utf8 来解析的
    就需要告诉 Servlet 也要按照 utf8 来解析请求!!

    req.setCharacterEncoding("utf8");

    这是针对请求进行设置的,意思是使用 utf8 格式来解析请求。如果请求中不加这个编码,就会出现这个乱码的情况,从而不能正确的在数据库中查询了

    resp.setCharacterEncoding("utf8"); 这是针对响应进行设置的,意思是构造的数据要按照 utf8 构造

    package controller;
    
    import model.User;
    import model.UserDao;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    
    @WebServlet("/login")
    public class LoginServlet extends HttpServlet {
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            req.setCharacterEncoding("utf8");
            resp.setCharacterEncoding("utf8");
            // 1. 获取到请求中的参数
            String username = req.getParameter("username");
            String password = req.getParameter("password");
            System.out.println("username=" + username + ", password=" + password);
            if (username == null || "".equals(username) || password == null || "".equals(password)) {
                resp.setContentType("text/html; charset=utf8"); // 
                resp.getWriter().write("当前用户名或密码为空!");
                return;
            }
            // 2. 和数据库中的内容进行比较
            UserDao userDao = new UserDao();
            User user = userDao.selectByName(username);
            if (user == null || !user.getPassword().equals(password)) {
                // 用户没有查到 或 密码不匹配, 也是登录失败!
                resp.setContentType("text/html; charset=utf8"); // 
                resp.getWriter().write("用户名或密码错误!");
                return;
            }
            // 3. 如果比较通过, 就创建会话.
            HttpSession session = req.getSession(true);
            // 把刚才的用户信息, 存储到会话中.
            session.setAttribute("user", user);
            // 4. 用户名密码正确 返回一个重定向报文, 跳转到博客列表页.
            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

    4、——验证

    访问 http://127.0.0.1:8080/blog_system/blog_login.html

    在这里插入图片描述


    七、检查登录状态

    当登录功能完成了之后,就需要调整一下之前的两个页面 (博客列表和博客详情)
    让这两个页面,必须登录后才能访问~~

    此处就做出这样的限制 (这样限制一下,后面实现一些其他功能会更简单)

    在进入博客列表页 / 详情页的时候,先检查一下用户的登录状态,如果用户当前已经是登录状态,才能继续使用。
    如果是未登录状态,则强制此跳转到 login 页面

    如何实现上述功能:

    • 可以在博客列表页 / 详情页,加载的时候,通过 ajax ,访问一下服务器,获取当前的登录状态,看看能不能获取到
    • 如果获取到了,就说明当前确实是已经登录了,此时就可以留在这个页面了
    • 如果没有获取到,说明就未登录,就需要跳转到登录页面

    1、约定交互接口

    请求:

    GET /login
    
    • 1

    响应:

    HTTP/1.1 200 OK
    Content-Type:application/json;
       
    {
    	userId: 1,
    	username: 'zhangsan',
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    登录了,就直接返回当前登录的用户信息;未登录,则直接返回一个 userld 为 0 的对象 (此处只是一种典型的约定方式,完全也可以采取其他的方式来约定比如使用 403 表示当前未登录…)


    2、 修改 LoginServlet

    加入 doGet 方法:

    类中加入

        private ObjectMapper objectMapper = new ObjectMapper();
    
    • 1

    修改 User 属性:

        private int userId = 0;
        private String username = "";
        private String password = "";
    
    • 1
    • 2
    • 3

    doGet 方法:

    在服务器这边拿到了 session 并且也拿到了里面 user 视为是登录成功!
    (如果登录成功的话,服务器会给客户端返回 sessionld,浏览器就会保存这个 sessionld,下次请求的时候就会带上这个 id)
    服务器拿到 sessionld 就可以去 hash 表里查就知道了当前的 session 对象是谁~~

    	// 这个方法用来让前端检测当前的登录状态.
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.setContentType("application/json; charset=utf8"); // 加载的时候,通过 ajax ,访问一下服务器,获取当前的登录状态
            HttpSession session = req.getSession(false);
            if (session == null) {
                // 检测下会话是否存在, 不存在说明未登录!
                User user = new User();
                resp.getWriter().write(objectMapper.writeValueAsString(user));
                return;
            }
            User user = (User) session.getAttribute("user");
            if (user == null) {
                // 虽然有会话, 但是会话里没有 user 对象, 也视为未登录.
                user = new User();
                resp.getWriter().write(objectMapper.writeValueAsString(user));
                return;
            }
            // 已经登录的状态!!
            // 注意:此处不要把密码给返回到前端
            user.setPassword("");
            resp.getWriter().write(objectMapper.writeValueAsString(user));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3、修改前端代码 common.js

    创建 common.js

    前端页面跳转方法:location.assign('login.html');

    // 这个文件里放一些页面公共的代码
    
    // 加上一个逻辑, 通过 GET /login 这个接口来获取下当前的登录状态
    function getUserInfo() {
        $.ajax({
            type: 'get',
            url: 'login',
            success: function(body) {
                // 判定此处的 body 是不是一个有效的 user 对象(userId 是否非 0)
                if (body.userId && body.userId > 0) {
                    // 登录成功!
                    // 不做处理!
                    console.log("当前用户登录成功!用户名: " + body.username);
                } else {
                    // 登录失败!
                    // 弹窗提示让用户 让前端页面, 跳转到 login.html
                    alert("当前您尚未登录! 请登录后再访问博客列表!");
                    location.assign('blog_login.html');
                }
            },
            error: function() {
                alert("当前您尚未登录! 请登录后再访问博客列表!");
                location.assign('blog_login.html');
            }
        });
    }
    
    getUserInfo();
    
    • 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

    在列表页和详情页引入:

    <!-- 在这里引入上述的 js 文件, 就可以执行到里面的代码, 也就进行了登录状态的监测了 -->
    <script src="js/common.js"></script>
    
    • 1
    • 2

    4、——验证

    在这里插入图片描述

    重启服务器,再次访问列表页,此时再次弹窗,点击登录

    虽然咱们刚才登录过,但是会话信息是保存在服务器内存中的 (HttpSession 的 hash 表,是内存里的),当服务器进程重启之后,自然内存中的数据就没了~~
    所以每次重启服务器之后,就都得重新登录~~


  • 相关阅读:
    springMVC入门
    【二分查找】leetcode 240. 搜索二维矩阵 II
    P5742 【深基7.例11】评等级
    react简易了解
    chatgpt赋能python:Python散点图的颜色设置
    VsCode个人插件
    「运维有小邓」企业/员工目录搜索
    Go文件布局
    【Qt炫酷动画】demo04-仿Android旋转图标的等待对话框动画
    【GlobalMapper精品教程】026:影像黑边白边出现的原因及解决办法汇总
  • 原文地址:https://blog.csdn.net/qq_56884023/article/details/126788814