博客系统1

  1. 创建项目

1. 数据库涉及实现

  1. 创建数据库表
    用户,博客,
    一对多
blog(blogId,title,content,postTime,userId)
user(userId,username,passwd)
  1. 数据库代码
  • 记录建表SQL语句
  • 封装数据库的连接操作DBUtil
  • 创建实体类
    和数据库表中对应的类
    blog表 => Blog类对应Blog的一个对象,就对应表中的一条记录。
    user表 => User类对应User的一个对象,就对应表中的一个记录。
  • 封装数据库的增删改查 Dao(Data Access Object) 数据访问对象
    针对 博客表,创建BlogDao
    针对 用户表,创建UserDao
    对表提供一些方法,进行增删改查。
  1. 前后端交互
    前后端分离:前端只像后端请求数据,而不请求具体的页面,后端也仅仅是返回数据。这样设定的目的是为了让前端和后端更加解耦合

前后端交互步骤:

  1. 约定前后端交互接口
  2. 开发后端接口
  3. 开发前端接口

2.博客列表页

博客列表页:让博客列表页在加载的时候,通过Ajax给服务器发送一个请求,服务器查数据库,获取到博客列表数据,返回给浏览器,浏览器在根据数据构造页面内容。

  1. 约定前后端交互接口
    在博客列表页,获取博客列表功能,前端发什么请求,后端返回什么响应。
    请求:GET /blog
    响应:json格式数据(数组)
[
	{
    
    
		blogId:1,
		title:"this is a blog",
		content:"aaaaaaaaaaa",
		postTime:"2023-03-20 20:52:00",
		userId:1
	},
	{
    
    
		blogId:2,
		title:"this is a blog2",
		content:"bbbbbbbbbbbbb",
		postTime:"2023-03-20 20:53:00",
		userId:1
	},
]

model:
api:

  1. 开发后端接口
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    
    

    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        BlogDao blogDao = new BlogDao();
        List<Blog> blogList = blogDao.selectAll();
        //把 blogList 转成符合要求的json格式字符串
        String respJson = objectMapper.writeValueAsString(blogList);
        resp.setContentType("application/json; charset=utf8");
        resp.getWriter().write(respJson);
    }
}
  1. 开发前端接口

之前写的HTML代码

        <div class="container-right">
            <div class="blog">
                <!-- blog 标题 -->
                <div class="title">我的第一篇博客</div>
                <div class="date">2022-10-11</div>
                <div class="desc">
                    从今天起,Lorem ipsum, dolor sit amet consectetur adipisicing elit. Dolore, earum, exercitationem iste corporis labore, vero dolores tenetur dolorum quae architecto consectetur aspernatur corrupti dicta excepturi laboriosam culpa voluptatum laudantium minima.
                </div>

                <a href="#">查看全文 &gt;&gt; </a>
            </div>
        </div>

    </div>

根据上述代码的格式来构造。

<script src="./js/jquery.min.js"></script>
    <script>
        //在页面加载时,向服务器发起请求,获取博客列表数据
        function getBlogs() {
    
    
            $.ajax({
    
    
                type:'get',
                url:'blog',
                success:function(body) {
    
    

                    let containerRight = document.querySelector('.container-right');
                    for (let blog of body) {
    
    
                        //构造页面内容,参考之前写的HTML代码
                        //构造整个博客的div
                        let blogDiv = document.createElement('div');
                        blogDiv.className = 'blog';

                        //构造标题
                        let titleDiv = document.createElement('div');
                        titleDiv.className = 'title';
                        titleDiv.innerHTML = blog.title;
                        blogDiv.appendChild(titleDiv);

                        //构造发布时间
                        let dateDiv = document.createElement('div');
                        dateDiv.className = 'date';
                        dateDiv.innerHTML = blog.postTime;
                        blogDiv.appendChild(dateDiv);
                        

                        //构造博客摘要
                        let descDiv = document.createElement('div');
                        descDiv.className = 'desc';
                        descDiv.innerHTML = blog.content;
                        blogDiv.appendChild(descDiv);

                        //构造查看全文按钮
                        let a = document.createElement('a');
                        a.innerHTML = '查看全文 &gt;&gt';

                        //期望点击之后可以跳转到博客详情页,为了让博客详情页知道时点了哪个博客
                        a.href = 'blog_detail.html?blogId=' + blogDiv;
                        blogDiv.appendChild(a);

                        //把blogDiv加到父元素中
                        containerRight.appendChild(blogDiv);
                    }
                }
            });
        }
        //记得调用
        getBlogs();
    </script>

3.博客详情页

点击查看全文按钮,就能跳转到博客详情页。
跳转过去后,在博客详情页发起一个Ajax请求,从服务器获取到当前的博客的具体内容,显示出来

1. 约定前后端交互接口

请求
GET /blog?blogId=1
之前的博客列表中,请求里没有query string,此处博客详情中,带有query string。
如果存在query string,就返回指定的博客详情,不存在,返回博客列表。

这里的路径blog,设置的和博客列表页是同一个,此处是不是同一个都行,这里约定成同一个路径,就直接在BlogServlet中修改,约定成不同的路径,就创建新的类,在新的类中实现。

响应:
HTTP/1.1 200 OK
json数据表示:json字符串
获取博客详情,此处的content是完整的

{
    
    
	blogId:1,
	title:"这是标题",
	content:"这是内容",
	postTime:"2023-03-22 20:00:00",
	userId:1
}

2. 后端代码

如果存在query string,就返回指定的博客详情,不存在,返回博客列表。

@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    
    
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //尝试获取 query string中的 blogId 字段
        String blogId = req.getParameter("blogId");  //getParameter规定返回字符串,只能在后续使用时,转成需要的类型
        BlogDao blogDao = new BlogDao();
        if (blogId == null) {
    
    
            // query string 不存在,说明本次请求的是博客详情页
            List<Blog> blogList = blogDao.selectAll();
            //把 blogList 转成符合要求的json格式字符串
            String respJson = objectMapper.writeValueAsString(blogList);
            resp.setContentType("application/json; charset=utf8");
            resp.getWriter().write(respJson);
        } else {
    
    
            //query string 存在,说明本次请求获取的时指定 id 的博客
            Blog blog = blogDao.selectById(Integer.parseInt(blogId));
            String respJson = objectMapper.writeValueAsString(blog);
            resp.setContentType("application/json; charset=utf8");
            resp.getWriter().write(respJson);
        }
    }
}

3. 前端代码

数据库中保存的是markdown渲染前的内容,而我们需要在页面中显示markdown渲染后的内容。这就我们需要使用editor.md对markdown内容进行转换。
具体转换,editor.md提供了一个方法:editormd.markdownToHTML。把md字符串,转成HTML片段。

4.博客登录页

在此处输入用户名和密码,点击登录,就会触发一个HTTP请求。
服务器验证用户名,密码,根据结果判断是否登录成功。

前后端交互接口

请求
POST /login
username=root&password=123

响应
HTTP/1.1 302
Location:blog_list.html
form表单

前端

在页面中加入form表单

后端

5.实现:让页面强制要求登录

当用户访问 博客列表页 / 详情页 / 编辑页时,要求用户必须是已经登录的状态,如果用户没有登录,就会强制跳转到登录页面。

实现思路:
在页面加载的时候,专门发起一个新的Ajax。(一个页面可以发送N个Ajax请求)
以博客列表页为例,会先发送一个请求,获取博客列表,再发一个Ajax获取用户的登录状态。
如果用户已登录,相安无事,如果未登录,页面跳转到登录页。

前后端交互接口

请求 GET /login

响应
响应返回用户登录信息,已登录正常返回,未登录返回userId:0

HTTP /1.1 200 OK 
{
    
    
	userId:1,
	username:"zzz"
}
HTTP /1.1 200 OK 
{
    
    
	userId:0,
	username:"111"
}

前端

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

checkLogin();

后端

6.显示用户信息

博客列表页

前后端交互接口

直接复用检测登录状态的接口。
这里的后端代码已经有了,稍微调整前端的代码,把得到的用户信息显示出来即可。
请求
GET /login

响应
HTTP /1.1 200 OK

{
    
    
	userId:1,
	username:"zhangsan",
	password:"123"
}
前端
        function checkLogin() {
    
    
            $.ajax({
    
    
                type: 'get',
                url: 'login',
                success:function(body) {
    
    
                    if (body.userId && body.userId > 0) {
    
    
                        console.log("当前用户已经登陆");
                        //加上功能:把当前用户的名字显示到界面上
                        let h3 = document.querySelector('.container-left .card h3');
                        h3.innerHTML = body.username;
                    } else {
    
    
                        location.assign('login.html');
                    }
                }
            });
        }
后端
        User user = (User) session.getAttribute("user");  //取 user 对象
        if (user == null) {
    
    
            user = new User();
            String respJson = objectMapper.writeValueAsString(user);
            resp.getWriter().write(respJson);
            return;
        }

        //成功取出user对象,resp返回 
        String respJson = objectMapper.writeValueAsString(user);
        resp.getWriter().write(respJson);

博客详情页

如何判断程序什么时候需要一个新的servlet?
看当前请求的路径,是否已经有了,每个servlet都会绑定到一个请求路径上

前后端交互接口

重新实现。
请求
GET /author?blogId=1

响应
HTTP /1.1 200 OK

{
    
    
	userId:1,
	username:"zhangsan",
	password:"123"
}
后端
@WebServlet("/author")
public class authorServlet extends HttpServlet {
    
    
    
    private ObjectMapper objectMapper = new ObjectMapper();
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        String blogId = req.getParameter("blogId");
        if (blogId == null) {
    
    
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("参数非法,缺少blogId");
            return;
        }
        
        //根据blogId查找 Blog 对象
        BlogDao blogDao = new BlogDao();
        Blog blog = blogDao.selectById(Integer.parseInt(blogId));
        if (blog == null) {
    
    
            //博客不存在
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("没有找到指定博客:blogId = " + blogId);
            return;
        }
        
        //根据blog中的userid找到对应的用户信息
        UserDao userDao = new UserDao();
        User author = userDao.selectById(blog.getUserId());
        String respJson = objectMapper.writeValueAsString(author);
        resp.setContentType("application/json; charset=utf8");
        resp.getWriter().write(respJson);
    }
}
前端

在blog_detail.html中添加

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

//函数调用
getAuthor();

7.实现退出登录状态

判定登陆状态:

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

实现退出登录:
要么删掉http session,要么删掉user,只要删掉一个就行。
httpsession的删除有些麻烦,getSession能够创建/获取会话,但没有删除会话的方法。可以通过设置会话的过期时间来达到类似的效果。
更好的选择是:删除user对象:removeAttribute

前后端交互接口

请求
GET /logout

响应
HTTP /1,1 302
Location:login.html

这里是通过a标签触发的页面跳转,不是Ajax。
a标签和form表单都可以触发页面跳转,Ajax不能触发页面跳转。

前端

        <a href="blog_list.html">主页</a>
        <a href="blog_edit.html">写博客</a>
        <a href="logout">注销</a>

后端

@WebServlet("/logout")
public class logoutServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        HttpSession httpSession = req.getSession(false);
        if (httpSession == null) {
    
    
            //未登录状态,直接提示出错
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("未登录");
            return;
        }
        httpSession.removeAttribute("user");  //移除 user 对象
        resp.sendRedirect("login.html");
    }

}

8. 发布博客

前后端交互接口

使用form表单,得让页面中多个form表单,同时让form里面可以感知到博客的内容

请求

POST /blog

title=标题&content=内容

响应

HTTP /1.1 302
Location:blog_list.html

前端

<div class="blog-edit-container">
    <form action="blog" method="post">

        <!-- 博客标题编辑区 -->
        <div class="title">
            <input type="text" id="title" placeholder="输入文章标题" name="title">
            <!-- <button id="submit">发布文章</button> -->
            <input type="submit" id="submit" value="发布文章">

        </div>

        <!-- 博客编辑器  
        这里用id是为了和markdown对接-->
        <div id="editor">
            <!-- editor.md文档要求的写法,editor.md对form表单是支持的 -->
            <textarea name="content" style="display: none;"></textarea>
        </div>
    </form>
</div>

后端

blogServlet

 @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
    //发布博客
    //读取请求,构造blog请求,插入数据库即可
    HttpSession httpSession = req.getSession(false);
    if (httpSession == null) {
    
    
        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write("未登录,无法发布");
        return;
    }

    User user = (User) httpSession.getAttribute("user");
    if (user == null) {
    
    
        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write("未登录,无法发布");
        return;
    }
    req.setCharacterEncoding("utf8");

    String title = req.getParameter("title");
    String content = req.getParameter("content");
    if (title == null || "".equals(title) || content == null || "".equals(content)) {
    
    
        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write("数据空");
        return;
    }

    Blog blog = new Blog();
    blog.setTitle(title);
    blog.setContent(content);
    blog.setUserId(user.getUserId());
    blog.setPostTime(new Timestamp(System.currentTimeMillis()));

    BlogDao blogDao = new BlogDao();
    blogDao.add(blog);
    resp.sendRedirect("blog_list.html");
}

猜你喜欢

转载自blog.csdn.net/weixin_44431128/article/details/129673945