?
??????點(diǎn)進(jìn)來(lái)你就是我的人了
博主主頁(yè):??????戳一戳,歡迎大佬指點(diǎn)!歡迎志同道合的朋友一起加油喔??????
目錄
一. 項(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è)面功能展示:?
?項(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)的包和類
?引入前端資源?
- 將前端資源都放在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":
使用相對(duì)路徑:假設(shè)你正在創(chuàng)建一個(gè)從"index.html"頁(yè)面到"MyServlet"的鏈接,你可以使用相對(duì)于當(dāng)前頁(yè)面的路徑,即"../myServlet",來(lái)指向Servlet。
使用絕對(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
屬性定義的字段名username
和password
,將用戶輸入的用戶名和密碼作為參數(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)步驟:
- 當(dāng)用戶點(diǎn)擊登錄按鈕時(shí),觸發(fā)表單的提交操作。
- 使用
$.ajax
發(fā)送 POST 請(qǐng)求,將用戶名和密碼作為請(qǐng)求參數(shù)發(fā)送到后端驗(yàn)證登錄。 - 在請(qǐng)求成功的回調(diào)函數(shù)中,根據(jù)后端返回的結(jié)果進(jìn)行判斷,如果登錄成功,則執(zhí)行相應(yīng)的操作(例如跳轉(zhuǎn)到博客列表頁(yè)),如果登錄失敗,則進(jìn)行相應(yīng)的提示。
- 后端實(shí)現(xiàn)步驟:
- 接收前端發(fā)送的 POST 請(qǐng)求,獲取請(qǐng)求中的用戶名和密碼參數(shù)。
- 在數(shù)據(jù)庫(kù)中查詢是否存在對(duì)應(yīng)的用戶信息,進(jìn)行用戶名和密碼的驗(yàn)證。
- 根據(jù)驗(yàn)證結(jié)果,返回相應(yīng)的結(jié)果給前端,表示登錄成功或登錄失敗。
2. blog_list.html (博客列表頁(yè))
- 前端實(shí)現(xiàn)步驟:
- 在頁(yè)面加載時(shí),使用
$.ajax
發(fā)送 GET 請(qǐng)求,向后端獲取博客列表數(shù)據(jù)。 - 在請(qǐng)求成功的回調(diào)函數(shù)中,遍歷返回的博客數(shù)據(jù),動(dòng)態(tài)構(gòu)造博客內(nèi)容并添加到頁(yè)面中。
- 后端實(shí)現(xiàn)步驟:
- 接收前端發(fā)送的 GET 請(qǐng)求,無(wú)需參數(shù)。
- 查詢數(shù)據(jù)庫(kù)中的博客數(shù)據(jù),獲取博客列表。
- 將查詢結(jié)果封裝為 JSON 格式,并返回給前端。
3. blog_edit.html (博客編輯頁(yè))
- 前端實(shí)現(xiàn)步驟:
- 當(dāng)用戶在編輯框中填寫(xiě)完博客標(biāo)題和內(nèi)容后,點(diǎn)擊發(fā)布文章按鈕,觸發(fā)表單的提交操作。
- 使用
$.ajax
發(fā)送 POST 請(qǐng)求,將博客標(biāo)題和內(nèi)容等數(shù)據(jù)發(fā)送到后端創(chuàng)建新的博客。 - 在請(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)步驟:
- 接收前端發(fā)送的 POST 請(qǐng)求,獲取請(qǐng)求中的博客標(biāo)題和內(nèi)容等參數(shù)。
- 將獲取到的博客信息插入到數(shù)據(jù)庫(kù)中,創(chuàng)建新的博客。
- 返回相應(yīng)的結(jié)果給前端,表示博客創(chuàng)建成功或創(chuàng)建失敗。
4. blog_detail.html (博客詳情頁(yè))
- 前端實(shí)現(xiàn)步驟:
- 在頁(yè)面加載時(shí),使用
$.ajax
發(fā)送 GET 請(qǐng)求,向后端獲取指定博客的詳細(xì)信息。 - 在請(qǐng)求成功的回調(diào)函數(shù)中,根據(jù)返回的博客數(shù)據(jù)更新頁(yè)面上的標(biāo)題、日期和博客正文內(nèi)容。
- 后端實(shí)現(xiàn)步驟:
- 接收前端發(fā)送的 GET 請(qǐng)求,從請(qǐng)求參數(shù)中獲取博客ID或其他標(biāo)識(shí)符。
- 根據(jù)博客ID查詢數(shù)據(jù)庫(kù),獲取指定博客的詳細(xì)信息。
- 將查詢結(jié)果封裝為 JSON 格式,并返回給前端。
四.博客系統(tǒng)中與數(shù)據(jù)庫(kù)進(jìn)行交互的類
DBUtil
類:提供了數(shù)據(jù)庫(kù)連接和資源釋放的工具方法,通過(guò) JDBC 技術(shù)與數(shù)據(jù)庫(kù)建立連接并執(zhí)行 SQL 語(yǔ)句。
Blog
類:表示博客對(duì)象,包含博客的標(biāo)題、內(nèi)容、發(fā)布時(shí)間等屬性,以及相應(yīng)的 getter 和 setter 方法。
BlogDao
類:封裝了對(duì)博客表的基本操作方法,包括新增博客、根據(jù)博客ID查詢博客、查詢所有博客列表、刪除指定博客等。
User
類:表示用戶對(duì)象,包含用戶的ID、用戶名和密碼等屬性,以及相應(yīng)的 getter 和 setter 方法。
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
、PreparedStatement
和Connection
是否為 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):
-
在
User
類和Blog
類中,你應(yīng)該確保每個(gè)屬性都有相應(yīng)的 getter 和 setter 方法。這些方法的實(shí)現(xiàn)通常是直接返回或者設(shè)置對(duì)應(yīng)的屬性。 -
在
BlogDao
類和UserDao
類中,你應(yīng)該確保每個(gè)數(shù)據(jù)庫(kù)操作都在 try-catch 塊中進(jìn)行,以便在出現(xiàn)異常時(shí)能夠捕獲到并進(jìn)行處理。此外,不要忘記在使用完PreparedStatement
和ResultSet
后關(guān)閉它們,否則可能會(huì)導(dǎo)致資源泄漏。 -
在
DBUtil
類中,你應(yīng)該確保在獲取連接時(shí)檢查連接是否為 null,如果為 null,則說(shuō)明連接獲取失敗,應(yīng)該進(jìn)行相應(yīng)的處理。在關(guān)閉資源時(shí),也應(yīng)該檢查資源是否為 null,如果不為 null,則需要關(guān)閉它。
五.?博客系統(tǒng)的后端接口
AuthorServlet:根據(jù)博客ID獲取博客作者的信息。
BlogServlet:處理博客相關(guān)的請(qǐng)求,包括獲取博客列表和獲取指定博客的詳細(xì)信息,以及發(fā)布新的博客。
LogoutServlet:處理用戶注銷的請(qǐng)求,將用戶從當(dāng)前會(huì)話中移除。
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ì)象(UserDao
、BlogDao
)一起工作,從數(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ò)
BlogDao
的selectById
方法獲取對(duì)應(yīng)的Blog
對(duì)象,再通過(guò)Blog
對(duì)象的getUserId
方法獲取對(duì)應(yīng)的用戶ID,然后通過(guò)UserDao
的selectById
方法獲取對(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ò) UserDao
的 selectByUsername
方法獲取對(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ò)UserDao
的selectByUsername
方法獲取對(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ì)?
-
表單(Form):整個(gè)登錄頁(yè)面被包含在一個(gè)HTML表單中。表單被設(shè)計(jì)用于收集用戶輸入的數(shù)據(jù),這里的數(shù)據(jù)就是用戶名和密碼。表單被發(fā)送到
../login
這個(gè)路徑,使用的是POST方法,意味著提交的數(shù)據(jù)將在請(qǐng)求體中發(fā)送。 -
用戶名和密碼輸入框:這兩個(gè)輸入框分別用于接收用戶的用戶名和密碼信息。用戶名輸入框的占位符提示用戶可以輸入手機(jī)號(hào)或郵箱作為用戶名。密碼輸入框類型為
password
,所以輸入的密碼信息不會(huì)被顯示出來(lái),以保護(hù)用戶的安全。 -
提交按鈕:用戶在輸入完用戶名和密碼后,可以點(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ì)步驟如下:
-
請(qǐng)求博客列表:頁(yè)面加載時(shí),
getBlogs
函數(shù)被調(diào)用。在這個(gè)函數(shù)中,jQuery的$.ajax
方法用于向服務(wù)器發(fā)送一個(gè)GET請(qǐng)求,目標(biāo)URL為'../blog'。 -
處理響應(yīng):服務(wù)器將以一個(gè)包含多個(gè)博客對(duì)象的JSON數(shù)組形式返回博客列表。這個(gè)JSON數(shù)組已經(jīng)被jQuery自動(dòng)解析成JavaScript的對(duì)象數(shù)組。
-
遍歷博客對(duì)象:每一個(gè)博客對(duì)象都包含了博客的標(biāo)題(title)、發(fā)布時(shí)間(postTime)、內(nèi)容(content)等信息。
getBlogs
函數(shù)中的for循環(huán)對(duì)數(shù)組進(jìn)行遍歷,每次遍歷都會(huì)處理一個(gè)博客對(duì)象。 -
構(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í)別出具體的博客。 -
添加到頁(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 = '查看全文 >>';
// 期望點(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ì)
-
HTML表單設(shè)計(jì):這部分的代碼定義了用戶用于輸入和提交博客內(nèi)容的表單。表單內(nèi)包含一個(gè)用于輸入標(biāo)題的文本框以及一個(gè)用于提交文章的按鈕。除此之外,還有一個(gè)占位的
<textarea>
元素,這個(gè)元素被設(shè)置為不顯示(style="display: none;"
),是為了與Markdown編輯器對(duì)接,讓編輯器的內(nèi)容可以被提交到服務(wù)器。 -
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ù)依賴插件的路徑。 -
用戶登錄狀態(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)求去幫我們完成注銷的邏輯
- 獲取到session
- 如果session不為空,將session中保存的user刪除
- 刪除后跳轉(zhuǎn)到用戶登陸頁(yè)面?
?后端收到請(qǐng)求開(kāi)始執(zhí)行注銷操作:
七.擴(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ù)。
- 在
checkLogin
和getAuthor
這兩個(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ì)在checkLogin
和getAuthor
這兩個(gè)異步操作都成功后才會(huì)成功。它的返回值values
是一個(gè)數(shù)組,包含了checkLogin
和getAuthor
的返回值,也就是 userId 和 authorId。然后比較這兩個(gè)值是否相等,如果相等就顯示刪除按鈕。- 也就是說(shuō),這段代碼實(shí)現(xiàn)了等待
checkLogin
和getAuthor
這兩個(gè)異步操作都完成后,再進(jìn)行后續(xù)的處理。
圖解如下:
?服務(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è)功能:
前端代碼的邏輯如下圖:
?后端代碼:
- 后端這里解析請(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)設(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) ! ! !文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-474136.html
文章來(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)!