国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【基于前后端分離的博客系統(tǒng)】Servlet版本

這篇具有很好參考價(jià)值的文章主要介紹了【基于前后端分離的博客系統(tǒng)】Servlet版本。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

【基于前后端分離的博客系統(tǒng)】Servlet版本

?

??????點(diǎn)進(jìn)來(lái)你就是我的人了
博主主頁(yè):??????戳一戳,歡迎大佬指點(diǎn)!

歡迎志同道合的朋友一起加油喔??????

【基于前后端分離的博客系統(tǒng)】Servlet版本


目錄

一. 項(xiàng)目簡(jiǎn)介?

1. 項(xiàng)目背景

2. 項(xiàng)目用到的技術(shù)

3. 項(xiàng)目功能簡(jiǎn)單介紹?

二. 博客系統(tǒng)頁(yè)面設(shè)計(jì)?

三. 項(xiàng)目準(zhǔn)備工作

前后端交互約定內(nèi)容的分析?

1. 接口路徑

2. 請(qǐng)求方法

3. 請(qǐng)求參數(shù)

4. 響應(yīng)數(shù)據(jù)

前后端交互?構(gòu)思

四.博客系統(tǒng)中與數(shù)據(jù)庫(kù)進(jìn)行交互的類

設(shè)計(jì)數(shù)據(jù)庫(kù)表

1.DBUtil類(數(shù)據(jù)庫(kù)工具類)

2.Blog 類 (博客條目類)

3.User 類 (用戶類)

4.BlogDao 類 (訪問(wèn)博客數(shù)據(jù)的對(duì)象)

5.UserDao 類 (訪問(wèn)用戶數(shù)據(jù)的對(duì)象)

五.?博客系統(tǒng)的后端接口

1.AuthorServlet 類 (處理關(guān)于作者的請(qǐng)求)

2.BlogServlet 類 (處理關(guān)于的博客請(qǐng)求)

3.LogoutServlet 類 (處理注銷的請(qǐng)求)

4. LoginServlet 類 (處理登錄的請(qǐng)求)

六. 前端代碼構(gòu)造請(qǐng)求

1. 登陸頁(yè)面功能設(shè)計(jì)?

2. 博客列表頁(yè)面功能設(shè)計(jì)

3. 博客詳情頁(yè)面功能設(shè)計(jì)?

4. 博客編輯頁(yè)面功能設(shè)計(jì)

5. 用戶注銷功能設(shè)計(jì)

七.擴(kuò)展功能

?實(shí)現(xiàn)刪除博客

實(shí)現(xiàn)客戶端代碼:

代碼重點(diǎn)部分解析:(使用Promise解決ajax的異步問(wèn)題,實(shí)現(xiàn)刪除邏輯)

圖解如下:

服務(wù)器端代碼:(邏輯比較簡(jiǎn)單,大家看注釋即可)

實(shí)現(xiàn)注冊(cè)功能:

前端代碼的邏輯如下圖:?編輯

后端代碼:

實(shí)現(xiàn)文章數(shù)量動(dòng)態(tài)變化:

后端代碼:(邏輯非常簡(jiǎn)單,從數(shù)據(jù)庫(kù)取出所有blog對(duì)象放在一個(gè)鏈表返回給客戶端)

前端代碼:由于jQuery ky 自動(dòng)將返回的 JSON 字符串解析為 JavaScript 對(duì)象或數(shù)組(取決于 JSON 的內(nèi)容),所有直接將文章數(shù)量的長(zhǎng)度即可,下圖解析

八. 博客系統(tǒng)設(shè)計(jì)源碼



一. 項(xiàng)目簡(jiǎn)介?

1. 項(xiàng)目背景

在網(wǎng)絡(luò)學(xué)完HTTP協(xié)議,前端學(xué)完html,css,js,后端學(xué)完Servlet開(kāi)發(fā)后,做一個(gè)博客系統(tǒng),鞏固一下所學(xué)知識(shí),并將所學(xué)知識(shí)運(yùn)用到實(shí)際當(dāng)中,以此來(lái)進(jìn)一步提升對(duì)學(xué)習(xí)編程的興趣

2. 項(xiàng)目用到的技術(shù)

  • 前端使用到html,css,js,使用ajax技術(shù)發(fā)送http請(qǐng)求,請(qǐng)求body的格式為json格式
  • 后端使用Servlet進(jìn)行開(kāi)發(fā)
  • 使用Mysql數(shù)據(jù)庫(kù)保存數(shù)據(jù)
  • 除此還引入了editor.md,editor.md是一個(gè)開(kāi)源的頁(yè)面markdown編輯器組件
  • 采用Maven構(gòu)建工具搭建項(xiàng)目開(kāi)發(fā)環(huán)境

3. 項(xiàng)目功能簡(jiǎn)單介紹?

  • 登陸頁(yè)面:輸入用戶及密碼,點(diǎn)擊提交,如果用戶或密碼錯(cuò)誤,會(huì)提示用戶或密碼錯(cuò)誤,賬號(hào)及密碼正確則登陸成功,成功后跳轉(zhuǎn)到博客列表頁(yè)面
  • 博客列表頁(yè)面:博客列表頁(yè)面展示所有發(fā)布過(guò)的文章,文章顯示最多顯示50字,如果想查看全文,則需要點(diǎn)擊文章下的顯示全文
  • 博客詳情頁(yè)面:點(diǎn)擊某篇文章的顯示全文按鈕,則會(huì)展示文章的全部?jī)?nèi)容
  • 博客編輯頁(yè)面:點(diǎn)擊博客列表的寫(xiě)博客,會(huì)跳轉(zhuǎn)到博客編輯頁(yè)面,輸入文章題目及文章內(nèi)容點(diǎn)擊發(fā)布文章,文章即可發(fā)布成功,發(fā)布成功后會(huì)跳轉(zhuǎn)到博客列表頁(yè)面,可以查看發(fā)布過(guò)的文章?
  • 博客注銷按鈕:點(diǎn)擊博客注銷按鈕,則會(huì)跳轉(zhuǎn)到博客登陸頁(yè)面?

二. 博客系統(tǒng)頁(yè)面設(shè)計(jì)?

這里附上靜態(tài)頁(yè)面設(shè)計(jì)的碼云地址,可以點(diǎn)擊查看,本篇文章只展示后端代碼與前端代碼交互的部分,想要查看博客系統(tǒng)頁(yè)面設(shè)計(jì)代碼,請(qǐng)點(diǎn)擊:博客系統(tǒng)頁(yè)面設(shè)計(jì)?

頁(yè)面功能展示:?【基于前后端分離的博客系統(tǒng)】Servlet版本

?項(xiàng)目鏈接:博客系統(tǒng)

三. 項(xiàng)目準(zhǔn)備工作

創(chuàng)建Maven項(xiàng)目在pom.xml中添加項(xiàng)目依賴

  • 后端采用Servlet開(kāi)發(fā)
  • 數(shù)據(jù)庫(kù)使用Mysql
  • jackson框架可以進(jìn)行序列化和反序列化,將java對(duì)象和json字符串相互轉(zhuǎn)化
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>20230528</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.14.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>

    </dependencies>
</project>

?創(chuàng)建與開(kāi)發(fā)相關(guān)的包和類

【基于前后端分離的博客系統(tǒng)】Servlet版本

?引入前端資源?

【基于前后端分離的博客系統(tǒng)】Servlet版本

  • 將前端資源都放在main/webapp目錄下,前端資源從上面碼云地址中獲取,web.xml存放在main/webapp/WEB-INF目錄
  • 注意我這里的html頁(yè)面全都存放在一個(gè)html文件夾下面了,意味我在HTML頁(yè)面中直接不能使用@WebServle注解里面的路徑來(lái)訪問(wèn)Servlet.

在構(gòu)造請(qǐng)求去訪問(wèn)Servlet時(shí),路徑是根據(jù)@WebServlet注解或web.xml配置文件中定義的路徑,而不是Servlet類文件在服務(wù)器上的實(shí)際位置。路徑是相對(duì)于整個(gè)Web應(yīng)用的上下文路徑(通常是打包后的war包所在的路徑),而不是HTML頁(yè)面所在的位置。

讓我們?cè)俅我砸粋€(gè)例子進(jìn)行說(shuō)明,假設(shè)你的Web應(yīng)用(war包解壓)的結(jié)構(gòu)如下:

/myApp       (Web應(yīng)用的上下文路徑,war包名)
    /WEB-INF
        /classes
            MyServlet.class (訪問(wèn)路徑為/myServlet)
    /subdir
        index.html

在這種情況下,無(wú)論你的HTML頁(yè)面位于哪里(例如在"/myApp/subdir/index.html"),你都可以通過(guò)以下方式來(lái)訪問(wèn)"MyServlet":

  1. 使用相對(duì)路徑:假設(shè)你正在創(chuàng)建一個(gè)從"index.html"頁(yè)面到"MyServlet"的鏈接,你可以使用相對(duì)于當(dāng)前頁(yè)面的路徑,即"../myServlet",來(lái)指向Servlet。

  2. 使用絕對(duì)路徑:無(wú)論你在哪里,你都可以使用相對(duì)于上下文路徑(即"/myApp")的絕對(duì)路徑,"/myServlet",來(lái)訪問(wèn)Servlet。完整的URL將是"http://example.com/myApp/myServlet"。

在URL中,"example.com"代表了網(wǎng)站的域名,也就是服務(wù)器的網(wǎng)絡(luò)地址。所以,訪問(wèn)Servlet的路徑并不依賴于HTML頁(yè)面所在的位置,而是依賴于Servlet的配置和Web應(yīng)用的上下文路徑。

前后端交互約定內(nèi)容的分析?

1. 接口路徑

  • 前端請(qǐng)求登錄:/login
  • 前端請(qǐng)求博客列表:/blog
  • 前端請(qǐng)求某篇博客詳情:/blog + location.search
  • 前端請(qǐng)求作者信息:/author + location.search
  • 前端請(qǐng)求注銷:/logout

2. 請(qǐng)求方法

  • 登錄請(qǐng)求使用POST方法:form表單提交登錄信息。
  • 獲取博客列表使用GET方法:通過(guò)AJAX發(fā)送GET請(qǐng)求。
  • 獲取博客詳情和作者信息使用GET方法:通過(guò)AJAX發(fā)送GET請(qǐng)求。
  • 注銷請(qǐng)求使用GET方法:通過(guò)超鏈接發(fā)送GET請(qǐng)求。

3. 請(qǐng)求參數(shù)

  • 登錄請(qǐng)求:在登錄表單中,通過(guò)name屬性定義的字段名usernamepassword,將用戶輸入的用戶名和密碼作為參數(shù)發(fā)送到后端。
  • 獲取博客列表、博客詳情和作者信息:通過(guò)URL的查詢參數(shù),例如blogId用于獲取博客詳情,username用于獲取作者信息。

4. 響應(yīng)數(shù)據(jù)

  • 后端返回的響應(yīng)數(shù)據(jù)是以JSON格式返回的,前端使用AJAX進(jìn)行處理。

前后端交互?構(gòu)思

1. login.html (博客登錄頁(yè))

  • 前端實(shí)現(xiàn)步驟:
  1. 當(dāng)用戶點(diǎn)擊登錄按鈕時(shí),觸發(fā)表單的提交操作。
  2. 使用 $.ajax 發(fā)送 POST 請(qǐng)求,將用戶名和密碼作為請(qǐng)求參數(shù)發(fā)送到后端驗(yàn)證登錄。
  3. 在請(qǐng)求成功的回調(diào)函數(shù)中,根據(jù)后端返回的結(jié)果進(jìn)行判斷,如果登錄成功,則執(zhí)行相應(yīng)的操作(例如跳轉(zhuǎn)到博客列表頁(yè)),如果登錄失敗,則進(jìn)行相應(yīng)的提示。
  • 后端實(shí)現(xiàn)步驟:
  1. 接收前端發(fā)送的 POST 請(qǐng)求,獲取請(qǐng)求中的用戶名和密碼參數(shù)。
  2. 在數(shù)據(jù)庫(kù)中查詢是否存在對(duì)應(yīng)的用戶信息,進(jìn)行用戶名和密碼的驗(yàn)證。
  3. 根據(jù)驗(yàn)證結(jié)果,返回相應(yīng)的結(jié)果給前端,表示登錄成功或登錄失敗。

2. blog_list.html (博客列表頁(yè))

  • 前端實(shí)現(xiàn)步驟:
  1. 在頁(yè)面加載時(shí),使用 $.ajax 發(fā)送 GET 請(qǐng)求,向后端獲取博客列表數(shù)據(jù)。
  2. 在請(qǐng)求成功的回調(diào)函數(shù)中,遍歷返回的博客數(shù)據(jù),動(dòng)態(tài)構(gòu)造博客內(nèi)容并添加到頁(yè)面中。
  • 后端實(shí)現(xiàn)步驟:
  1. 接收前端發(fā)送的 GET 請(qǐng)求,無(wú)需參數(shù)。
  2. 查詢數(shù)據(jù)庫(kù)中的博客數(shù)據(jù),獲取博客列表。
  3. 將查詢結(jié)果封裝為 JSON 格式,并返回給前端。

3. blog_edit.html (博客編輯頁(yè))

  • 前端實(shí)現(xiàn)步驟:
  1. 當(dāng)用戶在編輯框中填寫(xiě)完博客標(biāo)題和內(nèi)容后,點(diǎn)擊發(fā)布文章按鈕,觸發(fā)表單的提交操作。
  2. 使用 $.ajax 發(fā)送 POST 請(qǐng)求,將博客標(biāo)題和內(nèi)容等數(shù)據(jù)發(fā)送到后端創(chuàng)建新的博客。
  3. 在請(qǐng)求成功的回調(diào)函數(shù)中,根據(jù)后端返回的結(jié)果進(jìn)行判斷,如果博客創(chuàng)建成功,則執(zhí)行相應(yīng)的操作(例如跳轉(zhuǎn)到博客詳情頁(yè)),如果創(chuàng)建失敗,則進(jìn)行相應(yīng)的提示。
  • 后端實(shí)現(xiàn)步驟:
  1. 接收前端發(fā)送的 POST 請(qǐng)求,獲取請(qǐng)求中的博客標(biāo)題和內(nèi)容等參數(shù)。
  2. 將獲取到的博客信息插入到數(shù)據(jù)庫(kù)中,創(chuàng)建新的博客。
  3. 返回相應(yīng)的結(jié)果給前端,表示博客創(chuàng)建成功或創(chuàng)建失敗。

4. blog_detail.html (博客詳情頁(yè))

  • 前端實(shí)現(xiàn)步驟:
  1. 在頁(yè)面加載時(shí),使用 $.ajax 發(fā)送 GET 請(qǐng)求,向后端獲取指定博客的詳細(xì)信息。
  2. 在請(qǐng)求成功的回調(diào)函數(shù)中,根據(jù)返回的博客數(shù)據(jù)更新頁(yè)面上的標(biāo)題、日期和博客正文內(nèi)容。
  • 后端實(shí)現(xiàn)步驟:
  1. 接收前端發(fā)送的 GET 請(qǐng)求,從請(qǐng)求參數(shù)中獲取博客ID或其他標(biāo)識(shí)符。
  2. 根據(jù)博客ID查詢數(shù)據(jù)庫(kù),獲取指定博客的詳細(xì)信息。
  3. 將查詢結(jié)果封裝為 JSON 格式,并返回給前端。

四.博客系統(tǒng)中與數(shù)據(jù)庫(kù)進(jìn)行交互的類

  1. DBUtil:提供了數(shù)據(jù)庫(kù)連接和資源釋放的工具方法,通過(guò) JDBC 技術(shù)與數(shù)據(jù)庫(kù)建立連接并執(zhí)行 SQL 語(yǔ)句。

  2. Blog:表示博客對(duì)象,包含博客的標(biāo)題、內(nèi)容、發(fā)布時(shí)間等屬性,以及相應(yīng)的 getter 和 setter 方法。

  3. BlogDao:封裝了對(duì)博客表的基本操作方法,包括新增博客、根據(jù)博客ID查詢博客、查詢所有博客列表、刪除指定博客等。

  4. User:表示用戶對(duì)象,包含用戶的ID、用戶名和密碼等屬性,以及相應(yīng)的 getter 和 setter 方法。

  5. UserDao:封裝了對(duì)用戶表的基本操作方法,包括根據(jù)用戶ID查詢用戶信息、根據(jù)用戶名查詢用戶信息等。

這些類通過(guò)在后端實(shí)現(xiàn)了與數(shù)據(jù)庫(kù)的交互,可以方便地進(jìn)行用戶登錄驗(yàn)證、博客發(fā)布和查詢等操作。

設(shè)計(jì)數(shù)據(jù)庫(kù)表

  • 有用戶登陸,所以有一張用戶表,觀察博客列表有顯示用戶昵稱,所以用戶表設(shè)計(jì)有四個(gè)字段:用戶id,用戶名,密碼,昵稱
  • 有文章展示,所以有一張文章表,文章有文章id,標(biāo)題,發(fā)布時(shí)間,文章內(nèi)容,關(guān)聯(lián)用戶的外鍵
  • 一個(gè)用戶可以發(fā)布多篇文章,所以用戶與文章對(duì)應(yīng)關(guān)系為1:m,用戶id作為文章表的外鍵?

?創(chuàng)建表的時(shí)候可以插入一些數(shù)據(jù)便于后續(xù)的測(cè)試

--這個(gè)文件主要用來(lái)寫(xiě)建庫(kù)建表語(yǔ)句
--一般建議大家,在建表的時(shí)候把建表sql保留下來(lái),以備后續(xù)部署其他機(jī)器的時(shí)候就方便了.

create database if not exists java_blog_system;
use java_blog_system;

--刪除舊表,重新創(chuàng)建新表,刪除舊表是為了防止之前的殘留數(shù)據(jù)對(duì)后續(xù)的程序有負(fù)面影響
drop table if exists user;
drop table if exists blog;

--真正創(chuàng)建表
create table blog (
    blogId int primary key auto_increment,
    title varchar(128),
    content varchar(4096),
    postTime datetime,
    userId int
);

create table user(
    userId int primary key auto_increment,
    username varchar(20) unique,           --要求用戶名和別人不重復(fù)
    password varchar(20)
);

--構(gòu)造測(cè)試數(shù)據(jù)
insert into blog values (1,'這是我的第一篇博客','從今天開(kāi)始我要認(rèn)真敲代碼',now(),1);
insert into blog values (2,'這是我的第二篇博客','從昨天開(kāi)始我要認(rèn)真敲代碼',now(),1);
insert into blog values (3,'這是我的第三篇博客','從前天開(kāi)始我要認(rèn)真敲代碼',now(),1);

--構(gòu)造測(cè)試數(shù)據(jù)
insert into user values(1,"jack","123");
insert into user values(2,"Alice","123");

1.DBUtil類(數(shù)據(jù)庫(kù)工具類)

這個(gè)類是數(shù)據(jù)庫(kù)工具類,主要提供數(shù)據(jù)庫(kù)連接和關(guān)閉資源的方法。

  • getConnection():這個(gè)方法是獲取數(shù)據(jù)庫(kù)連接。它通過(guò) DataSource 獲取連接,并返回這個(gè)連接。如果發(fā)生異常,它將打印錯(cuò)誤信息并返回 null。

  • close(ResultSet rs, PreparedStatement ps, Connection conn):這個(gè)方法是關(guān)閉資源。它會(huì)檢查傳入的 ResultSet、PreparedStatementConnection 是否為 null,如果不為 null,那么就關(guān)閉它。如果在關(guān)閉資源時(shí)發(fā)生異常,它將打印錯(cuò)誤信息。

//針對(duì)用戶表提供的基本操作
//由于此處沒(méi)有寫(xiě)注冊(cè)的功能, 也就不必 add
//也沒(méi)有用戶刪號(hào)功能, 也就不必 delete
public class UserDao {
    //1.根據(jù) userId 來(lái)查詢用戶信息
    public User selectById(int userId) {
        //獲取連接數(shù)據(jù)庫(kù)的對(duì)象
        Connection connection = null;
        //獲取執(zhí)行預(yù)編譯SQL語(yǔ)句的對(duì)象
        PreparedStatement statement = null;
        //獲取遍歷結(jié)果集合的對(duì)象
        ResultSet resultSet = null;
        try {
            //1.和數(shù)據(jù)庫(kù)建立連接
            connection = DBUtil.getConnection();
            //2.構(gòu)造SQL語(yǔ)句
            String sql = "select * from user where userId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,userId);
            //3.執(zhí)行SQL語(yǔ)句
            resultSet = statement.executeQuery();
            //4.遍歷結(jié)果結(jié)果
            if (resultSet.next()) {
                User user = new User();
                user.setUserId(resultSet.getInt("userId"));
                user.setUsername(resultSet.getString("username"));
                user.setPassword(resultSet.getString("password"));
                return user;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //5.釋放必要的資源
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }

    //2.    根據(jù)username來(lái)查詢用戶信息(登錄的時(shí)候)
    public User selectByUsername(String username) {
        //獲取連接數(shù)據(jù)庫(kù)的對(duì)象
        Connection connection = null;
        //獲取執(zhí)行預(yù)編譯SQL語(yǔ)句的對(duì)象
        PreparedStatement statement = null;
        //獲取遍歷結(jié)果集合的對(duì)象
        ResultSet resultSet = null;
        try {
            //1.和數(shù)據(jù)庫(kù)建立連接
            connection = DBUtil.getConnection();
            //2.構(gòu)造SQL語(yǔ)句
            String sql = "select * from user where username = ?";
            statement = connection.prepareStatement(sql);
            statement.setString(1,username);
            //3.執(zhí)行SQL語(yǔ)句
            resultSet = statement.executeQuery();
            //4.遍歷結(jié)果結(jié)果
            if (resultSet.next()) {
                User user = new User();
                user.setUserId(resultSet.getInt("userId"));
                user.setUsername(resultSet.getString("username"));
                user.setPassword(resultSet.getString("password"));
                return user;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //5.釋放必要的資源
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }

}

2.Blog 類 (博客條目類)

該類代表一個(gè)博客條目, 這個(gè)類中有五個(gè)成員變量:

  • blogId:每篇博客的唯一標(biāo)識(shí),它是數(shù)據(jù)庫(kù)表中的主鍵。
  • title:博客的標(biāo)題。
  • content:博客的內(nèi)容。
  • postTime:博客的發(fā)表時(shí)間,這是一個(gè)Timestamp類型的數(shù)據(jù)。
  • userId:發(fā)表這篇博客的用戶的id。

然后有這些成員變量的 get 和 set 方法,用來(lái)獲取和設(shè)置這些變量的值。

其中, getPostTime 方法返回一個(gè)格式化的字符串表示的時(shí)間,格式是 "yyyy-MM-dd hh:mm:ss"。

public class Blog {
    private int blogId;
    private String title;
    private String content;
    private Timestamp postTime;
    private int userId;

    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 Timestamp getPostTimestamp() {
        return postTime;
    }

    public String getPostTime() {
        //把時(shí)間戳轉(zhuǎn)化成格式化時(shí)間
        //格式化字符串一定不要背?。?!不同的語(yǔ)言不同的庫(kù),都有格式化時(shí)間的操作不同庫(kù)的格式化時(shí)間字符串設(shè)定不同?。。?        //SimpleDateFormat M表示月份,N表示分鐘
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        return simpleDateFormat.format(postTime);
    }
    public void setPostTime(Timestamp postTime) {
        this.postTime = postTime;
    }

    public int getUserId() {
        return userId;
    }

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

3.User 類 (用戶類)

此類代表一個(gè)用戶,這個(gè)類中有三個(gè)成員變量:

  • userId:每個(gè)用戶的唯一標(biāo)識(shí),它是數(shù)據(jù)庫(kù)表中的主鍵。
  • username:用戶的用戶名。
  • password:用戶的密碼。

同樣地,這個(gè)類有這些成員變量的 get 和 set 方法,用來(lái)獲取和設(shè)置這些變量的值。

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;
    }
}

4.BlogDao 類 (訪問(wèn)博客數(shù)據(jù)的對(duì)象)

這是一個(gè)數(shù)據(jù)訪問(wèn)對(duì)象, 執(zhí)行與博客相關(guān)的數(shù)據(jù)庫(kù)操作, 這個(gè)類中有四個(gè)方法:

  • add(Blog blog):這個(gè)方法是添加一篇新的博客。首先獲取數(shù)據(jù)庫(kù)連接,然后構(gòu)造SQL語(yǔ)句,通過(guò) PreparedStatement 設(shè)置參數(shù),最后執(zhí)行SQL。

  • selectById(int blogId):這個(gè)方法是根據(jù)博客id查詢一篇博客。流程和 add 方法類似,只是執(zhí)行的是查詢操作,查詢到結(jié)果后需要?jiǎng)?chuàng)建 Blog 對(duì)象并賦值。

  • selectAll():這個(gè)方法是查詢所有的博客。同樣是執(zhí)行查詢操作,不過(guò)這個(gè)方法需要遍歷所有的查詢結(jié)果,并創(chuàng)建 Blog 對(duì)象。

  • delete(int blogId):這個(gè)方法是刪除一篇博客。首先獲取數(shù)據(jù)庫(kù)連接,然后構(gòu)造SQL語(yǔ)句,通過(guò) PreparedStatement 設(shè)置參數(shù),最后執(zhí)行SQL。

//通過(guò)這個(gè)類封裝對(duì) 博客表的基本操作
//此處暫時(shí)不涉及到修改博客~~ (修改博客也可以通過(guò) 刪除/新增 )
public class BlogDao {
    //1.新增一個(gè)博客
    public void add(Blog blog) {
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            //1. 數(shù)據(jù)庫(kù)建立連接
            connection = DBUtil.getConnection();
            //2. 構(gòu)造 SQL
            String sql = "insert into blog values(null,?,?,?,?)";
            statement = connection.prepareStatement(sql);
            statement.setString(1, blog.getTitle());
            statement.setString(2,blog.getContent());
            statement.setTimestamp(3,blog.getPostTimestamp());
            statement.setInt(4,blog.getUserId(  ));
            //執(zhí)行 sql
            statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection,statement,null);
        }
    }

    //2.根據(jù)博客 id 來(lái)指定查詢博客(博客詳情頁(yè)中)
    public Blog selectById(int blogId) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;

        try {
            //1.和數(shù)據(jù)庫(kù)建立連接
            connection = DBUtil.getConnection();
            //2.構(gòu)造 sql 語(yǔ)句
            String sql = "select * from blog where blogId = ?";
            //prepareStatement(sql)這個(gè)方法見(jiàn)sql語(yǔ)句發(fā)送給數(shù)據(jù)庫(kù)進(jìn)行預(yù)編譯
            //返回了一個(gè)PreparedStatement對(duì)象用于執(zhí)行預(yù)編譯的SQL語(yǔ)句
            statement = connection.prepareStatement(sql);
            statement.setInt(1,blogId);
            //3.執(zhí)行SQL語(yǔ)句
            resultSet = statement.executeQuery();
            //4.遍歷結(jié)果集合, 由于 blogID 在 blog 表中是唯一的(它是主鍵)
            // 此時(shí)的查詢結(jié)果, 要么是沒(méi)有查到任何數(shù)據(jù), 要么只有一條記錄!!
            //此處可以不使用while,直接使用if即可
            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) {
            e.printStackTrace();
        } finally {
            //5.釋放必要的資源
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }

    //3.直接查出數(shù)據(jù)庫(kù)中所有的博客列表(用于博客列表頁(yè))
    public List<Blog> selectAll() {
        List<Blog> blogs = new ArrayList<>();
        //連接數(shù)據(jù)庫(kù)的對(duì)象
        Connection connection = null;
        //執(zhí)行預(yù)編譯SQL語(yǔ)句的對(duì)象
        PreparedStatement statement = null;
        //遍歷結(jié)果結(jié)合的對(duì)象
        ResultSet resultSet = null;
        try {
            //1.和數(shù)據(jù)庫(kù)建立連接
            connection = DBUtil.getConnection();
            //2.構(gòu)造SQL語(yǔ)句  在SQL中加入order by 讓 postTime 按降序排序
            String sql = "select * from blog order by postTime desc";
            //獲取執(zhí)行預(yù)編譯SQL語(yǔ)句的對(duì)象
            statement = connection.prepareStatement(sql);
            //3. 執(zhí)行SQL語(yǔ)句
            resultSet = statement.executeQuery();
            //4.遍歷結(jié)果結(jié)合
            while (resultSet.next()) {
                Blog blog = new Blog();
                blog.setBlogId(resultSet.getInt("blogId"));
                blog.setTitle(resultSet.getString("title"));
                //注意這里的正文!!! 在博客列表頁(yè)中, 我們不需要把整個(gè)正文內(nèi)容顯示出來(lái)!!
                String content = resultSet.getString("content");
                if(content.length() >= 100) {
                    content = content.substring(0,100) + "...";
                }
                blog.setContent(content);
                blog.setPostTime(resultSet.getTimestamp("postTime"));
                blog.setUserId(resultSet.getInt("userId"));
                blogs.add(blog);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //5.釋放必要的資源
            DBUtil.close(connection,statement,resultSet);
        }

        return blogs;
    }

    //4.刪除指定博客
    public void delete(int blogId) {
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            //1.和數(shù)據(jù)庫(kù)建立連接
            connection = DBUtil.getConnection();
            //2.構(gòu)造SQL語(yǔ)句
            String sql = "delete from blog where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1, blogId);
            //3.執(zhí)行SQL語(yǔ)句
            statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //4.釋放必要的資源
            DBUtil.close(connection, statement, null);
        }
    }
}

5.UserDao 類 (訪問(wèn)用戶數(shù)據(jù)的對(duì)象)

這也是一個(gè)數(shù)據(jù)訪問(wèn)對(duì)象, 執(zhí)行與用戶相關(guān)的數(shù)據(jù)庫(kù)操作, 這個(gè)類有兩個(gè)方法:

  • selectById(int userId):這個(gè)方法是根據(jù)用戶id查詢用戶。流程和上面的方法類似,只是查詢的是用戶信息。

  • selectByUsername(String username):這個(gè)方法是根據(jù)用戶名查詢用戶。這個(gè)方法主要用于用戶登錄的時(shí)候驗(yàn)證用戶身份。

//針對(duì)用戶表提供的基本操作
//由于此處沒(méi)有寫(xiě)注冊(cè)的功能, 也就不必 add
//也沒(méi)有用戶刪號(hào)功能, 也就不必 delete
public class UserDao {
    //1.根據(jù) userId 來(lái)查詢用戶信息
    public User selectById(int userId) {
        //獲取連接數(shù)據(jù)庫(kù)的對(duì)象
        Connection connection = null;
        //獲取執(zhí)行預(yù)編譯SQL語(yǔ)句的對(duì)象
        PreparedStatement statement = null;
        //獲取遍歷結(jié)果集合的對(duì)象
        ResultSet resultSet = null;
        try {
            //1.和數(shù)據(jù)庫(kù)建立連接
            connection = DBUtil.getConnection();
            //2.構(gòu)造SQL語(yǔ)句
            String sql = "select * from user where userId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1,userId);
            //3.執(zhí)行SQL語(yǔ)句
            resultSet = statement.executeQuery();
            //4.遍歷結(jié)果結(jié)果
            if (resultSet.next()) {
                User user = new User();
                user.setUserId(resultSet.getInt("userId"));
                user.setUsername(resultSet.getString("username"));
                user.setPassword(resultSet.getString("password"));
                return user;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //5.釋放必要的資源
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }

    //2.    根據(jù)username來(lái)查詢用戶信息(登錄的時(shí)候)
    public User selectByUsername(String username) {
        //獲取連接數(shù)據(jù)庫(kù)的對(duì)象
        Connection connection = null;
        //獲取執(zhí)行預(yù)編譯SQL語(yǔ)句的對(duì)象
        PreparedStatement statement = null;
        //獲取遍歷結(jié)果集合的對(duì)象
        ResultSet resultSet = null;
        try {
            //1.和數(shù)據(jù)庫(kù)建立連接
            connection = DBUtil.getConnection();
            //2.構(gòu)造SQL語(yǔ)句
            String sql = "select * from user where username = ?";
            statement = connection.prepareStatement(sql);
            statement.setString(1,username);
            //3.執(zhí)行SQL語(yǔ)句
            resultSet = statement.executeQuery();
            //4.遍歷結(jié)果結(jié)果
            if (resultSet.next()) {
                User user = new User();
                user.setUserId(resultSet.getInt("userId"));
                user.setUsername(resultSet.getString("username"));
                user.setPassword(resultSet.getString("password"));
                return user;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //5.釋放必要的資源
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }

}

這些類的注意事項(xiàng):

  1. User 類和 Blog 類中,你應(yīng)該確保每個(gè)屬性都有相應(yīng)的 getter 和 setter 方法。這些方法的實(shí)現(xiàn)通常是直接返回或者設(shè)置對(duì)應(yīng)的屬性。

  2. BlogDao 類和 UserDao 類中,你應(yīng)該確保每個(gè)數(shù)據(jù)庫(kù)操作都在 try-catch 塊中進(jìn)行,以便在出現(xiàn)異常時(shí)能夠捕獲到并進(jìn)行處理。此外,不要忘記在使用完 PreparedStatementResultSet 后關(guān)閉它們,否則可能會(huì)導(dǎo)致資源泄漏。

  3. DBUtil 類中,你應(yīng)該確保在獲取連接時(shí)檢查連接是否為 null,如果為 null,則說(shuō)明連接獲取失敗,應(yīng)該進(jìn)行相應(yīng)的處理。在關(guān)閉資源時(shí),也應(yīng)該檢查資源是否為 null,如果不為 null,則需要關(guān)閉它。

五.?博客系統(tǒng)的后端接口

  1. AuthorServlet:根據(jù)博客ID獲取博客作者的信息。

  2. BlogServlet:處理博客相關(guān)的請(qǐng)求,包括獲取博客列表和獲取指定博客的詳細(xì)信息,以及發(fā)布新的博客。

  3. LogoutServlet:處理用戶注銷的請(qǐng)求,將用戶從當(dāng)前會(huì)話中移除。

  4. LoginServlet:處理用戶登錄的請(qǐng)求,驗(yàn)證用戶名和密碼,并在驗(yàn)證通過(guò)后創(chuàng)建會(huì)話以保存用戶信息。

這些類通過(guò)使用不同的 URL 映射到相應(yīng)的 Servlet,并根據(jù)請(qǐng)求的類型(GET 或 POST)執(zhí)行相應(yīng)的操作。它們與模型(User、Blog)和數(shù)據(jù)訪問(wèn)對(duì)象(UserDaoBlogDao)一起工作,從數(shù)據(jù)庫(kù)中讀取和寫(xiě)入數(shù)據(jù)。

這個(gè)博客系統(tǒng)的 API 提供了用戶登錄、注銷、發(fā)布博客、獲取博客列表和獲取博客作者等基本功能,可以作為一個(gè)簡(jiǎn)單的博客系統(tǒng)的后端接口。

1.AuthorServlet 類 (處理關(guān)于作者的請(qǐng)求)

這個(gè)類是一個(gè)服務(wù)器端Servlet,其功能是處理關(guān)于作者信息的請(qǐng)求。具體來(lái)說(shuō),它從HTTP請(qǐng)求中獲取blogId參數(shù),然后基于這個(gè)blogId,首先從BlogDao中獲取對(duì)應(yīng)的Blog對(duì)象,再通過(guò)該Blog對(duì)象中的userId字段從UserDao中獲取對(duì)應(yīng)的User對(duì)象(即博客作者)。如果獲取的信息合法,它會(huì)將User對(duì)象以JSON格式返回給客戶端。

實(shí)現(xiàn)細(xì)節(jié)與步驟:

  • doGet 方法中,首先通過(guò) HttpServletRequest 對(duì)象的 getParameter 方法獲取請(qǐng)求參數(shù) "blogId"。
  • 如果 "blogId" 參數(shù)不存在或者無(wú)效,那么直接返回錯(cuò)誤信息,結(jié)束處理。
  • 如果 "blogId" 參數(shù)有效,那么通過(guò) BlogDaoselectById 方法獲取對(duì)應(yīng)的 Blog 對(duì)象,再通過(guò) Blog 對(duì)象的 getUserId 方法獲取對(duì)應(yīng)的用戶ID,然后通過(guò) UserDaoselectById 方法獲取對(duì)應(yīng)的 User 對(duì)象。
  • 如果這個(gè) User 對(duì)象存在,那么將其轉(zhuǎn)換為JSON格式,并寫(xiě)入到響應(yīng)的正文中。
@WebServlet("/author")
public class AuthorServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String blogId = req.getParameter("blogId");
        if(blogId == null) {
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("參數(shù)非法, 缺少 blogId");
            return;
        }
        //根據(jù) blogId 查詢 Blog 對(duì)象
        BlogDao blogDao = new BlogDao();
        Blog blog = blogDao.selectById(Integer.parseInt(blogId));
        if(blog == null) {
            //博客不存在
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("沒(méi)有找到指定博客: blogId = " + blogId);
            return;
        }
        //根據(jù) blog 中的 userId 找到對(duì)應(yīng)的用戶信息
        UserDao userDao = new UserDao();
        User author = userDao.selectById(blog.getUserId());

        //設(shè)置響應(yīng)正文的格式為 json格式的字符串
        resp.setContentType("application/json; charset=utf8");
        //將 author對(duì)象 轉(zhuǎn)換成 json 格式的字符串寫(xiě)入到響應(yīng)的 body 中
        objectMapper.writeValue(resp.getWriter(),author);
    }
}

2.BlogServlet 類 (處理關(guān)于的博客請(qǐng)求)

這個(gè)類是另一個(gè)服務(wù)器端Servlet,其功能主要是處理關(guān)于博客的請(qǐng)求,包括獲取博客列表和發(fā)布新博客兩部分功能。具體來(lái)說(shuō),它在處理GET請(qǐng)求時(shí),會(huì)根據(jù)請(qǐng)求參數(shù) "blogId" 來(lái)決定是返回所有博客的列表還是返回特定ID的博客;而在處理POST請(qǐng)求時(shí),會(huì)從請(qǐng)求中讀取 "title" 和 "content" 參數(shù),然后創(chuàng)建一個(gè)新的Blog對(duì)象并將其添加到數(shù)據(jù)庫(kù)中。

實(shí)現(xiàn)細(xì)節(jié)與步驟:

  • doGet 方法中,同樣是首先嘗試獲取請(qǐng)求參數(shù) "blogId"。如果 "blogId" 參數(shù)不存在,那么返回所有博客的列表;如果 "blogId" 參數(shù)存在,那么返回對(duì)應(yīng)ID的博客。
  • doPost 方法中,首先檢查用戶是否已經(jīng)登錄,然后從請(qǐng)求中讀取 "title" 和 "content" 參數(shù),并根據(jù)這些信息創(chuàng)建一個(gè)新的 Blog 對(duì)象,然后將這個(gè) Blog 對(duì)象添加到數(shù)據(jù)庫(kù)中。
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 嘗試獲取一下 queryString 中的 blogId 字段.
        String blogId =req.getParameter("blogId");
        BlogDao blogDao = new BlogDao();
        if(blogId == null) {
            //queryString 不存在,說(shuō)明這次請(qǐng)求是獲取博客列表頁(yè)
            List<Blog> blogs = blogDao.selectAll();
            //設(shè)置body(響應(yīng)正文)的格式
            resp.setContentType("application/json; charset=utf8");
            //直接把 blogs 轉(zhuǎn)換成符合要求的 json 格式字符串,同時(shí)寫(xiě)入http響應(yīng)的body中 寫(xiě)法一
            objectMapper.writeValue(resp.getWriter(),blogs);
            //
            /*//將blog先轉(zhuǎn)換成json字符串  寫(xiě)法二
            String respJson = objectMapper.writeValueAsString(blogs);
            //然后寫(xiě)入http響應(yīng)
            resp.getWriter().write(respJson);*/
        }else {
            //queryString存在,說(shuō)明本次請(qǐng)求獲取的是指定 id 的博客
            Blog blog = blogDao.selectById(Integer.parseInt(blogId));
            if(blog == null) {
                System.out.println("當(dāng)前blogId= " + blogId + "對(duì)應(yīng)的博客不存在!");
            }
            //設(shè)置body(響應(yīng)正文)的格式
            resp.setContentType("application/json; charset=utf8");
            //直接把 blog 轉(zhuǎn)換成符合要求的 json 格式字符串,同時(shí)寫(xiě)入http響應(yīng)的body中
            objectMapper.writeValue(resp.getWriter(),blog);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //發(fā)布博客
        //讀取請(qǐng)求,構(gòu)造 Blog 對(duì)象,插入數(shù)據(jù)庫(kù)即可!!

        HttpSession httpSession = req.getSession(false);
        if(httpSession == null) {
            resp.setContentType("text/http;charset=utf8");
            resp.getWriter().write("當(dāng)前未登錄, 無(wú)法發(fā)布博客");
            return;
        }
        User user = (User) httpSession.getAttribute("user");
        if(user == null) {
            resp.setContentType("text/http;charset=utf8");
            resp.getWriter().write("當(dāng)前未登錄, 無(wú)法發(fā)布博客!");
            return;
        }
        //確保登錄之后, 就可以把作者拿到了

        //獲取博客標(biāo)題和正文
        req.setCharacterEncoding("utf8");
        String title = req.getParameter("title");
        String content = req.getParameter("content");
        if(title == null || "".equals(title) || content == null || "".equals(content)) {
            resp.setContentType("text/http;charset=utf8");
            resp.getWriter().write("當(dāng)前提交數(shù)據(jù)有誤! 標(biāo)題或者正文為空!");
            return;
        }

        //構(gòu)造 Blog 對(duì)象
        Blog blog = new Blog();
        blog.setTitle(title);
        blog.setContent(content);
        blog.setUserId(user.getUserId());
        //發(fā)布時(shí)間,在java中生成/在數(shù)據(jù)庫(kù)中生成都行
        blog.setPostTime(new Timestamp(System.currentTimeMillis()));
        //插入數(shù)據(jù)庫(kù)
        BlogDao blogDao = new BlogDao();
        blogDao.add(blog);

        //跳轉(zhuǎn)到博客博客列表頁(yè)
        resp.sendRedirect("html/blog_list.html");
    }
}

3.LogoutServlet 類 (處理注銷的請(qǐng)求)

這個(gè)類的功能很簡(jiǎn)單,就是處理用戶的登出請(qǐng)求。在處理GET請(qǐng)求時(shí),它會(huì)從當(dāng)前的會(huì)話中移除 "user" 屬性,然后重定向到登錄頁(yè)面。

實(shí)現(xiàn)細(xì)節(jié)與步驟:

  • doGet 方法中,首先嘗試獲取當(dāng)前的會(huì)話,然后從會(huì)話中移除 "user" 屬性,最后通過(guò) HttpServletResponse 對(duì)象的 sendRedirect 方法重定向到登錄頁(yè)面。
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession httpSession = req.getSession(false);
        if(httpSession == null) {
            //未登錄狀態(tài), 就直接提示出錯(cuò).
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("當(dāng)前為登錄!");
            return;
        }
        httpSession.removeAttribute("user");
        resp.sendRedirect("html/login.html");
    }
}

4. LoginServlet 類 (處理登錄的請(qǐng)求)

這個(gè)類主要負(fù)責(zé)處理用戶登錄的請(qǐng)求。在處理POST請(qǐng)求時(shí),會(huì)從請(qǐng)求中獲取 "username" 和 "password" 參數(shù),并通過(guò) UserDaoselectByUsername 方法獲取對(duì)應(yīng)的 User 對(duì)象,然后比較用戶輸入的密碼和數(shù)據(jù)庫(kù)中存儲(chǔ)的密碼是否一致,如果一致,說(shuō)明登錄驗(yàn)證通過(guò),會(huì)在當(dāng)前會(huì)話中設(shè)置 "user" 屬性為對(duì)應(yīng)的 User 對(duì)象,最后重定向到博客列表頁(yè)。此外,這個(gè)類還提供了一個(gè)處理GET請(qǐng)求的方法,用于獲取當(dāng)前已登錄的用戶信息。

實(shí)現(xiàn)細(xì)節(jié)與步驟:

  • doPost 方法中,首先從請(qǐng)求中獲取 "username" 和 "password" 參數(shù),然后通過(guò) UserDaoselectByUsername 方法獲取對(duì)應(yīng)的 User 對(duì)象。
  • 然后比較用戶輸入的密碼和數(shù)據(jù)庫(kù)中存儲(chǔ)的密碼是否一致。如果一致,那么在當(dāng)前會(huì)話中設(shè)置 "user" 屬性為對(duì)應(yīng)的 User 對(duì)象,然后通過(guò) HttpServletResponse 對(duì)象的 sendRedirect 方法重定向到博客列表頁(yè)。
  • 如果用戶名不存在或密碼不一致,那么返回錯(cuò)誤信息,結(jié)束處理。
  • doGet 方法中,首先嘗試獲取當(dāng)前的會(huì)話,然后從會(huì)話中獲取 "user" 屬性。如果這個(gè) User 對(duì)象存在,那么將其轉(zhuǎn)換為JSON格式,并寫(xiě)入到響應(yīng)的正文中。
@WebServlet("/login")
public class LonginServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //設(shè)置請(qǐng)求的編碼格式, 告訴 servlet 按照啥格式來(lái)理解請(qǐng)求
        req.setCharacterEncoding("utf8");
        //設(shè)置響應(yīng)的編碼格式, 告訴 servlet 按照啥格式來(lái)理解響應(yīng)
        //resp.setCharacterEncoding("utf8");

        resp.setContentType("text/html;charset=utf8");
        //1.讀取參數(shù)中的用戶名和密碼
        //注意!! 如果用戶名和密碼包括中文,此處可能會(huì)亂碼
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if(username == null || "".equals(username) || password == null || "".equals(password)) {
            //登錄失敗
            String html = "<h3> 登錄失敗! 缺少username 或者 password 字段 </h3>";
            resp.getWriter().write(html);
            return;
        }
        //2.讀數(shù)據(jù)庫(kù),看看用戶名是否存在,并且密碼是否匹配
        UserDao userDao = new UserDao();
        User user = userDao.selectByUsername(username);
        if(user == null) {
            //用戶不存在
            String html = "<h3> 登錄失敗! 用戶名或者密碼錯(cuò)誤 </h3>";
            resp.getWriter().write(html);
            return;
        }
        if(!password.equals(user.getPassword())) {
            //密碼不對(duì)
            String html = "<h3> 登錄失敗! 用戶名或者密碼錯(cuò)誤 </h3>";
            resp.getWriter().write(html);
            return;
        }
        //3.用戶名和密碼驗(yàn)證成功, 登錄成功,接下來(lái)就會(huì)創(chuàng)建會(huì)話, 使用該會(huì)話保存用戶的信息
        HttpSession session = req.getSession();
        session.setAttribute("user",user);
        //4.進(jìn)行重定向,跳轉(zhuǎn)到博客列表頁(yè)
        resp.sendRedirect("html/blog_list.html");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("application/json;charset=utf8");
        //使用這個(gè)方法獲取到用戶的登錄狀態(tài)

        //如果未登錄,這里的會(huì)話就拿不到!!
        HttpSession session = req.getSession(false);
        if(session == null) {
            //未登錄 , 返回一個(gè)空user對(duì)象
            User user = new User();
            //將user對(duì)象轉(zhuǎn)換成json格式的字符串寫(xiě)入響應(yīng)的body中
            objectMapper.writeValue(resp.getWriter(),user);
            return;
        }
        User user = (User)session.getAttribute("user");
        if(user == null) {
            user = new User();
            //將user對(duì)象轉(zhuǎn)換成json格式的字符串寫(xiě)入響應(yīng)的body中
            objectMapper.writeValue(resp.getWriter(),user);
            return;
        }
        //確實(shí)成功取出了對(duì)象,直接返回即可
        //將user對(duì)象轉(zhuǎn)換成json格式的字符串寫(xiě)入響應(yīng)的body中
        objectMapper.writeValue(resp.getWriter(),user);
    }
}

六. 前端代碼構(gòu)造請(qǐng)求

1. 登陸頁(yè)面功能設(shè)計(jì)?

  1. 表單(Form):整個(gè)登錄頁(yè)面被包含在一個(gè)HTML表單中。表單被設(shè)計(jì)用于收集用戶輸入的數(shù)據(jù),這里的數(shù)據(jù)就是用戶名和密碼。表單被發(fā)送到../login這個(gè)路徑,使用的是POST方法,意味著提交的數(shù)據(jù)將在請(qǐng)求體中發(fā)送。

  2. 用戶名和密碼輸入框:這兩個(gè)輸入框分別用于接收用戶的用戶名和密碼信息。用戶名輸入框的占位符提示用戶可以輸入手機(jī)號(hào)或郵箱作為用戶名。密碼輸入框類型為password,所以輸入的密碼信息不會(huì)被顯示出來(lái),以保護(hù)用戶的安全。

  3. 提交按鈕:用戶在輸入完用戶名和密碼后,可以點(diǎn)擊這個(gè)按鈕提交表單。提交后,用戶名和密碼信息將被發(fā)送到服務(wù)器進(jìn)行驗(yàn)證。

<!-- 登錄頁(yè)面的版心 -->
    <div class="login-container">
        <!-- 使用from包裹下列內(nèi)容,便于后續(xù)給服務(wù)器提交數(shù)據(jù) -->
        <form action="../login" method="post">
            <!-- 登錄對(duì)話框 -->
        <div class="login-dialog">
            <h3>登錄</h3>
            <div class="row">
                <span>用戶名</span>
                <input type="text" id="username" placeholder="手機(jī)號(hào)/郵箱" name="username">
            </div>
            <div class="row">
                <span>密碼</span>
                <input type="password" id="password" name="password">
            </div>
            <div class="row">
                <input type="submit" id="submit" value="登錄">
            </div>
        </div>
        </form>
    </div>

2. 博客列表頁(yè)面功能設(shè)計(jì)

  • 獲取并展示博客列表:通過(guò)調(diào)用getBlogs函數(shù),在頁(yè)面加載時(shí)向服務(wù)器發(fā)起GET請(qǐng)求獲取博客列表數(shù)據(jù)。這個(gè)請(qǐng)求的響應(yīng)體被期望為一個(gè)包含多個(gè)博客信息的JSON數(shù)組。每一個(gè)博客對(duì)象包含了博客的標(biāo)題、發(fā)布時(shí)間和內(nèi)容等信息。之后,代碼通過(guò)DOM操作將這些博客信息顯示在頁(yè)面上。每一篇博客在頁(yè)面上以一個(gè)div元素的形式存在,包括標(biāo)題、發(fā)布時(shí)間、內(nèi)容摘要和一個(gè)查看全文的鏈接。點(diǎn)擊查看全文的鏈接會(huì)跳轉(zhuǎn)到博客詳情頁(yè),并且通過(guò)查詢字符串參數(shù)把博客的ID傳遞給詳情頁(yè)。

詳細(xì)步驟如下:

  1. 請(qǐng)求博客列表:頁(yè)面加載時(shí),getBlogs函數(shù)被調(diào)用。在這個(gè)函數(shù)中,jQuery的$.ajax方法用于向服務(wù)器發(fā)送一個(gè)GET請(qǐng)求,目標(biāo)URL為'../blog'。

  2. 處理響應(yīng):服務(wù)器將以一個(gè)包含多個(gè)博客對(duì)象的JSON數(shù)組形式返回博客列表。這個(gè)JSON數(shù)組已經(jīng)被jQuery自動(dòng)解析成JavaScript的對(duì)象數(shù)組。

  3. 遍歷博客對(duì)象:每一個(gè)博客對(duì)象都包含了博客的標(biāo)題(title)、發(fā)布時(shí)間(postTime)、內(nèi)容(content)等信息。getBlogs函數(shù)中的for循環(huán)對(duì)數(shù)組進(jìn)行遍歷,每次遍歷都會(huì)處理一個(gè)博客對(duì)象。

  4. 構(gòu)建博客元素:每個(gè)博客對(duì)象都會(huì)被轉(zhuǎn)化為HTML元素以顯示在頁(yè)面上。為此,getBlogs函數(shù)創(chuàng)建了包含標(biāo)題、發(fā)布時(shí)間、內(nèi)容摘要和查看全文鏈接的div元素。其中,查看全文的鏈接會(huì)帶有一個(gè)查詢字符串參數(shù),這個(gè)參數(shù)包含了當(dāng)前博客的ID,可以用于在詳情頁(yè)識(shí)別出具體的博客。

  5. 添加到頁(yè)面:構(gòu)建完的博客div元素會(huì)被添加到頁(yè)面的'.container-right'元素中,以在頁(yè)面上顯示出博客的信息。

  • 檢查登錄狀態(tài)checkLogin函數(shù)通過(guò)向服務(wù)器發(fā)起GET請(qǐng)求到../login,檢查用戶的登錄狀態(tài)。如果響應(yīng)體中包含有用戶ID,并且用戶ID大于0,那么就認(rèn)為用戶已經(jīng)登錄,然后在頁(yè)面上顯示出用戶的用戶名。如果沒(méi)有用戶ID,或者用戶ID不大于0,那么就認(rèn)為用戶未登錄,代碼會(huì)強(qiáng)制跳轉(zhuǎn)到登錄頁(yè)面。

整個(gè)過(guò)程是異步的,即瀏覽器不會(huì)等待博客列表的請(qǐng)求和處理過(guò)程完成就繼續(xù)執(zhí)行其他的JavaScript代碼。相反,當(dāng)請(qǐng)求的響應(yīng)到達(dá)時(shí),jQuery會(huì)自動(dòng)調(diào)用預(yù)定義的成功回調(diào)函數(shù)來(lái)處理響應(yīng)數(shù)據(jù)。

<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
    <script>
        // 在頁(yè)面加載時(shí), 向服務(wù)器發(fā)起請(qǐng)求, 獲取博客列表數(shù)據(jù)
        function getBlogs() {
            $.ajax({
                type: 'get',
                url: '../blog',
                success: function(body) {
                    // 響應(yīng)的正文 是一個(gè) json 字符串, 此處已經(jīng)被 jquery 自動(dòng)解析成 js 對(duì)象數(shù)組了. 
                    // 直接 for 循環(huán)遍歷即可.
                    console.log(body); 
                    let containerRight = document.querySelector('.container-right');
                    for (let blog of body) {
                        // 構(gòu)造頁(yè)面內(nèi)容, 參考之前寫(xiě)好的 html 代碼
                        // 構(gòu)造整個(gè)博客 div
                        let blogDiv = document.createElement('div');
                        blogDiv.className = 'blog';
                        // 構(gòu)造標(biāo)題
                        let titleDiv = document.createElement('div');
                        titleDiv.className = 'title';
                        titleDiv.innerHTML = blog.title;
                        blogDiv.appendChild(titleDiv);
                        // 構(gòu)造發(fā)布時(shí)間
                        let dateDiv = document.createElement('div');
                        dateDiv.className = 'date';
                        dateDiv.innerHTML = blog.postTime;
                        blogDiv.appendChild(dateDiv);
                        // 構(gòu)造 博客 摘要
                        let descDiv = document.createElement('div');
                        descDiv.className = 'desc';
                        descDiv.innerHTML = blog.content;
                        blogDiv.appendChild(descDiv);
                        // 構(gòu)造查看全文按鈕
                        let a = document.createElement('a');
                        a.innerHTML = '查看全文 &gt;&gt;';
                        // 期望點(diǎn)擊之后能跳轉(zhuǎn)到博客詳情頁(yè). 為了讓博客詳情頁(yè)知道是點(diǎn)了哪個(gè)博客, 把 blogId 給傳過(guò)去
                        a.href = 'blog_detail.html?blogId=' + blog.blogId;
                        blogDiv.appendChild(a);

                        // 把 blogDiv 加到父元素中
                        containerRight.appendChild(blogDiv);
                    }
                }
            });
        }

        // 要記得調(diào)用
        getBlogs();

        function checkLogin() {
            $.ajax({
                type: 'get',
                url: '../login',
                success: function(body) {
                    if(body.userId && body.userId >0) {
                        //登錄成功
                        console.log("當(dāng)前用戶已經(jīng)登錄!");
                        //加上這個(gè)功能,把當(dāng)前用戶的名字顯示到界面上
                        let h3 = document.querySelector('.container-left .card h3');  
                        h3.innerHTML = body.username;
                    }else {
                        //當(dāng)前未登錄
                        //強(qiáng)制跳轉(zhuǎn)到登錄頁(yè)
                        location.assign('login.html');
                    }
                }
            });
        }
        
        checkLogin();
    </script>

3. 博客詳情頁(yè)面功能設(shè)計(jì)?

  • 獲取博客詳情:在頁(yè)面加載后,執(zhí)行一個(gè)ajax請(qǐng)求(即異步HTTP請(qǐng)求),以獲取博客的具體信息。此請(qǐng)求使用GET方法,URL是 '../blog' + location.search。這里的 location.search 包含當(dāng)前URL的查詢部分,用于指示服務(wù)器我們想要獲取哪篇博客的信息。一旦請(qǐng)求成功,服務(wù)器將返回一個(gè)包含博客信息的JSON對(duì)象。

  • 處理和展示博客詳情:在ajax請(qǐng)求成功后,會(huì)觸發(fā)success回調(diào)函數(shù)。在這個(gè)函數(shù)中,我們獲取JSON對(duì)象中的博客信息,然后把這些信息插入到頁(yè)面對(duì)應(yīng)的元素中。標(biāo)題和日期信息直接添加到對(duì)應(yīng)的div元素中。而博客的內(nèi)容,因?yàn)橥ǔJ荕arkdown格式,需要先轉(zhuǎn)換成HTML。這個(gè)轉(zhuǎn)換過(guò)程由editormd庫(kù)提供的 markdownToHTML 函數(shù)完成。

  • 檢查用戶登錄狀態(tài)checkLogin 函數(shù)通過(guò)發(fā)送一個(gè)GET請(qǐng)求到 '../login' URL,來(lái)獲取當(dāng)前登錄用戶的信息。如果請(qǐng)求成功,success回調(diào)函數(shù)會(huì)被調(diào)用。在這個(gè)函數(shù)中,我們檢查服務(wù)器返回的JSON對(duì)象中的userId屬性。如果這個(gè)屬性存在且大于0,就認(rèn)為用戶已經(jīng)登錄;否則,我們認(rèn)為用戶尚未登錄,然后將頁(yè)面重定向到登錄頁(yè)。

  • 獲取作者信息getAuthor 函數(shù)通過(guò)向 '../author' + location.search URL發(fā)送GET請(qǐng)求,來(lái)獲取博客作者的信息。如果請(qǐng)求成功,success回調(diào)函數(shù)會(huì)被調(diào)用。在這個(gè)函數(shù)中,我們從服務(wù)器返回的JSON對(duì)象中獲取作者的用戶名,然后將這個(gè)用戶名顯示到頁(yè)面上。

這些函數(shù)在頁(yè)面加載后會(huì)自動(dòng)調(diào)用,從而實(shí)現(xiàn)上述功能。在調(diào)用這些函數(shù)之前,需要先加載所依賴的庫(kù)(即jQuery和editormd)。這是通過(guò) <script src="..."></script> 標(biāo)簽完成的。在這些標(biāo)簽中,src屬性指向庫(kù)文件的URL。

<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
    <!-- 要保證這幾個(gè) js 的加載在jquery之后,因?yàn)閑ditor.md 依賴了 jquery -->
    <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>

    <script>
        $.ajax({
            type: 'get',
            url: '../blog' + location.search,
            success: function(body) {
                //處理響應(yīng)結(jié)果,此處的body就是表示一個(gè)博客的js對(duì)象
                //1.更新標(biāo)題
                let titleDiv = document.querySelector(".container-right .title");
                titleDiv.innerHTML = body.title;     
                //2.更新日期
                let dateDiv = document.querySelector('.date');
                dateDiv.innerHTML = body.postTime; 
                //3.更新博客正文
                //此處不應(yīng)該把博客正文添加到這個(gè)標(biāo)簽里面
                //editormd里面有一個(gè)api,能將Markdown格式的字符串轉(zhuǎn)換成HLML,輸出到#conten
                editormd.markdownToHTML('content',{ markdown: body.content });
            }
        });

        function checkLogin() {
            $.ajax({
                type: 'get',
                url: '../login',
                success: function(body) {
                    if(body.userId && body.userId >0) {
                        //登錄成功
                        console.log("當(dāng)前用戶已經(jīng)登錄!");
                    }else {
                        //當(dāng)前未登錄
                        //強(qiáng)制跳轉(zhuǎn)到登錄頁(yè)
                        location.assign('login.html');
                    }
                }
            });
        }
        checkLogin();

        //這是函數(shù)定義
        function getAuthor() {
            $.ajax({
                type: 'get',
                url: '../author' + location.search,
                success: function(body) {
                    //把 username 設(shè)置到界面上
                    let h3  = document.querySelector('.container-left .card h3');
                    h3.innerHTML = body.username;
                }
            });
        }
        
        //這里函數(shù)調(diào)用該函數(shù)才會(huì)執(zhí)行
        getAuthor();
    </script>

4. 博客編輯頁(yè)面功能設(shè)計(jì)

  1. HTML表單設(shè)計(jì):這部分的代碼定義了用戶用于輸入和提交博客內(nèi)容的表單。表單內(nèi)包含一個(gè)用于輸入標(biāo)題的文本框以及一個(gè)用于提交文章的按鈕。除此之外,還有一個(gè)占位的<textarea>元素,這個(gè)元素被設(shè)置為不顯示(style="display: none;"),是為了與Markdown編輯器對(duì)接,讓編輯器的內(nèi)容可以被提交到服務(wù)器。

  2. Markdown編輯器初始化:通過(guò)調(diào)用editormd()函數(shù),對(duì)Markdown編輯器進(jìn)行初始化。編輯器的尺寸設(shè)置為全寬(width: "100%")和高度為父元素高度減去50像素(height: "calc(100% - 50px)"),50像素是預(yù)留給標(biāo)題的空間。編輯器的初始內(nèi)容被設(shè)置為"# 在這里寫(xiě)下一篇博客",這是一個(gè)Markdown格式的標(biāo)題。editormd()函數(shù)中的path參數(shù)設(shè)置了Markdown編輯器庫(kù)依賴插件的路徑。

  3. 用戶登錄狀態(tài)的檢查:在checkLogin()函數(shù)中,使用jQuery的$.ajax()方法向服務(wù)器發(fā)送一個(gè)GET請(qǐng)求,目標(biāo)URL為'../login'。如果請(qǐng)求成功,會(huì)獲取到服務(wù)器返回的JSON對(duì)象,檢查其中的userId屬性。如果userId存在且大于0,說(shuō)明用戶已經(jīng)登錄,將在控制臺(tái)打印"當(dāng)前用戶已經(jīng)登錄!"。如果userId不存在或者不大于0,說(shuō)明用戶未登錄,那么會(huì)強(qiáng)制將頁(yè)面重定向到登錄頁(yè)面。

通過(guò)這三個(gè)步驟,實(shí)現(xiàn)了博客編輯頁(yè)的基本功能設(shè)計(jì):提供了一個(gè)用戶友好的Markdown編輯器界面供用戶編寫(xiě)博客,同時(shí)確保只有登錄的用戶才能訪問(wèn)編輯頁(yè)面和發(fā)布博客。

<!-- 博客編輯頁(yè)的版心 -->
    <div class="blog-edit-container">
        <!--  -->
        <form action="../blog" method="post">
            <!-- 標(biāo)題編輯區(qū) -->
        <div class="title">
            <input type="text" placeholder="在此處輸入標(biāo)題" name="title">
            <input type="submit" id="submit" value="發(fā)布文章">
        </div>
        <!-- 博客編輯器 -->
        <!-- 把md編輯器放到這個(gè)div中 -->
        <!-- 博客編輯器, 這里用 id 是為了和 markdown 編輯器對(duì)接 -->
        <div id="editor">
            <textarea name="content" style="display: none;"></textarea>
        </div>
        </form>
    </div>

    <script src="../js/jquery.min.js"></script>
    <script src="../editor.md/lib/marked.min.js"></script>
    <script src="../editor.md/lib/prettify.min.js"></script>
    <script src="../editor.md/editormd.js"></script>
    <script>
        // 初始化編輯器
        let editor = editormd("editor", {
            // 這里的尺寸必須在這里設(shè)置. 設(shè)置樣式會(huì)被 editormd 自動(dòng)覆蓋掉. 
            width: "100%",
            // 設(shè)定編輯器高度
            height: "calc(100% - 50px)", /* 減 titile 的高度 */
            // 編輯器中的初始內(nèi)容
            markdown: "# 在這里寫(xiě)下一篇博客",
            // 指定 editor.md 依賴的插件路徑
            path: "../editor.md/lib/"
        });

        function checkLogin() {
            $.ajax({
                type: 'get',
                url: '../login',
                success: function(body) {
                    if(body.userId && body.userId >0) {
                        //登錄成功
                        console.log("當(dāng)前用戶已經(jīng)登錄!");
                    }else {
                        //當(dāng)前未登錄
                        //強(qiáng)制跳轉(zhuǎn)到登錄頁(yè)
                        location.assign('login.html');
                    }
                }
            });
        }
        checkLogin();
    </script>

5. 用戶注銷功能設(shè)計(jì)

  • 通過(guò)a標(biāo)跳轉(zhuǎn)去構(gòu)造請(qǐng)求,后端接口接收到請(qǐng)求去幫我們完成注銷的邏輯
  1. 獲取到session
  2. 如果session不為空,將session中保存的user刪除
  3. 刪除后跳轉(zhuǎn)到用戶登陸頁(yè)面?

【基于前后端分離的博客系統(tǒng)】Servlet版本

?后端收到請(qǐng)求開(kāi)始執(zhí)行注銷操作:【基于前后端分離的博客系統(tǒng)】Servlet版本

七.擴(kuò)展功能

?實(shí)現(xiàn)刪除博客

刪除博客,肯定不是可以隨便刪除的,約定只有自己能刪除自己的博客,不能刪除別人的博客?? (此處咱們暫時(shí)不考慮管理員)

界面上的處理:?在博客詳情頁(yè)這里,就去進(jìn)行判定,判定看當(dāng)前這個(gè)博客的作者,是否就是登錄的用戶。如果是,就在導(dǎo)航欄里顯示一個(gè)“刪除按鈕";如果不是,就不顯示刪除按鈕
服務(wù)器處理:?用戶點(diǎn)擊刪除按鈕,觸發(fā)一個(gè)HTTP請(qǐng)求, HTTP請(qǐng)求就會(huì)讓服務(wù)器刪除指定的博客,服務(wù)器收到請(qǐng)求之后,就會(huì)把這個(gè)博客從數(shù)據(jù)庫(kù)里給刪除掉

實(shí)現(xiàn)客戶端代碼:

<!-- 博客詳情頁(yè) -->
<!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>博客詳情頁(yè)</title>
    <!-- ../來(lái)到上一級(jí)目錄 -->
    <link rel="stylesheet" href="../css/common.css">
    <link rel="stylesheet" href="../css/blog_detail.css">   
    <link rel="stylesheet" href="../editor.md/css/editormd.min.css">
</head>
<body>
    <!-- 導(dǎo)航欄 -->
    <div class="nav">
        <img src="../imgs/demo4.jpg" alt="盈寶">
        <span>我的博客系統(tǒng)</span>
        <!-- 只是空白元素,用來(lái)將后面的超鏈接文本擠到右邊去 -->
        <div class="spacer"></div>
        <a href="blog_list.html">主頁(yè)</a>
        <a href="blog_edit.html">寫(xiě)博客</a>
        <a href="../logout">注銷</a>
        <form action="../blogDelete" method="get">
        <!-- <input> 標(biāo)簽主要用于用戶輸入數(shù)據(jù), -->
        <!-- 其 type="hidden" 屬性意味著這個(gè)輸入字段在頁(yè)面上是不可見(jiàn)的。 -->
        <!-- 這種輸入字段通常用于在表單提交時(shí)存儲(chǔ)或傳遞信息,而不需要用戶進(jìn)行交互。 -->
        <input type="hidden" name="blogId" id="blogId">
        <!-- 這里,設(shè)置了 display 屬性為 none, -->
        <!-- //這意味著該元素不會(huì)在頁(yè)面上顯示。即使元素占用的空間也會(huì)消失,就好像這個(gè)元素從來(lái)就不存在于頁(yè)面上一樣。 -->
        <button id="deleteButton" style="display:none">刪除博客</button>
        </form>
    </div>

    <!-- 頁(yè)面的主體部分 -->
    <!-- .container 作為頁(yè)面的版心 -->
    <!-- 這個(gè)部分是在頁(yè)面中 水平居中對(duì)齊的 左右兩側(cè)會(huì)留出一定的邊距 這個(gè)東西很多網(wǎng)站都有 稱為"版心" -->
    <div class="container">
        <!-- 左側(cè)個(gè)人信息 -->
        <div class="container-left">
            <!-- 整個(gè)用戶信息區(qū) -->
            <div class="card">
                <!-- 用戶的頭像 -->
                <img src="../imgs/demo1.png" alt="越前龍馬">
                <!-- 用戶名 -->
                <h3>吳少的博客</h3>
                <!-- gitee地址 -->
                <a >gitee地址</a>
                <!-- 統(tǒng)計(jì)信息 -->
                <div class="counter">
                    <span>文章</span>
                    <span>分類</span>
                </div>               
                <div class="counter">
                    <!-- 兩篇文章,一個(gè)分類 -->
                    <span class="blogconunt">2</span>
                    <span>1</span>
                </div>
            </div>
        </div>

        <!-- 右側(cè)內(nèi)容詳情 -->
        <div class="container-right">
            <div class="coatain">
                <!-- 博客標(biāo)題 -->
                <h3 class="title"></h3>
                <!-- 博客日期 -->
                <div class="date"></div>
                <!-- 博客正文 為了配合 editormd 進(jìn)行格式轉(zhuǎn)換,此處務(wù)必改成id -->
                <div id ="content">
    
                </div>
            </div>
        </div>
    </div>
    <script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
    <!-- 要保證這幾個(gè) js 的加載在jquery之后,因?yàn)閑ditor.md 依賴了 jquery -->
    <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>

    <script>
        $.ajax({
            type: 'get',
            url: '../blog' + location.search,
            success: function(body) {
                //處理響應(yīng)結(jié)果,此處的body就是表示一個(gè)博客的js對(duì)象
                //1.更新標(biāo)題
                let titleDiv = document.querySelector(".container-right .title");
                titleDiv.innerHTML = body.title;     
                //2.更新日期
                let dateDiv = document.querySelector('.date');
                dateDiv.innerHTML = body.postTime; 
                //3.更新博客正文
                //此處不應(yīng)該把博客正文添加到這個(gè)標(biāo)簽里面
                //editormd里面有一個(gè)api,能將Markdown格式的字符串轉(zhuǎn)換成HLML,輸出到#conten
                editormd.markdownToHTML('content',{ markdown: body.content });

                //獲取到博客詳情頁(yè)是,同時(shí)設(shè)置 blogId
                document.getElementById('blogId').value = body.blogId;
            }
        });


        function checkLogin() {
            return new Promise((resolve, reject) => {
                $.ajax({
                    type: 'get',
                    url: '../login',
                    success: function(body) {
                        if(body.userId && body.userId >0) {
                            resolve(body.userId);
                        } else {
                            location.assign('login.html');
                            reject();
                        }
                    }
                });
            });
        }

        // checkLogin();

        function getAuthor() {
            return new Promise((resolve, reject) => {
                $.ajax({
                    type: 'get',
                    url: '../author' + location.search,
                    success: function(body) {
                        resolve(body.userId);
                        //每次進(jìn)入博客詳情頁(yè)的時(shí)候,獲取這篇或者對(duì)應(yīng)的作者名字
                        let h3 =document.querySelector(".container-left .card h3");
                        h3.innerHTML =body.username;
                    }
                });
            });
        }
                
        //這里函數(shù)調(diào)用該函數(shù)才會(huì)執(zhí)行
        // getAuthor()

        window.onload = function() {
            Promise.all([checkLogin(), getAuthor()]).then((values) => {
                if(values[0] === values[1]) {
                    document.getElementById('deleteButton').style.display = 'block';
                }
            });
        }



        //獲取文章數(shù)量
        function getBlogCount() {
            $.ajax({
                type:'get',
                url:'../count',
                success: function(body) {
                    let blogconunt =document.querySelector(".blogconunt");
                    blogconunt.innerHTML =body.length;
                }
            });
        }
        getBlogCount();
    </script>
</body>
</html>

代碼重點(diǎn)部分解析:(使用Promise解決ajax的異步問(wèn)題,實(shí)現(xiàn)刪除邏輯)

  • 這段代碼的目的是在網(wǎng)頁(yè)加載完成后,比較當(dāng)前登錄用戶的ID與博客作者的ID是否相同,如果相同則顯示刪除按鈕。
  • 其中使用了 Promise 和 Promise.all 這兩個(gè)JavaScript的概念。
  • 首先了解一下 Promise,Promise 是 JavaScript 中用來(lái)處理異步操作的一種對(duì)象。它有三種狀態(tài):pending(等待),fulfilled(成功),rejected(失?。romise 對(duì)象接受一個(gè)函數(shù)作為參數(shù),這個(gè)函數(shù)接收兩個(gè)參數(shù):resolve 和 reject,分別表示異步操作成功和失敗后的回調(diào)函數(shù)。
  • checkLogingetAuthor 這兩個(gè)函數(shù)中,它們都返回一個(gè) Promise 對(duì)象。這兩個(gè)函數(shù)都發(fā)送了一個(gè) ajax 請(qǐng)求,成功后通過(guò) resolve 函數(shù)返回結(jié)果。
  • window.onload 事件在頁(yè)面加載完成后觸發(fā),這里使用了 Promise.all 方法。這個(gè)方法接收一個(gè) Promise 對(duì)象的數(shù)組作為參數(shù),返回一個(gè)新的 Promise 對(duì)象。新的 Promise 對(duì)象在數(shù)組中所有 Promise 對(duì)象都成功時(shí)才會(huì)成功,返回值是一個(gè)數(shù)組,包含了每個(gè) Promise 對(duì)象的返回值。如果有一個(gè) Promise 對(duì)象失敗,新的 Promise 對(duì)象就失敗。
  • 這里 Promise.all([checkLogin(), getAuthor()]) 返回一個(gè)新的 Promise 對(duì)象,它會(huì)在 checkLogingetAuthor 這兩個(gè)異步操作都成功后才會(huì)成功。它的返回值 values 是一個(gè)數(shù)組,包含了 checkLogingetAuthor 的返回值,也就是 userId 和 authorId。然后比較這兩個(gè)值是否相等,如果相等就顯示刪除按鈕。
  • 也就是說(shuō),這段代碼實(shí)現(xiàn)了等待 checkLogingetAuthor 這兩個(gè)異步操作都完成后,再進(jìn)行后續(xù)的處理。

圖解如下:

【基于前后端分離的博客系統(tǒng)】Servlet版本

?服務(wù)器端代碼:(邏輯比較簡(jiǎn)單,大家看注釋即可)

@WebServlet("/blogDelete") 
public class BlogDeleteServlet extends HttpServlet { 

    @Override 
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 設(shè)置請(qǐng)求的字符編碼為UTF-8。
        req.setCharacterEncoding("utf-8");
        // 從請(qǐng)求中獲取名為"blogId"的參數(shù)。這個(gè)參數(shù)應(yīng)該是要?jiǎng)h除的博客的ID。
        String blogId = req.getParameter("blogId");
        
        // 創(chuàng)建一個(gè)BlogDao對(duì)象,用于操作數(shù)據(jù)庫(kù)。
        BlogDao blogDao = new BlogDao(); 
        // 調(diào)用BlogDao的delete方法,傳入要?jiǎng)h除的博客的ID。注意,這里將blogId從String轉(zhuǎn)換為了int。
        blogDao.delete(Integer.parseInt(blogId));

        // 重定向用戶到博客列表頁(yè)。重定向是將用戶的瀏覽器從一個(gè)URL導(dǎo)向到另一個(gè)URL。
        resp.sendRedirect("html/blog_list.html"); 
    }
}

實(shí)現(xiàn)注冊(cè)功能:

前端代碼的邏輯如下圖:【基于前后端分離的博客系統(tǒng)】Servlet版本

?后端代碼:

  • 后端這里解析請(qǐng)求的數(shù)據(jù)后,我這里做了一下簡(jiǎn)單的判斷,防止前端程序員代碼出錯(cuò)所以做了一個(gè)二次驗(yàn)證,符合注冊(cè)要求就將數(shù)據(jù)提交給數(shù)據(jù)庫(kù)
package api;

import model.Blog;
import model.UserDao;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: WuYimin
 * Date: 2023-06-04
 * Time: 12:14
 */
@WebServlet("/register")
public class RegisterServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("Utf-8");
        resp.setContentType("text/html;charset=utf8");

        //從http請(qǐng)求獲取參數(shù)
        String username = req.getParameter("username");
        String password1 = req.getParameter("password1");
        String password2 = req.getParameter("password2");

        if(username == null || username.equals("")) {
            resp.getWriter().write("<h3> 您未輸入用戶名</h3>");
            return;
        }
        if(password1 == null || password1.equals("")) {
            resp.getWriter().write("<h3> 您未輸入密碼</h3>");
            return;
        }
        if(password2 == null || password2.equals("")) {
            resp.getWriter().write("<h3> 您未再次輸入密碼</h3>");
            return;
        }

        //判斷第二次輸入密碼是否正確
        if(!password1.equals(password2)) {
            resp.getWriter().write("<h3> 您兩次輸入的密碼不一致,請(qǐng)返回,并重新輸入! </h3>");
            return;
        }

        //判斷數(shù)據(jù)庫(kù)是否有重名的用戶
        UserDao userDao = new UserDao();
        if(userDao.selectByUsername(username) != null) {
            resp.getWriter().write("<h3> 非常抱歉,你注冊(cè)的用戶名已經(jīng)被其他用戶使用,請(qǐng)換一個(gè)試試吧~</h3>");
            return;
        }
        //代碼走到這里,說(shuō)明一切正常
        //開(kāi)始往數(shù)據(jù)庫(kù)插入數(shù)據(jù)
        userDao.adduser(username,password1);
        //一切都完成后,重定向至登錄界面
        resp.sendRedirect("html/login.html");
    }
}

實(shí)現(xiàn)文章數(shù)量動(dòng)態(tài)變化:

  • 這里的需求就是在用戶刪除或添加一篇博客以后,文章的數(shù)量也會(huì)隨著跟著增加或減少

后端代碼:(邏輯非常簡(jiǎn)單,從數(shù)據(jù)庫(kù)取出所有blog對(duì)象放在一個(gè)鏈表返回給客戶端)

@WebServlet("/count") 
public class GetCountServlet extends HttpServlet { 

    private ObjectMapper objectMapper = new ObjectMapper(); // 創(chuàng)建一個(gè)ObjectMapper對(duì)象,該對(duì)象是Jackson庫(kù)中的類,用于處理JSON。

    // 將所有博客返回給請(qǐng)求數(shù)據(jù)的客戶端
    @Override 
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
        
        // 設(shè)置請(qǐng)求的字符編碼為UTF-8。
        req.setCharacterEncoding("utf8"); 
        
         // 設(shè)置響應(yīng)的MIME類型為application/json,并設(shè)置字符編碼為UTF-8。這表示響應(yīng)將是JSON格式的。
        resp.setContentType("application/json;charset=utf8");
        
        // 創(chuàng)建一個(gè)BlogDao對(duì)象,用于操作數(shù)據(jù)庫(kù)。
        BlogDao blogDao = new BlogDao(); 

        // 調(diào)用BlogDao的selectAll方法,獲取所有的博客。
        List<Blog> blogs = blogDao.selectAll(); 
        
        // 使用ObjectMapper將博客列表轉(zhuǎn)換為JSON格式,并寫(xiě)入響應(yīng)。resp.getWriter()獲取響應(yīng)的Writer。
        objectMapper.writeValue(resp.getWriter(),blogs); 
    }
}

前端代碼:由于jQuery ky 自動(dòng)將返回的 JSON 字符串解析為 JavaScript 對(duì)象或數(shù)組(取決于 JSON 的內(nèi)容),所有直接將文章數(shù)量的長(zhǎng)度即可,下圖解析

【基于前后端分離的博客系統(tǒng)】Servlet版本

八. 博客系統(tǒng)設(shè)計(jì)源碼

在做前后端邏輯處理的時(shí)候,前端代碼有些稍微的改動(dòng),本文沒(méi)有提及到,請(qǐng)點(diǎn)擊查看源碼,查看改動(dòng)的細(xì)節(jié)以及所有后端的設(shè)計(jì)實(shí)現(xiàn):博客系統(tǒng)前后端設(shè)計(jì)源碼

后續(xù)還有很多擴(kuò)展功能補(bǔ)充,大家敬請(qǐng)期待,我們下期再見(jiàn) ! ! !

【基于前后端分離的博客系統(tǒng)】Servlet版本文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-474136.html

到了這里,關(guān)于【基于前后端分離的博客系統(tǒng)】Servlet版本的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 個(gè)人博客系統(tǒng)(前后端分離)

    個(gè)人博客系統(tǒng)(前后端分離)

    努力經(jīng)營(yíng)當(dāng)下,直至未來(lái)明朗! 普通小孩也要熱愛(ài)生活! 個(gè)人博客系統(tǒng)采用前后端分離的方法來(lái)實(shí)現(xiàn),同時(shí)使用了數(shù)據(jù)庫(kù)來(lái)存儲(chǔ)相關(guān)的數(shù)據(jù),同時(shí)使用tomcat進(jìn)行項(xiàng)目的部署。前端主要有四個(gè)頁(yè)面構(gòu)成:登錄頁(yè)、列表頁(yè)、詳情頁(yè)以及編輯頁(yè),以上模擬實(shí)現(xiàn)了最簡(jiǎn)單個(gè)博客列表

    2024年02月02日
    瀏覽(22)
  • 博客系統(tǒng)(使用前后端分離)

    博客系統(tǒng)(使用前后端分離)

    這篇博客相當(dāng)于是,根據(jù)前面的所學(xué)的知識(shí),來(lái)做一個(gè)綜合練習(xí) 登錄頁(yè): 列表詳情頁(yè): 博客詳情頁(yè) 博客編輯頁(yè) 因?yàn)槭遣┛凸芾硐到y(tǒng),我們涉及數(shù)據(jù)庫(kù)的話,這個(gè)兩個(gè)表的結(jié)構(gòu)如下: 列出數(shù)據(jù)庫(kù)的基本代碼. 我們會(huì)對(duì)數(shù)據(jù)庫(kù)有基本的操作. Blog表 =Blog類對(duì)應(yīng)的Blog的一個(gè)對(duì)象,就對(duì)應(yīng)表中

    2024年02月16日
    瀏覽(16)
  • 前后端分離實(shí)現(xiàn)博客系統(tǒng)

    前后端分離實(shí)現(xiàn)博客系統(tǒng)

    基于servlet+jdbc進(jìn)行后端開(kāi)發(fā),設(shè)計(jì)了一個(gè)可以發(fā)布博客,查看博客詳情,刪除博客,登錄注銷功能的簡(jiǎn)易博客系統(tǒng)。 1.1 登陸頁(yè)面 1.2 博客列表頁(yè)面 1.3 博客詳情頁(yè)面 1.4 博客編輯頁(yè)面 2.1.1 創(chuàng)建maven項(xiàng)目 我們?cè)趇dea里面點(diǎn)擊new project創(chuàng)建一個(gè)maven項(xiàng)目,然后一路next選擇好項(xiàng)目位置

    2024年02月14日
    瀏覽(21)
  • 博客系統(tǒng)(前后端分離)

    博客系統(tǒng)(前后端分離)

    ??作者:銀河罐頭 ??系列專欄:JavaEE ?? “種一棵樹(shù)最好的時(shí)間是十年前,其次是現(xiàn)在” 創(chuàng)建項(xiàng)目,引入依賴,把之前寫(xiě)的前端頁(yè)面拷貝進(jìn)去。 依賴主要是:servlet, mysql, jackson 在當(dāng)前的博客系統(tǒng)中,主要涉及到 2 個(gè)實(shí)體,博客 和 用戶。 創(chuàng)建 2 張表,來(lái)表示博客和用戶。

    2023年04月14日
    瀏覽(22)
  • 項(xiàng)目實(shí)戰(zhàn)-前后端分離博客系統(tǒng)

    純后端講解 完整的前臺(tái)后臺(tái)代碼編寫(xiě) 主流技術(shù)棧(SpringBoot,MybatisPlus,SpringSecurity,EasyExcel,Swagger2,Redis,Echarts,Vue,ElementUI....) 完善細(xì)致的需求分析 由易到難循序漸進(jìn) ?我們有前臺(tái)和后臺(tái)兩套系統(tǒng)。兩套系統(tǒng)的前端工程都已經(jīng)提供好了。所以我們只需要寫(xiě)兩套系統(tǒng)的后端。 ?但是

    2024年02月14日
    瀏覽(19)
  • 【Java EE】-博客系統(tǒng)一(前后端分離)

    【Java EE】-博客系統(tǒng)一(前后端分離)

    作者 :學(xué)Java的冬瓜 博客主頁(yè) :?冬瓜的主頁(yè)?? 專欄 :【JavaEE】 分享 : 謎一樣的 沉默著的 故事你真的在聽(tīng)嗎 ——《平凡之路》 主要內(nèi)容 :準(zhǔn)備工作:創(chuàng)建maven,引入依賴。設(shè)計(jì)數(shù)據(jù)庫(kù)并編寫(xiě)數(shù)據(jù)庫(kù)代碼。前后端分離功能的實(shí)現(xiàn)。博客列表,博客詳情,登錄功能,注冊(cè)功

    2024年02月12日
    瀏覽(24)
  • 【Java EE】-博客系統(tǒng)二(前后端分離)

    【Java EE】-博客系統(tǒng)二(前后端分離)

    作者 :學(xué)Java的冬瓜 博客主頁(yè) :?冬瓜的主頁(yè)?? 專欄 :【JavaEE】 分享 : 徘徊著的 在路上的 你要走嗎 易碎的 驕傲著 那也曾是我的模樣 ——《平凡之路》 主要內(nèi)容 :顯示用戶信息、上傳頭像、新增博客、刪除博客、修改博客。 分兩個(gè)子功能:列表頁(yè)顯示登錄用戶信息;

    2024年02月11日
    瀏覽(24)
  • SSM項(xiàng)目前后端分離+IDEA運(yùn)行環(huán)境(含前端源碼)(個(gè)人博客系統(tǒng))

    SSM項(xiàng)目前后端分離+IDEA運(yùn)行環(huán)境(含前端源碼)(個(gè)人博客系統(tǒng))

    目錄 ?后端項(xiàng)目環(huán)境配置 1、創(chuàng)建一個(gè)SpringBoot項(xiàng)目,添加MyBatis框架和數(shù)據(jù)庫(kù)MySQL驅(qū)動(dòng)依賴 2、配置項(xiàng)目文件:application.yml 3、創(chuàng)建數(shù)據(jù)庫(kù)表 4、創(chuàng)建分層結(jié)構(gòu)目錄 返回統(tǒng)一數(shù)據(jù)格式? 創(chuàng)建統(tǒng)一數(shù)據(jù)格式返回類:AjaxResult 創(chuàng)建實(shí)現(xiàn)統(tǒng)一數(shù)據(jù)返回的保底類:ResponseAdvice 統(tǒng)一處理 登錄

    2024年02月13日
    瀏覽(49)
  • 基于Ant DesignPro Vue + SpringBoot 前后端分離 - 后端微服化 + 接口網(wǎng)關(guān) + Nacos

    基于Ant DesignPro Vue + SpringBoot 前后端分離 - 后端微服化 + 接口網(wǎng)關(guān) + Nacos

    通過(guò)Ant DesignPro Vue + SpringBoot 搭建的后臺(tái)管理系統(tǒng)后,實(shí)現(xiàn)了前后端分離,并實(shí)現(xiàn)了登錄認(rèn)證,認(rèn)證成功后返回該用戶相應(yīng)權(quán)限范圍內(nèi)可見(jiàn)的菜單。 后端采用SpringCloud構(gòu)建微服,采用SpringCloud Gateway做為服務(wù)網(wǎng)關(guān),采用Nacos做為統(tǒng)一配置中心,并在服務(wù)網(wǎng)關(guān)部分解決了前端跨域調(diào)

    2024年02月12日
    瀏覽(17)
  • ruoyi-vue(若依前后端分離版本)環(huán)境搭建 用idea 安裝redis 后端配置 配置node環(huán)境 前端配置

    ruoyi-vue(若依前后端分離版本)環(huán)境搭建 用idea 安裝redis 后端配置 配置node環(huán)境 前端配置

    1.在https://gitee.com/y_project/RuoYi-Vue下載源碼并解壓至本地文件 2.將sql文件下的兩個(gè)sql文件導(dǎo)入數(shù)據(jù)庫(kù)生成表? 3.在E:eclipsespaceoneRuoYi-Vue-masterruoyi-adminsrcmainresourcesapplication-druid.yml修改數(shù)據(jù)庫(kù)名和密碼 4.在E:eclipsespaceoneRuoYi-Vue-masterruoyi-adminsrcmainresourcesapplication.yml配置red

    2024年04月14日
    瀏覽(30)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包