??作者:銀河罐頭
??系列專欄:JavaEE
??“種一棵樹最好的時(shí)間是十年前,其次是現(xiàn)在”
準(zhǔn)備工作
創(chuàng)建項(xiàng)目,引入依賴,把之前寫的前端頁(yè)面拷貝進(jìn)去。
依賴主要是:servlet, mysql, jackson
數(shù)據(jù)庫(kù)設(shè)計(jì)
表設(shè)計(jì)
在當(dāng)前的博客系統(tǒng)中,主要涉及到 2 個(gè)實(shí)體,博客 和 用戶。
創(chuàng)建 2 張表,來(lái)表示博客和用戶。用戶和博客之間的關(guān)系是一對(duì)多。
把一些基本的數(shù)據(jù)庫(kù)操作先封裝好,以備后用。
-- 這個(gè)文件主要寫建庫(kù)建表語(yǔ)句
-- 建議 在建表的時(shí)候把 sql 語(yǔ)句保留下來(lái),以便后續(xù)部署其他機(jī)器的時(shí)候就方便了。
create database if not exists java_blog_system;
use java_blog_system;
-- 刪除舊表,重新創(chuàng)建新表
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,
useId int
);
create table user(
userId int primary key auto_increment,
username varchar(20) unique, -- 要求用戶名和別人不重復(fù)
password varchar(20)
);
封裝數(shù)據(jù)庫(kù)的連接操作
public class DBUtil {
private static DataSource dataSource = new MysqlDataSource();
static {
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java_blog_system?characterEncoding=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) {
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();
}
}
}
}
創(chuàng)建實(shí)體類
實(shí)體類,和表中記錄對(duì)應(yīng)的類。
blog 表,Blog 類對(duì)應(yīng)。Blog 的一個(gè)對(duì)象,就對(duì)應(yīng)表中的一條記錄。
user 表,User 類對(duì)應(yīng)。User 的一個(gè)對(duì)象,就對(duì)應(yīng)表中的一條記錄。
實(shí)體類里要有哪些屬性,是和表里的列是密切相關(guān)的。
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 getPostTime() {
return postTime;
}
public void setPostTime(Timestamp postTime) {
this.postTime = postTime;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
}
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;
}
}
封裝數(shù)據(jù)庫(kù)的增刪改查
針對(duì)博客表,創(chuàng)建 BlogDao
針對(duì)用戶表,創(chuàng)建 UserDao
提供一些方法,來(lái)進(jìn)行增刪改查。
Dao, Data Access Object 訪問數(shù)據(jù)的對(duì)象
//通過這個(gè)類,封裝對(duì)博客表的基本操作
public class BlogDao {
//1.新增一個(gè)博客
public void add(Blog blog) {
}
//2.根據(jù) 博客 id 來(lái)查詢博客(博客詳情頁(yè)中)
public Blog selectById(int blogId){
return null;
}
//3.查詢出數(shù)據(jù)庫(kù)中所有的博客列表(博客列表頁(yè))
public List<Blog> selectAll(){
return null;
}
//4.刪除指定博客
public void delete(int blogId){
}
}
//通過這個(gè)類,封裝對(duì)博客表的基本操作
public class BlogDao {
//1.新增一個(gè)博客
public void add(Blog blog) {
Connection connection = null;
PreparedStatement statement = null;
try {
//1.建立連接
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) {
e.printStackTrace();
}finally {
//4.斷開連接,釋放資源
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
String sql = "select * from blog while blogId = ?";
statement = connection.prepareStatement(sql);
statement.setInt(1,blogId);
//3.執(zhí)行 SQL
resultSet = statement.executeQuery();
//4.遍歷結(jié)果集合
//blogId 是自增主鍵,是唯一的。所以要么是沒有查到,要么是查到了一條記錄
//此處可以不使用 where, 直接 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<>();
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
//1.建立連接
connection = DBUtil.getConnection();
//2.構(gòu)造 SQL 語(yǔ)句
String sql = "select * from blog";
statement = connection.prepareStatement(sql);
//3.執(zhí)行 sql 語(yǔ)句
statement.executeQuery();
//4.遍歷結(jié)果集合
while(resultSet.next()){
Blog blog = new Blog();
blog.setBlogId(resultSet.getInt("blogId"));
blog.setTitle(resultSet.getString("title"));
//注意這里的正文,博客列表頁(yè)不需要把整個(gè)正文都顯示出來(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 {
DBUtil.close(connection, statement, resultSet);
}
return blogs;
}
//4.刪除指定博客
public void delete(int blogId){
Connection connection = null;
PreparedStatement statement = null;
try {
//1.建立連接
connection = DBUtil.getConnection();
//2.構(gòu)建 SQL 語(yǔ)句
String sql = "delete from blog while blogId = ?";
statement = connection.prepareStatement(sql);
statement.setInt(1,blogId);
//3.執(zhí)行 sql
statement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
//4.關(guān)閉連接
DBUtil.close(connection,statement,null);
}
}
}
//針對(duì)用戶表提供的基本操作
//由于沒寫注冊(cè)功能,此處就不寫 add 了
//也沒有用戶刪號(hào)這個(gè)功能,也就不必 delete
public class UserDao {
//根據(jù) userId 來(lái)查用戶信息
public User selectById(int userId){
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
//1.建立連接
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) {
e.printStackTrace();
}finally {
DBUtil.close(connection,statement,resultSet);
}
return null;
}
//根據(jù) username 來(lái)查用戶信息(登錄的時(shí)候)
public User selectByUsername(String username){
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
//1.建立連接
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) {
e.printStackTrace();
}finally {
DBUtil.close(connection,statement,resultSet);
}
return null;
}
}
實(shí)現(xiàn)博客列表頁(yè)
當(dāng)前博客列表頁(yè)上的數(shù)據(jù)都是寫死的,正確的做法應(yīng)該是從服務(wù)器獲取數(shù)據(jù)并顯示到頁(yè)面上。
讓博客列表頁(yè),加載的時(shí)候,通過 ajax 給服務(wù)器發(fā)一個(gè)請(qǐng)求,服務(wù)器查數(shù)據(jù)庫(kù)獲取博客列表數(shù)據(jù),返回給瀏覽器,瀏覽器根據(jù)數(shù)據(jù)構(gòu)造頁(yè)面內(nèi)容。
這樣的交互過程,稱為 “前后端分離”。
前端只是向后端索要數(shù)據(jù),而不是請(qǐng)求具體的頁(yè)面。后端也僅僅是返回?cái)?shù)據(jù)。
這樣設(shè)定的目的就是讓前端和后端更加解耦。
由瀏覽器進(jìn)行具體的頁(yè)面渲染,減少了服務(wù)器的工作量。
上述是實(shí)現(xiàn)博客列表頁(yè)的基本思路。
接下來(lái)需要:
1.約定前后端交互接口
獲取博客列表功能,前端要發(fā)啥請(qǐng)求,后端要返回啥響應(yīng)。
2.開發(fā)后端代碼
3.開發(fā)前端代碼
約定前后端交互接口
開發(fā)后端代碼
@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);
}
}
把 servlet 放到 api 包里,api 不一定非得是 類/方法,也可以是 “網(wǎng)絡(luò)請(qǐng)求”(處理 http 請(qǐng)求,返回 http 響應(yīng))
開發(fā)前端代碼
在博客列表頁(yè)加載過程中,通過 ajax 訪問服務(wù)器數(shù)據(jù),再把拿到的數(shù)據(jù)構(gòu)造到頁(yè)面中。
<script src="./js/jquery.min.js"></script>
<script>
//在頁(yè)面加載的時(shí)候,向服務(wù)器發(fā)起請(qǐng)求,獲取博客列表數(shù)據(jù)
function getBlogs() {
$.ajax({
type:'get',
url: 'blog',
success: function(body) {
//響應(yīng)的正文是 json 字符串,已經(jīng)被 jquery 自動(dòng)解析成 js 對(duì)象數(shù)組了
// for 循環(huán)遍歷即可
let containerRight = document.querySelector('.container-right');
for(let blog of body){
//構(gòu)造頁(yè)面內(nèi)容,參考之前寫好的 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 傳過去
a.href = 'blog_detail.html?blogId=' + blog.blogId;
blogDiv.appendChild(a);
//把 blogDiv 加到父元素中
containerRight.appendChild(blogDiv);
}
}
});
}
//記得調(diào)用函數(shù)
getBlogs();
</script>
此時(shí),博客列表頁(yè)實(shí)現(xiàn)完成。
此時(shí)看的的博客列表頁(yè)是空著的,因?yàn)椴┛土斜淼臄?shù)據(jù)來(lái)自于數(shù)據(jù)庫(kù),而現(xiàn)在數(shù)據(jù)庫(kù)是空著的。
-- 構(gòu)造測(cè)試數(shù)據(jù)
insert into blog values(1, '這是我的第一篇博客', '從今天開始我要認(rèn)真敲代碼', now(),1);
insert into blog values(2, '這是我的第二篇博客', '從昨天開始我要認(rèn)真敲代碼', now(),1);
insert into blog values(3, '這是我的第三篇博客', '從前天開始我要認(rèn)真敲代碼', now(),1);
此處只是把 idea 當(dāng)做是個(gè) 記事本,來(lái)記錄下 sql 而已。真正要執(zhí)行,要復(fù)制到 sql 客戶端里執(zhí)行。
有 2 個(gè) bug,
1.不應(yīng)該顯示時(shí)間戳,而應(yīng)該顯示格式化時(shí)間
需要用到一個(gè) 格式化時(shí)間的類,轉(zhuǎn)換一下。
SimpleDateFormat
public Timestamp getPostTimestamp() {
return postTime;
}
public String getPostTime(){
//把時(shí)間戳轉(zhuǎn)換成格式化時(shí)間
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
return simpleDateFormat.format(postTime);
}
2.順序,新的博客在上面,老的博客在下面。
實(shí)現(xiàn)博客詳情頁(yè)
接下來(lái)實(shí)現(xiàn)博客詳情頁(yè),點(diǎn)擊"查看全文"按鈕,就能跳轉(zhuǎn)到博客詳情頁(yè)中。跳轉(zhuǎn)之后,在博客詳情頁(yè)中,對(duì)服務(wù)器發(fā)起 ajax 請(qǐng)求,從服務(wù)器獲取數(shù)據(jù),然后顯示出來(lái)。
約定前后端交互接口
開發(fā)后端代碼
@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();
//嘗試獲取下 query string 中的 blogId字段
String blogId = req.getParameter("blogId");
if (blogId == null) {
//說(shuō)明 query string 不存在,說(shuō)明這次請(qǐng)求是在獲取博客列表頁(yè)
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 {
//說(shuō)明 query string 存在,說(shuō)明這次請(qǐng)求是在獲取博客詳情頁(yè)
Blog blog = blogDao.selectById(Integer.parseInt(blogId));
if(blog == null) {
System.out.println("當(dāng)前 blogId = " + blogId + " 對(duì)應(yīng)的博客不存在!");
}
String respJson = objectMapper.writeValueAsString(blog);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(respJson);
}
}
}
開發(fā)前端代碼
在 blog_datail.html 中,加入 ajax,來(lái)獲取數(shù)據(jù)。
<script src="js/jquery.min.js"></script>
<!-- 保證 這幾個(gè) js 的加載要在 jquery 之后,因?yàn)?edit.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) {//回調(diào)函數(shù)
//處理響應(yīng)結(jié)果,此處的 body 就表示一個(gè)博客的 js 對(duì)象
//1.更新標(biāo)題
let titleDiv = document.querySelector('.title');
titleDiv.innerHTML = body.title;
//2.更新日期
let dateDiv = document.querySelector('.date');
dateDiv.innerHTML = body.postTime;
//3.更新博客正文
//此處不應(yīng)該直接把博客內(nèi)容填充到標(biāo)簽里
editormd.markdownToHTML('content',{ markdown: blog.content });
}
});
</script>
代碼改完之后,重新啟動(dòng)服務(wù)器,發(fā)現(xiàn)此時(shí)博客詳情頁(yè)的結(jié)果還是之前未修改的狀態(tài)。
始端用戶訪問加速節(jié)點(diǎn)時(shí),如果該節(jié)點(diǎn)有緩存住了要被訪問的數(shù)據(jù)時(shí)就叫做命中,如果沒有的話需要回原服務(wù)器取,就是沒有命中。
緩沖問題解決了,但是頁(yè)面仍然不是預(yù)期的效果。
修改上述代碼之后,發(fā)現(xiàn)正文有了,但是標(biāo)題沒出來(lái)。
就需要抓包,看下服務(wù)器返回結(jié)果是否符合預(yù)期,就可以確定是前端問題還是后端問題了。
響應(yīng)結(jié)果是正確的,后端代碼應(yīng)該沒問題。
接下來(lái)檢查前端代碼。
解決方案,把選擇器寫的更準(zhǔn)確一些。
實(shí)現(xiàn)博客登錄頁(yè)
此處輸入用戶名,密碼,點(diǎn)擊登錄,就會(huì)觸發(fā)一個(gè) http 請(qǐng)求。服務(wù)器驗(yàn)證用戶名和密碼,如果登陸成功,就跳轉(zhuǎn)到博客列表頁(yè)。
當(dāng)前還只是一個(gè) 輸入框,還不能提交請(qǐng)求,需要給改成 form 表單。
約定前后端交互接口
開發(fā)前端代碼
在頁(yè)面里加上 form 表單,使點(diǎn)擊登錄操作能 觸發(fā)請(qǐng)求。
<!-- 垂直水平居中的登錄對(duì)話框 -->
<div class="login-dialog">
<form action="login" method="post">
<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>
</form>
</div>
請(qǐng)求已經(jīng)構(gòu)造出來(lái)了。
開發(fā)后端代碼
此處需要價(jià)格 servlet 來(lái)處理 登錄請(qǐng)求。
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//設(shè)置請(qǐng)求的編碼,告訴 servlet 按照啥格式理解請(qǐng)求
req.setCharacterEncoding("utf8");
//設(shè)置響應(yīng)的編碼,告訴 servlet 以啥樣的格式構(gòu)造響應(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())){
//密碼錯(cuò)誤
String html = "<h3> 登錄失??! 用戶名或密碼錯(cuò)誤 </h3>";
resp.getWriter().write(html);
return;
}
//3.用戶名密碼驗(yàn)證通過,登陸成功,接下來(lái)創(chuàng)建會(huì)話,使用該會(huì)話保存用戶信息
HttpSession session = req.getSession(true);
session.setAttribute("username", username);
//4.進(jìn)行重定向,跳轉(zhuǎn)到博客列表頁(yè)
resp.sendRedirect("blog_list.html");
}
}
這樣還是無(wú)法登錄成功,因?yàn)閿?shù)據(jù)庫(kù)沒有保存用戶名密碼。
構(gòu)造一些測(cè)試數(shù)據(jù),保存到數(shù)據(jù)庫(kù)里。
登陸成功。
實(shí)現(xiàn)強(qiáng)制要求登陸
當(dāng)用戶訪問博客列表頁(yè)/詳情頁(yè)/編輯頁(yè),要求用戶必須是已登錄的狀態(tài),如果用戶還沒有登錄,就會(huì)強(qiáng)制跳轉(zhuǎn)到登錄頁(yè)面。
約定前后端交互接口
開發(fā)后端代碼
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/json;charset=utf8");
//使用這個(gè)方法來(lái)獲取用戶的登錄狀態(tài)
HttpSession session = req.getSession(false);
if(session == null){
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;
}
//確實(shí)取出了 user 對(duì)象,直接返回即可
String respJson = objectMapper.writeValueAsString(user);
resp.getWriter().write(respJson);
}
開發(fā)前端代碼
function checkLogin() {
$.ajax({
type: 'get',
url: 'login',
success: function(body) {
if(body.userId && body.userId > 0){
//登陸成功
console.log("當(dāng)前用戶已登錄!");
} else {
//當(dāng)前未登錄,強(qiáng)制跳轉(zhuǎn)到登錄頁(yè)
location.assign('blog_login.html');
}
}
});
}
checkLogin();
把這段代碼加到 列表頁(yè),詳情頁(yè),編輯頁(yè)的代碼中。
重啟服務(wù)器之后,之前的登錄狀態(tài)就沒了。
實(shí)現(xiàn)顯示用戶信息
目前頁(yè)面的用戶信息部分是寫死的.
這個(gè)地方時(shí)寫死的,希望能夠動(dòng)態(tài)生成。
1.如果是博客列表頁(yè),此處顯示登錄用戶的信息。
2.如果是博客詳情頁(yè),此處顯示該文章的作者。
比如我以"張三"的身份登錄,查看博客列表頁(yè)時(shí),左側(cè)應(yīng)該顯示"張三"的用戶信息,然后我點(diǎn)開李四寫的博客,跳轉(zhuǎn)到博客詳情頁(yè),應(yīng)該顯示作者"李四"的信息。
約定前后端交互接口
列表頁(yè)
function checkLogin() {
$.ajax({
type: 'get',
url: 'login',
success: function(body) {
if(body.userId && body.userId > 0){
//登陸成功
console.log("當(dāng)前用戶已登錄!");
//把當(dāng)前用戶的名字顯示到界面上
let h3 = document.querySelector('.container-left .card h3');
h3.innerHTML = body.username;
} else {
//當(dāng)前未登錄,強(qiáng)制跳轉(zhuǎn)到登錄頁(yè)
location.assign('blog_login.html');
}
}
});
}
詳情頁(yè)
@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("沒有找到指定博客: blogId = " + blogId);
return;
}
//根據(jù) Blog 中的 userId 找到 用戶信息
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);
}
}
function getAuthor() {
$.ajax({
type: 'get',
url: 'author',
success: function(body) {
//把 username 設(shè)置到界面上
let h3 = document.querySelector('.container-left .card h3');
h3.innerHTML = body.username;
}
});
}
getAuthor();
抓包后發(fā)現(xiàn)是少了 query string.
改下前端代碼。
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;
}
});
}
getAuthor();
退出登錄狀態(tài)
注銷(sign out)
判定登錄狀態(tài):
1.看是否能查到 http session 對(duì)象;
2.看 session 對(duì)象里有沒有 user 對(duì)象
實(shí)現(xiàn)退出登錄,要么把 session 干掉,要么把 user 干掉。只要干掉一個(gè)就可以。
HttpSession 對(duì)象要想干掉,麻煩點(diǎn)。getSession 能夠創(chuàng)建/獲取會(huì)話,沒有刪除會(huì)話的方法。直接刪除還不好刪,可以通過設(shè)置會(huì)話的過期時(shí)間來(lái)達(dá)到類似的效果,但并不優(yōu)雅。
更好的辦法是把 user 干掉,通過 removeAttribute 就刪了。
之前的邏輯中, httpSession 和 user 是一榮俱榮一損俱損的情況,但是引入注銷邏輯后,就出現(xiàn)有 httpSession 沒 user 的情況。
明確思路之后,先設(shè)計(jì)前后端交互接口。
約定前后端交互接口
開發(fā)后端代碼
@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){
//未登錄,提示出錯(cuò)
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write("當(dāng)前未登錄!");
return;
}
httpSession.removeAttribute("user");
resp.sendRedirect("login.html");
}
}
開發(fā)前端代碼
把 blog_detail.html, blog_edit.html, blog_list.html 這幾個(gè)的 a 標(biāo)簽改下就行。
發(fā)布博客
寫博客寫了一些內(nèi)容,點(diǎn)擊"發(fā)布文章",沒有反應(yīng)。
約定前后端交互接口
開發(fā)后端代碼
@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();
//嘗試獲取下 query string 中的 blogId字段
String blogId = req.getParameter("blogId");
if (blogId == null) {
//說(shuō)明 query string 不存在,說(shuō)明這次請(qǐng)求是在獲取博客列表頁(yè)
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 {
//說(shuō)明 query string 存在,說(shuō)明這次請(qǐng)求是在獲取博客詳情頁(yè)
Blog blog = blogDao.selectById(Integer.parseInt(blogId));
if(blog == null) {
System.out.println("當(dāng)前 blogId = " + blogId + " 對(duì)應(yīng)的博客不存在!");
}
String respJson = objectMapper.writeValueAsString(blog);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(respJson);
}
}
@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/html; charset=utf8");
resp.getWriter().write("當(dāng)前未登錄,無(wú)法發(fā)布博客!");
return;
}
User user = (User) httpSession.getAttribute("user");
if(user == null){
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write("當(dāng)前未登錄,無(wú)法發(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)造一個(gè) 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("blog_list.html");
}
}
開發(fā)前端代碼
把頁(yè)面改造下,能夠構(gòu)造出請(qǐng)求。
<div class="blog-edit-container">
<form action="blog" method="post">
<!-- 博客標(biāo)題編輯區(qū) -->
<div class="title">
<input type="text" id="title" placeholder="輸入文章標(biāo)題" name="title">
<input type="submit" id="submit" value="發(fā)布文章">
</div>
<!-- 博客編輯器, 這里用 id 是為了和 markdown 編輯器對(duì)接 -->
<div id="editor">
<textarea name="content" style="display: none;"></textarea>
</div>
</form>
</div>
代碼改完之后,再次運(yùn)行,發(fā)現(xiàn)只顯示一小段了。
前端代碼有問題,看 chrome 開發(fā)者工具。
之前給編輯器設(shè)置的高度。
<form action="blog" method="post" style="height: 100%;">
發(fā)現(xiàn)亂碼了?!
亂碼是提交博客的時(shí)候亂的,還是獲取博客的時(shí)候亂的?
這里大概率是提交的時(shí)候亂的,因?yàn)楂@取數(shù)據(jù)這個(gè)功能前面已經(jīng)測(cè)試過了,而提交這個(gè)功能還沒有測(cè)試過。
只要看下數(shù)據(jù)庫(kù)是不是亂的。
應(yīng)該是提交的時(shí)候就亂了。
把亂碼的第4條刪掉。
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-412795.html
//獲取博客標(biāo)題和正文
req.setCharacterEncoding("utf8");
String title = req.getParameter("title");
String content = req.getParameter("content");
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-412795.html
到了這里,關(guān)于博客系統(tǒng)(前后端分離)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!