• 博客系统详解


    博客系统的前端代码,我放在码云的,这里就不给大家重复展示了。

    https://gitee.com/zhang-qinyang1/class102.git

    目录

    1.设计一个简单网站的基本思路

    2.准备工作

    3.编写数据库的代码

    3.1数据库设计

    3.2数据库封装

    4.开发服务器controller和客户端的功能

    4.1博客列表页的展示

    4.2博客详情页的展示

    4.3登录功能

    4.4检测用户登录状态

    4.5能够正确显示用户信息

    4.6实现注销博客功能

    4.7实现发布博客功能

    4.8实现删除博客功能

    5.小结


    1.设计一个简单网站的基本思路

    (1)设计前后端交互的接口
    (2)实现服务器部分
    (3)实现客户端部分
    我们主要是采取客户端渲染来实现前后端分离

    2.准备工作

    这里的准备工作实际上就是一个servlet使用前所需要的导入工作

    ①创建Maven项目

    这里我给博客系统命名为blog_system

    ②引入依赖(servlet,Jackon,mysql)

    这里是在中央仓库去找你需要的

    Maven Repository: Search/Browse/Explore (mvnrepository.com)https://mvnrepository.com/③创建必要的目录

    在main的目录下创建webapp这个目录文件,之后再webapp这个目录下创建WEB-INF这个目录文件,在WEB-INF这个目录下创建文件web.xml;并且对其进行配置固定内容如下:

    1. web-app PUBLIC
    2. "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    3. "http://java.sun.com/dtd/web-app_2_3.dtd" >
    4. <web-app>
    5. <display-name>Archetype Created Web Applicationdisplay-name>
    6. web-app>

    ④编写代码

    我们先试着随便输出一个来测试以上环境是否配置无误

    1. import javax.servlet.ServletException;
    2. import javax.servlet.annotation.WebServlet;
    3. import javax.servlet.http.HttpServlet;
    4. import javax.servlet.http.HttpServletRequest;
    5. import javax.servlet.http.HttpServletResponse;
    6. import java.io.IOException;
    7. @WebServlet("/test")
    8. public class TestServlet extends HttpServlet {
    9. @Override
    10. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    11. req.setCharacterEncoding("utf8");
    12. resp.setContentType("text/html;charset=utf8");
    13. resp.getWriter().write("这是用来测试环境的servlet");
    14. }
    15. }

    ⑤⑥部署打包(用smart Tomcat)

     ⑦在浏览器中验证

    这里的时候需要注意一级路径与二级路径分别是什么

    3.编写数据库的代码

    数据库这里的操作大多是JDBC,因此大家首先需要对其熟悉,才能够看懂后续代码操作。

    3.1数据库设计

    数据库设计在这里的话就是指的创建数据库/数据表的结构
    对于我们博客的功能来说,我们需要发布博客,获取博客,以及用户登录。所以,建表的时候抓住实体,我们就很容易想到建博客表和用户表来进行操作。

    1. create database if not exists blog_system;
    2. use blog_system;
    3. --创建一个博客表
    4. drop table if exists blog;
    5. create table blog(
    6. blogId int primary key auto_increment,
    7. title varchar(1024),
    8. content mediumtext,--对于blog而言,往往是较长的,所以我们这里用mediumtext来表示
    9. userId int,--文章作者id
    10. postTime datetime--发布的时间
    11. );
    12. --创建一个user表
    13. drop table if exists user;
    14. create table user(
    15. userId int primary key auto_increment,
    16. username varchar(1024) unique,
    17. password varchar(124)
    18. );
    19. insert into user values(null,'zhangsan','123');
    20. insert into user values(null,'lisi','123');

    然后将它插入到数据库中去,显示如下:

    插入的时候注意一些细节,比如去掉注释这些,不然的话就会报错

    3.2数据库封装

    (1)先创建DBUtil封装数据库连接的操作

    1. package model;
    2. import com.mysql.jdbc.Connection;
    3. import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
    4. import javax.sql.DataSource;
    5. import java.sql.PreparedStatement;
    6. import java.sql.ResultSet;
    7. import java.sql.SQLException;
    8. //使用这个类和数据库建立连接
    9. public class DBUtil {
    10. //这个不用背,每次复制粘贴就行
    11. private static final String URL = "jdbc:mysql://127.0.0.1:3306/my_blog?characterEncoding=utf8&useSSL=false";
    12. private static final String USERNAME = "root";
    13. //数据库密码
    14. private static final String PASSWORD = "123456";
    15. private static volatile DataSource dataSource=null;
    16. private static DataSource getDataSource(){
    17. if (dataSource==null){
    18. synchronized (DBUtil.class){
    19. if (dataSource==null){
    20. dataSource=new MysqlDataSource();
    21. ((MysqlDataSource)dataSource).setURL(URL);
    22. ((MysqlDataSource)dataSource).setUser(USERNAME);
    23. ((MysqlDataSource)dataSource).setPassword(PASSWORD);
    24. }
    25. }
    26. }
    27. return dataSource;
    28. }
    29. public static Connection getConnection()throws SQLException {
    30. return (Connection) getDataSource().getConnection();
    31. }
    32. public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet) throws SQLException {
    33. if (resultSet!=null){
    34. resultSet.close();
    35. }
    36. if(statement!=null){
    37. statement.close();
    38. }
    39. if (connection!=null){
    40. connection.close();
    41. }
    42. }
    43. }

    (2)创建实体类,使用实体来表示数据库中的一条内容,此处主要创建的是Blog类以及User类

    这里只展示Blog类

    1. package model;
    2. import java.sql.Timestamp;
    3. //每个Blog对象,对应blog表中的一条记录
    4. public class Blog {
    5. //因为都是private,所以这里要重写get,set方法
    6. private int BlogId;
    7. private String title;
    8. private String content;
    9. private int UserId;
    10. private Timestamp postTime;
    11. public void setBlogId(int blogId) {
    12. BlogId = blogId;
    13. }
    14. public int getBlogId() {
    15. return BlogId;
    16. }
    17. public String getTitle() {
    18. return title;
    19. }
    20. public void setTitle(String title) {
    21. this.title = title;
    22. }
    23. public String getContent() {
    24. return content;
    25. }
    26. public void setContent(String content) {
    27. this.content = content;
    28. }
    29. public int getUserId() {
    30. return UserId;
    31. }
    32. public void setUserId(int userId) {
    33. UserId = userId;
    34. }
    35. public Timestamp getPostTime() {
    36. return postTime;
    37. }
    38. public void setPostTime(Timestamp postTime) {
    39. this.postTime = postTime;
    40. }
    41. }

    (3)封装针对数据的增删查改,把提供增删查改的这样的类,称为DAO。这里也只展示BlogDao

    1. package model;
    2. import com.mysql.jdbc.Connection;
    3. import java.sql.PreparedStatement;
    4. import java.sql.ResultSet;
    5. import java.sql.SQLException;
    6. import java.util.ArrayList;
    7. import java.util.List;
    8. // 这个类用于去封装博客表的基本操作
    9. public class BlogDao {
    10. // 1. 向博客列表中插入一个博客
    11. public void insert(Blog blog) throws SQLException {
    12. // JDBC 基本代码
    13. Connection connection=null;
    14. PreparedStatement statement=null;
    15. try {
    16. // ①和数据库建立连接.
    17. connection = DBUtil.getConnection();
    18. // ② 构造 SQL 语句
    19. String sql = "insert into blog values(null, ?, ?, ?, now())";
    20. statement=connection.prepareStatement(sql);
    21. //第一个参数表示的是第几个?的位置
    22. statement.setString(1, blog.getTitle());
    23. statement.setString(2, blog.getContent());
    24. statement.setInt(3,blog.getUserId());
    25. // ③执行 SQL
    26. statement.executeUpdate();
    27. } catch (SQLException e) {
    28. e.printStackTrace();
    29. } finally {
    30. // ④ 关闭连接, 释放资源
    31. DBUtil.close(connection,statement,null);
    32. }
    33. }
    34. // 2. 能够获取到博客表中的所有博客的信息 (用于在博客列表页, 此处每篇博客不一定会获取到完整的正文)
    35. public List selectAll() throws SQLException {
    36. Listblogs=new ArrayList<>();
    37. Connection connection=null;
    38. PreparedStatement statement=null;
    39. ResultSet resultSet=null;
    40. try {
    41. connection=DBUtil.getConnection();
    42. String sql="select * from blog";
    43. statement=connection.prepareStatement(sql);
    44. resultSet=statement.executeQuery();
    45. //遍历整个集合
    46. while(resultSet.next()){
    47. Blog blog=new Blog();
    48. blog.setBlogId(resultSet.getInt("blogId"));
    49. blog.setTitle(resultSet.getString("title"));
    50. blog.setContent(resultSet.getString("content"));
    51. blog.setUserId(resultSet.getInt("userId"));
    52. blog.setPostTime(resultSet.getTimestamp("postTime"));
    53. blogs.add(blog);
    54. }
    55. } catch (SQLException e) {
    56. e.printStackTrace();
    57. }finally {
    58. DBUtil.close(connection, statement,resultSet );
    59. }
    60. return blogs;
    61. }
    62. // 3. 能够根据博客 id 获取到指定的博客内容 (用于在博客详情页)
    63. public Blog selectOne(int blogId) throws SQLException {
    64. Connection connection=null;
    65. PreparedStatement statement=null;
    66. ResultSet resultSet=null;
    67. try {
    68. connection=DBUtil.getConnection();
    69. String sql="select * from blog where blogId=?";
    70. statement=connection.prepareStatement(sql);
    71. statement.setInt(1,blogId);
    72. resultSet=statement.executeQuery();
    73. //由于id是唯一的,要么是0条,要么是唯一的,是主键作为查询条件的,所以直接用if
    74. if (resultSet.next()){
    75. Blog blog=new Blog();
    76. blog.setBlogId(resultSet.getInt("blogId"));
    77. blog.setTitle(resultSet.getString("title"));
    78. blog.setContent(resultSet.getString("content"));
    79. blog.setUserId(resultSet.getInt("userId"));
    80. blog.setPostTime(resultSet.getTimestamp("postTime"));
    81. return blog;
    82. }
    83. } catch (SQLException e) {
    84. e.printStackTrace();
    85. }finally {
    86. DBUtil.close(connection,statement,resultSet);
    87. }
    88. return null;
    89. }
    90. // 4. 从博客表中, 根据博客 id 删除博客.
    91. public void delete(int blogId) throws SQLException {
    92. Connection connection=null;
    93. PreparedStatement statement=null;
    94. try {
    95. connection=DBUtil.getConnection();
    96. String sql="delete from blog where blogId=?";
    97. statement.setInt(1,blogId);
    98. statement.executeUpdate();
    99. } catch (SQLException e) {
    100. e.printStackTrace();
    101. }finally {
    102. DBUtil.close(connection,statement,null);
    103. }
    104. }
    105. // 注意, 上述操作是 增删查, 没有改
    106. }

    4.开发服务器controller和客户端的功能

    4.1博客列表页的展示

    ①需求:

    这个页面需要能够展示出数据库中的博客列表

    ②约定前后端的交互接口:

    ③编写服务器代码:

    1. package controller;
    2. import com.fasterxml.jackson.databind.ObjectMapper;
    3. import model.Blog;
    4. import model.BlogDao;
    5. import javax.servlet.ServletException;
    6. import javax.servlet.annotation.WebServlet;
    7. import javax.servlet.http.HttpServlet;
    8. import javax.servlet.http.HttpServletRequest;
    9. import javax.servlet.http.HttpServletResponse;
    10. import java.io.IOException;
    11. import java.util.List;
    12. @WebServlet("/blog")
    13. public class BlogServlet extends HttpServlet {
    14. private ObjectMapper objectMapper=new ObjectMapper();
    15. @Override
    16. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    17. //从数据库中查询到博客列表,转成json格式,直接返回
    18. //查询数据库
    19. BlogDao blogDao=new BlogDao();
    20. List blogs=blogDao.selectAll();
    21. //将blogs转成json格式
    22. String respJson=objectMapper.writeValueAsString(blogs);
    23. resp.setContentType("application/json;charset=utf8");
    24. resp.getWriter().write(respJson);
    25. }
    26. }

    ④编写客户端代码:

    在页面加载的时候,让页面通过ajax访问服务器,获取到数据库中的博客数据,并且填到页面中

    1. !DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
    6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    7. <title>博客列表title>
    8. <link rel="stylesheet" href="css/blog_list.css">
    9. <link rel="stylesheet" href="css/common.css">
    10. head>
    11. <body>
    12. <div class="nav">
    13. <img src="images/1.jpg" alt="">
    14. <span >我的博客系统span>
    15. <div class="spacer">div>
    16. <a href="blog_list.html">主页a>
    17. <a href="blog_edit.html">写博客a>
    18. <a href="#">注销a>
    19. div>
    20. <div class="container">
    21. <div class="left">
    22. <div class="card">
    23. <img src="./images/3.webp" alt="">
    24. <h3>知名博主h3>
    25. <a href="">博客网址a>
    26. <div class="counter">
    27. <span>文章span>
    28. <span>分类span>
    29. div>
    30. <div class="counter">
    31. <span>2span>
    32. <span>1span>
    33. div>
    34. div>
    35. div>
    36. <div class="right">
    37. div>
    38. div>
    39. div>
    40. <script src="http://code.jquery.com/jquery-2.1.1.min.js">script>
    41. <script>
    42. //在页面加载的时候,通过ajax给服务器发送消息,获取到博客列表信息,并且显示在页面上
    43. function getBlogList(){
    44. $.ajax({
    45. type:'get',
    46. url:'blog',
    47. success:function(body){
    48. //获取到的body就是一个js对象数组,每个元素就是一个js对象,根据这个对象来构造一个div
    49. //1.把原来.right里面原有的内容清空,替换成从数据库服务器拿到的
    50. let rightDiv=document.querySelector('.right');
    51. rightDiv.innerHTML='';//清空原有数据
    52. //2.遍历body,构造出一个个blogDiv
    53. for(let blog of body){
    54. let blogDiv=document.createElement('div');
    55. //针对blogDiv设置一个属性,类名设为blog
    56. blogDiv.className='blog';
    57. //构造内部元素
    58. //构造标题
    59. let titleDiv=document.createElement('div');
    60. titleDiv.className='title';
    61. titleDiv.innerHTML=blog.title;
    62. //作为子元素添加进去
    63. blogDiv.appendChild(titleDiv);
    64. //构造发布时间
    65. let dateDiv=document.createElement('div');
    66. dateDiv.className='date';
    67. dateDiv.innerHTML=blog.postTime;
    68. blogDiv.appendChild(dateDiv);
    69. //构造摘要
    70. let descDiv=document.createElement('div');
    71. descDiv.className='desc';
    72. descDiv.innerHTML=blog.content;
    73. blogDiv.appendChild(descDiv);
    74. //链接 查看全文(这里用的是a标签)
    75. let a=document.createElement('a');
    76. a.innerHTML='查看全文 >>';
    77. //希望点击后能跳转到博客详情页
    78. //跳转要告知哪个博客的详情页
    79. a.href='blog_detail.html?blogId='+blog.blogId;
    80. blogDiv.appendChild(a);
    81. //把blogDiv挂到dom树上
    82. rightDiv.appendChild(blogDiv);
    83. }
    84. },
    85. error:function(){
    86. alert("获取博客列表失败");
    87. }
    88. });
    89. }
    90. getBlogList();
    91. script>
    92. body>
    93. html>

    展示效果:当我们在数据库中插入一条数据后,他会更新到列表页的最前面

    值得注意的是:

    a.db.sql中的代码并不会生效,我们需要把其添加到mysql中去

    b.可以利用postman来看我们构造的响应body中返回的数据格式与希望的是不是一致的

    c.转换时间格式:yyyy-MM-dd HH:mm:ss(分别指的是年月日 小时分钟秒钟) 

    d.要想插入的博客是按照时间约后发布越在上面,就不能忘了加上order by desc来进行降序的排列

    4.2博客详情页的展示

    ①需求:

    这里用来获取博客的详情页,发送的请求。希望得到的页面能够显示出博客对应的正文内容
    而这个正文内容,我们通过ajax来进行获取。在blog_detail.html页面加载的时候,触发ajax请求来访问服务器,获取到博客内容,再次填充到博客详情页里面去。

    ②前后端的交互接口:

    注意:这里我们是通过某个博客的id来确定具体要访问哪个内容的。与博客列表页不同之处有以下三点:

    (1)请求中带有参数blogId

    (2)结果响应不是json数组,而是单一的对象(因为具体到了某一个)

    (3) 响应博客的正文,不用进行截断了

    ③编写服务器代码:

    此处服务器代码几乎和博客列表页一致,唯一不同的点就是有了个blogId来区分是博客列表还是详情,因此我们还是放到同一个方法中来实现

    1. package controller;
    2. import com.fasterxml.jackson.databind.ObjectMapper;
    3. import model.Blog;
    4. import model.BlogDao;
    5. import javax.servlet.ServletException;
    6. import javax.servlet.annotation.WebServlet;
    7. import javax.servlet.http.HttpServlet;
    8. import javax.servlet.http.HttpServletRequest;
    9. import javax.servlet.http.HttpServletResponse;
    10. import java.io.IOException;
    11. import java.util.List;
    12. @WebServlet("/blog")
    13. public class BlogServlet extends HttpServlet {
    14. private ObjectMapper objectMapper=new ObjectMapper();
    15. @Override
    16. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    17. //从数据库中查询到博客列表,转成json格式,直接返回
    18. //查询数据库
    19. BlogDao blogDao=new BlogDao();
    20. // 先尝试获取到 req 中的 blogId 参数. 如果该参数存在, 说明是要请求博客详情页
    21. // 如果该参数不存在, 说明是要请求博客的列表.
    22. //因为这里是get,我们直接可以在URL中判断该参数是否存在
    23. String param=req.getParameter("blogId");
    24. //当其不存在是则是获取列表页
    25. if(param==null){
    26. List blogs=blogDao.selectAll();
    27. //将blogs转成json格式
    28. String respJson=objectMapper.writeValueAsString(blogs);
    29. resp.setContentType("application/json;charset=utf8");
    30. resp.getWriter().write(respJson);
    31. }else {//存在参数,则是获取博客详情页
    32. int blogId = Integer.parseInt(param);//包装类转换成Integer
    33. Blog blog = blogDao.selectOne(blogId);
    34. //用json对象来进行接收
    35. String respJson = objectMapper.writeValueAsString(blog);
    36. resp.setContentType("application/json;charset=utf8");
    37. resp.getWriter().write(respJson);
    38. }
    39. }
    40. }

    ④编写客户端代码:

    前端代码这边,要想构造一个请求获取博客详情,就得知道当前用户的博客id,当然这个id已经在当前的blog_detail.html页面的url中了。在前端的时候,我们可以通过location.search来获取到blogId.

    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
    6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    7. <title>博客详情页title>
    8. <link rel="stylesheet" href="css/blog_detail.css">
    9. link<link rel="stylesheet" href="css/common.css">
    10. <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    11. <script src="http://code.jquery.com/jquery-2.1.1.min.js">script>
    12. <script src="editor.md/lib/marked.min.js">script>
    13. <script src="editor.md/lib/prettify.min.js">script>
    14. <script src="editor.md/editormd.js">script>
    15. head>
    16. <body>
    17. <div class="nav">
    18. <img src="images/1.jpg" alt="">
    19. <span >我的博客系统span>
    20. <div class="spacer">div>
    21. <a href="blog_list.html">主页a>
    22. <a href="blog_edit.html">写博客a>
    23. <a href="#">注销a>
    24. div>
    25. <div class="container">
    26. <div class="left">
    27. <div class="card">
    28. <img src="./images/3.webp" alt="">
    29. <h3>知名博主h3>
    30. <a href="">博客网址a>
    31. <div class="counter">
    32. <span>文章span>
    33. <span>分类span>
    34. div>
    35. <div class="counter">
    36. <span>2span>
    37. <span>1span>
    38. div>
    39. div>
    40. div>
    41. <div class="right">
    42. <div class="blog-content">
    43. <h3>h3>
    44. <div class="date">div>
    45. <div id="content">div>
    46. div>
    47. div>
    48. div>
    49. <script>
    50. function getBlogDetail(){
    51. $.ajax({
    52. type:'get',
    53. url:'blog'+location.search,
    54. success:function(body){
    55. //根据body内容来构造页面
    56. //①title
    57. let h3=document.querySelector(".blog-content>h3");
    58. h3.innerHTML=body.title;
    59. //②postTime
    60. let dateDiv=document.querySelector(".date");
    61. dateDiv.innerHTML=body.postTime;
    62. //③正文
    63. // let content=document.querySelector("#content");
    64. // content.innerHTML=body.content;
    65. //为了展现出来是Markdown的形式,所以引入editor.markdown操作
    66. editormd.markdownToHTML('content',{
    67. markdown:body.content
    68. });
    69. }
    70. });
    71. }
    72. getBlogDetail();
    73. script>
    74. body>

    注意!!!这里的难点主要有三个问题:

    a.弄清楚blogId到底是怎么来的

     b.为什么要引入editor.md?

    因为我们编辑的时候用的是Markdown格式的,要想响应的时候也是,就需要引入相应的需求

    c.为了防止出现乱码情况,我们一定要设置格式!!!而且要注意整体和局部之间的关系!!!

    展示结果: 

    4.3登录功能

    ①需求:

    实现登录,当登录之后跳转至博客列表页

    ②实现前后端交互:

    很显然,我们这里决定基于form表单来实现,当然ajax也可以,只是这里用form表单

    ③服务器代码:

    1. package controller;
    2. import model.User;
    3. import model.UserDao;
    4. import javax.servlet.ServletException;
    5. import javax.servlet.annotation.WebServlet;
    6. import javax.servlet.http.HttpServlet;
    7. import javax.servlet.http.HttpServletRequest;
    8. import javax.servlet.http.HttpServletResponse;
    9. import javax.servlet.http.HttpSession;
    10. import java.io.IOException;
    11. @WebServlet("/login")
    12. public class LoginServlet extends HttpServlet {
    13. @Override
    14. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    15. req.setCharacterEncoding("utf-8");
    16. resp.setCharacterEncoding("utf-8");
    17. //①获取到参数中的请求
    18. String username=req.getParameter("username");
    19. String password=req.getParameter("password");
    20. if(username == null || "".equals(username) || password == null || "".equals(password)) {
    21. // 请求内容缺失,登录失败
    22. resp.setContentType("text/html; charset=utf8");
    23. resp.getWriter().write("当前的用户名或者密码为空");
    24. return;
    25. }
    26. //②和数据库里面存储的数据进行比对
    27. UserDao userDao=new UserDao();
    28. User user=userDao.selectByName(username);
    29. if(user == null || !user.getPassword().equals(password)) {
    30. // 用户没有查到或者密码不匹配,登录失败
    31. resp.setContentType("text/html;charset=utf8");
    32. resp.getWriter().write("用户名或密码错误");
    33. return;
    34. }
    35. //③如果比较通过,则用会话记录下来,即创建会话
    36. HttpSession session= req.getSession(true);
    37. //将信息存入会话中
    38. session.setAttribute("user",user);
    39. //④跳转页面,页面重定向
    40. resp.sendRedirect("blog_list.html");
    41. }
    42. }

    ④客户端代码:

    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
    6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    7. <title>登录页面title>
    8. <link rel="stylesheet" href="css/common.css">
    9. <link rel="stylesheet" href="css/blog_login.css">
    10. head>
    11. <body>
    12. <div class="nav">
    13. <img src="./images/1.jpg" alt="">
    14. <span>我的博客系统span>
    15. <div class="spacer">div>
    16. <a href="blog_list.html">主页a>
    17. <a href="blog_edit.html">写博客a>
    18. div>
    19. <div class="login-container">
    20. <form action="login" method="post">
    21. <div class="login-dialog">
    22. <h3>登录h3>
    23. <div class="row">
    24. <span>用户名span>
    25. <input type="text" id="username" name="username">
    26. div>
    27. <div class="row">
    28. <span>密码span>
    29. <input type="password" id="password" name="password">
    30. div>
    31. <div class="row">
    32. <input type="submit" id="submit" value="提交">
    33. div>
    34. div>
    35. form>
    36. div>
    37. body>
    38. html>

    结果展示:

     注意!!!值得注意的是用form表单的时候,不要忘了添加name属性,还有css里面的格式会随着属性的改变可能会不同。并且form表单中提交按钮不能是button,要是type类型的submit。

    4.4检测用户登录状态

    ①需求:

    当登录功能完成后,我们要调节一下列表页和详情页,使得这两个页面只有登录了才能访问,要是没有登录就跳转到登录页面

    ②实现前后端交互接口:

    ③服务器代码:

    我们就直接在上述代码中直接添加一个doGet方法,用来检验这个时候用户的登录状态就好。因此,我们这里的代码也就是直接连着的上面的

    1. //用来检验用户的登录状态
    2. @Override
    3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    4. resp.setContentType("application/json;charset=utf8");
    5. //①获取开始存在的会话,看会话是否存在,会话都不存在的话,那么必然是没有登录
    6. //因为false是不会创建新的会话的
    7. HttpSession session=req.getSession(false);
    8. if (session==null){
    9. //没有会话
    10. //创建对象,里面为空,并返回,注意这里返回空,就不用和数据库进行连接,,但是仍然是json格式的,所以用mapper来转换
    11. User user=new User();
    12. resp.getWriter().write(objectMapper.writeValueAsString(user));
    13. return;
    14. }
    15. //拿到了会话,但是会话里面的user为空值
    16. User user= (User) session.getAttribute("user");
    17. if (user==null){
    18. //这个时候,仍然是没有登录,那么仍然返回空
    19. user = new User();
    20. resp.getWriter().write(objectMapper.writeValueAsString(user));
    21. return;
    22. }
    23. //获取到了session,且里面的user并不为空,那么可以继续访问
    24. //但是注意不要把密码返回给了前端
    25. user.setPassword("");
    26. resp.getWriter().write(objectMapper.writeValueAsString(user));
    27. }

    ④客户端代码:

    对于一些通用的代码,我们可以放到一个js里面来统一管理,然后其他页面直接调用它就好,这里我们把其命名为common.js。然后把这个js分别引入到列表页和详情页即可。

    common.js

    1. // 这个文件里放一些 页面公共的代码
    2. // 加上一个逻辑,通过 GET/login 这个接口来获取下当前的登录状态
    3. function getUserInfo() {
    4. $.ajax({
    5. type: 'get',
    6. url: 'login',
    7. success: function(body) {
    8. // 判定此处的 body 是不是一个有效的 user 对象(userId 是否非 0)
    9. if(body.userId && body.userId >0) {
    10. // 登录成功
    11. // 不做处理
    12. console.log("当前用户登录成功!用户名:" + body.username);
    13. }else{
    14. // 登录失败
    15. // 让前端页面,跳转到 login.html
    16. alert("当前你尚未登录!请登录后再访问博客列表!");
    17. location.assign('blog_login.html');
    18. }
    19. },
    20. error: function() {
    21. alert("当前你尚未登录!请登录后再访问博客列表!");
    22. location.assign('blog_login.html');
    23. }
    24. });
    25. }
    26. getUserInfo();

    引入:

    结果展示:(当我们直接访问列表页时) 

    当我们点击确定或者等待一段时间后,就会跳转到登录页面:

     而当我们登录后就不会出现这种情况。

    4.5能够正确显示用户信息

    由于我们登录的人是可能与发布文章的博主是不同的人,但是我们发现,当我们进去的时候登录的用户是zhangsan,但是写博客的可能不是zhangsan,所以我们要实现能够根据具体情况进行转换。

    ①需求:

    让在列表页显示的用户名称是用户的登陆者,而在详情页具体某篇博客时,显示的是作者本人。

    博客列表页:

    而我们可以发现针对博客列表页来说,其实前面已经处理过了,我们通过ajax得到了body,此时我们只需要在前端代码中稍作改动,拿到当前改动的这个名字,作为当前用户名即可。

    所以我们只需要在common.js里面做改动

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

    此时我们在列表页看到的用户名就是我们登录时的用户名了

    博客详情页:

    根据上面的操作,我们发现,当我们进入任何一篇内容时,用户名都是zhangsan;

     然而实际上第五篇的作者却是wangwu,所以说我们得在列表页与详情页作出区分。而如何区分呢?很显然,我们可以通过blogId来提供新的接口,这个接口可以让客户端指定blogId,从而获取指定blogId的作者信息

    ②前后端交互接口:

    ③后端服务器代码:

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

    ④客户端代码: (这里主要是此处的核心代码)

    1. //从服务器获取一下当前博客的作者信息,并且显示到界面上
    2. function getAuthorInfo(){
    3. $.ajax({
    4. type:'get',
    5. url:'authorInfo'+location.search,
    6. success:function(body){
    7. // 此处的 body, 就是服务器返回的 User 对象, 是文章的作者信息
    8. if (body.username) {
    9. // 如果响应中的 username 存在, 就把这个值设置到页面上.
    10. changeUserName(body.username)
    11. }else{
    12. console.log("获取作者信息失败"+body.reason);
    13. }
    14. }
    15. });
    16. }
    17. getAuthorInfo();

    common.js里面的内容也要稍作修改,代码如下:

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

    结果展示:

    我们就可以看到第五篇博客的作者是wangwu了 

    我们需要注意的点是,在完成局部功能函数后,一定不要忘记调用它,不然就毫无意义了。

    4.6实现注销博客功能

    ①需求:
    退出当前的登录状态,当我们点击注销按钮之后,就会在服务器上取消登录并且能够跳转到登录页面。

    ②前后端交互接口:

    ③服务器后端代码:

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

    1. package controller;
    2. import javax.servlet.ServletException;
    3. import javax.servlet.annotation.WebServlet;
    4. import javax.servlet.http.HttpServlet;
    5. import javax.servlet.http.HttpServletRequest;
    6. import javax.servlet.http.HttpServletResponse;
    7. import javax.servlet.http.HttpSession;
    8. import java.io.IOException;
    9. @WebServlet("/logout")
    10. public class LogoutServlet extends HttpServlet {
    11. @Override
    12. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    13. //先找到当前用户的会话
    14. HttpSession session=req.getSession(false);
    15. if (session==null){
    16. //用户没登录,就不用注销啦
    17. resp.getWriter().write("当前用户没有登录,无法注销");
    18. }
    19. //然后把这个用户的会话中的信息删掉
    20. session.removeAttribute("user");
    21. resp.sendRedirect("blog_login.html");
    22. }
    23. }

    什么叫做登录呢?

    用户有一个session,同时session有一个user属性,两个同时具备,才叫做登录状态。注销,只需要破坏上面的任意一个条件就OK啦,此处选择的破坏第二个条件,也就是让session不具备user属性,把user属性从session中删了。为啥不删会话呢?因为没有提供删会话的API。我们可以看我们检测登录状态的代码,会看到两者必须同时具备;

    无论删谁,都会返回null,也就是说都会被认为是未登录状态。 

     ④客户端代码:

    我们把博客列表页,详情页和编辑页的注销按钮的herf属性作出修改即可

     结果展示:(此时当我们点击注销,就会回到登录页面)

     注意!!!博客登录页不用设置注销按钮的。

    4.7实现发布博客功能

    ①需求:

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

    ②前后端交互接口:

    ③服务器代码:(在blogservlet中继续创建写在post请求中的)

    1. @Override
    2. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    3. resp.setCharacterEncoding("utf8");
    4. req.setCharacterEncoding("utf8");
    5. HttpSession session=req.getSession();
    6. if (session==null){
    7. //当前用户未登录,不能提交
    8. resp.setContentType("text/html;charset=utf8");
    9. resp.getWriter().write("当前用户未登录,不能提交博客!");
    10. return;
    11. }
    12. User user= (User) session.getAttribute("user");
    13. if (user==null){
    14. //当前用户未登录,不能提交
    15. resp.setContentType("text/html;charset=utf8");
    16. resp.getWriter().write("当前用户未登录,不能提交博客!");
    17. return;
    18. }
    19. //先从请求中取出参数,博客的标题和正文
    20. resp.setCharacterEncoding("utf8");
    21. String title=req.getParameter("title");
    22. String content=req.getParameter("content");
    23. if (title==null||"".equals(title)||content==null||"".equals(content)){
    24. //参数有误
    25. resp.getWriter().write("提交博客失败,缺少必要的参数");
    26. return;
    27. }
    28. //构造blog对象,把当前的信息填进去,并插入数据库中
    29. //此处要设置的属性主要是title,content,userId;而postTime和blogId都是系统自动生成的
    30. Blog blog=new Blog();
    31. blog.setTitle(title);
    32. blog.setContent(content);
    33. blog.setUserId(user.getUserId());//作者id就是当前提交这个博客的用户的身份信息
    34. BlogDao blogDao=new BlogDao();
    35. blogDao.insert(blog);
    36. //新增成功后重定向到列表页
    37. resp.sendRedirect("blog_list.html");
    38. }
    39. }

    ④客户端代码:

    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
    6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    7. <title>博客编辑页title>
    8. <link rel="stylesheet" href="css/common.css">
    9. <link rel="stylesheet" href="css/blog_edit.css">
    10. <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    11. <script src="jquery.js">script>
    12. <script src="editor.md/lib/marked.min.js">script>
    13. <script src="editor.md/lib/prettify.min.js">script>
    14. <script src="editor.md/editormd.js">script>
    15. head>
    16. <body>
    17. <div class="nav">
    18. <img src="./images/1.jpg" alt="">
    19. <span>我的博客系统span>
    20. <div class="spacer">div>
    21. <a href="blog_list.html">主页a>
    22. <a href="blog_edit.html">写博客a>
    23. <a href="logout">注销a>
    24. div>
    25. <div class="blog-edit-container">
    26. <form action="blog" method="post" style="height:100%">
    27. <div class="title">
    28. <input type="text" placeholder="在此处输入标题" name="title">
    29. <input type="submit" value="发布文章" id="submit">
    30. div>
    31. <div id="editor">
    32. <textarea name="content" style="display: none">textarea>
    33. div>
    34. form>
    35. div>
    36. <script>
    37. // 初始化编辑器
    38. let editor = editormd("editor", {
    39. // 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉.
    40. width: "100%",
    41. // 设定编辑器高度
    42. height: "calc(100% - 50px)",
    43. // 编辑器中的初始内容
    44. markdown: "# 在这里写下一篇博客",
    45. // 指定 editor.md 依赖的插件路径
    46. path: "editor.md/lib/",
    47. // 此处要加上一个重要的选项, 然后 editor.md 就会自动把用户在编辑器输入的内容同步保存到 隐藏的 textarea 中
    48. saveHTMLToTextarea: true,
    49. });
    50. script>
    51. body>
    52. html>

     结果显示:(当我们写一篇新的文章提交后,会自动跳转到博客列表,并且刷新当前内容)

    注意!!

    form表单的添加与修改,以及为了延续Markdown使用的textarea的调节。还有别忘了在将button修改后应该注意对应在css中同样进行修改,不然样式就会有所改变。 

    4.8实现删除博客功能

    ①需求:

    我们这里的删除博客,不考虑管理员删除,只考虑我们自己删除。因此,在博客详情页上我们就要进行判定,判定当前这个博客的作者,是否就是登录的用户,如果是,就在导航栏显示一个“删除按钮”,如果哦不是,就不显示删除按钮。而在博客详情页这里,其实既从服务器获取了当前用户的登录信息,又获取到了博客的作者信息,我们主要看这两个接口返回的用户信息是不是一样的。

    ②前后端交互:

     

    ③服务器代码:

    1. package controller;
    2. import model.Blog;
    3. import model.BlogDao;
    4. import model.User;
    5. import javax.servlet.ServletException;
    6. import javax.servlet.annotation.WebServlet;
    7. import javax.servlet.http.HttpServlet;
    8. import javax.servlet.http.HttpServletRequest;
    9. import javax.servlet.http.HttpServletResponse;
    10. import javax.servlet.http.HttpSession;
    11. import java.io.IOException;
    12. @WebServlet("/blogDelete")
    13. public class BlogDeleteServlet extends HttpServlet {
    14. @Override
    15. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    16. //1.检查用户是否登录
    17. HttpSession session=req.getSession();
    18. if (session==null){
    19. resp.setContentType("text/html;charset=utf8");
    20. resp.getWriter().write("当前尚未登录,不能删除");
    21. return;
    22. }
    23. User user= (User) session.getAttribute("user");
    24. if (user==null){
    25. resp.setContentType("text/html;charset=utf8");
    26. resp.getWriter().write("当前尚未登录,不能删除");
    27. return;
    28. }
    29. //2.获取到参数中的blogId
    30. String blogId=req.getParameter("blogId");
    31. if (blogId==null||"".equals(blogId)){
    32. resp.setContentType("text/html;charset=utf8");
    33. resp.getWriter().write("当前blog参数不对");
    34. return;
    35. }
    36. //3.获取要删除的博客信息
    37. BlogDao blogDao=new BlogDao();
    38. Blog blog=blogDao.selectOne(Integer.parseInt(blogId));
    39. if (blog==null){
    40. resp.setContentType("text/html;charset=utf8");
    41. resp.getWriter().write("当前要生成的博客不存在");
    42. return;
    43. }
    44. //4.当前用户是不是作者
    45. if (user.getUserId()!=blog.getUserId()){
    46. resp.setContentType("text/html;charset=utf8");
    47. resp.getWriter().write("当前登录的用户不是作者,没有权限");
    48. return;
    49. }
    50. //5.没有问题了,进行删除
    51. blogDao.delete(Integer.parseInt(blogId));
    52. //6.重定向到列表页
    53. resp.sendRedirect("blog_list.html");
    54. }
    55. }

    ④客户端代码:

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

    结果如下:

    当我们删了我是新文章后跳转到列表就没有这篇文章了。

    注意!!!:

    a.调用顺序,因为ajax是并发执行的,我们不知道谁会先执行完,我们就决定使用回调的方式来完成

     回调函数部分如下:

    5.小结

    这只是一个很简单的网站,可能很多地方没有考虑全面,仅供参考。详细代码,可以在页首查看我的码云地址。

    感谢观看嘿嘿嘿~ 

  • 相关阅读:
    ActiveMQ用法
    【操作系统】esp栈指针
    SwiftUI 为不同视图限制不同的屏幕旋转方向
    Android BottomSheetDialogFragment 使用详解,设置圆角、固定高度、默认全屏等
    使用Kubernetes部署Kubernetes集群
    【django+vue】项目搭建、解决跨域访问
    MySQL数据库
    深入探讨 Presto 中的缓存
    http(下)
    LeetCode 0952.按公因数计算最大组件大小:建图 / 并查集
  • 原文地址:https://blog.csdn.net/weixin_58850105/article/details/126907030