目錄
一、準(zhǔn)備工作
二、設(shè)計數(shù)據(jù)庫
三、編寫數(shù)據(jù)庫代碼
1、建表sql
2、封裝數(shù)據(jù)庫的連接操作
3、創(chuàng)建實體類
4、封裝數(shù)據(jù)庫的一些增刪改查
(1)BlogDao
新增博客:?
根據(jù)博客 id 來查詢指定博客(用于博客詳情頁)
直接查詢出數(shù)據(jù)庫中所有的博客列表
刪除博客
(2)UserDao
根據(jù) userId 來查詢用戶信息
?根據(jù) username 來查詢用戶信息(登錄的時候)
四、圍繞博客列表頁實現(xiàn)獲取博客列表功能
1、約定前后端交互接口
2、編寫后端代碼
3、編寫前端代碼
五、博客詳情頁
1、約定前后端交互接口
2、實現(xiàn)后端代碼
3、實現(xiàn)前端代碼
六、實現(xiàn)登錄頁
1、約定前后端交互接口
2、修改前端代碼
3、修改后端代碼
七、頁面強制登錄
1、約定前后端交互接口
2、編寫后端代碼
3、實現(xiàn)前端代碼
八、顯示用戶信息
1、約定前后端交互接口
?2、編寫后端代碼
3、編寫前端代碼
九、退出登錄狀態(tài)
1、約定前后端接口
2、編寫后端代碼
3、編寫前端代碼
十、發(fā)布博客
1、約定前后端交互接口
?編輯
2、編寫服務(wù)器代碼
3、編寫客戶端代碼
一、準(zhǔn)備工作
創(chuàng)建項目,引入依賴,把之前寫的前端頁面頁拷貝過去
要引入的依賴有: servlet ,mysql,jackson
引入依賴完成之后,再創(chuàng)建目錄,然后將 web.xml 的內(nèi)容復(fù)制粘貼上去
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
</web-app>
然后再將之前寫的前端代碼復(fù)制粘貼到 webapp 目錄下
?此時,準(zhǔn)備工作就完成了
二、設(shè)計數(shù)據(jù)庫
結(jié)合之前的需求,在當(dāng)前的博客系統(tǒng)中,主要涉及到兩個實體:博客,用戶
我們就可以創(chuàng)建兩張表來表示 博客 和 用戶
這兩個實體之間的關(guān)系如何?
一對多的關(guān)系,一個用戶可以寫多個博客,但是一個博客只能屬于一個用戶
三、編寫數(shù)據(jù)庫代碼
1、建表sql
把一些基本的數(shù)據(jù)庫操作先封裝好,以備后續(xù)使用
在 src 目錄下,創(chuàng)建一個 db.sql,在里面寫上數(shù)據(jù)庫的建庫建表語句
--這個文件,主要用來寫建庫建表語句
--一般在建表的時候,把建表的 sql 保留下來,以備后續(xù)部署其它及其的時候就方便了
create database if not exists blog_system;
use blog_system;
--刪除舊表,重新創(chuàng)建新表,防止之前的殘留數(shù)據(jù)面對后續(xù)的程序有負(fù)面影響
drop table if exists user;
drop table if exists blog;
--進行建表
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 , --要求用戶名和別人不同
password varchar(20)
);
2、封裝數(shù)據(jù)庫的連接操作
在 java 目錄下,創(chuàng)建一個 DBUtil 類
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
//通過這個類,把數(shù)據(jù)庫連接過程封裝一下
//此處,把 DBUtil 作為一個工具類,提供 static 方法,供其它方法來調(diào)用
public class DBUtil {
//靜態(tài)成員是跟隨類對象的,類對象在整個進程中,只有唯一一份
//靜態(tài)成員相當(dāng)于也是唯一的實例(單例模式,餓漢模式)
private static DataSource dataSource = new MysqlDataSource();
static {
//使用靜態(tài)代碼塊,針對 dataSourse 進行初始化
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java?charactorEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("123456");
}
//通過這個方法來建立連接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
//通過這個方法來斷開連接,釋放資源
public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet) {
//此處的三個 try catch 分開寫更好,避免前面的異常導(dǎo)致后面的代碼無法執(zhí)行
if (resultSet != null){
try {
resultSet.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (statement != null){
try {
statement.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (connection != null){
try {
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}
3、創(chuàng)建實體類
實體類,就是和表中的記錄對應(yīng)的類
實體類里要有哪些屬性,都是和當(dāng)前表中的列是密切相關(guān)的
于是,我們便要根據(jù)之前表中的列,來創(chuàng)建好 blog 和 user 這兩個類:
并且,將里面的每個屬性,都手動加上 get set 訪問器
4、封裝數(shù)據(jù)庫的一些增刪改查
針對 博客表 ,創(chuàng)建一個 BlogDao
針對 用戶表,創(chuàng)建一個 UserDao
它們提供了一些方法,來進行增刪改查
(1)BlogDao
?里面一共有四個方法:
1、新增博客
2、根據(jù)博客 id 來查詢指定博客(用于博客詳情頁)
3、直接查詢出數(shù)據(jù)庫中所有的博客列表
4、刪除博客
新增博客:?
//1、新增一個博客
public void add(Blog blog){
Connection connection = null;
PreparedStatement statement = null;
//1、和數(shù)據(jù)庫建立連接
try {
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.getPostTime());
statement.setInt(4,blog.getUserId());
//3、執(zhí)行 sql
statement.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
DBUtil.close(connection,statement,null);
}
}
根據(jù)博客 id 來查詢指定博客(用于博客詳情頁)
//2、根據(jù)博客 id 來查詢指定博客(用于博客詳情頁)
public Blog selectById(int blogId){
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try{
//1、和數(shù)據(jù)庫建立連接
connection = DBUtil.getConnection();
//2、構(gòu)造 SQL 語句
String sql = "select *from blog where blogId = ?";
statement = connection.prepareStatement(sql);
statement.setInt(1,blogId);
//3、執(zhí)行 sql
resultSet = statement.executeQuery();
//4、遍歷結(jié)果集合,由于此處的 blogId ,在 blog 表中是唯一的(主鍵)
//此時的查詢結(jié)果,要么是沒有查到任何數(shù)據(jù),要么是只有一條記錄
//所以,此處使用 if 即可,不用使用 while
if (resultSet.next()){
Blog blog = new Blog();
blog.setBlogId(resultSet.getInt("blogId"));
blog.setTitle(resultSet.getString("title"));
blog.setContent(resultSet.getString("content"));
blog.setPostTime(resultSet.getTimestamp("postTime"));
blog.setUserId(resultSet.getInt("userId"));
return blog;
}
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
//5、釋放必要的資源
DBUtil.close(connection,statement,resultSet);
}
return null;
}
直接查詢出數(shù)據(jù)庫中所有的博客列表
注意:這里面的博客列表頁,顯示的博客內(nèi)容,應(yīng)該只是一個簡單的摘要(文章內(nèi)容的一小部分)
而不是整篇文章,完整的文章應(yīng)該放到博客詳情頁顯示
所以,我們可以對比較長的正文進行一個裁剪
//3、直接查詢出數(shù)據(jù)庫中所有的博客列表(用于博客列表頁)
public List<Blog> selectAll(){
List<Blog> blogs = new ArrayList<>();
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try{
//1、和服務(wù)器建立連接
connection = DBUtil.getConnection();
//2、構(gòu)造 sql 語句
String sql = "select *from blog";
statement = connection.prepareStatement(sql);
//3、執(zhí)行 sql
resultSet = statement.executeQuery();
//4、遍歷結(jié)果集合
while (resultSet.next()) {
Blog blog = new Blog();
blog.setBlogId(resultSet.getInt("blogId"));
blog.setTitle(resultSet.getString("title"));
//這里的正文,在博客列表頁中,不需要把整個正文顯示出來
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) {
throw new RuntimeException(e);
}finally {
DBUtil.close(connection,statement,resultSet);
}
return blogs;
}
刪除博客
//4、刪除指定博客
public void delete(int blogId){
Connection connection = null;
PreparedStatement statement = null;
try{
//1、和數(shù)據(jù)庫建立連接
connection = DBUtil.getConnection();
//2、構(gòu)造 sql 語句
String sql = "delete from blog where blogId = ?";
statement = connection.prepareStatement(sql);
statement.setInt(1,blogId);
//3、執(zhí)行 sql
statement.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
//進行關(guān)閉
DBUtil.close(connection,statement,null);
}
}
(2)UserDao
這里面一共有兩個方法:
1、根據(jù) userId 來查詢用戶信息
2、根據(jù) username 來查詢用戶信息(登錄的時候)
根據(jù) userId 來查詢用戶信息
//1、根據(jù) userId 來查詢用戶信息
public User selectById(int userId){
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
//1、和數(shù)據(jù)庫建立連接
connection = DBUtil.getConnection();
//2、構(gòu)造 sql
String sql = "select *from user where userId = ?";
statement = connection.prepareStatement(sql);
statement.setInt(1,userId);
//3、執(zhí)行 sql
resultSet = statement.executeQuery();
//4、遍歷結(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) {
throw new RuntimeException(e);
}finally {
DBUtil.close(connection,statement,resultSet);
}
return null;
}
?根據(jù) username 來查詢用戶信息(登錄的時候)
//2、根據(jù) username 來查詢用戶信息(登錄的時候)
public User selectByUsername(String username){
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
//1、和數(shù)據(jù)庫建立連接
connection = DBUtil.getConnection();
//2、構(gòu)造 sql
String sql = "select *from user where username = ?";
statement = connection.prepareStatement(sql);
statement.setString(1,username);
//3、執(zhí)行 sql
resultSet = statement.executeQuery();
//4、遍歷結(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) {
throw new RuntimeException(e);
}finally {
DBUtil.close(connection,statement,resultSet);
}
return null;
}
寫到這里,就把數(shù)據(jù)庫的操作封裝完畢了
四、圍繞博客列表頁實現(xiàn)獲取博客列表功能
接下來,我們圍繞博客列表頁,實現(xiàn)獲取博客列表的功能
當(dāng)前,我們的博客列表頁上面的數(shù)據(jù),都是固定的,顯然是不科學(xué)的
正確的做法,應(yīng)該是通過數(shù)據(jù)庫讀取數(shù)據(jù),顯示到頁面上?
此處,就需要打通前后端交互的操作
讓博客列表頁,在加載的時候,通過 ajax 給服務(wù)器,發(fā)一個請求,服務(wù)器查數(shù)據(jù)庫,獲取到博客列表數(shù)據(jù),返回給瀏覽器,瀏覽器再根據(jù)數(shù)據(jù)構(gòu)造頁面內(nèi)容
這樣的交互過程,也稱為 “前后端分離”
前端只向后端請求數(shù)據(jù),而不請求具體的頁面,后端也僅僅是返回數(shù)據(jù),這樣設(shè)定的目的就是為了前端和后端更加解耦合
上述是進行實現(xiàn)博客列表頁的基本思路
接下來,我們需要:
1、約定前后端交互接口
2、開發(fā)后端代碼
3、開發(fā)前端代碼
1、約定前后端交互接口
在 博客列表頁,獲取博客列表的功能下,前端要發(fā)什么請求,后端返回什么響應(yīng),都需要進行約定
確定了前后端接口之后,前端和后端再分別按照這個格式進行開發(fā)
2、編寫后端代碼
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
BlogDao blogDao = new BlogDao();
List<Blog> blogs = blogDao.selectAll();
//需要把 blogs 轉(zhuǎn)成符合要求的 json 格式的字符串
String respJson = objectMapper.writeValueAsString(blogs);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(respJson);
}
}
此時,我們會發(fā)現(xiàn),這里面的類,越寫越多了,看起來會顯得比較雜亂
這個時候,我們就可以對它們進行一個分類來處理
model :管理數(shù)據(jù) / 操作數(shù)據(jù)的部分
把這個 Servlet 放到 api 中,api 不到一定非得是一個 類 / 方法,也可以是? “網(wǎng)絡(luò)接口”(處理 HTTP 請求,返回 HTTP 響應(yīng))
3、編寫前端代碼
再博客列表頁,加載過程中,觸發(fā) ajax 訪問服務(wù)器中的數(shù)據(jù)
再把拿到的數(shù)據(jù),構(gòu)造到頁面中
構(gòu)造頁面內(nèi)容的時候,我們可以參考之前寫好了的 html 代碼進行構(gòu)造
<script src="./js/jquery.mini.js"></script>
<script>
//在頁面加載時 ,向服務(wù)器發(fā)起一個請求,獲取博客列表數(shù)據(jù)
function getBlogs(){
$.ajax({
type:'get',
url:'blog',
success:function(body){
//響應(yīng)的 body 是一個 json 字符串,此處已經(jīng)被 jquery 自動解析成 js 對象數(shù)組了
//直接 for 循環(huán)遍歷即可
let containerRight = document.querySelector('.containner');
for(let blog of body){
//構(gòu)造頁面內(nèi)容,就參考之前寫好的 html 代碼
//構(gòu)造整個博客 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ā)布時間
let dateDiv = document.createElement('div');
datteDiv.className = 'date';
dateDiv.innerHTML = blog.postTime;
blogDiv.appendChild(dateDiv);
//構(gòu)造博客摘要
let descDiv = document.createElement('div');
descDiv.className = 'desc';
desc.innerHTML = blog.content;
blogDiv.appendChild(descDiv);
//構(gòu)造查看全文按鈕
let a = document.createElement('a');
a.innerHTML = '查看全文 >>';
//期望點擊之后,可以跳轉(zhuǎn)到博客詳情頁,為了讓博客詳情頁知道點了哪個博客,把 blogId 傳過去
a.href = 'blog_detail.html?blogId=' + blog.blogId;
blogDiv.appendChild(a);
//把 blogDiv 給加到父元素中
containerRight.appendChild(blogDiv);
}
}
});
}
//要記得調(diào)用
getBlogs();
</script>
此時,博客列表頁功能實現(xiàn)就完成了
此時,我們插入一個測試用例,就會發(fā)現(xiàn)兩個問題:
?1、時間戳:不應(yīng)該顯示時間戳,而是應(yīng)該顯示格式化的時間
需要用到一個格式化時間的類,SimpleDateFormat 來幫我們轉(zhuǎn)化一下就可以了?
2、順序,新的博客在上面,老的博客在下面
直接在 sql 中加一個 order by 即可
五、博客詳情頁
接下來,實現(xiàn)博客詳情頁
點擊 “查看全文” 按鈕,就可以跳轉(zhuǎn)到博客詳情頁中
跳轉(zhuǎn)過去后,在博客詳情頁中發(fā)起一個 ajax ,從服務(wù)器獲取到當(dāng)前博客的具體內(nèi)容,再顯示出來
1、約定前后端交互接口
注意:由于此處是獲取博客詳情了,所以此處的 content 是完整的,不用像博客列表頁一樣進行截斷?
2、實現(xiàn)后端代碼
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//嘗試獲取一下 query string 中的 blogId 字段
String blogId = req.getParameter("blogId");
BlogDao blogDao = new BlogDao();
if (blogId == null){
//queryString 不存在,該請求是獲取博客列表頁
List<Blog> blogs = blogDao.selectAll();
//需要把 blogs 轉(zhuǎn)成符合要求的 json 格式的字符串
String respJson = objectMapper.writeValueAsString(blogs);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(respJson);
}else {
//query string 存在,本次請求是獲取指定 id 的博客
Blog blog = blogDao.selectById(Integer.parseInt(blogId));
if (blog == null){
System.out.println("當(dāng)前 bligId = " + blogId + "對應(yīng)博客不存在!");
}
String respJson = objectMapper.writeValueAsString(blog);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(respJson);
}
}
3、實現(xiàn)前端代碼
在 blog_detail.html 中,加入 ajax ,來獲取上述數(shù)據(jù)
location.search 就是獲取當(dāng)前頁面的 query string
這里的 location 不是 http?header 中 location ,是 js 代碼中的一個全局對象,類似于 document
?注意:當(dāng)前寫的 博客內(nèi)容,是使用 markdown 形式來組織的,比如博客的內(nèi)容是
內(nèi)容中,會帶有一些 markdown 的符號,最終顯示到網(wǎng)頁上,希望用戶看到的是渲染之后的結(jié)果,也就是把 # 轉(zhuǎn)換成一級標(biāo)題
數(shù)據(jù)庫中保存的 content 是渲染前的內(nèi)容?,但是最終顯示給用戶看,我們希望用戶看到的是渲染后的內(nèi)容
這里就需要使用 editor.md 對 markdown 內(nèi)容進行轉(zhuǎn)換
具體如何轉(zhuǎn)換呢?
editor.md 提供了一個方法: editormd.markdownToHTML ,效果是把 md 字符串,轉(zhuǎn)換成 html 片段,輸出到 #content 這個標(biāo)簽內(nèi)部(使用時要記得引入 editor.md 的依賴)
代碼改完之后,重新啟動服務(wù)器,發(fā)現(xiàn)此時博客詳情頁的結(jié)果,還是之前未修改的狀態(tài)
這個內(nèi)容,還是之前寫死的內(nèi)容,但是實際上,代碼已經(jīng)把這個內(nèi)容給刪掉了
這個是因為瀏覽器自身也有緩存,瀏覽器從服務(wù)器這邊獲取頁面,這個操作是通過網(wǎng)絡(luò)傳輸完成的,速度是比較慢的
瀏覽器就會把頁面內(nèi)容給緩存到本地(客戶端電腦的硬盤上),后續(xù)再訪問同一個頁面,就直接讀緩存了??
這樣就帶來了一個問題:如果服務(wù)器代碼改了,客戶端命中緩存之后就不一定能及時感知到變化
解決方案:強制刷新, ctrl + f5 ,此時是無視本地緩存的,100%重新訪問服務(wù)器的
緩存問題解決之后,發(fā)現(xiàn)當(dāng)前頁面雖然變化了,但是結(jié)果仍然不是很理想,查看報錯信息,就會發(fā)現(xiàn)是 editormd 的代碼寫錯了,進行修改后再次刷新頁面:
修改完之后,發(fā)現(xiàn)正文有了,但是標(biāo)題沒有出來
我們通過抓包來看看服務(wù)器返回結(jié)果是否符合預(yù)期,就可以確定是前端問題還是后端問題
根據(jù)響應(yīng)結(jié)果,我們可以判斷出后端代碼應(yīng)該沒什么問題,響應(yīng)結(jié)果是正確的,接下來檢查前端代碼?
我們會發(fā)現(xiàn),這是由于前端代碼中出現(xiàn)了兩個 title ,querySelector 默認(rèn)返回的是第一個 title ,然后導(dǎo)致對標(biāo)題的修改改錯了地方
解決方案:把選擇器寫的更精確一些即可
重新啟動服務(wù)器,再次刷新頁面,就可以發(fā)現(xiàn),現(xiàn)在標(biāo)題就正確出現(xiàn)了
<!-- 右側(cè)信息 -->
<div class="containner-right">
<!-- 博客標(biāo)題 -->
<h3 class="title"></h3>
<!-- 博客發(fā)布時間 -->
<div class="date"></div>
<!-- 博客正文 為了配合 editormd 進行格式轉(zhuǎn)換,此處一定要改成 id -->
<div id="content">
</div>
</div>
</div>
<script src="js/jquery.mini.js"></script>
<!-- 引入 editor.md 的依賴 -->
<!-- 要保證這個幾個 js 的加載,在 jquery 之后,因為 edit.md 依賴了 jquery -->
<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>
$.ajax({
type:'get',
url:'blog' + location.search,
success:function(body){
//處理響應(yīng)結(jié)果,此處的 body 就是表示一個博客的 js 對象
//1、更新標(biāo)題
let titleDiv = document.querySelector('.containner-right .title');
titleDiv.innerHTML = body.title;
//2、更新日期
let dateDiv = document.querySelector('.date');
dateDiv.innerHTML = body.postTime;
//3、更新博客正文
//此處,不應(yīng)該直接把博客正文填充到這個標(biāo)簽里
editormd.markdownToHTML('content',{markdown: body.content});
}
});
</script>
六、實現(xiàn)登錄頁
在此處輸入用戶名和密碼,點擊登錄,就會觸發(fā)一個 HTTP 請求
服務(wù)器驗證用戶名和密碼,然后就可以根據(jù)結(jié)果判斷是否登錄成功,如果登錄成功,就跳轉(zhuǎn)到博客列表頁
當(dāng)前,只是一個單純的輸入框,還不能提交請求,所以我們需要把它改成 form 表單
1、約定前后端交互接口
2、修改前端代碼
把頁面里加上 form 表單,使點擊登錄操作能夠觸發(fā)請求
1、加上 form 標(biāo)簽,把 input 都包裹進去
2、input 加上 name 屬性
3、把按鈕改成 input submit 類型
<!-- 垂直水平居中的對話框 -->
<div class="login-dialog">
<form action="login" method="post">
<h3>登錄</h3>
<div class="row">
<span>用戶名</span>
<input type="text" id = "username" placeholder="手機號 / 郵箱" 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="登錄"></button>
</div>
</form>
</div>
3、修改后端代碼
此處需要加個 Servlet 來處理登錄請求
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//設(shè)置請求的編碼,告訴 servlet 按照什么格式來理解請求
req.setCharacterEncoding("utf8");
//設(shè)置響應(yīng)的編碼,告訴 servlet 按照什么格式來構(gòu)造請求
//resp.setCharacterEncoding("utf8");
resp.setContentType("text/html;charset=utf8");
//1、讀取參數(shù)中的用戶名和密碼
//注意!!如果用戶名密碼包含中文,此處的讀取可能會亂碼,所以設(shè)置一下 utf8
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ù)庫,看看用戶名是否存在并且密碼是否匹配
UserDao userDao = new UserDao();
User user = userDao.selectByUsername(username);
if (user ==null){
//用戶不存在
String html = "<h3> 登陸失??! 用戶名 或 密碼 錯誤!! </h3>";
resp.getWriter().write(html);
return;
}
if (!password.equals(user.getPassword())){
//密碼不對
String html = "<h3> 登陸失??! 用戶名 或 密碼 錯誤!! </h3>";
resp.getWriter().write(html);
return;
}
//3、如果用戶名和密碼驗證通過,登錄成功,接下來創(chuàng)建會話,使用該會話來保存用戶信息
HttpSession session = req.getSession(true);
session.setAttribute("user",user);
//4、進行重定向,跳轉(zhuǎn)到博客列表頁
resp.sendRedirect("blog_list.html");
}
}
七、頁面強制登錄
當(dāng)用戶訪問 博客列表頁/詳情頁/編輯頁,要求用戶必須使已經(jīng)登錄的狀態(tài)
如果用戶還未登錄,就強制跳轉(zhuǎn)到登錄頁面
本質(zhì)就是要求,想要使用系統(tǒng),就得先登錄
實現(xiàn)思路:
在頁面加載的時候,專門發(fā)起一個新的 ajax (一個頁面里可以發(fā)起N個 ajax)
以博客列表頁為例:
先發(fā)一個請求獲取到博客列表,再發(fā)送一個 ajax 獲取用戶的登錄狀態(tài),如果用戶已經(jīng)登錄了,就沒事,如果未登錄,則頁面跳轉(zhuǎn)到登錄頁
1、約定前后端交互接口
2、編寫后端代碼
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/json;charset=utf8");
//使用這個方法來獲取到用戶的登錄狀態(tài)
//如果用戶未登錄,這里的會話就拿不到
HttpSession session = req.getSession(false);
if (session == null){
//未登錄,返回一個空的 user 對象
User user = new User();
String respJson = objectMapper.writeValueAsString(user);
resp.getWriter().write(respJson);
return;
}
User user = (User) session.getAttribute("user");
if (user == null){
user = new User();
String respJson = objectMapper.writeValueAsString(user);
resp.getWriter().write(respJson);
return;
}
//確實成功去除了 user 對象,直接返回即可
String respJson = objectMapper.writeValueAsString(user);
resp.getWriter().write(respJson);
}
3、實現(xiàn)前端代碼
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)前未登錄
//強制跳轉(zhuǎn)到登錄頁
location.assign('login.html');
}
}
});
}
checkLogin();
八、顯示用戶信息
這個用戶信息,目前是已經(jīng)被寫死了的,我們希望能夠讓它動態(tài)生成一個用戶信息
1、如果是博客列表頁,此處顯示登錄用戶的信息
2、如果是博客詳情頁,此時現(xiàn)實的是該文章的作者
1、約定前后端交互接口
博客列表頁代碼調(diào)整:
對于博客詳情頁的代碼,我們重新寫一個 servlet?
什么時候需要新寫一個 servlet?
主要看到當(dāng)前請求的路徑,是新的還是已經(jīng)有了的
?2、編寫后端代碼
@WebServlet("/author")
public class authorServlet extends HttpServlet {
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 對象
BlogDao blogDao = new BlogDao();
Blog blog = blogDao.selectById(Integer.parseInt(blogId));
if (blog == null){
//博客不存在
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("沒有找到指定博客:blogId = " + blogId);
return;
}
//根據(jù) blog 中的 userId 找到對應(yīng)的用戶信息
UserDao userDao = new UserDao();
User author = userDao.selectById(blog.getUserId());
String respJson = objectMapper.writeValueAsString(author);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(respJson);
}
}
3、編寫前端代碼
function getAuthor(){
$.ajax({
type:'get',
url:'author' + location.search,
success: function(body){
//把 username 給設(shè)置到頁面上
let h3 = document.querySelector('.containner-left .card h3');
h3.innerHTML = body.username;
}
});
}
getAuthor();
九、退出登錄狀態(tài)
這里的注銷指的是退出登錄狀態(tài),不是指刪除賬戶
判定登錄狀態(tài):
1、看是否能查到 http session 對象
2、看 session 對象里有沒有 user 對象
實現(xiàn)退出登錄,要么是把 http session 給干掉,要么把 user 給干掉,二者選其一即可
如果有會話,但是沒有 user 對象,也視為未登錄
如果想要干掉 http session 會比較麻煩,所以我們選擇干掉 user 對象
1、約定前后端接口
2、編寫后端代碼
@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)
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("當(dāng)前未登錄!");
return;
}
httpSession.removeAttribute("user");
resp.sendRedirect("login.html");
}
}
3、編寫前端代碼
十、發(fā)布博客
1、約定前后端交互接口
使用 form 表單,得讓頁面中多個 form 標(biāo)簽,同時得讓 form 里面能夠感知到博客的內(nèi)容
2、編寫服務(wù)器代碼
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//發(fā)布博客
//讀取請求,構(gòu)造出 blog 對象,插入數(shù)據(jù)庫
HttpSession httpSession = req.getSession(false);
if (httpSession == null){
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("當(dāng)前未登錄,無法發(fā)布博客");
return;
}
User user = (User) httpSession.getAttribute("user");
if (user == null){
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("當(dāng)前未登錄,無法發(fā)布博客");
return;
}
//確保登錄之后,就可以把作者給拿到了
//獲取博客標(biāo)題和正文
String title = req.getParameter("title");
String content = req.getParameter("content");
if (title == null || ".equals(title) || content == null || ".equals(content)){
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("當(dāng)前提交數(shù)據(jù)有誤,標(biāo)題或者正文為空");
return;
}
//構(gòu)造Blog 對象
Blog blog = new Blog();
blog.setTitle(title);
blog.setContent(content);
blog.setUserId(user.getUserId());
//發(fā)布時間
blog.setPostTime(new Timestamp(System.currentTimeMillis()));
//插入數(shù)據(jù)
BlogDao blogDao = new BlogDao();
blogDao.add(blog);
//跳轉(zhuǎn)到博客列表頁
resp.sendRedirect("blog_list.html");
}
3、編寫客戶端代碼
對代碼進行調(diào)整:
1、加上 form 標(biāo)簽
2、給 input 標(biāo)簽加上 name 屬性
3、把 button 改成 input 標(biāo)簽,并且 type 為? submit
4、editor.md 文檔要求寫法:加上 textarea
editor.md對于 form 表單也是支持的,就是可以在 form 里放一個隱藏的 textarea,editor.md 就會自動的把用戶輸入的 markdown 內(nèi)容填寫到 textarea 里,后續(xù)點擊 submit就能自動提交
?此時,我們再運行代碼看一下結(jié)果
這時候我們發(fā)現(xiàn)了一個問題:代碼改完之后,編輯器發(fā)生了基因突變
前端代碼樣式有問題,我們只能通過觀察 chorme 開發(fā)者工具來找問題
我們可以看到,是當(dāng)前的 form 出現(xiàn)了問題
?具體分析如下:
解決方案:給 form 設(shè)置高度
此時,我們就可以正常發(fā)布博客了,但是此時卻出現(xiàn)了亂碼的問題:
亂碼有兩種情況:
1、提交博客的時候亂碼
2、獲取博客的時候亂碼
我們只需要看一下數(shù)據(jù)庫,就知道是哪種情況;?
這時候就可以發(fā)現(xiàn),是提交的時候亂碼了
注意:在獲取標(biāo)題和正文的時候,需要指定請求的字符集,也就是告訴 servlet 按照哪種字符集來進行解析請求
此時,再次點擊提交,就可以正常的發(fā)布博客了文章來源:http://www.zghlxwxcb.cn/news/detail-691338.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-691338.html
到了這里,關(guān)于博客系統(tǒng) Java Web 開發(fā)(Servlet)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!