Servlet实现博客系统

博客系统

一。博客系统的基本情况:

1.四个页面:

(1)博客列表页,显示列表页上有哪些博客

(2)博客详情页,点击某个博客可以观看对应的详情页

(3)博客编辑页,用户可以在这里进行博客的编写

(4)登陆页

2。博客系统要实现的功能

(1)实现博客列表:让页面从服务器上拿到博客数据(数据库)

(2)实现博客详情页:点击想看的博客,让其从服务器上拿到完整的数据

(3)实现登陆的页面

(4)实现强制执行登陆的页面:当用户在列表页,详情页,编辑页的时候,发现用户没有登陆,那么就进行强制执行登陆,跳转到登陆页

(5)实现显示用户信息,从服务器上获取;博客列表页上要有用户的信息;博客详情页上要有作者的详细信息

(6)实现退出登陆

(7)实现发布博客

二。准备工作:

(1)创建项目,引入依赖,创建目录结构

依赖(servlet,mysql,Jackson)

目录结构:在main路径下创建一个webapp文件夹,在webapp这个文件夹中创建WEB-INF文件夹,在WEB-INF文件夹中创建web.xml,一般将前端的代码都放在webapp这个文件夹之中

(2)数据库的设计

Step1:找到实体:博客(blog表),用户(user表)

Step2:确定实体之间的关系:一个博客只能属于一个用户,一个用户可以创建多个博客;因此就要在博客表中引入一个userId这样的属性

blig表(blogid,title,content,postTime,userid)

user表(userid, username,password)

在main路径下创建一个db.sql的文件,这个文件中存放的是对数据库的操作(创库,创表,增数据...)

sql 复制代码
create database if not exits java666_blog_system charset=utf8;
use java666_blog_system;
drop table if exits blog;
drop table if exits user;
create table blog(blogid int primary key auto-increment,title varchar(1024),content varchar(4096),postTime datatime, userid int);
create table user(userid int primary key auto-increment,username varchar(50) unique,password varchar(50));
//插入一些数据,方便后面进行
insert into blog values(1,"第一篇博客","今天",now(),1);
insert into blog values(2,"第二篇博客","昨天",now(),1);
insert into blog values(3,"第三篇博客","前天",now(),1);
insert into user values(1,"zhangsan",'123');
insert into user values(2,'lisi','123');

(3)对数据库操作的代码进行封装

首先要在main文件夹中创建一个model文件夹,在model中创建一个文件叫做DBUtil.java,在这个文件中创建一个类(DBUtil类),通过这个类封装数据库建立连接的操作

由于接下来的很多servlet代码都要用到数据库,因此就要有单独的地方吧datasource这里的操作进行封装,而不是只放到某个servlet的init之中,此处就可以使用单例模式来表示,单例模式线程是不安全的,当前servlet本身就是在多线程环境下完成的,Tomcat收到多个请求的时候,就会用多线程的方式执行不同的servlet代码

java 复制代码
package model;

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class DBUtil{
    private volatile static DataSource dataSource=null;
    private static DataSource getDataSource(){
        if(dataSource==null){
            synchronized (DBUtil.class){
                if(dataSource==null){
                    dataSource=new MysqlDataSource();
                    ((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3301/java109_blog_system");
                    ((MysqlDataSource)dataSource).setUser("root");
                    ((MysqlDataSource)dataSource).setPassword("2222");
                }
            }
        }
        return dataSource;
    }
    public static Connection getConnection() throws SQLException {
        return getDataSource().getConnection();
    }
    public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet){
        try {
            resultSet.close();
            statement.close();
            resultSet.close();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

DBUtil完成了数据的创建和关闭的过程

下一步:创建实体类

每个表都要用一个专门的类来表示,表里的一条数据就会对应到这个类的一个对象,这样就把数据库中的数据和代码连接起来

在model文件夹中创建Blog.java文件

java 复制代码
public class Blog{
  private int blogid;
  private String title;
  private String content;
  private TimeStamp postTime;
  private int userid;
  //下面的getter和setter让idea自动生成一下
   public int getBlogId() {
        return blogId;
    }

    public void setBlogId(int blogId) {
        this.blogId = blogId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public Timestamp getPostTime() {
        return postTime;
    }

    public String getPostTime() {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return format.format(postTime);
    }

    public void setPostTime(Timestamp postTime) {
        this.postTime = postTime;
    }
}

同样在model文件夹中创建User.java文件

java 复制代码
public class User{
  private int userid;
  private String username;
  private String password;
  public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

下面就要创建两个类(BlogDao,UserDao),来完成针对博客表和用户表的增删查改操作

Dao:Data Access Object 数据访问对象,通过这个类的对象,来完成针对数据表的操作

通过BlogDao完成对Blog表的操作,在model文件夹中创建BlogDao.java文件

在这个BlogDao类中,要实现四种功能:(1)新增数据(提交博客时会用到)(2)查询博客列表(博客列表页),把数据库中所有的博客都拿到 (3)根据博客id查询指定的博客 (4)根据博客id删除博客

(1)新增数据:

java 复制代码
public void insert(Blog blog){
  Connection connection=DBUtil.getConnection();//建立连接
  PreparedStatement statement=null;
  try{
    String sql="insert into blog values(null,?,?,now(),?)";//构建sql
    statement=connection.preparedStatement(sql);
    statement.setString(1,blog.getTitle());
    statement.setString(2,blog.getContent());
    statement.setString(3,blog.getUserid());
    statement.executeUpdate();//执行sql
  }catch(SQLException){
    throw new RuntimeException();
  }finally{
    DBUtil.close(connection,statement,null);
  }
}

(2)查询博客列表:

java 复制代码
public List<Blog> SelectAll(){
  List<Blog> blogList=new ArrayList<>();
  Connection connection=null;
  PreparedStatement statement=null;
  ResultSet resultSet=null;
  //上面这么写是为了便于最后进行释放资源
  try{
    connection=DBUtil.getConnection();
    String sql="select * from blog";
    statement=connection.preparedStatement(sql);
    resultSet=statement.executeQuery();
    while(resultSet.next()){
      Blog blog=new Blog();
      blog.setBlogId(resultSet.getInt("blogid"));
      blog.setTitle(resultSet.getString("title"));
      //由于此处正文是整个文章的内容,太多了,因此在列表页中,只希望出现一小部分(摘要),对此content只需要进行一个小小的截断,这个截断长度是可以自定的
      String content=resultSet.getString("content");
      if(content.length()>100){
        content=content.subString(0,100)+"......";
      }
      blog.setContent(content);
      blog.setPostTime(result.setTimestamp("postTime"));
      blog.setUserid(result.setInt("userid"));
      blogList.add(blog);
    }
  }catch(SQLException e){
    throw new RuntimeException();
  }finally{
    DBUtil.close(connection,statement,resultSet);
  }
  return blogList;
}

(3).根据博客id查询指定的博客

java 复制代码
public Blog selectOne(int blogid){
  Connection connection=null;
  PreparedStatement statement=null;
  ResultSet resultset=null;
  try{
    connection=DBUtil.getConnection();
    String sql="select * from blog where blogid=?";
    statement=connection.preparedStatemnt(sql);
    statement.setInt(1,blogid);
    resultSet=statement.executeQuery();
    //由于这里的resultSet非0即1,所以不用使用while(此处拿着blogid进行查询,blogid是主键,是唯一的)
    if(resultSet.next()){
      Blog blog=new Blog();
      blog.setBlogId(resultSet.getInt("blogid"));
      blog.setTitle(resultSet.getString("title"));
      blog.setContent(resultSet.getString("content"));
      blog.setPostTime(resultSet.getTimestamp("postTime"));
      blog.setUserid(resultSet.getInt("userid"));
      return blog;
    }catch(SQLException e){
      throw new RuntimeException();
    }finally{
      DBUtil.close(connection,statement,resultSet);
    }
    return null;
  }
}

//4.根据博客id删除博客

java 复制代码
public void delete(int blogid){
  Connection connection=null;
  PreparedStatement statement=null;
  try{
    connection=DBUtil.getConnection();
    String sql="delete from blog where blogid = ?";
    statement=connection.preparedStatement(sql);
    statement.setInt(1,blogid);
    statement.executeUpdate();
  }catch (SQLException e) {
      e.printStackTrace();
  } finally {
      DBUtil.close(connection, statement, null);
  }
}

通过UserDao完成User表的操作:

UserDao要实现的操作:(1)根据userid查询到对应的用户信息(获取用户信息) (2)根据username来查询到对应的用户信息(登陆)

(1)根据userid查询对应的用户信息

java 复制代码
public User getUserById(int userid){
  Connection connection=null;
  PreparedStatement statement=null;
  ResultSet resultSet=null;
  try{
    connection=DBUtil.getConnection();
    String sql="select * from user where userid = ?";
    statement=connection.PreparedStaement(sql);
    statement.setInt(1,userid);
    resultSet=statement.executeQuery();
    if(resultSet.next()){
      User user=new User();
      user.setUserid(resultSet.getInt("userid"));
      user.setUserName(resultSet.getString("useName"));
      user.setUserPassword(resultSet.getString("password"));
      return user;
    }catch(SQLException e){
      throw new RuntimeException();
    }finally{
      DBUtil.close(connection,statement,resultSet);
    }
    return null;
  }
}

(2)根据username来查询到对应的用户信息(登陆)

java 复制代码
public User getUserByUserName(String username){
  Connection connection=null;
  PreparedStatement Statement=null;
  ResultSet resultset=null;
  try{
    connection=DBUtil.getConnection();
    String sql="select * from user where username = ?";
    statement=connection.preparedStatement(sql);
    statement.setUserName(1,username);
    resultSet=statement.executeQuery();
    if(resultSet.next()){
      User user=new User();
      user.setUserid(resultSet.getInt("userid"));
      user.setUserName(resultSet.getString("userName"));
      user.setUserPassword(resultSet.getString("userPassword"));
      return user;
    }catch(SQLException e){
      throw new RuntimeException();
    }finally{
      DBUtil.close(connection,statement,resultSet);
    }
    return null;
  }
}

以上就是所有的准备工作,下面就要进行前后端交互的代码,从而实现四个页面和7个功能

三。前后端交互:

1.获取博客列表页

在博客列表加载的时候,发送ajax给服务器发送请求,从服务器(数据库)中拿到博客列表数据,并显示到页面上

(1)约定前后端交互接口:

请求:GET /blog

响应:HTTP/1.1 200 ok

​ Content---Type:application/json

​ Body:

[
  {
    blogid=1,
    title="这是标题",
    content="这时文章",
    postTime="......",
    userid=1
  }
]

(2)编写请求代码(前端)

让浏览器给服务器发送请求,在前端中新加入script标签(注:前端代码已经把部分写好了,没有写与前后端交互的部分,其他部分见博客最后)

html 复制代码
<script>
  function getblogs(){//把ajax封装到函数之中,js中定义函数,使用function关键字,不需要返回值类型
    $.ajax({
      type:'get',
      url:'blog',
      success:function(body){
        //.....当服务器成功响应之后,调用的回调函数,根据返回的响应数据,构造页面片段
      }
    })
    grtblogs();//定义完函数一定要在最后面调用一下
  }
</script>
  

(3)让服务器处理请求,返回响应数据(查询数据库)

java 复制代码
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
  private ObjectMapper objectMapper = new ObjectMapper();
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    BlogDao blogDao=new BlogDao();
    List<Blog> blogs=blogDao.selectAll();
    String respJson=objectMapper.writeValueAsString(blogs);
    resp.setContentType("application/json; charset=utf8");
    resp.getWritter().write(respjson);
  } 
}

(4)让前端代码处理上述响应数据

构造html片段,显示到页面上,这里构造的过程还是之前的api

querySelector:获取页面已有的元素

createElement:创建新的元素

.innerHtml:设置元素里的内容

.className:设置元素的class属性

appendChild:把这个元素加到另一个元素的末尾

由这个例子就可以大概知道下面要怎么构造这个响应

html 复制代码
<!-- 右侧信息 -->
        <div class="container-right">
            <!-- 这个 div 表示一个 博客  -->
            <!-- <div class="blog">
                <div class="title">我的第一篇博客博客博客博客</div>
                <div class="date">2023-05-11 20:00:00</div>
                <div class="desc">
                    从今天起, 我要认真敲代码. 
                </div>
                <a href="blog_detail.html?blogId=1">查看全文 &gt;&gt; </a>
            </div> -->
        </div>
javascript 复制代码
success:function(body){
  let containerDiv=document.querySelector('container-right');//这句话后面有解释
  for(int i=0;i<body.length;i++){
    let blog=body[i];//blog就是形如{blogid='1',title='xxx',......}
    let blogDiv=document.createElement('div');//构造整个博客
    blogdiv.className='blog';
    
    let titleDiv=document.createElement('div')//构建标题
    titleDiv.className='title';
    titleDiv.innerHTML=blog.title;
    
    let dateDiv=document.createElement('div')
    dateDiv.className='date';
    dateDiv.innerHTML=blog.postTime;
    
    let descDiv=document.createElement('div');
    descDiv.className='desc';
    descDiv.innerHTML=blog.content;
    
    let a=document.createElement('a');
    a.innerHTML="查看全文";
    a.href="blog_detil.html?blogid="+blog.blogid;//点击不同的博客跳转到不同的博客详情页
    //上面的操作完成了每个零件的创建,还要往往一起进行组装
    blogDiv.appendChild(TitleDiv);
    blogDiv.appendChild(dateDiv);
    blogDiv.appendChild(descDiv);
    blogDiv.appendChild(a);
    //最后要在循环外边获取container-right,所以加上let containerDiv=document.querySelector('container-right');
    containerDiv.appendChild(blogDiv);
  }
}

通过运行就可以发现存在两个问题:

(1)页面显示的postTime是一个时间戳,我们无法看懂,所以要把这里的上次登陆时间转换成人们能看懂的形式

解决方法:要在Blog类中找到PostTime的getter方法,由于标准库提供了一个SimpleDateFormat类,完成时间戳到格式化的转换(这个不用背,用的时候查一下就行)

java 复制代码
public String getPostTime() {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return format.format(postTime);
}

(2)顺序问题:

正常来说,咱们希望新的博客在上面,老的博客在下面

解决办法,在BlogDao类中找到getBlogs方法,然后改sql语句就行

java 复制代码
String sql="select * from blog order by postTime desc";

2.博客详情页:

点不同的博客跳转过去都会带有不同的blogid的queryString,后续在博客详情页中,就可以给服务器发ajax请求,根据这里的blogid查询数据库,博客的具体内容再返回,前端还是把得到的数据构造到页面上

点击查看全文就会跳转到这个网址:127.0.0.1:8080/blog_system/blog_detail.html?blogid=4

(1)约定前后端接口:

请求:GET/blog?blogid=1

响应:HTTP/1.1 200 ok

​ Content_Type: application/json

{
  blogid:1,
  title:"这时一篇博客",
  content:"这时博客正文",
  postTime:"20231-11-07 10:20:00",
  userid:1
}

(2)让前端代码通过ajax发起请求

在前端代码中找到blog_detail.html文件,在这个文件中添加script标签

html 复制代码
<script>
  function getBlog(){
    $.ajax({
      type:'get',
      url:'blog'+location.search
      success:function(body){
         //当服务器成功传回响应
      }
    })
  }
  get getBlog();
</script>

此处有个问题,发起ajax请求时要带有blogid,blogid当前处于博客详情页的url之中,这里通过loaction.search来获取到url中的queryString

通过观察,我们可以发现博客列表页和博客详情页可以通过是否有queryString进行区分,博客列表页没有queryString,博客详情页有queryString,因此可以把这两个需求写在一个servlet之中,通过是否包含queryString进行判断进行哪个逻辑代码

(3)后端接收处理请求,并给出响应

java 复制代码
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      //从queryString中查询一下是否有内容,如果有就查指定博客,如果没有就查询所以博客
      BlogDao blogDao=new BlogDao();
      String blogid=req.getParameter("blogid");
      if(blogid==null){
        List<Blog> blogs=blogDao.selectAll();
        //以json的形式返回给客户端
        String respJson=objectMapper.writeValueAsString(blogs);
        resp.setContentType("application/json;charset=utf8");
        resp.getWritter.write(respJson);
      }else{
        Blog blog=blogDao.selectOne(Integer.parseInt(blogId));
        //以json的形式返回给客户端
        String respJson=objectMapper.writeValueAsString(blog);
        resp.setContentType("application/json;charset=utf8");
        resp.getWritter.write(respJson);
      }
    }
}

上述代码就将博客列表页和博客详情页进行了结合,让他们放在一个servlet的doget请求之中,通过是否包含queryString进行判断进行哪个代码逻辑

(4)前端拿到响应之后,把响应数据构造成html片段

写之前,blog_detail.html文件中给了页面的html代码

html 复制代码
<!-- 右侧信息 -->
        <div class="container-right">
            <h3></h3>
            <div class="date"></div>
            <div id="content">
            </div>
        </div>

下面就要在上面图片中已经写好的html代码中加入要填入的东西,但是不能直接填,要让代码变得灵活,实现代码的复用

querySelector是获取页面已有元素

java 复制代码
success function(blog){//因为后端返回的就是blog,所以这里的body可以写成blog
  let h3=document.querySelector('.container-right h3');//不是div标签,前面要加点,后面h3不用加点
  h3.innerHTML=blog.Title;
  let dateDiv=document.querySelector('container-right .date')//是div标签,前面不要加点,后面date要加点
  dadteDiv.innerHTML=blog.PostTime;
  let contentDiv=document.querySelector('.container-right .content');
  contentDiv.innerHTML=blog.content;
}

此时经过运行就会出现问题:

(1)刷新发现内容没变,和上一次没改的内容一样,这是因为浏览器缓存的原因,只需要按ctrl+f5进行强制刷新即可

(2)显示效果是markdown的原始情况,但是我们希望得到的是mackdown渲染之后的情景

Step1:确保引入了jquery依赖(在前端)

html 复制代码
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>

Step2:引入editor.md依赖(在前端)

html 复制代码
<link rel="stylesheet" href="editor.md/css/editormd.min.css" />
<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>

Step3:进行渲染

editormd.markdownToHTML('content',{markdown:blog.content});

这里的editormd是库里给的一个全局变量,把依赖正确饮用,这个变量就可以使用

小括号里的第一个是一个html标签,可以有很多属性,class属性往往是用来和css样式配合使用的,id属性则是一个身份标识,要求一个页面上,id必须唯一

小括号第二个是要渲染的对象,这里是要对content正文进行渲染,所以对象就是content

3.实现登陆:

在登陆页面,在输入框中填写用户名和密码,点击登陆,就会给服务器发起HTTP请求(可以使用ajax,也可使用form)

服务器处理登陆请求,读取用户名密码,在数据库中查询,匹配如果成功创建会话,跳转到博客列表页

(1)约定前后端交互接口

请求:POST/login

content-Type:application/www-form-urlencoded

username=zhangsan %ipassword=lisi

响应:HTTP/1.1 302

Location: blog_list.html

(2)让前端发送请求(login.html)

html 复制代码
<!-- 登录页的版心 -->
    <div class="login-container">
        <!-- 登录对话框 -->
        <div class="login-dialog">
            <h3>登录</h3>
            <!-- 使用 form 包裹一下下列内容, 便于后续给服务器提交数据 -->
            <form action="login" method="post">
                <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>
            </form>
        </div>
    </div>

(3)让服务器处理请求并返回响应,新建一个loginServlet文件

java 复制代码
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      req.setCharacterEncoding("utf8");
      // 1. 从请求中, 获取到用户名和密码.
      String username=req.getParameter("username");
      String password=req.getParameter("password");
      //2.读取数据库,看看这里的用户名和密码是否和数据库中的匹配
      UserDao userDao=new UserDao();
      User user=userDao.selectUserByName(username);
      if(user==null){
        String html="<h3>用户名或密码错误! </h3>";
        resp.setContentType("test/html;charset=utf8");
        resp.getWriter().write(html);
        return;
      }
      if(!password.equals(user.getPassword())){
        String html = "<h3>用户名或密码错误! </h3>";
        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write(html);
        return;
      }
      //用户名和密码都正确,此时要创建一个对话
      HttpSession session=req.getSession(true);
      //此时要把用户对象放入一个回话之中,下次访问其他对象就可以直接进入
      session.setAttribute("user",user);
      //4.返回一个重定向响应,能跳转到博客列表页
      resp.sendRedirect("blog_list.html");
    }
}

4.强制要求登陆:

在博客列表页,详情页,编辑页,判定当前用户是否登陆,如果没有登陆,则强制跳转到登陆页

在这几个页面中,在页面加载是,给服务器发起ajax请求,从服务器上获取登陆状态

(1)约定前后端交互接口

GET/login

登陆成功HTTP/1.1 200

登陆失败HTTP/1.1 403

也有一种方法来区分成功和失败,都返回200,但是两者的body不同

(2)前端部分

在blog_list.html文件中的script标签中加上一下代码

html 复制代码
function getLoginSatus(){
  $.ajax({
    type:'get',
    url:'login',
    success:function(body){
      console.log("已经登陆了");
    }
    error:function(body){
      location.assign('login.html');//前端页面跳转的写法
    }
  })
}
getLoginStatus();

一个页面通常会触发多个ajax,这些ajax之间是并发执行的,彼此之间互不干预

这里不使用后端的跳转是因为只有判断为没登陆的时候才能触发error,就是因为error了,后端无法发送响应,才需要前端去进行跳转

(3)后端部分处理请求

找到后端代码中的loginServlet.java文件,加入doget方法

java 复制代码
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  HttpSession session=req.getSession(false);//如果有session就直接使用,如果没有session就为空,不创建
  if(session==null){
    resp.setStatus(403);
    return;
  }
  User user=(User)session.getAttribute("user");
  if(user==null){
    resp.setStatus(403);
    return;
  }
  resp.setStatus(200);
}

上述这些代码是用在博客列表页中的,同样的道理,只需把前端代码复制粘贴进详情页和编辑页中的前端script标签之中即可

5.显示用户信息

博客列表页:显示当前登陆的用户信息

博客详情页:显示当前文章作者的信息

在页面加载的时候给服务器发ajax请求,服务器返回对应的用户数据,根据发起请求不同的页面,服务器返回不同的信息即可

(1)约定前后端接口

博客列表页:获取当前登陆的用户信息

请求:GET/userInfo

响应:HTTP/1.1 200 ok application/json

{
   userid:1
   username:'zhangsan'
}

博客详情页:获取当前博客的作者信息

请求:GET/authorInfo?blogid=1

响应:HTTP/1.1 200 ok application/json

{
   userid:1
   username:'zhangsan'
}

(2)博客列表页的前后端代码

在blog_list.html文件的script标签中加入一下代码

javascript 复制代码
function getUserInfo(){
  $.ajax({
    type:'get',
    url:'userInfo',
    success:function(body){
      //.....
    }
  })
}

后端代码:

java 复制代码
@WebServlet("/userInfo")
public class UserInfoServlet etends HttpServlet{
  protected void doGet(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;
    }
    resp.setContentType("application/json;charset=utf8");//把user对象转换成json,并返回给浏览器
    user.setPassword("");//这么写的目的是user中有password,如果把password传给客户很不安全,所以设置成空
    string respJson=objectMapper.writeValueAsString(user);
    resp.getWriter.write(respJson);
  }
}

前端代码处理响应:

把响应的数据放到对应页面位置上

javascript 复制代码
success:function(body){
  let h3=document.querySelector('.card h3');//把拿到的响应数据,取出其中usename,设置到页面的h3标签之中
  h3.innerHTML=user.username;
}

(3)博客详情页的前后端代码

在blog_detail.html文件中写入一下内容

javascript 复制代码
function getAuthorInfo(){
  $.ajax({
    type:'get',
    url:'getAuthorInfo'+location.search
    success:function(user){
      //.....
    }
  })
}

后端代码:

创建AuthorInfoServlet文件

java 复制代码
@WebServlet("/getAuthorInfo")
public class AuthorInfoServlet etends HttpServlet{
  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;
    }
    BlogDao blogDao=new BlogDao();//在blog表中查询对应的blog对象
    Blog blog=blogDao.getBlog(Integer.parseInt(blogid));
    if(blog==null){
      resp.setContentType("text/html; charset=utf8");
      resp.getWriter().write("blogid没找到");
      return;
    }
    UserDao userDao=new UserDao();
    User user=userDao.getUserByid(blog.getuserid());
    if(user==null){
      resp.setContentType("text/html; charset=utf8");
      resp.getWriter().write("userid没找到");
      return;
    }
    //把这个对象返回给浏览器
    user.setPassword("");
    String respJson=objectMappe.writeValueAsString(user);
    resp.setContentType("application/json;charset=utf8");
    resp.getWriter().write(respJson);
  }

原理就是先从请求中获取到blogid,然后用blogid找到对应的blog对象,然后拿到blog对象中的userid,根据这个userid获取到user表中的对象,最后作为响应传给前端

前端处理响应:

javascript 复制代码
success:function(user){
  let h3=document.querySelector('.card h3');
  h3.innerHTML=user.username;
}

注意:在博客列表页的前端代码中,使用了appendChild,在详情页以及之后的功能就没有使用过,主要是因为要看处理响应的代码中是否要创建html标签,如果不创建html标签,那么就用上面这个方式,把东西传到标签之中,如果要创建html标签,那么就要使用appendChild的方式进行写前端代码

6.退出登陆:

博客列表页,博客详情页,博客编辑页导航栏中都有一个"注销"的按钮

让用户点击"注销"的时候,就会发送一个HTTP请求(GET请求),服务器收到这个GET请求的时候,就会把user这个Attribute删除,由于判断用户是否登陆的代码的逻辑中,同时要判断session是否存在,和user Attribute是否存在,所以只要破坏一个,那么就会让登陆状态发生改变

那为什么不直接删除session本身呢?

主要是因为servlet没提供删除session的方法,session提供了removeAttribute这样的方法,就可以把user这个Attribute给删除了

(1)约定前后端交互接口

请求:GET/logout

响应:直接重定向到登陆页 HTTP/1.1 302 Location:login.html

(2)编写前端,发送请求:

不用写ajax,直接给a标签设置href属性即可,博客列表页,博客详情页,博客编辑页都要加上这个标签

html 复制代码
<a href="logout">注销</a>

(3)后端代码处理请求,给出响应

创建logout文件

java 复制代码
@WebServlet("/logout")
public class LogoutServlet etends HttpServlet{
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
    HttpSession session=req.getSession(false);
    if(session==null){
      resp.setContentType("text/html;charset=utf8");
      resp.getWriter.write("当前尚未登陆");
    }
    session.removeAttribute("user");//把会话中的user属性删除
    resp.sendRedirect("login.html");
  }

四。原初的前端代码:

(1)blog_detail.html

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客详情页</title>
    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/blog_detail.css">
</head>
<body>
    <!-- 导航栏. nav 是 导航 这个词的缩写 -->
    <div class="nav">
        <!-- logo -->
        <img src="image/logo2.jpg" alt="">
        <div class="title">我的博客系统</div>
        <!-- 只是一个空白, 用来把后面的链接挤过去 -->
        <!-- 这是一个简单粗暴的写法~~ -->
        <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="container-left">
            <!-- 这个 div 表示整个用户信息的区域 -->
            <div class="card">
                <!-- 用户的头像 -->
                <img src="image/kun.jpg" alt="">
                <!-- 用户名 -->
                <h3>比特汤老湿</h3>
                <!-- github 地址 -->
                <a href="https://www.github.com">github 地址</a>
                <!-- 统计信息 -->
                <div class="counter">
                    <span>文章</span>
                    <span>分类</span>
                </div>
                <div class="counter">
                    <span>2</span>
                    <span>1</span>
                </div>
            </div>
        </div>

        <!-- 右侧信息 -->
        <div class="container-right">
            <h3>这是我的第一篇博客</h3>
            <div class="date">2023-05-11 20:00:00</div>
            <div class="content">
                <p>从今天开始我要认真写代码, Lorem ipsum dolor sit amet consectetur adipisicing elit. Ducimus quas soluta ipsam, molestiae totam accusamus minus beatae nam quo suscipit doloribus corporis eius alias ab minima impedit ullam? Optio, magni.</p>
                <p>从今天开始我要认真写代码, Lorem ipsum dolor sit amet consectetur adipisicing elit. Ducimus quas soluta ipsam, molestiae totam accusamus minus beatae nam quo suscipit doloribus corporis eius alias ab minima impedit ullam? Optio, magni.</p>
                <p>从今天开始我要认真写代码, Lorem ipsum dolor sit amet consectetur adipisicing elit. Ducimus quas soluta ipsam, molestiae totam accusamus minus beatae nam quo suscipit doloribus corporis eius alias ab minima impedit ullam? Optio, magni.</p>
                <p>从今天开始我要认真写代码, Lorem ipsum dolor sit amet consectetur adipisicing elit. Ducimus quas soluta ipsam, molestiae totam accusamus minus beatae nam quo suscipit doloribus corporis eius alias ab minima impedit ullam? Optio, magni.</p>
            </div>
        </div>
    </div>

</body>
</html>

(2)blog_edit.html

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客编辑页</title>
    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/blog_edit.css">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
    <!-- 引入 editor.md 的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    <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>
    <!-- 导航栏. nav 是 导航 这个词的缩写 -->
    <div class="nav">
        <!-- logo -->
        <img src="image/logo2.jpg" alt="">
        <div class="title">我的博客系统</div>
        <!-- 只是一个空白, 用来把后面的链接挤过去 -->
        <!-- 这是一个简单粗暴的写法~~ -->
        <div class="spacer"></div>
        <a href="blog_list.html">主页</a>
        <a href="blog_edit.html">写博客</a>
        <!-- 这里的地址回头再说 -->
        <a href="">注销</a>
    </div>

    <!-- 博客编辑页的版心 -->
    <div class="blog-edit-container">
        <form action="">
            <!-- 标题编辑区 -->
            <div class="title">
                <input type="text" id="title-input">
                <input type="submit" id="submit">
            </div>
            <!-- 博客编辑器 -->
            <!-- 把 md 编辑器放到这个 div 中 -->
            <div id="editor">

            </div>
        </form>
    </div>

    <script>
        var editor = editormd("editor", {
            // 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉. 
            width: "100%",
            // 设定编辑器高度
            height: "calc(100% - 50px)",
            // 编辑器中的初始内容
            markdown: "# 在这里写下一篇博客",
            // 指定 editor.md 依赖的插件路径
            path: "editor.md/lib/"
        });
    </script>
</body>
</html>

(3)blog_list.html

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客列表页</title>

    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/blog_list.css">
</head>
<body>
    <!-- 导航栏. nav 是 导航 这个词的缩写 -->
    <div class="nav">
        <!-- logo -->
        <img src="image/logo2.jpg" alt="">
        <div class="title">我的博客系统</div>
        <!-- 只是一个空白, 用来把后面的链接挤过去 -->
        <!-- 这是一个简单粗暴的写法~~ -->
        <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="container-left">
            <!-- 这个 div 表示整个用户信息的区域 -->
            <div class="card">
                <!-- 用户的头像 -->
                <img src="image/kun.jpg" alt="">
                <!-- 用户名 -->
                <h3>比特汤老湿</h3>
                <!-- github 地址 -->
                <a href="https://www.github.com">github 地址</a>
                <!-- 统计信息 -->
                <div class="counter">
                    <span>文章</span>
                    <span>分类</span>
                </div>
                <div class="counter">
                    <span>2</span>
                    <span>1</span>
                </div>
            </div>
        </div>
        <!-- 右侧信息 -->
        <div class="container-right">
            <!-- 这个 div 表示一个 博客  -->
            <div class="blog">
                <!-- 博客标题 -->
                <div class="title">我的第一篇博客博客博客博客</div>
                <!-- 博客的发布时间 -->
                <div class="date">2023-05-11 20:00:00</div>
                <!-- 博客的摘要-->
                <div class="desc">
                    <!-- 使用 lorem 生成一段随机的字符串 -->
                    从今天起, 我要认真敲代码. Lorem ipsum dolor sit amet consectetur, adipisicing elit. Debitis repellendus voluptatum, reiciendis rem consectetur incidunt aspernatur eveniet excepturi magni quis sint, provident est at et pariatur dolorem aliquid fugit voluptatem.
                </div>
                <!-- html 中不能直接写 大于号, 大于号可能会被当成标签的一部分 -->
                <a href="blog_detail.html?blogId=1">查看全文 &gt;&gt; </a>
            </div>
            <div class="blog">
                <!-- 博客标题 -->
                <div class="title">我的第一篇博客</div>
                <!-- 博客的发布时间 -->
                <div class="date">2023-05-11 20:00:00</div>
                <!-- 博客的摘要-->
                <div class="desc">
                    <!-- 使用 lorem 生成一段随机的字符串 -->
                    从今天起, 我要认真敲代码. Lorem ipsum dolor sit amet consectetur, adipisicing elit. Debitis repellendus voluptatum, reiciendis rem consectetur incidunt aspernatur eveniet excepturi magni quis sint, provident est at et pariatur dolorem aliquid fugit voluptatem.
                </div>
                <!-- html 中不能直接写 大于号, 大于号可能会被当成标签的一部分 -->
                <a href="blog_detail.html?blogId=1">查看全文 &gt;&gt; </a>
            </div>
            <div class="blog">
                <!-- 博客标题 -->
                <div class="title">我的第一篇博客</div>
                <!-- 博客的发布时间 -->
                <div class="date">2023-05-11 20:00:00</div>
                <!-- 博客的摘要-->
                <div class="desc">
                    <!-- 使用 lorem 生成一段随机的字符串 -->
                    从今天起, 我要认真敲代码. Lorem ipsum dolor sit amet consectetur, adipisicing elit. Debitis repellendus voluptatum, reiciendis rem consectetur incidunt aspernatur eveniet excepturi magni quis sint, provident est at et pariatur dolorem aliquid fugit voluptatem.
                </div>
                <!-- html 中不能直接写 大于号, 大于号可能会被当成标签的一部分 -->
                <a href="blog_detail.html?blogId=1">查看全文 &gt;&gt; </a>
            </div>
        </div>
    </div>
</body>
</html>

(4)login.html

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客登录页</title>
    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/login.css">
</head>
<body>
    <!-- 导航栏. nav 是 导航 这个词的缩写 -->
    <div class="nav">
        <!-- logo -->
        <img src="image/logo2.jpg" alt="">
        <div class="title">我的博客系统</div>
        <!-- 只是一个空白, 用来把后面的链接挤过去 -->
        <!-- 这是一个简单粗暴的写法~~ -->
        <div class="spacer"></div>
        <a href="blog_list.html">主页</a>
        <a href="blog_edit.html">写博客</a>
    </div>

    <!-- 登录页的版心 -->
    <div class="login-container">
        <!-- 登录对话框 -->
        <div class="login-dialog">
            <h3>登录</h3>
            <!-- 使用 form 包裹一下下列内容, 便于后续给服务器提交数据 -->
            <form action="">
                <div class="row">
                    <span>用户名</span>
                    <input type="text" id="username">
                </div>
                <div class="row">
                    <span>密码</span>
                    <input type="password" id="password">
                </div>
                <div class="row">
                    <input type="submit" id="submit" value="登录">
                </div>
            </form>
        </div>
    </div>
</body>
</html>
相关推荐
阿龟在奔跑38 分钟前
引用类型的局部变量线程安全问题分析——以多线程对方法局部变量List类型对象实例的add、remove操作为例
java·jvm·安全·list
飞滕人生TYF40 分钟前
m个数 生成n个数的所有组合 详解
java·递归
代码小鑫1 小时前
A043-基于Spring Boot的秒杀系统设计与实现
java·开发语言·数据库·spring boot·后端·spring·毕业设计
真心喜欢你吖1 小时前
SpringBoot与MongoDB深度整合及应用案例
java·spring boot·后端·mongodb·spring
激流丶1 小时前
【Kafka 实战】Kafka 如何保证消息的顺序性?
java·后端·kafka
周全全1 小时前
Spring Boot + Vue 基于 RSA 的用户身份认证加密机制实现
java·vue.js·spring boot·安全·php
uzong2 小时前
一个 IDEA 老鸟的 DEBUG 私货之多线程调试
java·后端
AiFlutter2 小时前
Java实现简单的搜索引擎
java·搜索引擎·mybatis
飞升不如收破烂~2 小时前
Spring boot常用注解和作用
java·spring boot·后端
秦老师Q2 小时前
Java基础第九章-Java集合框架(超详细)!!!
java·开发语言