目錄
??一、數(shù)據(jù)庫設(shè)計部分
1、建表分析:系統(tǒng)中一共要實現(xiàn)幾張表??
2、開始建表
??二、大概框架與實現(xiàn)功能
???三、代碼實現(xiàn)部分
??前言1:工具類的實現(xiàn)(utils包下)
1、數(shù)據(jù)庫連接的工具類
2 、 用戶信息判空的工具類
3、判斷當(dāng)前用戶是否已經(jīng)登錄的工具類
??前言2:java實體類的定義(model包下)
1、User實體類
2、 Blog實體類
??前言3:AppResult類定義響應(yīng)的返回格式j(luò)son(Common包下)
??1、實現(xiàn)登錄功能
1.1 UserDao下:selectByUsername通過用戶名查詢對應(yīng)用戶
1.2 Servlet實現(xiàn)后端
1.3?前端實現(xiàn)?blog_login.html
??2、獲取當(dāng)前用戶的登錄信息
2.1?Servlet實現(xiàn)后端
2.2 前端代碼實現(xiàn):blog_list.html
??3、獲取當(dāng)前所有的文章列表
3.1 BlogDao:selectAll在數(shù)據(jù)庫中查詢所有的博客記錄
3.2 Servlet實現(xiàn)后端
?3.3?前端實現(xiàn):blog_list.html
??4、查看全文功能
界面1實現(xiàn) :顯示這篇博客的具體內(nèi)容
4.1 UserDao下?:User selectById(Long id)通過Id獲取對應(yīng)用戶信息
4.2 BolgDao下:Blog selectById(Long Id)通過Id查詢對應(yīng)博客
4.3 Servlet實現(xiàn)后端
4.4?前端實現(xiàn):blog_details.html
界面2實現(xiàn) :顯示用戶信息
4.1 Servlet實現(xiàn)后端
4.2?前端實現(xiàn) blog_details.html
???5、發(fā)布博客
?5.1 blogDao下:insert(Blog blog)寫入博客到數(shù)據(jù)庫
5.2 Servlet實現(xiàn)后端
?5.3 前端實現(xiàn) blog_eidt.html
??6、刪除當(dāng)前文章
6.1 BlogDao下:deleteById(Long id)通過Id刪除博客
6.2 Servlet實現(xiàn)后端
(1)更新后的UserServlet
(2)blogServlet中實現(xiàn)doDelete方法
6.3? 前端實現(xiàn):blog_details.html
??7、注銷功能
7.1 Servlet實現(xiàn)后端
7.2 前端實現(xiàn)?blog_list.html
??一、數(shù)據(jù)庫設(shè)計部分
1、建表分析:系統(tǒng)中一共要實現(xiàn)幾張表??
分析:系統(tǒng)中一共要實現(xiàn)幾張表?
????????注意:凡是不能被數(shù)據(jù)庫或者編程系統(tǒng)中的基本數(shù)據(jù)類型表示的,都可以考慮成是一個類或者是一張表。比如:學(xué)生類:學(xué)生有Id,姓名,成績...就可以設(shè)計為一張表,這個學(xué)生對象就可以設(shè)計為一個類,類的屬性和表的字段一一對應(yīng)。
1、用戶表:一共有四個字段(后面根據(jù)博客功能實現(xiàn),又新增了一個屬性:是否為同一個用戶)
(1)用戶名
(2)密碼
(3)Github地址
(4)發(fā)表的文章數(shù)量
2、博客表
(1)標(biāo)題
(2)發(fā)布內(nèi)容
(3)發(fā)布時間
(4)作者Id(與用戶表關(guān)聯(lián))
2、開始建表
#創(chuàng)建數(shù)據(jù)庫
create database blog_db char set utf8mb4 collate utf8mb4_general_ci;
#選擇數(shù)據(jù)庫
use blog_db;
#建立用戶表
create table user(
id bigint primary key auto_increment comment'用戶Id,自增類型',
username varchar(50) unique not null comment'用戶名',
password varchar(50) not null comment '密碼'
);
# 查看表是否建立成功
select * from user;
# 建立博客表
use blog_db;
create table blog(
id bigint primary key auto_increment comment'id,自增類型',
title varchar(1024) not null comment'標(biāo)題',
content text not null comment'內(nèi)容',
createTime datetime not null comment'發(fā)布時間',
userId bigint not null comment'用戶名'
);
select * from blog;
最終建表效果
????????由于不實現(xiàn)注冊功能,所以這里我們給它初始化兩個用戶,方便后續(xù)直接進(jìn)行登錄。
use blog_db; insert into user values (null,'小花花','123456'); insert into user values (null,'小葉子','123456');
blog表中也初始化兩條數(shù)據(jù)方便后續(xù)使用;
??二、大概框架與實現(xiàn)功能
????????在具體開始實現(xiàn)代碼之前,我們首先要分析出做的這個Blog系統(tǒng)大概都有什么樣的功能。我們將實現(xiàn)的功能總結(jié)如下:
(1)登錄功能
(2)獲取當(dāng)前用戶的登錄信息
(3)獲取當(dāng)前所有的文章列表
(4)查看文章詳情
(5)寫博客
(6)退出登錄
在實現(xiàn)代碼的過程中,為了方便管理,我們將不同功能的代碼放在不同的包下,主要分為五個包:
(1)Utils:存放各種工具類
(2)model:存放Java實體類,根據(jù)數(shù)據(jù)庫中的表創(chuàng)建各種實體類
(3)dao:負(fù)責(zé)實體類與數(shù)據(jù)庫之間的聯(lián)系
(4)Servlet:后端代碼的實現(xiàn)
(5)common:存放公共類,用于響應(yīng)的時候以Json格式返回。
????????一般Utils,model提前寫好,commom中隨用隨寫,真正要寫的代碼:先寫Dao中的,從數(shù)據(jù)庫中查詢內(nèi)容——>寫Servlet中的后端代碼:從前端獲取數(shù)據(jù)進(jìn)行處理并返回響應(yīng),在這個過程中一般會用到Dao包中從數(shù)據(jù)庫中獲取到的內(nèi)容——>寫前端html中的代碼:主要是從控件中獲取內(nèi)容用ajax發(fā)送數(shù)據(jù)給后端,并通過回調(diào)函數(shù)來處理后端的響應(yīng)。(自己的一點點感悟~~有一點點寫代碼的感覺,但不多...哈哈哈)簡單知道這些之后,接下來我們就可以愉快的寫代碼啦~~
???三、代碼實現(xiàn)部分
??前言1:工具類的實現(xiàn)(utils包下)
1、數(shù)據(jù)庫連接的工具類
????????建立用戶表和博客表之后,后續(xù)要進(jìn)行數(shù)據(jù)庫的連接等操作,所以在這里我們將數(shù)據(jù)庫的部分功能進(jìn)行封裝,方便后續(xù)直接使用。
public class DBUtils {
//1、定義數(shù)據(jù)源
private static DataSource dataSource;
//定義三個屬性
private static final String URL = "jdbc:mysql://localhost:3306/blog_db?characterEncoding=utf8&useSSL=false";
private static final String USER = "root";
private static final String PASSWORD = "123456";
//2、完成數(shù)據(jù)的初始化
static {
MysqlDataSource mysqlDataSource = new MysqlDataSource();
mysqlDataSource.setURL(URL);
mysqlDataSource.setUser(USER);
mysqlDataSource.setPassword(PASSWORD);
dataSource = mysqlDataSource;
}
//3、構(gòu)造方法私有化
private DBUtils(){};
//4、構(gòu)造一個連接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
//5、依次關(guān)閉連接,釋放資源
public static void close(PreparedStatement statement, Connection connection, ResultSet resultSet){
if((resultSet != null)){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection !=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
2 、 用戶信息判空的工具類
用戶信息的判空,封裝成一個類:檢驗字符串是否為空。
public class StringUtils {
public static boolean isEmpty(String value){
if(value == null || value.equals("")){
//true表示為空
return true;
}
//不為空:返回false
return false;
}
}
3、判斷當(dāng)前用戶是否已經(jīng)登錄的工具類
????????在完成登錄之后,后續(xù)所有的子頁面的各種操作都是在保證當(dāng)前用戶已經(jīng)登錄的情況下完成的,所以將用戶是否登錄封裝為一個類。
public class UserUtils {
public static User checkUserLoginStatus(HttpServletRequest request){
//先判斷req是否為空
if(request == null){
return null;
}
//否則獲取session
HttpSession session = request.getSession(false);
//獲取用戶對象
User user = (User) session.getAttribute(AppConfig.USER_SESSION_KEY);
return user;
}
}
??前言2:java實體類的定義(model包下)
實體類的每個屬性都和數(shù)據(jù)庫表中的字段名一一對應(yīng)。isAuthor屬性后面會介紹到。
1、User實體類
public class User {
//三個屬性和數(shù)據(jù)庫表中的字段要對應(yīng)
private Long id;
private String username;
@JsonIgnore//不參與json序列化
private String password;
private boolean isAuthor;
//無參構(gòu)造
public User(){}
//提供相應(yīng)的get和set方法
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
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;
}
public boolean isAuthor() {
return isAuthor;
}
public void setAuthor(boolean author) {
isAuthor = author;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
2、 Blog實體類
public class Blog {
private Long id;
private String title;
private String content;
private Timestamp createTime;//發(fā)布時間,類型 java.sql.Timestamp
private Long userId;//用戶id
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
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 getCreateTime() {
return createTime;
}
public void setCreateTime(Timestamp createTime) {
this.createTime = createTime;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
@Override
public String toString() {
return "Blog{" +
"id=" + id +
", title='" + title + '\'' +
", content='" + content + '\'' +
", createTime=" + createTime +
", userId=" + userId +
'}';
}
}
??前言3:AppResult類定義響應(yīng)的返回格式j(luò)son(Common包下)
第一點:?實現(xiàn)一個統(tǒng)一的數(shù)據(jù)返回格式,定義一個AppResult類來實現(xiàn)。
第二點:
/**
* 響應(yīng)的時候以json的形式返回,這里定義一個共用的類
*/
public class AppResult<T> {
//用于描述狀態(tài)碼的屬性
private int code;
//描述信息
private String message;
//返回數(shù)據(jù):泛型當(dāng)類里面?zhèn)鞯氖荱ser,后面data里面存的就是User
private T data;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
//三個參數(shù)的構(gòu)造方法
public AppResult(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
//工廠方法
/**
* 普通的成功方法
* @return
*/
public static AppResult success(){
return new AppResult(0,"操作成功",null);
}
public static AppResult success(String message){
return new AppResult(1,message,null);
}
public static <T>AppResult success(T data){
return new AppResult(0,"操作成功",data);
}
public static <T>AppResult success(String message,T data){
return new AppResult(0,message,data);
}
/**
* 失敗方法
* @return
*/
public static AppResult failed(){
return new AppResult(1000,"操作失敗",null);
}
public static AppResult failed(String message){
return new AppResult(1000,message,null);
}
}
//定義全局的配置變量
public class AppConfig {
public static final String USER_SESSION_KEY = "USER_SESSION_KEY";
}
??1、實現(xiàn)登錄功能
1.1 UserDao下:selectByUsername通過用戶名查詢對應(yīng)用戶
????????分析:在登錄的時候,前端輸入用戶名和密碼,此時我們需要從數(shù)據(jù)庫中獲取用戶名,判斷該用戶名是否存在,因此提供一個通過用戶名來查詢用戶信息的數(shù)據(jù)庫訪問方法,給定用戶名,該用戶名在數(shù)據(jù)庫中存在的話,就返回該用戶信息(包括id,用戶名,密碼);否則返回null。
提供一個selectByUsername的數(shù)據(jù)庫訪問方法:UserDao.java。
public class UserDao {
/**
* 根據(jù)用戶名查詢用戶信息
* @param username 用戶名
* @return 返回一個user對象
*/
public User selectByUsername(String username){
//1、判斷username是否為空
if(StringUtils.isEmpty(username)){
return null;
}
//2、說明username有值,連接數(shù)據(jù)庫,查詢用戶名為username的用戶
//(1)進(jìn)行數(shù)據(jù)庫連接操作,定義數(shù)據(jù)庫訪問的相關(guān)對象
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
//(2)連接數(shù)據(jù)庫
connection = DBUtils.getConnection();
//(3)定義sql語句
String sql = "select id,username,password from user where username = ?";
//(4)對sql進(jìn)行預(yù)處理
statement = connection.prepareStatement(sql);
//(5)設(shè)置占位符的值
statement.setString(1,username);
//(6)執(zhí)行sql,獲取結(jié)果
resultSet = statement.executeQuery();
//(7)解析結(jié)果集,構(gòu)造對象
if(resultSet.next()){
//創(chuàng)建一個User對象
User user = new User();
user.setId(resultSet.getLong(1));
user.setUsername(resultSet.getString(2));
user.setPassword(resultSet.getString(3));
//返回結(jié)果
return user;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//3、釋放資源
DBUtils.close(statement,connection,resultSet);
}
//4、說明沒有查詢到結(jié)果,返回null
return null;
}
}
每次寫完一個方法,就測試是否能通過:
1.2 Servlet實現(xiàn)后端
@WebServlet("/user")
public class UserServlet extends HttpServlet {
ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、設(shè)置編碼格式
req.setCharacterEncoding("UTF-8");
resp.setContentType("application/json;charset = utf-8");
//定義統(tǒng)一返回的數(shù)據(jù)類型
AppResult appResult;
//2、獲取session對象
HttpSession session = req.getSession(false);
//3、判斷session
//如果session為空,說明用戶沒有登錄,是不能獲取當(dāng)前的用戶信息的
// if(session == null || session.getAttribute(AppConfig.USER_SESSION_KEY) == null){
// //設(shè)置HTTP狀態(tài)碼:403表示沒有權(quán)限訪問,在前端的時候會調(diào)用到error方法
// resp.setStatus(403);
// //返回錯誤信息
// appResult = AppResult.failed("用戶沒有登錄,請登錄后再試!");
// resp.getWriter().write(objectMapper.writeValueAsString(appResult));
// //中斷代碼流程
// return;
// }
//3、也可以直接調(diào)用工具類的方法
if(UserUtils.checkUserLoginStatus(req) == null){
//設(shè)置HTTP狀態(tài)碼:403表示沒有權(quán)限訪問,在前端的時候會調(diào)用到error方法
resp.setStatus(403);
//返回錯誤信息
appResult = AppResult.failed("用戶沒有登錄,請登錄后再試!");
resp.getWriter().write(objectMapper.writeValueAsString(appResult));
//中斷代碼流程
return;
}
//4、校驗成功,獲取用戶信息
User user = (User) session.getAttribute(AppConfig.USER_SESSION_KEY);
//5、設(shè)置返回結(jié)果中的對象,返回對象
appResult = AppResult.success(user);
resp.getWriter().write(objectMapper.writeValueAsString(appResult));
}
}
Postman測試后端 登錄功能
每次寫完一個接口,就用Postman測試一下。打開Postman,進(jìn)行如下操作?
?情況1:用戶名和密碼為空
?情況2:輸入錯誤的密碼
?情況3:輸入錯誤的用戶名
情況4:輸入正確的用戶名和密碼
?OK~實現(xiàn)前端代碼啦!
1.3?前端實現(xiàn)?blog_login.html
1、前后端交互協(xié)議
(1)請求:POST/login
? ? ? ? Content-Type;application/x-www.form-urlencoded
????????username = test & password=123456
(2)響應(yīng):?返回json格式
? ??????{
????????????????"code":0
????????????????"message":"登錄成功"
????????}
2、前端現(xiàn)在要做的事情:
(1)往后端login下發(fā)送POST請求;
(2)構(gòu)造??username = test & password=123456形式的數(shù)據(jù)發(fā)送給后端;
(3)根據(jù)后端返回的響應(yīng)做出操作:如果后端返回的是0,說明登錄成功,跳轉(zhuǎn)到博客列表頁;如果失敗,彈出message的錯誤信息。
3、實現(xiàn)前端:提交數(shù)據(jù)的方式有兩種
(1)通過表單提交
(2)通過ajax提交
4、注意點:
?error表示HTTP響應(yīng)狀態(tài)碼返回的不是200的時候都會調(diào)用。
<!-- 引入jquery -->
<script src = "./js/jquery-3.6.3.min.js"></script>
<script>
// <!-- 在頁面加載完成之后再執(zhí)行jS,目的是保證元素已經(jīng)初始化成功后再執(zhí)行后續(xù)操作 -->
$(function(){
//4、為登錄按鈕綁定事件
$('#btn_login_submit').click(function(){
//1、獲取控件輸入的用戶名和密碼:id選擇器
let usernameEl = $('#username');
if(!usernameEl.val()){
alert('用戶名不能為空');
//讓用戶名輸入框獲得焦點
usernameEl.focus();
return;
}
//獲取輸入的密碼并做校驗
let passwordEl = $('#password');
if(!passwordEl.val()){
console.log(passwordEl.val());
alert('請輸入密碼');
passwordEl.focus();
return;
}
//2、構(gòu)造要給后端發(fā)送的數(shù)據(jù)
let postData = {
username : usernameEl.val(),
password : passwordEl.val()
};
//3、發(fā)送ajax請求
$.ajax({
type:'post',
url: 'login',
contentType:'application/x-www-form-urlencoded',
data : postData,
//成功回調(diào)
success : function(respData){
if(respData.code == 0){
//登錄成功,跳轉(zhuǎn)到博客頁面
location.assign('blog_list.html');
}else{
//登錄失敗
alert(respData.message);
}
},
//回調(diào)失敗
error : function(){
console.log('訪問出現(xiàn)問題');
}
});
});
})
</script>
</html>
??2、獲取當(dāng)前用戶的登錄信息
????????在第一步登錄功能實現(xiàn)中,當(dāng)用戶登錄成功之后,已經(jīng)將用戶的信息保存在了session中,所以直接從session中獲取即可。
(1)用戶發(fā)送請求到URL;
(2)服務(wù)器從session中取出用戶信息;
(3)轉(zhuǎn)化為Json字符串并返回。
2.1?Servlet實現(xiàn)后端
@WebServlet("/user")
public class UserServlet extends HttpServlet {
ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、設(shè)置編碼格式
req.setCharacterEncoding("UTF-8");
resp.setContentType("application/json;charset = utf-8");
//定義統(tǒng)一返回的數(shù)據(jù)類型
AppResult appResult;
//2、獲取session對象
HttpSession session = req.getSession(false);
//3、判斷session
//如果session為空,說明用戶沒有登錄,是不能獲取當(dāng)前的用戶信息的
if(session == null || session.getAttribute(AppConfig.USER_SESSION_KEY) == null){
//設(shè)置HTTP狀態(tài)碼:403表示沒有權(quán)限訪問,在前端的時候會調(diào)用到error方法
resp.setStatus(403);
//返回錯誤信息
appResult = AppResult.failed("用戶沒有登錄,請登錄后再試!");
resp.getWriter().write(objectMapper.writeValueAsString(appResult));
//中斷代碼流程
return;
}
//4、校驗成功,獲取用戶信息
User user = (User) session.getAttribute(AppConfig.USER_SESSION_KEY);
//5、設(shè)置返回結(jié)果中的對象,返回對象
appResult = AppResult.success(user);
resp.getWriter().write(objectMapper.writeValueAsString(appResult));
}
}
?1、代碼關(guān)鍵點1
Postman測試后端
在Postman中線執(zhí)行登錄流程再獲取用戶信息。
????????但是通過上面的結(jié)果我們可以看到,關(guān)于用戶的隱私字段不應(yīng)該在網(wǎng)上顯示出來,所以在Json序列化的時候就應(yīng)該排除這個字段。
? ? ? ? 序列化:將java對象轉(zhuǎn)化為可以在網(wǎng)絡(luò)上傳輸?shù)臄?shù)據(jù)類型,便于在網(wǎng)絡(luò)上傳輸,比如字符串,byte數(shù)組等等(java->json,java->xml);反序列化:就是將字符串,byte數(shù)組,json格式等還原為java對象(json->java,xml->java)。
再次測試:
2.2 前端代碼實現(xiàn):blog_list.html
步驟:
(1)給h3標(biāo)簽(用戶名標(biāo)簽)設(shè)置id選擇器名;
?(2)引入Js依賴
(3)頁面加載之后就發(fā)送ajax請求?
?(4)在回調(diào)函數(shù)中處理響應(yīng)
?statusCode是一個對象,每個狀態(tài)碼表示一個屬性,屬性的類型是一個function。
部分關(guān)鍵代碼實現(xiàn)如下:?
<script src = "js/jquery-3.6.3.min.js"></script>
<script>
// 目標(biāo):只要博客頁面一啟動,就發(fā)送ajax請求給后端,同時回調(diào)函數(shù)獲取響應(yīng)的用戶信息,設(shè)置到前端的控件上
//實現(xiàn)功能2:用戶控件上顯示用戶信息
$(function(){
//1、發(fā)送ajax請求獲取響應(yīng)數(shù)據(jù)
$.ajax({
type:'get',
url:'user',
//回調(diào)函數(shù)
//HTTP狀態(tài)碼返回為200的時候調(diào)用
success : function(respData){
if(respData.code == 0){
//成功
//獲取從后端返回的User信息
let user = respData.data;
//給前端的控件上設(shè)置該用戶信息
$('#h_list_username').html(user.username);
}else{
//失敗
}
},
//HTTP返回狀態(tài)碼不是200的時候都會調(diào)用error
error : function(){
},
//403狀態(tài)可以調(diào)用error,但是有一個更加具體的方法statusCode,專門為不同的HTTP狀態(tài)碼定義不同的方法
statusCode : {
403:function(){
//打印日志并跳轉(zhuǎn)到登錄界面
console.log('用戶無權(quán)訪問,強制跳轉(zhuǎn)到登錄界面');
location.assign('blog_login.html');
}
}
});
</script>
??3、獲取當(dāng)前所有的文章列表
約定前后端交互接口:
?根據(jù)以上響應(yīng)的格式,在DAO中的sql,應(yīng)該是從數(shù)據(jù)庫的blog中查詢所有,查詢出來的是一個集合,集合的每個對象都是一條博客記錄,包括id,標(biāo)題,內(nèi)容,發(fā)布時間,用戶id。
3.1 BlogDao:selectAll在數(shù)據(jù)庫中查詢所有的博客記錄
public class BlogDao {
/**
* 獲取所有的文章列表
* @return
*/
public List<Blog> selectAll(){
//1、定義數(shù)據(jù)庫訪問的相關(guān)對象
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
//2、建立數(shù)據(jù)庫連接
try {
connection = DBUtils.getConnection();
//3、定義sql語句:按發(fā)布時間排序
String sql = "select id,title,content,createTime,userId " +
"from blog order by createTime desc";
//4、處理sql
statement = connection.prepareStatement(sql);
//5、執(zhí)行sql語句,獲取結(jié)果集
resultSet = statement.executeQuery();
//6、遍歷結(jié)果集
List<Blog> blogList = null;
while (resultSet.next()){
//為空的時候初始化一個集合
if(blogList == null){
blogList = new ArrayList<Blog>();
}
//集合中的每個元素都是一個blog對象
Blog blog = new Blog();
blog.setId(resultSet.getLong(1));
blog.setTitle(resultSet.getString(2));
blog.setContent(resultSet.getString(3));
//判斷一下內(nèi)容長度,如果內(nèi)容長度大于20,就顯示前20個字,否則顯示所有
if(blog.getContent().length()>=20){
blog.setContent(blog.getContent().substring(0,20)+"...");
}
blog.setCreateTime(resultSet.getTimestamp(4));
blog.setUserId(resultSet.getLong(5));
//把blog對象加入集合中
blogList.add(blog);
}
//返回結(jié)果
return blogList;
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtils.close(statement,connection,resultSet);
}
return null;
}
}
?寫完之后用測試類測試:
3.2 Servlet實現(xiàn)后端
注意:要實現(xiàn)博客表中所有信息的獲取,前提是要保證用戶已經(jīng)登錄。
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
//用戶數(shù)據(jù)庫訪問的Dao
private BlogDao blogDao = new BlogDao();
//用于轉(zhuǎn)換Json
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、設(shè)置編碼格式
req.setCharacterEncoding("UTF-8");
resp.setContentType("application/json;charset=utf-8");
//2、前提:要獲取所有的博客的前提是:要保證用戶已經(jīng)登錄
//校驗用戶的登錄狀態(tài)
if(UserUtils.checkUserLoginStatus(req) == null){
//設(shè)置HTTP狀態(tài)碼:403表示沒有權(quán)限訪問,在前端的時候會調(diào)用到error方法
resp.setStatus(403);
//返回錯誤信息
resp.getWriter().write(objectMapper.writeValueAsString(AppResult.failed("用戶沒有登錄,請登錄后再試!")));
//中斷代碼流程
return;
}
//3、獲取所有的博客(是個集合)并返回
List<Blog> blogs = blogDao.selectAll();
//在這里處理一下:如果集合為空的話,就返回一個空集合[],而不是null的形式
if(blogs == null){
blogs = new ArrayList<Blog>();
}
//正常返回給前端
resp.getWriter().write(objectMapper.writeValueAsString(AppResult.success(blogs)));
}
}
Postman測試后端代碼
?進(jìn)行登錄之后,再次測試;
?3.3?前端實現(xiàn):blog_list.html
注意,在獲取時間的時候,要對時間格式進(jìn)行處理,用一個寫好的函數(shù)formatDate。
(1)引入js文件
// common.js文件內(nèi)容 function formatDate(time) { var date = new Date(time); var year = date.getFullYear(), month = date.getMonth() + 1,//月份是從0開始的 day = date.getDate(), hour = date.getHours(), min = date.getMinutes(), sec = date.getSeconds(); var newTime = year + '-' + (month < 10 ? '0' + month : month) + '-' + (day < 10 ? '0' + day : day) + ' ' + (hour < 10 ? '0' + hour : hour) + ':' + (min < 10 ? '0' + min : min) + ':' + (sec < 10 ? '0' + sec : sec); return newTime; }
(2)調(diào)用函數(shù)
處理之前
處理之后
?
//實現(xiàn)功能3:頁面一加載就進(jìn)行博客列表的獲取
$.ajax({
type:'get',
url:'blog',
//回調(diào)方法
success:function(respData){
//根據(jù)自定義的狀態(tài)碼處理
if(respData.code == 0){
//成功,構(gòu)造文章列表:構(gòu)造一個函數(shù)
buildArticlelList(respData.data);
}else{
//失敗
alert(respData.message);
}
},
error:function(respData){
console.log("訪問出現(xiàn)錯誤");
},
statusCode:{
403:function(){
//未登錄狀態(tài):強制讓用戶跳轉(zhuǎn)到登錄界面
location.assign('blog_login.html');
}
}
});
// 構(gòu)建文章列表:data是一個集合
function buildArticlelList(data){
//如果集合為空
if(data.length == 0 || data == null){
//當(dāng)文章列表為空時
let htmlString = '<h3>沒有文章,快發(fā)布一篇吧!</h3>'
$('#container-right').html(htmlString);
return;
}
//說明文章列表不為空:遍歷集合方式1:
for(let i=0;i<data.length;i++){
let blogItem = data[i];
// 對對應(yīng)的控件進(jìn)行相應(yīng)的賦值
let htmlString = ' <div class="blog-content">'
+ ' <div class="blog-title">'
+ blogItem.title
+ ' </div>'
+ ' <div class="blog-datetime">'
+ formatDate(blogItem.createTime)
+ ' </div>'
+ ' <div class="content">'
+ blogItem.content
+ ' </div>'
+ ' <div class="aEl">'
+ ' <a href="./blog_details.html?blog_Id='+blogItem.id+'">+查看全文 >> </a>'
+ ' </div>'
+ ' <hr>'
+ ' </div>';
// console.log(htmlString);
//這里用append,表示追加元素 .html()表示只添加一條數(shù)據(jù)
$('.container-right').append(htmlString);
}
//遍歷集合方式2:data是要遍歷的數(shù)組或集合
// data.forEach(element=>{
// //element相當(dāng)于data[i]
// })
}
??4、查看全文功能
????????實現(xiàn)功能:點擊“查看全文”按鈕會調(diào)轉(zhuǎn)到新的blog_details.html頁面。這里需要明確當(dāng)前要查看的是哪一個文章,此時就要從數(shù)據(jù)庫中查詢出文章id作為參數(shù)傳入,在前端對應(yīng)代碼如下:?
此時在博客子頁面,顯示的界面如下:
界面1:右側(cè)顯示這篇博客的具體內(nèi)容——>需要根據(jù)文章Id查詢文章詳情——>在BlogDao中實現(xiàn)一個Blog selectById(Long Id)方法;
界面2:左側(cè)實現(xiàn)這篇博客的作者——>需要根據(jù)用戶Id查詢用戶信息——>在UserDao中實現(xiàn)一個User selectById(Long Id)方法。
界面1實現(xiàn) :顯示這篇博客的具體內(nèi)容
4.1 UserDao下?:User selectById(Long id)通過Id獲取對應(yīng)用戶信息
/**
* 根據(jù)用戶Id查詢用戶信息
* @param id 用戶信息
* @return 對應(yīng)的用戶記錄
*/
public User selectById(Long id){
//判斷id是否為空
if(id == null || id <=0){
return null;
}
//連接數(shù)據(jù)庫獲取查詢數(shù)據(jù)
//(1)初始化數(shù)據(jù)庫對象
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
//(2)獲取數(shù)據(jù)庫連接
try {
connection = DBUtils.getConnection();
//(3)定義sql
String sql = "select id,username,password from user where id = ?";
//(4)預(yù)處理sql
statement = connection.prepareStatement(sql);
//(5)設(shè)置占位符
statement.setLong(1,id);
//(6)獲取結(jié)果集
resultSet = statement.executeQuery();
//(7)獲取結(jié)果集
if(resultSet.next()){
//(8)封裝對象:創(chuàng)建一個用戶對象并設(shè)置參數(shù)
User user = new User();
user.setId(resultSet.getLong(1));
user.setUsername(resultSet.getString(2));
user.setPassword(resultSet.getString(3));
//(9)返回結(jié)果
return user;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtils.close(statement,connection,resultSet);
}
return null;
}
測試代碼
4.2 BolgDao下:Blog selectById(Long Id)通過Id查詢對應(yīng)博客
/**
* 根據(jù)博客Id查詢對應(yīng)的博客信息
* @param id 博客Id
* @return 一條博客記錄
*/
public Blog selectById(Long id){
//非空校驗
if(id == null || id <= 0){
return null;
}
//數(shù)據(jù)庫操作
//(1)定義數(shù)據(jù)庫對象
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
//(2)獲取數(shù)據(jù)庫連接
try {
connection = DBUtils.getConnection();
//(3)定義sql語句
String sql = "select id,title,content,createTime,userId from blog where id =?";
//(4)預(yù)處理sql
statement = connection.prepareStatement(sql);
//(5)設(shè)置占位符
statement.setLong(1,id);
//(6)執(zhí)行sql
resultSet = statement.executeQuery();
//(7)獲取結(jié)果集
if(resultSet.next()){
//封裝Blog對象
Blog blog = new Blog();
blog.setId(resultSet.getLong(1));
blog.setTitle(resultSet.getString(2));
blog.setContent(resultSet.getString(3));
blog.setCreateTime(resultSet.getTimestamp(4));
blog.setUserId(resultSet.getLong(5));
//(8)返回結(jié)果
return blog;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtils.close(statement,connection,resultSet);
}
return null;
}
測試
4.3 Servlet實現(xiàn)后端
????????之前我們在實現(xiàn)“獲取所有的博客列表”時,BlogServlet中寫了一個doGet方法是直接獲取所有的博客記錄。所以現(xiàn)在我們的目標(biāo)是,傳入id,返回一個blog記錄,也是一個doGet方法。所以我們現(xiàn)在有兩種方法,重新實現(xiàn)一個新的Servlet的doGet方法,接收blogId參數(shù),返回對應(yīng)的博客內(nèi)容,還有一種是在我們現(xiàn)有的BlogServlet代碼中,判斷有沒有傳進(jìn)來參數(shù)blogId,如果有的話,就執(zhí)行新的方法,返回對應(yīng)的博客記錄;否則就執(zhí)行之前的selectAll方法,查詢出所有的博客列表信息。在這里我們采用后面這種方式實現(xiàn)。
更新后的BlogServlet代碼
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
//用戶數(shù)據(jù)庫訪問的Dao
private BlogDao blogDao = new BlogDao();
//用于轉(zhuǎn)換Json
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、設(shè)置編碼格式
req.setCharacterEncoding("UTF-8");
resp.setContentType("application/json;charset=utf-8");
//2、前提:要獲取所有的博客的前提是:要保證用戶已經(jīng)登錄
//校驗用戶的登錄狀態(tài)
if(UserUtils.checkUserLoginStatus(req) == null){
//設(shè)置HTTP狀態(tài)碼:403表示沒有權(quán)限訪問,在前端的時候會調(diào)用到error方法
resp.setStatus(403);
//返回錯誤信息
resp.getWriter().write(objectMapper.writeValueAsString(AppResult.failed("用戶沒有登錄,請登錄后再試!")));
//中斷代碼流程
return;
}
//-------------功能4實現(xiàn):在原先基礎(chǔ)上實現(xiàn):如果傳入blogId參數(shù),就返回對應(yīng)的博客信息,否則返回所有的博客列表信息
//獲取blogId參數(shù)
String blogId = req.getParameter("blog_Id");
//定義返回的Json變量
String jsonStr = null;
//判斷參數(shù)是否為空:如果是空的話,就查詢所有的博客列表
if(StringUtils.isEmpty(blogId)){
//3、獲取所有的博客(是個集合)并返回
List<Blog> blogs = blogDao.selectAll();
//在這里處理一下:如果集合為空的話,就返回一個空集合[],而不是null的形式
if(blogs == null){
blogs = new ArrayList<Blog>();
}
//序列化
jsonStr = objectMapper.writeValueAsString(AppResult.success(blogs));
}else{
//否則根據(jù)傳入的blogId查詢對應(yīng)的博客信息
Blog blog = blogDao.selectById(Long.valueOf(blogId));
//序列化成字符串
jsonStr = objectMapper.writeValueAsString(AppResult.success(blog));
}
//正常返回給前端
resp.getWriter().write(jsonStr);
}
}
問題:
怎么從前端將blogId的參數(shù)傳遞給后端?注意代碼中的blog_Id在前后端之間的聯(lián)系,是怎么將其傳遞給后端的。?就像我們在網(wǎng)址欄輸入地址后會出現(xiàn)一個參數(shù)。
?方法:
?在postman中測試:在url后面直接拼接?blog_Id=5的參數(shù),可以成功查詢。(可以查詢成功的是blog_Id是5和4,其他的都不能成功)
上述總結(jié):前端可以通過JS中的location對象獲取瀏覽器地址欄中query string中的參數(shù)列表。
?接下來在前端實現(xiàn)代碼。
4.4?前端實現(xiàn):blog_details.html
<!--引入Jquery-->
<script src = 'js/jquery-3.6.3.min.js'></script>
<script src = 'js/common.js'></script>
<!-- 再引入編輯器插件 -->
<script src="./editor.md/editormd.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>
$(function(){
//動態(tài)為標(biāo)簽賦值
//獲取當(dāng)前博客的詳細(xì)信息
$.ajax({
type:'get',
url:'blog' + location.search,//這里的blog對應(yīng)servlet中的相對應(yīng)@WebServlet后的"/blog"
//回調(diào)函數(shù)
success : function(respData){
if(respData.code == 0){
//成功,為頁面中的相應(yīng)標(biāo)簽賦值
//查詢
let blog = respData.data;
//賦值
$('#div_datails_title').html(blog.title);
$('#div_datails_createTime').html(formatDate(blog.createTime));
// $('#div_datails_content').html(blog.content);
// 在這里對內(nèi)容做markdown格式處理
editormd.markdownToHTML('div_datails_content',{markdown:blog.content})
}else{
//錯誤
alert(respData.message);
}
},
error : function(){
//打印日志
console.log('訪問出現(xiàn)錯誤');
},
statusCode : {
403 : function(){
//強制跳轉(zhuǎn)到登錄界面
location.assign('blog_login.html')
}
}
})
})
</script>
注意點
界面2實現(xiàn) :顯示用戶信息
4.1 Servlet實現(xiàn)后端
????????注意這里的用戶信息不能從sessio獲取,session中獲取的是當(dāng)前登錄的用戶信息,是唯一且固定的,但是現(xiàn)在顯示的是每個博客的作者,也就是博客是誰寫的。因為我們一開始在數(shù)據(jù)庫設(shè)計的時候,blog表中的useId其實就是對應(yīng)user表中的id,因此可以查詢出當(dāng)前的用戶名,顯示在前端控件上。所以這里的用戶信息是從數(shù)據(jù)庫中獲取的。
所以在表中設(shè)立關(guān)聯(lián)字段的目的就是為了與其他表建立聯(lián)系。
做法:和上面的BlogServlet的實現(xiàn)過程相同,可以通過新實現(xiàn)一個Servlet,也可以修改現(xiàn)有的UserServlet,判斷是否有參數(shù)傳入來執(zhí)行不同的接口。
更新后的UserServlet代碼
@WebServlet("/user")
public class UserServlet extends HttpServlet {
ObjectMapper objectMapper = new ObjectMapper();
private UserDao userDao = new UserDao();
private BlogDao blogDao = new BlogDao();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、設(shè)置編碼格式
req.setCharacterEncoding("UTF-8");
resp.setContentType("application/json;charset = utf-8");
//定義統(tǒng)一返回的數(shù)據(jù)類型
AppResult appResult;
//2、獲取session對象
HttpSession session = req.getSession(false);
//3、判斷session
//如果session為空,說明用戶沒有登錄,是不能獲取當(dāng)前的用戶信息的
// if(session == null || session.getAttribute(AppConfig.USER_SESSION_KEY) == null){
// //設(shè)置HTTP狀態(tài)碼:403表示沒有權(quán)限訪問,在前端的時候會調(diào)用到error方法
// resp.setStatus(403);
// //返回錯誤信息
// appResult = AppResult.failed("用戶沒有登錄,請登錄后再試!");
// resp.getWriter().write(objectMapper.writeValueAsString(appResult));
// //中斷代碼流程
// return;
// }
//3、也可以直接調(diào)用工具類的方法
if(UserUtils.checkUserLoginStatus(req) == null){
//設(shè)置HTTP狀態(tài)碼:403表示沒有權(quán)限訪問,在前端的時候會調(diào)用到error方法
resp.setStatus(403);
//返回錯誤信息
appResult = AppResult.failed("用戶沒有登錄,請登錄后再試!");
resp.getWriter().write(objectMapper.writeValueAsString(appResult));
//中斷代碼流程
return;
}
//-----功能4實現(xiàn):子頁面對應(yīng)顯示用戶信息:根據(jù)是否傳入?yún)?shù)來判斷執(zhí)行哪個方法
String blogId = req.getParameter("blog_Id");
//定義返回的json格式
String jsonStr = null;
//(1)如果當(dāng)前沒有傳入?yún)?shù)
if(StringUtils.isEmpty(blogId)){
//校驗成功,獲取用戶信息
User user = (User) session.getAttribute(AppConfig.USER_SESSION_KEY);
//設(shè)置返回結(jié)果中的對象,返回對象
jsonStr = objectMapper.writeValueAsString(AppResult.success(user));
} else{
//(2)說明當(dāng)前傳入了參數(shù)
//先查詢博客信息
Blog blog = blogDao.selectById(Long.valueOf(blogId));
//查詢到的博客為空
if(blog == null){
jsonStr = objectMapper.writeValueAsString(AppResult.failed("沒有找到對應(yīng)的博客"));
resp.getWriter().write(jsonStr);
return;
}
//說明查到了博客信息,則根據(jù)博客表中的userId查詢用戶
User user = userDao.selectById(blog.getUserId());
//判斷user是否為空
if(user == null){
jsonStr = objectMapper.writeValueAsString(AppResult.failed("沒有找到對應(yīng)的用戶"));
resp.getWriter().write(jsonStr);
return;
}
//返回用戶作者
jsonStr = objectMapper.writeValueAsString(AppResult.success(user));
}
resp.getWriter().write(jsonStr);
}
}
postman測試
情況1:給定一個存在的博客記錄
情況2:給定一條不存在的博客記錄
4.2?前端實現(xiàn) blog_details.html
控件賦值的時候要與標(biāo)簽對應(yīng)。
?
//獲取作者信息
$.ajax({
type:'get',
url:'user'+location.search,
//回調(diào)函數(shù)
success : function(respData){
//成功
if(respData.code == 0){
let user = respData.data;
//為控件賦值
$('#h_details_username').html(user.username);
}else{
alert(respData.message);
}
},
error: function(){
//打印日志
console.log('訪問出現(xiàn)問題');
},
statusCode :{
403 : function(){
//強制跳轉(zhuǎn)到登錄界面
location.assign(blog_login.html);
}
}
});
})
前端:
?博客列表頁面下:blog_list.html中與后端BlogServlet中的blog_Id要對應(yīng)。
???5、發(fā)布博客
(1)dao包下:在blogDao中實現(xiàn):sql要做的事情,將新寫入的內(nèi)容用Insert(Blog?blog)語句插入到數(shù)據(jù)庫中;
(2)Servlet中對提交的參數(shù)做非空校驗,并且從sessio中獲取用戶信息(作者)->博客作者就是當(dāng)前系統(tǒng)中的登錄用戶;
(3)前端發(fā)送post請求,將用戶在頁面上輸入的數(shù)據(jù)提交到服務(wù)器;成功之后,前端跳轉(zhuǎn)到list界面,展示相應(yīng)的blog頁面。
?5.1 blogDao下:insert(Blog blog)寫入博客到數(shù)據(jù)庫
/**
* 發(fā)布一篇新的博客
* @param blog Blog對象
* @return 受影響的行數(shù)
*/
public int insert(Blog blog){
//1、非空校驗
if(blog == null || StringUtils.isEmpty(blog.getTitle())
|| StringUtils.isEmpty(blog.getContent())
|| blog.getUserId() == null
|| blog.getCreateTime() == null){
return 0;
}
//數(shù)據(jù)庫操作
//(1)定義操作數(shù)據(jù)庫過程中的變量
Connection connection = null;
PreparedStatement statement = null;
//(2)創(chuàng)建數(shù)據(jù)庫連接
try {
connection = DBUtils.getConnection();
//(3)定義SQL語句
String sql = "insert into blog values (null,?,?,?,?);";
//(4)預(yù)處理sql
statement = connection.prepareStatement(sql);
//(5)設(shè)置占位符的值
statement.setString(1,blog.getTitle());
statement.setString(2,blog.getContent());
statement.setTimestamp(3,blog.getCreateTime());
statement.setLong(4,blog.getUserId());
//(6)執(zhí)行sql
int row = statement.executeUpdate();
//返回結(jié)果
return row;
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtils.close(statement,connection,null);
}
return 0;
}
測試:
在數(shù)據(jù)庫中查詢:發(fā)現(xiàn)數(shù)據(jù)寫入成功。
5.2 Servlet實現(xiàn)后端
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//設(shè)置編碼集
req.setCharacterEncoding("UTF-8");
resp.setContentType("application/json;charset=utf-8");
//獲取當(dāng)前用戶的登錄信息:從session中獲取
User user = UserUtils.checkUserLoginStatus(req);
//登錄用戶的校驗:沒有登錄的用戶是不能寫博客的
if(user == null){
//設(shè)置HTTP狀態(tài)碼,用于前端處理不同的響應(yīng)方法
resp.setStatus(403);
//返回錯誤信息
String jsonStr = objectMapper.writeValueAsString(AppResult.failed("無權(quán)訪問,請先登錄!"));
resp.getWriter().write(jsonStr);
return;
}
//說明用戶已經(jīng)登錄
//(1)接收用戶提供的參數(shù)
String title = req.getParameter("title");
String content = req.getParameter("content");
if(StringUtils.isEmpty(title) || StringUtils.isEmpty(content)){
String jsonStr = objectMapper.writeValueAsString(AppResult.failed("標(biāo)題或者正文不能為空"));
resp.getWriter().write(jsonStr);
}
//(2)創(chuàng)建blog對象,傳入blogDao中的insert方法
Blog blog = new Blog();
blog.setTitle(title);
blog.setContent(content);
blog.setCreateTime(new Timestamp(System.currentTimeMillis()));
//當(dāng)前登錄用戶是誰,寫博客的作者就是誰
blog.setUserId(user.getId());
//(3)調(diào)用數(shù)據(jù)庫
int row = blogDao.insert(blog);
//(4)判斷行數(shù)
if(row <= 0){
//返回錯誤信息
String jsonStr = objectMapper.writeValueAsString(AppResult.failed("保存文章失敗,請聯(lián)系管理員!"));
resp.getWriter().write(jsonStr);
return;
}
//(5)返回成功信息
resp.getWriter().write(objectMapper.writeValueAsString(AppResult.success("文章發(fā)布成功")));
}
postman測試后端
情況1:標(biāo)題和內(nèi)容都正常輸入
在數(shù)據(jù)庫查詢:發(fā)現(xiàn)數(shù)據(jù)插入成功:
?情況2:當(dāng)標(biāo)題和內(nèi)容為空的時候
?5.3 前端實現(xiàn) blog_eidt.html
介紹:前端要集成一個編輯器插件:?
(1)引入編輯器樣式
(2)引入Jquery
?(3)引入編輯器插件
(4)初始化編輯器
(5)現(xiàn)在我們要在前端輸入?yún)?shù),并獲取參數(shù)的值。比如?要輸入title,我們可以通過input標(biāo)簽獲取輸入的值;但是現(xiàn)在對于"內(nèi)容”,在div標(biāo)簽里面生成的值是在editor編輯器里面。所以這里需要進(jìn)行一點改動,設(shè)置隱藏域。
要與獲取的控件Id名對應(yīng)。??
要處理的小細(xì)節(jié):
(1)markdown格式問題
上面說這個編輯器是支持markdown格式的,現(xiàn)在我們輸入文字并提交;
?????????返回博客列表頁面并查看詳情,發(fā)現(xiàn)并沒有顯示markdown格式,因此我們要對這個問題做一下處理。
?在blog_details.html文件下也要引入插件,并對內(nèi)容做markdown格式處理。(具體代碼見4.4節(jié))這樣就可以用markdown格式顯示內(nèi)容了。
現(xiàn)在再打開查看詳情界面,顯示正常。
(2)文字長度問題?
解決辦法:blogDao下的selectAll方法,增加一個判斷條件。(完整代碼見3.1節(jié))
// blog_eidt.html文件下
// 綁定按鈕的點擊事件
$('#submit').click(function(){
// alert(123); 測試按鈕是否綁定成功
//(1)獲取用戶輸入并提交數(shù)據(jù)
let titleEl = $('#title');
let contentEl = $('#text_edit_content');
// 判斷輸入的標(biāo)題和內(nèi)容是否為空
if(!titleEl.val()){
alert('請輸入文章標(biāo)題');
return;
}
if(!contentEl.val()){
alert('請輸入內(nèi)容');
return;
}
//(2)構(gòu)造發(fā)送的數(shù)據(jù)
let postData = {
title : titleEl.val(),
content : contentEl.val()
}
//(3)提交請求
$.ajax({
type : 'post',
url : 'blog',
contentType :'application/x-www-form-urlencoded',
data : postData,
//回調(diào)函數(shù)
success : function(respData){
//發(fā)送成功,則跳轉(zhuǎn)到blog_list.html頁面
if(respData.code == 0){
location.assign('blog_list.html');
}else{
alert(respData.message);
}
},
error : function(){
console.log('訪問出現(xiàn)錯誤!');
},
statusCode : {
403 : function(){
//未登錄,沒有權(quán)限訪問。強制跳轉(zhuǎn)到登錄界面
location.assign('blog_login.html');
}
}
})
})
??6、刪除當(dāng)前文章
刪除文章:只能當(dāng)前用戶自己刪自己的:
(1)登錄人(從session中獲取use對象,獲取該對象的id)和博客作者(根據(jù)傳入的blogId)要相同:后端UserServlet實現(xiàn),返回Booean類型給前端;
(2)前端User類設(shè)置一個isAuthor屬性,根據(jù)根據(jù)author是否為True來初始化一個刪除按鈕并綁定事件;
(3)Dao下:sql的delete方法;
6.1 BlogDao下:deleteById(Long id)通過Id刪除博客
/**
* 刪除博客
* @param id 傳入要刪除的博客id
* @return 返回受影響的行數(shù)
*/
public int deleteById(Long id){
//非空校驗
if(id == null || id == 0){
return 0;
}
//(1)定義sql的對象
Connection connection = null;
PreparedStatement statement = null;
//(2)定義數(shù)據(jù)庫連接
try {
connection = DBUtils.getConnection();
//(3)定義sql語句
String sql = "delete from blog where id = ?";
//(4)預(yù)處理sql
statement = connection.prepareStatement(sql);
//(5)設(shè)置占位符
statement.setLong(1,id);
//(6)執(zhí)行sql
int row = statement.executeUpdate();
//(7)返回結(jié)果
return row;
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtils.close(statement,connection,null);
}
return 0;
}
6.2 Servlet實現(xiàn)后端
(1)更新后的UserServlet
主要在之前的基礎(chǔ)上:加了下面的代碼
@WebServlet("/user")
public class UserServlet extends HttpServlet {
ObjectMapper objectMapper = new ObjectMapper();
private UserDao userDao = new UserDao();
private BlogDao blogDao = new BlogDao();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、設(shè)置編碼格式
req.setCharacterEncoding("UTF-8");
resp.setContentType("application/json;charset = utf-8");
//定義統(tǒng)一返回的數(shù)據(jù)類型
AppResult appResult;
//2、獲取session對象
HttpSession session = req.getSession(false);
//3、判斷session
//如果session為空,說明用戶沒有登錄,是不能獲取當(dāng)前的用戶信息的
// if(session == null || session.getAttribute(AppConfig.USER_SESSION_KEY) == null){
// //設(shè)置HTTP狀態(tài)碼:403表示沒有權(quán)限訪問,在前端的時候會調(diào)用到error方法
// resp.setStatus(403);
// //返回錯誤信息
// appResult = AppResult.failed("用戶沒有登錄,請登錄后再試!");
// resp.getWriter().write(objectMapper.writeValueAsString(appResult));
// //中斷代碼流程
// return;
// }
//3、也可以直接調(diào)用工具類的方法
if(UserUtils.checkUserLoginStatus(req) == null){
//設(shè)置HTTP狀態(tài)碼:403表示沒有權(quán)限訪問,在前端的時候會調(diào)用到error方法
resp.setStatus(403);
//返回錯誤信息
appResult = AppResult.failed("用戶沒有登錄,請登錄后再試!");
resp.getWriter().write(objectMapper.writeValueAsString(appResult));
//中斷代碼流程
return;
}
//-----功能4實現(xiàn):子頁面對應(yīng)顯示用戶信息:根據(jù)是否傳入?yún)?shù)來判斷執(zhí)行哪個方法
String blogId = req.getParameter("blog_Id");
//定義返回的json格式
String jsonStr = null;
//(1)如果當(dāng)前沒有傳入?yún)?shù)
if(StringUtils.isEmpty(blogId)){
//校驗成功,獲取用戶信息
User user = (User) session.getAttribute(AppConfig.USER_SESSION_KEY);
//設(shè)置返回結(jié)果中的對象,返回對象
jsonStr = objectMapper.writeValueAsString(AppResult.success(user));
} else{
//(2)說明當(dāng)前傳入了參數(shù)
//先查詢博客信息
Blog blog = blogDao.selectById(Long.valueOf(blogId));
//查詢到的博客為空
if(blog == null){
jsonStr = objectMapper.writeValueAsString(AppResult.failed("沒有找到對應(yīng)的博客"));
resp.getWriter().write(jsonStr);
return;
}
//說明查到了博客信息,則根據(jù)博客表中的userId查詢用戶
User user = userDao.selectById(blog.getUserId());
//判斷user是否為空
if(user == null){
jsonStr = objectMapper.writeValueAsString(AppResult.failed("沒有找到對應(yīng)的用戶"));
resp.getWriter().write(jsonStr);
return;
}
//判斷當(dāng)前登錄的用戶是不是文章作者
User currentUser = (User) session.getAttribute(AppConfig.USER_SESSION_KEY);
if(currentUser.getId() == blog.getUserId()){
//表示當(dāng)前登錄用戶就是作者,設(shè)置user的isAuthor為True
user.setAuthor(true);
}
//返回用戶作者
jsonStr = objectMapper.writeValueAsString(AppResult.success(user));
}
resp.getWriter().write(jsonStr);
}
}
postman測試:使用用戶名為“小花花”登錄,其中blog_id=4是小花花寫的博客,blog_id=5是另一個作者寫的博客。
情況1:登錄是小花花,博客作者也是小花花,說明是作者;
?情況2:登錄作者是小花花,博客作者是小葉子,不是同一個?作者,返回false。
(2)blogServlet中實現(xiàn)doDelete方法
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//(1)設(shè)置編碼格式
req.setCharacterEncoding("UTF-8");
resp.setContentType("application/json;charset = utf-8");
//(2)獲取前端控件參數(shù)
String blogId = req.getParameter("blog_Id");
//非空校驗
if(StringUtils.isEmpty(blogId)){
resp.getWriter().write(objectMapper.writeValueAsString(AppResult.failed("沒有找到文章編號!")));
}
//(3)獲取當(dāng)前登錄的用戶,session中獲取
User user = UserUtils.checkUserLoginStatus(req);
//表明用戶沒有登錄
if(user == null){
String jsonStr = objectMapper.writeValueAsString(AppResult.failed("當(dāng)前用戶沒有登錄,請登錄后再試!"));
resp.getWriter().write(jsonStr);
//設(shè)置HTTP狀態(tài)碼
resp.setStatus(403);
//中斷代碼流程
return;
}
//(4)判斷當(dāng)前登錄的用戶與博客作者是否相同:通過登錄用戶的用戶Id與寫博客的用戶Id做比較
//首先獲取博客對象
Blog blog = blogDao.selectById(Long.valueOf(blogId));
//判斷博客是否存在
if(blog == null){
resp.getWriter().write(objectMapper.writeValueAsString(AppResult.failed("文章不存在")));
}
//判斷是否同一個作者
if(user.getId() != blog.getUserId()){
resp.getWriter().write(objectMapper.writeValueAsString(AppResult.failed("無權(quán)刪除別人的文章!")));
return;
}
//(5)否則調(diào)用Dao進(jìn)行刪除操作
int row = blogDao.deleteById(Long.valueOf(blogId));
//(6)判斷結(jié)果
if(row != 1){
resp.getWriter().write(objectMapper.writeValueAsString(AppResult.failed("刪除失敗")));
return;
}
//說明刪除成功
resp.getWriter().write(objectMapper.writeValueAsString(AppResult.success("刪除成功")));
}
postman測試:
(1)情況1:無權(quán)刪除文章
?(2)登錄用戶和博客作者是同一個人,可以刪除;
?去數(shù)據(jù)庫查看,對應(yīng)的文章也已經(jīng)刪除成功。
6.3? 前端實現(xiàn):blog_details.html
????????根據(jù)后端傳回來的結(jié)果是否成功(成功說明可以刪除文章),初始化一個刪除按鈕并綁定事件?。?
????????對應(yīng)前端代碼增加如下:deleteBlog是一個刪除方法,封裝好之后調(diào)用。
在點擊詳情之后,都會初始化一個刪除按鈕,但是只有右權(quán)限刪除的時候,刪除按鈕才起作用,否則會給出提示無權(quán)刪除。不論是不是
?重點代碼如下:
//獲取作者信息
$.ajax({
type:'get',
url:'user'+location.search,
//回調(diào)函數(shù)
success : function(respData){
//成功
if(respData.code == 0){
let user = respData.data;
//為控件賦值
$('#h_details_username').html(user.username);
//生成刪除按鈕
let htmlStr = '<a href="javascript:void(0);">刪除</a>'
//javascript:void(0)表示點擊按鈕什么事情都不干
//將html轉(zhuǎn)為jquery對象并追加到頁面上
let deleteEl = $(htmlStr);
//得到這個按鈕的父標(biāo)簽
$('.opts').append(deleteEl);
//綁定事件
deleteEl.click(deleteBlog);
}else{
alert(respData.message);
}
},
error: function(){
//打印日志
console.log('訪問出現(xiàn)問題');
},
statusCode :{
403 : function(){
//強制跳轉(zhuǎn)到登錄界面
location.assign(blog_login.html);
}
}
});
//刪除文章事件
function deleteBlog(){
//如果是否定,則直接刪除
if(!confirm("是否刪除?")){
return;
};
//否則向后端發(fā)送請求,刪除文章
$.ajax({
type : 'delete',
url : 'blog' + location.search,
//回調(diào)函數(shù)
success : function(respData){
//成功
if(respData.code == 0){
//成功,則跳轉(zhuǎn)到詳情頁
location.assign('blog_list.html');
}else{
alert(respData.message);
}
},
error: function(){
//打印日志
console.log('訪問出現(xiàn)問題');
},
statusCode :{
403 : function(){
//強制跳轉(zhuǎn)到登錄界面
location.assign(blog_login.html);
}
}
});
??7、注銷功能
作用:在服務(wù)器把用戶對應(yīng)的session銷毀。
(1)LogOutServlet:獲取session;銷毀:session.invalitate();
(2)前端退出按鈕a_list_logout:綁定事件。
7.1 Servlet實現(xiàn)后端
@WebServlet("/logout")
public class LogOutServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("application/json;charset = utf-8");
//獲取session
HttpSession session = req.getSession();
//判斷session是否有效
if(session!=null){
//銷毀session
session.invalidate();
}
resp.getWriter().write(objectMapper.writeValueAsString(AppResult.success("注銷成功")));
}
}
postman測試
7.2 前端實現(xiàn)?blog_list.html
目前只在這個頁面實現(xiàn)bblog_list.htmllog_list.html。
// 功能7:為注銷按鈕綁定事件
$('#a_list_logout').click(function(){
//發(fā)送ajax請求
$.ajax({
type: 'type',
url : 'logout',
success : function(respData){
//成功
if(respData.code == 0){
//跳轉(zhuǎn)到登錄頁面
location.assign('nlog_login.html');
}else{
//失敗
alert(respData.message);
}
},
error : function(){
console.log('訪問出現(xiàn)問題');
}
})
})
第一次做完這個項目,在知識上,主要有以下體會:
- 一般都和數(shù)據(jù)庫有關(guān)聯(lián),第一步是分析需求,建表;
- 創(chuàng)建這個項目主要分為幾個包:utils下存放工具類,model中存放各種實體類(這個類的屬性和數(shù)據(jù)庫中表的字段一一對應(yīng)),dao包下實現(xiàn)類與數(shù)據(jù)庫之間的交互,一般是進(jìn)行數(shù)據(jù)庫的增刪改查獲取結(jié)果供servlet包使用;common包中定義定義全局的配置變量,比如sessionId;還有響應(yīng)時候的數(shù)據(jù)格式:以json的形式返回,定義為一個共用的類;servlet包下負(fù)責(zé)從前端獲取請求,主要有獲取控件的參數(shù),對參數(shù)進(jìn)行校驗,然后對前端發(fā)送的請求進(jìn)行相應(yīng)的處理,一般會調(diào)用dao的結(jié)果,然后再將響應(yīng)結(jié)果返回給前端;前端一般通過給控件設(shè)置選擇器(id選擇器,類選擇器,或者button按鈕綁定事件),然后獲取控件輸入的值封裝成數(shù)據(jù),對參數(shù)進(jìn)行校驗,發(fā)送ajax 請求給后端(主要是type,url,data,content-type)然后通過會回調(diào)函數(shù)獲取后端結(jié)果做處理。
- 在前后端交互的過程中,要約定好參數(shù)的解析方式。
????????行文至此,歷時一周,終于完成了這個簡單的博客系統(tǒng)項目。雖然做的功能比較簡單且單薄,而且掌握的不夠充分,但是對于自己來說確實學(xué)習(xí)到了很多東西。感覺有一種“哦~原來是這樣的”恍然大悟,或者說某個時刻的豁然開朗。雖然掌握的內(nèi)容不多,寫的也不是很全面,但是是對自己所學(xué)東西的一個記錄吧~希望之后回過頭來再看,能有更多的啟發(fā)和思考。文章來源:http://www.zghlxwxcb.cn/news/detail-554332.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-554332.html
到了這里,關(guān)于【Servlet綜合項目練習(xí)】實現(xiàn)一個簡單的博客系統(tǒng)~的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!