天天看點

Lesson12:部落格系統(基于模闆引擎)一、準備工作二、資料庫三、Thymeleaf的相關操作四、實作登入頁面、強制登入功能和注冊頁面五、實作部落格詳情頁六、部落格删除頁七、實作部落格編輯頁八、實作部落格修改頁九、實作上傳頭像功能十、實作問題回答頁十一、實作登出功能 十二、實作部落格清單頁

目錄

一、準備工作

1.1 基本環境的搭建

1.2 對部落格系統的分析

二、資料庫

2.1 資料庫設計

2.2 封裝資料庫相關操作并驗證

2.2.1 建立相關的類

2.2.2 封裝資料庫的相關操作

三、Thymeleaf的相關操作

3.1 Thymeleaf初始化的優化

3.2 編寫模闆檔案

四、實作登入頁面、強制登入功能和注冊頁面

4.1 實作登入頁面

4.2 實作強制登入功能

4.3 實作注冊頁面

五、實作部落格詳情頁

5.1 部落格詳情展示

5.2 評論的顯示

5.3 評論功能

5.4 提問功能

5.5 問題删除功能

5.6 注意

⑤如何将頁面按照markdown的形式顯示。

5.7 前端代碼

六、部落格删除頁

七、實作部落格編輯頁

7.1 前端頁面的設計

7.2 後端實作邏輯

八、實作部落格修改頁

九、實作上傳頭像功能

十、實作問題回答頁

10.1 前端頁面設計

10.2 後端設計

十一、實作登出功能

 十二、實作部落格清單頁

12.1 實作流程

12.2 生成模闆

12.3 部落格的内容太多,超過設定的背景

12.4 完成Servlet的代碼,實作整個頁面的渲染

一、準備工作

1.1 基本環境的搭建

主要分為7個步驟:

建立maven項目、引入依賴、建立目錄結構、寫一個測試代碼、打包、部署、驗證

具體操作見部落格Lesson9:Tomcat 和 Servlet(基礎篇)_劉減減的部落格-CSDN部落格中的第三小節。

1.2 對部落格系統的分析

主要包含三個部分的邏輯:

①處理資料庫的部分 M(Model)

②處理前端請求的部分 C(controller)

③前端展示的細節部分 V(View)

這三個部分稱為MVC,是一個通用的概念,可以使用該架構開發web程式。

二、資料庫

2.1 資料庫設計

主要包含四個部分的資料:部落格資料、使用者資料、評論資料、問題資料

Lesson12:部落格系統(基于模闆引擎)一、準備工作二、資料庫三、Thymeleaf的相關操作四、實作登入頁面、強制登入功能和注冊頁面五、實作部落格詳情頁六、部落格删除頁七、實作部落格編輯頁八、實作部落格修改頁九、實作上傳頭像功能十、實作問題回答頁十一、實作登出功能 十二、實作部落格清單頁
Lesson12:部落格系統(基于模闆引擎)一、準備工作二、資料庫三、Thymeleaf的相關操作四、實作登入頁面、強制登入功能和注冊頁面五、實作部落格詳情頁六、部落格删除頁七、實作部落格編輯頁八、實作部落格修改頁九、實作上傳頭像功能十、實作問題回答頁十一、實作登出功能 十二、實作部落格清單頁
Lesson12:部落格系統(基于模闆引擎)一、準備工作二、資料庫三、Thymeleaf的相關操作四、實作登入頁面、強制登入功能和注冊頁面五、實作部落格詳情頁六、部落格删除頁七、實作部落格編輯頁八、實作部落格修改頁九、實作上傳頭像功能十、實作問題回答頁十一、實作登出功能 十二、實作部落格清單頁
Lesson12:部落格系統(基于模闆引擎)一、準備工作二、資料庫三、Thymeleaf的相關操作四、實作登入頁面、強制登入功能和注冊頁面五、實作部落格詳情頁六、部落格删除頁七、實作部落格編輯頁八、實作部落格修改頁九、實作上傳頭像功能十、實作問題回答頁十一、實作登出功能 十二、實作部落格清單頁

2.2 封裝資料庫相關操作并驗證

2.2.1 建立相關的類

分别建立Blog、User、Comment、Question四個類,類中的成員變量和資料庫中保持一緻,生成成員變量的get、set和toString方法。以建立Blog類為例:

public class Blog {
    private int blogId;
    private String blogTitle;
    private String blogContent;
    private Timestamp blogTime;
    private int blogAuthor;
    private int visitCount;
    private int commentCount;

    public int getVisitCount() {
        return visitCount;
    }

    public void setVisitCount(int visitCount) {
        this.visitCount = visitCount;
    }

    public int getCommentCount() {
        return commentCount;
    }

    public void setCommentCount(int commentCount) {
        this.commentCount = commentCount;
    }

    public int getBlogId() {
        return blogId;
    }

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

    public String getBlogTitle() {
        return blogTitle;
    }

    public void setBlogTitle(String blogTitle) {
        this.blogTitle = blogTitle;
    }


    public String getBlogContent() {
        return blogContent;
    }

    public void setBlogContent(String blogContent) {
        this.blogContent = blogContent;
    }

    public Timestamp getBlogTime() {
        return blogTime;
    }

    public void setBlogTime(Timestamp blogTime) {
        this.blogTime = blogTime;
    }

    public int getBlogAuthor() {
        return blogAuthor;
    }

    public void setBlogAuthor(int blogAuthor) {
        this.blogAuthor = blogAuthor;
    }

    @Override
    public String toString() {
        return "Blog{" +
                "blogId=" + blogId +
                ", blogTitle='" + blogTitle + '\'' +
                ", blogContent='" + blogContent + '\'' +
                ", blogTime=" + blogTime +
                ", blogAuthor=" + blogAuthor +
                ", visitCount=" + visitCount +
                ", commentCount=" + commentCount +
                '}';
    }
}
           

2.2.2 封裝資料庫的相關操作

DBUtil:封裝資料庫的連接配接

BlogDao:實作對部落格的增删改查。

        insert方法:增加一個部落格

        delete方法:删除一個部落格

        selectOne方法:根據部落格Id查找部落格。使用者部落格詳情頁。

        selectAll方法:查找所有的部落格。使用者部落格清單頁的展示。

        updateBlog方法:修改部落格的内容和标題。用于部落格修改頁。

        updateVisistCount:更新部落格的通路次數。在使用者的資訊卡片上展示。

        updateCommentCount:更新部落格的評論次數。在使用者的資訊卡片上展示。

        selectVisitAllCount:根據使用者Id查找使用者的總通路次數。在使用者的資訊卡片上展示。

        selectVisitCount:根據部落格Id查找部落格的總通路次數。在使用者的資訊卡片上展示。

        main方法:測試以上方法是否正确

UserDao:實作使用者資訊的增删改查

        login:注冊。注意在注冊十,隻需要輸入使用者名和密碼,使用者的其他屬性可以通過其他方法增加。

        getBlogCount:根據使用者Id,得到使用者總的部落格數量。使用者資訊卡片的展示會用到。

        selectByName:根據使用者名 查找使用者。登入時使用的是使用者名密碼登入,是以需要實作該功能。

        uploadImages:上傳頭像。使用者可以修改自己的頭像。

        changeBlogCount:修改使用者的部落格數量。新增一篇部落格時會用到該方法。

        deleteUser:删除使用者。

        main方法:測試以上方法是否正确

CommentDao:實作評論的增加、删除和選擇。

        insert:新增一個評論。

        delete:删除一個評論。

        selelctAll:查找該部落格的所有評論。

QuestionDao:實作問題的增加、删除和選擇。

        insert:新增一個問題。

        delete:删除一個問題。

        selelctAll:查找所有問題。

        selelctOne:根據問題ID查找問題。主要使用者回答頁面的使用。

以上的方法都是類似的。隻展示DButil和BlogDao的代碼,其他代碼可以在這個基礎上修改得到。

public class DBUtil {
    private static final String URL = "jdbc:mysql://127.0.0.1:3306/BlogSystem?characterEncoding=utf8&useSSL=false"; // BlogSystem是資料庫的名稱
    private static final String username = "root";
    private static final String password = "1111";  // 輸入資料庫的密碼
    private static DataSource dataSource = new MysqlDataSource();
    static {
        ((MysqlDataSource)dataSource).setURL(URL);
        ((MysqlDataSource)dataSource).setUser(username);
        ((MysqlDataSource)dataSource).setPassword(password);
    }
    public Connection getConnectin() throws SQLException {
        return dataSource.getConnection();
    }
    public void close(Connection connection, PreparedStatement statement, ResultSet resultSet){
        if(resultSet != null){
            try {
                resultSet.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if(statement != null){
            try {
                statement.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if(connection == null){
            try {
                connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}
           
package Model;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class BlogDao {
    public static void insert(Blog blog){
        DBUtil dbUtil = new DBUtil();
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            connection = dbUtil.getConnectin();
            String sql = "insert into blog (blogId,blogTitle,blogContent,blogTime,blogAuthor) values(null,?,?,?,?)";
            statement = connection.prepareStatement(sql);
            statement.setString(1,blog.getBlogTitle());
            statement.setString(2,blog.getBlogContent());
            statement.setTimestamp(3,blog.getBlogTime());
            statement.setInt(4,blog.getBlogAuthor());
            int ret = statement.executeUpdate();
            if(ret == 1){
                UserDao userDao = new UserDao();
                int blogcount = userDao.getBlogCount(blog.getBlogAuthor());
                System.out.println(blogcount);
                blogcount +=1;
                userDao.changeBlogCount(blogcount,blog.getBlogAuthor());
                System.out.println("插入成功");
            }else{
                System.out.println("插入失敗");
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            dbUtil.close(connection,statement,null);
        }
    }
    public static void delete(int blogId){
        DBUtil dbUtil = new DBUtil();
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            connection = dbUtil.getConnectin();
            String sql = "delete from blog where blogId=?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,blogId);
            int ret = statement.executeUpdate();
            if(ret == 1){
                System.out.println("删除成功");
            }else{
                System.out.println("删除失敗");
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            dbUtil.close(connection,statement,null);
        }
    }
    public static Blog selectOne(int blogId){
        DBUtil dbUtil = new DBUtil();
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        Blog blog = new Blog();
        try {
            connection = dbUtil.getConnectin();
            String sql = "select * from blog where blogId=?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,blogId);
            resultSet = statement.executeQuery();
            if(resultSet.next()){
                blog.setBlogTitle(resultSet.getString("blogTitle"));
                blog.setBlogTime(resultSet.getTimestamp("blogTime"));
                blog.setBlogContent(resultSet.getString("blogContent"));
                blog.setBlogAuthor(resultSet.getInt("blogAuthor"));
                blog.setBlogId(resultSet.getInt("blogId"));
                blog.setCommentCount(resultSet.getInt("commentCount"));
                blog.setVisitCount(resultSet.getInt("visitCount"));
                return blog;
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            dbUtil.close(connection,statement,resultSet);
        }
        return null;
    }
    public static List<Blog> selectAll(){
        DBUtil dbUtil = new DBUtil();
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        List<Blog> blogs = new ArrayList<>();
        try {
            connection = dbUtil.getConnectin();
            String sql = "select * from blog order by blogTime desc";
            statement = connection.prepareStatement(sql);
            resultSet = statement.executeQuery();
            while (resultSet.next()){
                Blog blog = new Blog();
                blog.setBlogTitle(resultSet.getString("blogTitle"));
                blog.setBlogTime(resultSet.getTimestamp("blogTime"));
                String content = resultSet.getString("blogContent");
                if(content.length() > 100){
                    content = content.substring(0,100) + "...";
                    System.out.println(100);
                }
                blog.setBlogContent(content);
                blog.setBlogAuthor(resultSet.getInt("blogAuthor"));
                blog.setBlogId(resultSet.getInt("blogId"));
                blog.setCommentCount(resultSet.getInt("commentCount"));
                blog.setVisitCount(resultSet.getInt("visitCount"));
                blogs.add(blog);
            }
            return blogs;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            dbUtil.close(connection,statement,resultSet);
        }
        return null;
    }
    public static void updateBlog(String blogTitle, String blogContent, int blogId){
        DBUtil dbUtil = new DBUtil();
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            connection = dbUtil.getConnectin();
            String sql = "update blog set blogTitle = ?,blogContent=? where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setString(1,blogTitle);
            statement.setString(2,blogContent);
            statement.setInt(3,blogId);
            int ret = statement.executeUpdate();
            if(ret == 1){
                System.out.println("修改成功");
            }else{
                System.out.println("修改失敗");
            }
            return;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            dbUtil.close(connection,statement,null);
        }
    }
    public static void updateVisitCount(int count,int blogId){
        DBUtil dbUtil = new DBUtil();
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            connection = dbUtil.getConnectin();
            String sql = "update blog set visitCount = ? where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,count);
            statement.setInt(2,blogId);
            int ret = statement.executeUpdate();
            if(ret == 1){
                System.out.println("部落格通路次數修改成功");
            }else{
                System.out.println("部落格通路次數修改失敗");
            }
            return;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            dbUtil.close(connection,statement,null);
        }
    }
    public static void updateCommentCount(int count,int blogId){
        DBUtil dbUtil = new DBUtil();
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            connection = dbUtil.getConnectin();
            String sql = "update blog set commentCount = ? where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,count);
            statement.setInt(2,blogId);
            int ret = statement.executeUpdate();
            if(ret == 1){
                System.out.println("評論次數修改成功");
            }else{
                System.out.println("評論次數修改失敗");
            }
            return;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            dbUtil.close(connection,statement,null);
        }
    }
    public static int selectVisitAllCount(int userId){
        DBUtil dbUtil = new DBUtil();
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        int count = 0;
        try {
            connection = dbUtil.getConnectin();
            String sql = "select sum(visitCount) from blog where blogAuthor = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,userId);
            resultSet = statement.executeQuery();
            if(resultSet.next()){
                count = resultSet.getInt("sum(visitCount)");
                return count;
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            dbUtil.close(connection,statement,null);
        }
        return 0;
    }
    public static int selectVisitCount(int blogId){
        DBUtil dbUtil = new DBUtil();
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        int count = 0;
        try {
            connection = dbUtil.getConnectin();
            String sql = "select visitCount from blog where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,blogId);
            resultSet = statement.executeQuery();
            if(resultSet.next()){
                count = resultSet.getInt("visitCount");
                return count;
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            dbUtil.close(connection,statement,null);
        }
        return 0;
    }
           

三、Thymeleaf的相關操作

3.1 Thymeleaf初始化的優化

如果我們按照部落格Lesson11:模闆引擎_劉減減的部落格-CSDN部落格中的操作實作部落格清單頁,就會在部落格清單頁中建立一個TemplateEngine執行個體,并且進行初始化操作。現在如果我們要在其他的頁面,如部落格詳情頁中使用TemplateEngine,就沒法使用之前建立好的TemplateEngine對象。解決方案有以下幾種:

①如果其他頁面需要用到TemplateEngine,就再建立TemplateEngine執行個體,并再初始化一遍。

評價:可行但是不建議采用。TemplateEngine和資料庫中的DataSource類似,隻需要一份執行個體即可。如果搞了多份,每份TemplateEngine執行個體都要加載模闆,會多消耗一份記憶體。會導緻同一份模闆資料,在資料庫中存在了多份。

②能否把TemplateEngine做成一個單例呢?

ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(getServletContext());
resolver.setPrefix("/WEB-INF/template/");
resolver.setSuffix(".html");
resolver.setCharacterEncoding("utf-8");
           

從TemplateEngine的初始化代碼可以看到,初始化必須用到ServletContext對象。這個對象又隻能在Servlet的環境下來擷取到,即将上面的初始化代碼,随便在servlet的某個方法裡都好寫。但是放在一個和Servlet無關的單獨的類中,都不好寫。是以這種方法并不推薦。

③借助Servlet中自身關于ServletContext的機制

ServletContext是上下文對象,每個webapp都有一個上下文對象 。可以讓一個webapp内部的servlet之間共享同一個ServletContext。這樣就可以依據ServletContext來進行多個Servlet之間的資料共享操作。基于這個機制,我們可以建立好一個TemplateEngine,儲存到ServletContext中,這樣,所有的servlet都可以使用這個TemplateEngine。

具體的解決方案:

在ServletContext被Tomcat建立好了之後,就立刻進行初始化TemplateEngine,并且把engine儲存到ServletContext裡面。servlet可以拿到ServletContext,進一步就可以拿到engine。

我們怎樣才能捕獲到Tomcat建立ServletContext呢?

使用Servlet的監聽器可以允許程式員監聽某些動作,當動作被Tomcat觸發的時候,可以執行程式員自己寫的代碼。此時我們要監聽的就是Tomcat建立ServletContext這個動作。

// 通過這個注解,Tomcat才能認識這是一個監聽器對象
@WebListener
public class ThymeLeafListener implements ServletContextListener {
    // servletContext被建立時觸發
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        // 1.建立一個模闆引擎對象
        TemplateEngine engine = new TemplateEngine();
        // 2.建立一個ServletContextTemplateResolver對象,功能就是從磁盤上加載模闆引擎檔案
        // 這裡需要改一下擷取方式
        ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(servletContextEvent.getServletContext());
        //3對resolver對象設定一些屬性 加載 /WEB-INF/template/ 目錄中, 以 .html 結尾的檔案, 作為模闆引擎
        resolver.setPrefix("/WEB-INF/template/");
        resolver.setSuffix(".html");
        resolver.setCharacterEncoding("utf-8");
        // 4 把resolver和engine關聯起來
        engine.setTemplateResolver(resolver);
        // 5.将建立好的engine放到servletContext裡面
        servletContextEvent.getServletContext().setAttribute("engine",engine);
        System.out.println("Themeleaf初始化完畢");
    }
    // ServletContext被銷毀前觸發 ,暫時不需要寫
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        
    }
}
           

注意:@WebListener這個注解一定要寫。

3.2 編寫模闆檔案

step1:把之前寫的靜态頁面,全都拷貝到webapp目錄中

step2:修改html檔案,制作成Thymeleaf的模闆檔案

        将需要動态替換的部分,使用占位符占個位置。

step3:建立目錄結構,将剛剛制作好的模闆檔案移到該目錄下。具體操作見部落格

Lesson11:模闆引擎_劉減減的部落格-CSDN部落格的3.3小節。

四、實作登入頁面、強制登入功能和注冊頁面

4.1 實作登入頁面

登入頁面是一個純靜态的頁面,不需要通過servlet來渲染。登入頁面的實作流程見部落格Lesson10:ServletAPI詳解(HttpServlet、HttpServletRequest、HttpServletResponse)_劉減減的部落格-CSDN部落格的第4.5小節。

@WebServlet("/login")
public class loginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if(username == null || "".equals(username) || password == null || "".equals(password)){
            resp.getWriter().write("<h3>輸入不合法</h3>");
            return;
        }
        User user = UserDao.selectByName(username);
        if(user == null){
            resp.getWriter().write("<h3>使用者名或者密碼錯誤</h3>");
            return;
        }
        boolean result = user.getPassword().equals(password);
        if(!result){
            resp.getWriter().write("<h3>使用者名或者密碼錯誤</h3>");
            return;
        }
        HttpSession session = req.getSession(true);
        session.setAttribute("user",user); // 将使用者儲存在session中
        resp.sendRedirect("bloglist.html"); // 登入成功後,跳轉到部落格清單頁
    }
}
           

4.2 實作強制登入功能

我們約定,隻有使用者登入後,才能進入到部落格系統中,通路到部落格系統的各個頁面。如果不登陸,直接嘗試通路部落格系統中的某一個頁面,就會自動的跳轉到登入頁面。

如何實作呢?我們可以在進入代碼入口的時候,先判定一下目前是否在已經登入的狀态。這個功能可以通過session機制實作,在登入的時候 将user資訊儲存在session中,然後從session中讀取user,如果可以讀取到,就證明已經登入過了,執行正常的頁面渲染。如果未讀取到,則是未登入狀态,就跳轉到登入頁面。由于每個頁面都會用到強制登入功能,是以将其封裝在一個類中。

public class Util {
    public static User checkLogin(HttpServletRequest req){
        HttpSession session = req.getSession(false);
        if(session == null){
            return null;
        }
        User user = (User) session.getAttribute("user");
        if(user == null){
            return null;
        }
        return user;
    }
}
           

4.3 實作注冊頁面

注冊頁面的前端實作部分隻需要在登入頁面上進行微調就可以。是以再次不做贅述。注冊頁面的實作邏輯:

點選登入頁面上的注冊——》跳轉到注冊頁面——》輸入使用者名和兩次密碼——》點選注冊,觸發一個POST請求——》後端進行處理

後端拿到使用者名和密碼後:先從請求中讀出使用者名和密碼——》判斷輸入是否合法——》在資料庫中查詢該使用者是否已經存在——》不存在,判斷兩次密碼輸入是否一緻——》一緻就往資料庫中的user表中插入該資料。

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if(username == null || "".equals(username) || password == null || "".equals(password)){
            resp.getWriter().write("<h3>輸入不合法</h3>");
            return;
        }
        User user = UserDao.selectByName(username);
        if(user == null){
            resp.getWriter().write("<h3>使用者名或者密碼錯誤</h3>");
            return;
        }
        boolean result = user.getPassword().equals(password);
        if(!result){
            resp.getWriter().write("<h3>使用者名或者密碼錯誤</h3>");
            return;
        }
        HttpSession session = req.getSession(true);
        session.setAttribute("user",user); // 将使用者儲存在session中
        resp.sendRedirect("bloglist.html"); // 登入成功後,跳轉到部落格清單頁
    }
}
           

五、實作部落格詳情頁

5.1 部落格詳情展示

實作流程:先判斷是否登入——》從請求中讀到blogId——》判斷blogId的合法性——》根據blogId去資料庫查詢部落格——》查詢出來之後就更新部落格的通路次數。

注意,此時資訊卡片頁顯示的是部落格作者的資訊,根據部落格的作者姓名在使用者表裡查到部落格作者的資訊。

@WebServlet("/blogdetail.html")
public class DetailServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setContentType ("text/html;charset=utf-8");
        // loginuser 表示目前登入的使用者
        User Loginuser = Util.checkLogin(req);
        if(Loginuser == null){
            resp.sendRedirect("login.html");
            return;
        }
        String blogId = req.getParameter("blogId");
        HttpSession session = req.getSession(false);
        session.setAttribute("blogId",blogId);
        if(blogId == null || "".equals(blogId)){
            resp.getWriter().write("<h3>blogId不存在</h3>");
            return;
        }
        Blog blog = BlogDao.selectOne(Integer.parseInt(blogId));
        if(blog == null){
            resp.getWriter().write("<h3>查詢的部落格不存在</h3>");
            return;
        }else{
            // 此頁的資訊卡片需要顯示作者的資訊,根據部落格的作者姓名在使用者表裡查到部落格作者的資訊
            User BlogUser = UserDao.selectOne(blog.getBlogAuthor());
            if(BlogUser == null){
                resp.getWriter().write("<h3>查詢的部落格作者不存在</h3>");
                return;
            }
            // 從評論表中查詢出所有的部落格
            List<Comment> comments = CommentDao.selectAll(Integer.parseInt(blogId));
            ServletContext servletContext = getServletContext();
            TemplateEngine engine = (TemplateEngine) servletContext.getAttribute("engine");
            WebContext webContext = new WebContext(req,resp,servletContext);
            // 如果讀出的評論不為空,就顯示評論
            if(comments != null){
                System.out.println(comments);
                webContext.setVariable("comments",comments);
            }
            // 進入到部落格的詳情頁,該部落格的通路量需要+1,先讀出通路量,+1後,再寫回到資料庫
            int visitCount = blog.getVisitCount();
//            System.out.println("通路前的次數"+visitCount);
            visitCount +=1;
            BlogDao.updateVisitCount(visitCount,Integer.parseInt(blogId));
//            Blog blog2 = BlogDao.selectOne(Integer.parseInt(blogId));
//            int visitCount2 = blog2.getVisitCount();
//            System.out.println("通路後的次數"+visitCount2);
            webContext.setVariable("blog",blog);
            webContext.setVariable("bloguser",BlogUser);
//            System.out.println("登入使用者"+Loginuser);
            // 在評論時,顯示的是登入使用者的資訊
            webContext.setVariable("loginuser",Loginuser);

            int count = BlogDao.selectVisitCount(Integer.parseInt(blogId));
            webContext.setVariable("visitCount",count);
            // 隻有登入的使用者和部落格作者是同一個人,才能删除部落格、修改部落格,為了避免發生錯誤,隻有是同一個人才顯示這兩個按鈕
            webContext.setVariable("showDeleteButton",Loginuser.getUserID()==blog.getBlogAuthor());
            webContext.setVariable("showAmendButton",Loginuser.getUserID()==blog.getBlogAuthor());
            engine.process("blogdetail",webContext, resp.getWriter());
        }
    }
}
           

5.2 評論的顯示

實作流程和部落格清單頁的實作流程類似,在此不做贅述。

5.3 評論功能

先判斷是否登入——》從請求中讀到評論内容——》判斷評論内容的合法性——》從session中讀到blogId和user——》構造comment對象——》插入comment資料庫——》根據blogID去blog中查詢出被評論的部落格,更新部落格的評論次數。

@WebServlet("/comment")
public class CommentServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");
        User user = Util.checkLogin(req);
        if(user == null){
            resp.sendRedirect("login.html");
            return;
        }
        Comment comment = new Comment();
        String commentContent = req.getParameter("comment");
        System.out.println("從req中讀到的評論内容"+commentContent);
        if(commentContent == null || "".equals(commentContent)){
            resp.getWriter().write("輸入的評論不能為空");
            return;
        }
        HttpSession session = req.getSession(false);
        String blogId = (String) session.getAttribute("blogId");
//        System.out.println("從session中讀到的blogId" + blogId);
        comment.setCommentContent(commentContent);
        comment.setCommentTime(new Timestamp(System.currentTimeMillis()));
        comment.setCommentUser(user.getUserName());
        comment.setBlogId(Integer.parseInt(blogId));
//        System.out.println(user.getUserPicture());
        comment.setCommentUserImage(user.getUserPicture());
//        System.out.println("登入使用者的圖檔位址"+ user.getUserPicture());
//        System.out.println(comment);
        // comment表中新增一個資料後,評論數量+1
        CommentDao.insert(comment);
        Blog blog = BlogDao.selectOne(Integer.parseInt(blogId));
//        System.out.println("找出來的blog"+blog);
        int commentCount = blog.getCommentCount();
//        System.out.println("commentCount更新前" + commentCount);
        commentCount = commentCount + 1;
        BlogDao.updateCommentCount(commentCount,Integer.parseInt(blogId));
//        Blog blog2 = BlogDao.selectOne(Integer.parseInt(blogId));
//        int commentCount2 = blog2.getCommentCount();
//        System.out.println(blog2);
//        commentCount += 1;
//        System.out.println("commentCount更新後" + commentCount2);
        resp.sendRedirect("blogdetail.html?blogId=" + blogId);
    }
}
           

5.4 提問功能

提問功能的實作流程和評論功能類似,再此不做贅述。

問題送出後,會觸發一個post請求。在servlet中處理post請求主要是将請求中的問題内容讀出來并插入到資料庫中。同時,問題送出後會跳轉到問題展示頁。此時會觸發一個get請求,servlet在處理get請求時主要是顯示所有的問題。

@WebServlet("/question.html")
public class QuestionServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");
        // loginuser 表示目前登入的使用者
        User Loginuser = Util.checkLogin(req);
        if(Loginuser == null){
            resp.sendRedirect("login.html");
            return;
        }
//        String blogId = req.getParameter("blogId");
        List<Question> questions = QuestionDao.selectAll();
        if(questions != null){
            ServletContext servletContext = getServletContext();
            TemplateEngine engine = (TemplateEngine) servletContext.getAttribute("engine");
            WebContext webContext = new WebContext(req,resp,servletContext);
            webContext.setVariable("questions",questions);
            engine.process("question",webContext, resp.getWriter());
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");
//        System.out.println("進入到questionServlet的post請求");
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");
        User user = Util.checkLogin(req);
        if(user == null){
            resp.sendRedirect("KnowledgeList_list.html");
            return;
        }
        Question question = new Question();
        String commentContent = req.getParameter("comment");
//        System.out.println("從req中讀到的問題内容"+commentContent);
        if(commentContent == null || "".equals(commentContent)){
            resp.getWriter().write("輸入的問題不能為空");
            return;
        }
        HttpSession session = req.getSession(false);
        String blogId = (String) session.getAttribute("blogId");
//        System.out.println("從session中讀到的blogId" + blogId);
        question.setQuestionContent(commentContent);
        question.setUserId(user.getUserID());
        question.setBlogId(Integer.parseInt(blogId));
        QuestionDao.insert(question);
        resp.sendRedirect("question.html");
    }
}
           

5.5 問題删除功能

實作流程和删除部落格類似,再此不做贅述。

5.6 注意

①commentservlet是部落格評論功能建構的form表單觸發的get請求,在清單頁的servlet代碼的session中加入blogID,這樣在commentservlet中就能知道被評論的部落格是誰了,為更新部落格的評論次數做鋪墊。

②此時登入的使用者就是評價的使用者。是以評論中部落格的相關使用者資訊就是登入使用者的相關資訊。

③注意,部落格詳情頁涉及到兩個使用者,一個是登入使用者,一個是部落格作者。隻有登入的使用者和部落格作者是同一個人,才能删除部落格、修改部落格。為了保險起見,隻有是同一個人才顯示删除部落格和修改部落格這兩個按鈕。

④一個input輸入框會對應兩個按鈕,此時不能使用form标簽了。可以用函數來實作。

⑤如何将頁面按照markdown的形式顯示。

借助editor.md的内置功能,

<script>
        // 先從html中拿到要渲染的字元串
        var markdown = document.querySelector("#content").innerHTML;
        // 清空原來的div
        document.querySelector("#content").innerHTML = "";
        // 通過内置的方式完成渲染,要想能夠調用, 也需要先引入 editor.md 的 js 
        editormd.markdownToHTML('content',{markdown:markdown})
    </script>
           

5.7 前端代碼

<!DOCTYPE html>
<html >
<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>Document</title>
    <link rel="stylesheet" href="./css/common.css" target="_blank" rel="external nofollow" >
    <link rel="stylesheet" href="./css/detail.css" target="_blank" rel="external nofollow" >
    
    <link rel="stylesheet" href="./editor.md/css/editormd.css" target="_blank" rel="external nofollow"  />
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/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="pictures/logo.jpg" alt="圖示">
        <span>我的部落格系統</span>
        <span id="spacer"></span>
        <a href="KnowledgeList_list.html" target="_blank" rel="external nofollow" >首頁</a>
        <a href="KnowledgeList_edit.html" target="_blank" rel="external nofollow" >寫部落格</a>
        <a th:if = "${showDeleteButton}" th:href="${'deleteBlog?blogId=' + blog.blogId}" target="_blank" rel="external nofollow" >删除部落格</a>
        <a th:if = "${showAmendButton}" th:href="${'amendBlog?blogId=' + blog.blogId}" target="_blank" rel="external nofollow" >修改部落格</a>
        <a href="question.html" target="_blank" rel="external nofollow" >問答區</a>
        <a href="logout" target="_blank" rel="external nofollow"  id="logout">登出</a>
    </div>
    <div class="container">
        <div class="left">
            <div class="card">
                <img th:src="${bloguser.userPicture}" alt="頭像">
<!--                <img src="./pictures/piyo.jpg" alt="頭像">-->
                <h3 th:text = "${bloguser.userName}"></h3>
                <a href="#" target="_blank" rel="external nofollow" >GitHub位址</a>
                <div class="count">
                    <span>文章</span>
                    <span>通路量</span>
                </div>
                <div class="count">
                    <span th:text = ${bloguser.blogCount}></span>
                    <span th:text = ${visitCount}></span>
                </div>
            </div>
            <div class="comment">
                <div class="commentedit">
                    <div><img th:src="${loginuser.userPicture}" alt="頭像"></div>
                    <form name="form" method="post">
                        <input type="textarea" name="comment" id="comment" placeholder="請發表有價值的評論,部落格評論不歡迎灌水,良好的社群氛圍需大家一起維護。" >
                        <div class="button">
                            <input type="button" value="送出評論" id="button1" onclick="addComment()">
                            <input type="button" value="送出問題" id="button1" onclick="addQuestion()">
                        </div>
                    </form>
                </div>
                <div class="commentList">
                    <div class="commentInfo" th:each = "comment: ${comments}">
                        <div class="commentListLeft">
                            <img th:src="${comment.commentUserImage}" alt="使用者頭像">
<!--                            <img src="./pictures/piyo.jpg" alt="使用者頭像">-->
                        </div>
                        <div class="commentListRight">
                            <div class="commentListUserInfo">
                                <span th:text = "${comment.commentUser}"></span>
                                <span th:text = "${comment.commentTime}"></span>
                            </div>
                            <div class="commentListdetail">
                                <span th:text = "${comment.commentContent}"></span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>   
        </div>
        <div class="right">
            <div class="essay">
                <div class="title" th:text = "${blog.blogTitle}"></div>
                <div class="date" th:text = "${blog.blogTime}"></div>
                <div class="content" th:text = "${blog.blogContent}" style="background-color:transparent" id="content"></div>
            </div>
        </div>
    </div>
    <script>
        // 先從html中拿到要渲染的字元串
        var markdown = document.querySelector("#content").innerHTML;
        // 清空原來的div
        document.querySelector("#content").innerHTML = "";
        // 通過内置的方式完成渲染,要想能夠調用, 也需要先引入 editor.md 的 js 
        editormd.markdownToHTML('content',{markdown:markdown})
    </script>
    <script type="text/javascript">
        function addComment(){
            document.form.action="comment";//送出的url
            document.form.submit();
        }

        function addQuestion(){
            document.form.action="question.html";//送出的url
            document.form.submit();
        }
    </script>

</body>
</html>
           

六、部落格删除頁

先判斷是否登入——》從請求中讀取部落格Id——》判斷blogId是否為空——》去資料庫中查詢是否存在——》存在就删除——》跳轉到部落格清單頁。

@WebServlet("/deleteBlog")
public class DeleteBlogServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        User user = Util.checkLogin(req);
        if(user == null){
            resp.sendRedirect("login.html");
            return;
        }
        String blogId = req.getParameter("blogId");
        if(blogId == null){
            resp.getWriter().write("<h3>blogId不存在</h3>");
            return;
        }
        Blog blog = BlogDao.selectOne(Integer.parseInt(blogId));
        if(blog == null){
            resp.getWriter().write("<h3>您要删除的部落格不存在</h3>");
            return;
        }
        BlogDao.delete(Integer.parseInt(blogId));
        resp.sendRedirect("bloglist.html");
    }
}
           

七、實作部落格編輯頁

7.1 前端頁面的設計

①加入form表單,點選送出按鈕可以觸發一個HTTP請求。

②需要送出一個标題和正文,标題已經是一個input标簽了,正文可以在editor.md這個第三方庫中進行編輯,editor.md也支援form表單,具體設定為:

先在部落格編輯區的div中放一個隐藏的text area。再在初始化editor對象的時候,指定一個特殊的選項saveHTMLToTextArea:true,這個選項的功能就是editor.md把使用者編輯的正文放到這個text area裡面。

<div class="container">
        <div class="container-edit">
            <!-- 包含兩個部分,标題編輯區和内容編輯區 -->
            <form action="edit" method="post" style="height: 100%">
                <div class="container-edit-title">
                    <input type="text"  id="title" autocomplete="off" placeholder="在這裡輸入标題" name="title">
                    <!-- <input type="button" value="釋出文章" > -->
                    <input type="submit" value="釋出文章" class="submit-button">
                </div>
                <div id="editor" >
                    <textarea name="content" style="display: none"></textarea>
                </div>
            </form>
            
        </div>
    </div>
    <script>
            // 初始化 editor.md
            var editor = editormd("editor", {
            // 這裡的尺寸必須在這裡設定. 設定樣式會被 editormd 自動覆寫掉. 
            width: "1000px",
            // 設定編輯器高度
            height: "calc(100% - 60px)",
            // 編輯器中的初始内容
            markdown: "# 在這裡寫下一篇部落格",
            // 指定 editor.md 依賴的插件路徑
            path: "./editor.md/lib/",
            saveHTMLToTextArea:true
        });
    </script>
           

7.2 後端實作邏輯

先判斷是否登入——》從請求中讀去部落格标題和部落格内容——》判斷部落格标題和部落格内容是否合法——》構造部落格對象并插入到資料庫中——》跳轉到部落格清單頁。

@WebServlet("/edit")
public class EditServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");
        //1. 判斷是否登入
        User user = Util.checkLogin(req);
        if(user == null){
            resp.sendRedirect("login.html");
            return;
        }
        // 2.從rep中拿到title和content,并校驗
        String title = req.getParameter("title");
//        System.out.println(title);
        String content = req.getParameter("content");
//        System.out.println(content);
        if(title == null || "".equals(title) || content == null || "".equals(content)){
            resp.getWriter().write("<h3>标題或者正文缺失</h3>");
            return;
        }
        //3.建構一個blog,插入到資料庫中
        Blog blog = new Blog();
        blog.setBlogContent(content);
        blog.setBlogTitle(title);
        blog.setBlogTime(new Timestamp(System.currentTimeMillis()));
        blog.setBlogAuthor(user.getUserID());
        BlogDao blogDao = new BlogDao();
        blogDao.insert(blog);
        //4.跳轉到部落格清單頁
        resp.sendRedirect("bloglist.html");
    }
}
           

八、實作部落格修改頁

和部落格清單頁的前端頁面類似,隻不過部落格的标題和内容是從資料庫中讀出來的。可以先用thymeleaf進行占位,将從資料庫中讀到的資料再給渲染進去。

<div class="container">
        <div class="container-edit">
            <!-- 包含兩個部分,标題編輯區和内容編輯區 -->
            <form action="amendBlog" method="post" style="height: 100%">
                <div class="container-edit-title">
                    <input type="text"  id="title" autocomplete="off" th:value="${blog.blogTitle}" name="title">
                    <!-- <input type="button" value="釋出文章" > -->
                    <input type="submit" value="确認修改" class="submit-button">
                </div>
                <div id="editor" >
                    <textarea name="content" style="display: none" th:text = "${blog.blogContent}"></textarea>
                </div>
            </form>
            
        </div>
    </div>
    <script  th:inline="javascript">
            // 初始化 editor.md
            let content = /*[[${blog.blogContent}]]*/ "123";
            var editor = editormd("editor", {
            // 這裡的尺寸必須在這裡設定. 設定樣式會被 editormd 自動覆寫掉. 
            width: "1000px",
            // 設定編輯器高度
            height: "calc(100% - 60px)",
            // 編輯器中的初始内容
            markdown: content,
            // 指定 editor.md 依賴的插件路徑
            path: "./editor.md/lib/",
            saveHTMLToTextArea:true
        });
    </script>
           

當跳轉到部落格修改頁,會觸發一個get請求,在處理這個get請求時需要将要修改的部落格的标題的内容傳回。

當修改完成點選送出按鈕,會觸發一個post請求,對該請求的處理流程為:先判斷是否登入——》從請求中讀取部落格标題和部落格内容——》判斷部落格标題和部落格内容是否合法——》更新部落格——》跳轉到部落格詳情頁,跳轉時需要帶上部落格的id。

@WebServlet("/amendBlog")
public class AmendServlet extends HttpServlet {
    public static int blogId = 0;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        User user = Util.checkLogin(req);
        if(user == null){
            resp.sendRedirect("login.html");
            return;
        }
        String blogID = req.getParameter("blogId");
        if(blogID == null){
            resp.getWriter().write("<h3>blogId不存在</h3>");
            return;
        }
        blogId = Integer.parseInt(blogID);
        Blog blog = BlogDao.selectOne(blogId);
        if(blog == null){
            resp.getWriter().write("<h3>您要修改的部落格不存在</h3>");
            return;
        }
        System.out.println("要修改的部落格:"+ blog);
        ServletContext servletContext = getServletContext();
        TemplateEngine engine = (TemplateEngine) servletContext.getAttribute("engine");
        WebContext webContext = new WebContext(req,resp,servletContext);
        webContext.setVariable("blog",blog);
        engine.process("blog_amend.html",webContext, resp.getWriter());
//        resp.sendRedirect("blog_amend.html");

    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");
        //1. 判斷是否登入
        User user = Util.checkLogin(req);
        if(user == null){
            resp.sendRedirect("KnowledgeList_login.html");
            return;
        }
        // 2.從rep中拿到title和content,并校驗
        String title = req.getParameter("title");
//        System.out.println(title);
        String content = req.getParameter("content");
//        System.out.println(content);
        if(title == null || "".equals(title) || content == null || "".equals(content)){
            resp.getWriter().write("<h3>标題或者正文缺失</h3>");
            return;
        }
        BlogDao.updateBlog(title,content,blogId);
        resp.sendRedirect("blogdetail.html?blogId="+blogId);
    }
}
           

九、實作上傳頭像功能

先建立一個images檔案夾,将使用者的頭像儲存到這個檔案夾裡。

判斷是否登入——》擷取到images所在的磁盤路徑——》擷取到圖檔的part對象——》将頭像存放在images目錄中,并以使用者名命名——》跳轉到登入頁面。

此時使用者session中的user資訊是沒有頭像屬性的,會導緻上傳的頭像無法正常顯示出來。使用者重新登入後,才能更新session中的user資訊,此時才能将頭像正常顯示出來。

@MultipartConfig
@WebServlet("/imageUpload")
public class UploadImageServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        User user = Util.checkLogin(req);
        if(user == null){
            resp.sendRedirect("login.html");
            return;
        }
        String username = user.getUserName();
        System.out.println("使用者的名字"+ username);
        // 擷取到image所在的磁盤路徑
        String path = getServletContext().getRealPath("/images");
        path = path + "/" + username + ".jpg";
       // System.out.println(path);
        // 擷取到圖檔的Part對象
        Part part = req.getPart("image");
        // 擷取到圖檔名字
        //String name = part.getSubmittedFileName();
        //System.out.println(name);
        // 把圖檔放在指定目錄中
        String path2 = "./images/"+ username + ".jpg";
        part.write(path);
        UserDao userDao = new UserDao();
        userDao.uploadImages(username,path2);
        resp.sendRedirect("login.html");
    }
}
           

十、實作問題回答頁

10.1 前端頁面設計

前端頁面的設計參考Lesson6:JS的WEBAPI執行個體練習_劉減減的部落格-CSDN部落格

中4.2TodoList的設計。此時約定:點選檢視答案,部落格的内容才會顯示出來。使用display這個參數來控制,讀出display的屬性,如果display=none,點選後就将display=block。如果此時display=block,點選後就将display=none。可以用一個函數來實作這個功能。

<div class="container1" id="container">
        <div class="left">
            <h3>答題區</h3>
            <input type="textarea" name="answer" id="answer" width="200px" height="200px" >
        </div>
        <div class="right">
            <input type="button" value="點選檢視答案" id="answerbutton">
            <span th:text = "${blog.blogContent}" style="display: none;" id="contentspan"></span>
        </div>
    </div>
    <script>
        let button = document.querySelector("#answerbutton");
        button.onclick = function(){
            let span = document.querySelector("#contentspan");
            if(span.style.display == "none"){
                span.style.display = "block";
            }else {
                span.style.display = "none";
            }
        }
    </script>
           

10.2 後端設計

點選回答按鈕,會觸發一個get請求,對該請求的處理流程為:先判斷是否登入——》從請求中讀取問題ID——》判斷問題id是否合法——》根據問題ID在問題資料庫中查找問題——》如果查找到問題,根據問題中的部落格ID屬性在部落格資料庫中找出部落格資訊——》将部落格資訊渲染到頁面中。

@WebServlet("/answer.html")
public class AnswerServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        req.setCharacterEncoding("utf-8");
        User user = Util.checkLogin(req);
        if(user == null){
            resp.sendRedirect("login.html");
            return;
        }
        String questionId = req.getParameter("questionId");
//        String blogId = req.getParameter("blogId");
        if(questionId == null){
            resp.getWriter().write("<h3>您要回答的問題不存在</h3>");
        }
//        if(blogId == null){
//            resp.getWriter().write("<h3>您要回答的問題相關的部落格不存在</h3>");
//        }
        Question question = new Question();
        question = QuestionDao.selectOne(Integer.parseInt(questionId));
        if(question == null){
            resp.getWriter().write("<h3>您要回答的問題相關的部落格沒有查詢到</h3>");
        }
        Blog blog = BlogDao.selectOne(question.getBlogId());
        if(blog == null){
            resp.getWriter().write("<h3>您要回答的問題相關的部落格沒有查詢到</h3>");
        }
        WebContext webContext = new WebContext(req,resp,getServletContext());
        TemplateEngine engine = (TemplateEngine) getServletContext().getAttribute("engine");
        webContext.setVariable("blog",blog);
        engine.process("answer",webContext, resp.getWriter());
    }
}
           

十一、實作登出功能

實作流程:判斷是否登入——》擷取會話session——》從session中删除user字段——》跳轉到登入頁面。

@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        User user = Util.checkLogin(req);
        if(user == null){
            resp.getWriter().write("<h3>未登入</h3>");
            resp.sendRedirect("login.html");
        }
        HttpSession session = req.getSession();
        session.removeAttribute("user");
        resp.sendRedirect("login.html");
    }
}
           

 十二、實作部落格清單頁

12.1 實作流程

通過伺服器代碼,查詢資料庫,再将使用者的資訊和部落格清單展示在頁面上。此處需要用到Thymeleaf。

12.2 生成模闆

将需要動态替換的資訊使用th進行占位。部落格清單的顯示需要用到循環,具體步驟為:

①去掉原來頁面多餘的div,隻保留一個頁面的div。

②通過th:each的方式,根據伺服器查詢到包含所有部落格資訊的部落格數組,來循環建構出這裡的部落格div。

③根據資料庫中查詢出的每個部落格,将部落格的标題、時間、部落格内容資訊,顯示到頁面上。

④沒必要将部落格正文的所有内容都顯示出來,可以隻顯示一部分,增加一個檢視全文按鈕。此時,需要将blogId給拼接到a标簽的url中,這時才能夠知道需要從伺服器中擷取哪個部落格。

12.3 部落格的内容太多,超過設定的背景

給背景加上個css屬性:overflow:auto 如果目前元素的内容溢出,就會自動加個滾動條。此時指的是給标簽加上滾動條,而不是給頁面加上滾動條。

<div class="container">
        <div class="left">
            <div class="card">
                <img th:src="${user.userPicture}" alt="頭像">
<!--                <img src="./pictures/piyo.jpg" alt="頭像">-->
                <h3 th:text = "${user.userName}"></h3>
                <a th:href = "${user.userLink}">GitHub位址</a>
                <div class="count">
                    <span>文章</span>
                    <span>總通路量</span>
                </div>
                <div class="count">
                    <span th:text = ${user.blogCount}></span>
                    <span th:text = ${visitCount}></span>
                </div>
            </div>  
        </div>
        <div class="right">
            <div class="essay" th:each = "blog: ${blogs}">
                <div class="title" th:text = "${blog.blogTitle}"></div>
                <div class="date" th:text = "${blog.blogTime}"></div>
                <div class="content" th:text = "${blog.blogContent}"> </div>
                <a th:href="${'KnowledgeList_detail.html?blogId=' + blog.blogId }" target="_blank" rel="external nofollow"  class="detail">檢視全文&gt&gt</a>
            </div>
        </div>
    </div>
           

12.4 完成Servlet的代碼,實作整個頁面的渲染

@WebServlet("/bloglist.html")
public class ListServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset = utf-8");
        User user = Util.checkLogin(req);
        if(user == null){
            resp.sendRedirect("login.html");
            return;
        }
        // 從資料庫中讀出所有的部落格
        List<Blog> blogs = BlogDao.selectAll();
        // 在登入頁面時已經将使用者的資訊儲存到session中,在強制登入方法中從session中讀到登入資訊,傳回一個User
        int visitCount = BlogDao.selectVisitAllCount(user.getUserID());
        WebContext webContext = new WebContext(req,resp,getServletContext());
        TemplateEngine engine = (TemplateEngine) getServletContext().getAttribute("engine");
        webContext.setVariable("blogs",blogs);
        webContext.setVariable("user",user);
        webContext.setVariable("visitCount",visitCount);
        engine.process("bloglist",webContext, resp.getWriter());
    }
}
           

如果需要源代碼,可以在評論區留言呀!!!