青岛商务学校网站建设,wordpress 全文,北京企业聚集,如何建设一个简易网站目录一、准备工作二、引入依赖三、创建必要的目录四、编写代码五/六、打包部署(直接基于 smart tomcat)七、验证代码正式编写服务器代码编写数据库相关的操作代码创建数据库/表结构(数据库设计)数据库代码封装数据库操作封装针对数据的增删改查#xff01;博客列表页约定前后端…
目录一、准备工作二、引入依赖三、创建必要的目录四、编写代码五/六、打包部署(直接基于 smart tomcat)七、验证代码正式编写服务器代码编写数据库相关的操作代码创建数据库/表结构(数据库设计)数据库代码封装数据库操作封装针对数据的增删改查博客列表页约定前后端接口编写服务器代码编写客户端代码问题一问题二刷新页面的时候发现一哆嗦~博客详情页约定前后端交互接口前端代码登录页约定前后端交互接口后端逻辑约定前后端交互接口正确显示用户信息针对博客列表页针对博客详情页实现注销功能约定前后端交互接口发布博客功能约定前后端交互接口删除博客完整代码一、准备工作
打开你的idea创建一个Maven。 File-new-Project 选择创建maven项目然后点击next 取个名字
二、引入依赖
这里我们需要引入的依赖是servletjacksonmysql。 这个操作嘛直接看JavaEE-实现一个服务器版本的“表白墙” 里面有引入依赖的相关操作~
pox.mal
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdorg.example/groupIdartifactIdblog_system/artifactIdversion1.0-SNAPSHOT/versionpropertiesmaven.compiler.source8/maven.compiler.sourcemaven.compiler.target8/maven.compiler.target/propertiesdependencies!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --dependencygroupIdjavax.servlet/groupIdartifactIdjavax.servlet-api/artifactIdversion3.1.0/versionscopeprovided/scope/dependency!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --dependencygroupIdcom.fasterxml.jackson.core/groupIdartifactIdjackson-databind/artifactIdversion2.12.6.1/version/dependency!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion5.1.47/version/dependency/dependencies
/project三、创建必要的目录
点开我们项目下的src文件找到main然后右键创建一个新的目录webapp在webapp下创建一个目录叫做WEB-INF然后在WEB-INF下面创建一个新的xml文件叫做web.xml 在web.xml文件中填写以下代码
!DOCTYPE web-app PUBLIC-//Sun Microsystems, Inc.//DTD Web Application 2.3//ENhttp://java.sun.com/dtd/web-app_2_3.dtd web-appdisplay-nameArchetype Created Web Application/display-name
/web-app四、编写代码
找到java文件在java文件下创建一个类叫做HelloServlet 写一个简单的实验代码
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class HelloServlet extends HttpServlet {Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().write(你好博客系统);}
}
五/六、打包部署(直接基于 smart tomcat)
这里打包部署在博主之前的博客已经介绍过使用idea自带的插件smart tomcat来完成
七、验证代码
在浏览器验证程序。
正式编写服务器代码 找到我们以前的博客系统的前端代码然后复制粘贴到当前的webapp目录下 就可以把前端页面给引入项目中。
C和M先来实现Model层~~ 先来实现数据库相关的代码
编写数据库相关的操作代码
创建数据库/表结构(数据库设计)
设计数据库需要根据当前的需求来进行设计~~
这里来介绍一下前面写的博客系统页面
1.博客列表页显示博客的列表
2.博客详情页点击博客列表页上面列出的博客篇数可以跳转到完整的博客内容
3.登录页面
4.博客编辑页基于editor.md整了一个markdown编辑器根据这个页面来发布博客存储博客~当点击发布的时候博客被发布到服务器上就要被存起来 获取博客~当博客列表页和博客详情页能够拿到博客的内容还能够进行登录校验
设计表就需要抓住需求中的实体(关键性的名词)
博客表用来存储所有博客数据~ 用户表用户登录就需要用到这个表~
数据库代码
-- 编写建库建表的 sqlcreate database if not exists java107_blog;use java107_blog;-- 创建一个博客表
drop table if exists blog;
create table blog (blogId int primary key auto_increment,title varchar(1024),content mediumtext,userId int, -- 文章作者的 idpostTime datetime -- 发布时间
);-- 创建一个用户表
drop table if exists user;
create table user (userId int primary key auto_increment,username varchar(128) unique, -- 后续会使用用户名来登录一般用于登录的用户名都是不能重复的password varchar(128)
);insert into user values(null, zhangsan, 123);
insert into user values(null, lisi, 123);522215编写完成之后打开mysql客户端然后复制上面的SQL语句粘贴到我们的mysql客户端 检查数据库和表是否创建完成
封装数据库操作
先创建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;// 使用这个类和数据库建立连接
public class DBUtil {private static final String URL jdbc:mysql://127.0.0.1:3306/java107_blog?characterEncodingutf8useSSLfalse;private static final String USERNAME root;private static final String PASSWORD 1234;private static volatile DataSource dataSource null;private static DataSource getDataSource() {if (dataSource null) {synchronized (DBUtil.class) {if (dataSource null) {dataSource new MysqlDataSource();((MysqlDataSource) dataSource).setURL(URL);((MysqlDataSource) dataSource).setUser(USERNAME);((MysqlDataSource) dataSource).setPassword(PASSWORD);}}}return dataSource;}public static Connection getConnection() throws SQLException {return getDataSource().getConnection();}public void close(Connection connection, PreparedStatement statement, ResultSet resultSet) {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);}}}}
创建实体类 使用实体类表示数据库中的一条记录
此处主要创建了Blog类和User类 User类
// 每个 User 对象对应 user 表里的一条记录
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;}
}
Blog类
import java.sql.Timestamp;// 每个 Blog 对象对应 blog 表里的一条记录
public class Blog {private int blogId;private String title;private String content;private int userId;private Timestamp postTime;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 int getUserId() {return userId;}public void setUserId(int userId) {this.userId userId;}public Timestamp getPostTime() {return postTime;}public void setPostTime(Timestamp postTime) {this.postTime postTime;}
}
封装针对数据的增删改查
把提供了增删改查这样的类称为DAO BlogDao类
package Model;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;// 用于去封装博客表的基本操作
public class BlogDao {// 1. 往博客表里插入一个博客.public void insert(Blog blog) {// JDBC 基本代码~Connection connection null;PreparedStatement statement null;try {// 1) 和数据库建立连接connection DBUtil.getConnection();// 2) 构造 SQL 语句String sql insert into blog values(null, ?, ?, ?, now());statement connection.prepareStatement(sql);statement.setString(1, blog.getTitle());statement.setString(2, blog.getContent());statement.setInt(3, blog.getUserId());// 3) 执行 SQLstatement.executeUpdate();} catch (SQLException e) {throw new RuntimeException(e);} finally {// 4) 关闭连接释放资源DBUtil.close(connection, statement, null);}}// 2. 能够获取到博客表中的所有博客表的信息(用于博客列表页),此处每篇博客不一定获取到完整的正文public ListBlog selectAll() {ListBlog blogs new ArrayList();Connection connection null;PreparedStatement statement null;ResultSet resultSet null;try {connection DBUtil.getConnection();String sql select * from blog;statement connection.prepareStatement(sql);resultSet statement.executeQuery();while (resultSet.next()) {Blog blog new Blog();blog.setBlogId(resultSet.getInt(blogId));blog.setTitle(resultSet.getString(title));blog.setContent(resultSet.getString(content));blog.setUserId(resultSet.getShort(userId));blog.setPostTime(resultSet.getTimestamp(postTime));blogs.add(blog);}} catch (SQLException e) {throw new RuntimeException(e);} finally {DBUtil.close(connection, statement, resultSet);}return blogs;}// 3. 能够根据博客 id 获取到指定的博客内容(用于博客详情页)public Blog selectOne(int blogId) {Connection connection null;PreparedStatement statement null;ResultSet resultSet null;try {connection DBUtil.getConnection();String sql select * from blog where blogId ?;statement connection.prepareStatement(sql);statement.setInt(1, blogId);resultSet statement.executeQuery();// 此处我们是使用 主键 来作为查询条件的,查询结果要么是1要么是0if (resultSet.next()) {Blog blog new Blog();blog.setBlogId(resultSet.getInt(blogId));blog.setTitle(resultSet.getString(title));blog.setContent(resultSet.getString(content));blog.setUserId(resultSet.getShort(userId));blog.setPostTime(resultSet.getTimestamp(postTime));return blog;}} catch (SQLException e) {throw new RuntimeException(e);} finally {DBUtil.close(connection, statement, resultSet);}return null;}// 4. 从博客表中根据博客id删除博客.public void delete(int blogId) {Connection connection null;PreparedStatement statement null;try {connection DBUtil.getConnection();String sql delete from blog where blogId ?;statement connection.prepareStatement(sql);statement.setInt(1, blogId);statement.executeUpdate();} catch (SQLException e) {throw new RuntimeException(e);} finally {DBUtil.close(connection, statement, null);}}// 注意上述操作是增删查没有改~}
UserDao类
package Model;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;// 提供了针对 用户表 的基本操作
public class UserDao {// 需要实现的操作// 针对这个类来说就简化的写就行了像注册/销号这样的功能就不考虑了。// 主要实现// 1. 根据用户名来查找用户信息。// 会在登录逻辑中使用~public User selectByName(String username) {Connection connection null;PreparedStatement statement null;ResultSet resultSet null;try {connection DBUtil.getConnection();String sql select * from user where username ?;statement connection.prepareStatement(sql);statement.setString(1, username);resultSet statement.executeQuery();// 此处 username 使用了 unique 约束要么能查到一个要么一个都查不到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;}// 2. 根据用户 id 来找用户信息// 博客详情页就可以根据用户 id 来查询作者的名字把作者名字显示出来。public User selectById(int userId) {Connection connection null;PreparedStatement statement null;ResultSet resultSet null;try {connection DBUtil.getConnection();String sql select * from user where userId ?;statement connection.prepareStatement(sql);statement.setInt(1, userId);resultSet statement.executeQuery();// 此处 username 使用了 unique 约束要么能查到一个要么一个都查不到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;}
} 结过上述的逻辑终于把数据库操作都准备好了~ 接下来就可以实现服务器中的后续代码了~ (Model就搞定了要去写Controller了) 针对这里的这四个页面 分别来“约定前后端交互接口”“编写服务器代码”“编写客户端代码”
博客列表页
这个页面需要能够展示出数据库中的博客的列表
约定前后端接口
请求
GET /blog响应
[{blogId: 1,title: 这是第一篇博客,content: 这是博客正文,userId: 1,postTime: 2023-03-18 09:00:00},{blogId: 2,title: 这是第二篇博客,content: 这是博客正文,userId: 1,postTime: 2023-03-18 09:00:00},
]这里的content与其说是“正文”不如说是正文的摘要。 博客列表里面主要显示有哪些博客~ 因此就需要在这个地方把正文进行截取(如果正文太长就只截取前面的一部分)
编写服务器代码
接下来进一步编写服务器和客户端之间的代码~ 我们试着先来写一下看一下是否能连接成功
package controller;import com.fasterxml.jackson.databind.ObjectMapper;
import model.Blog;
import model.BlogDao;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;
import java.util.List;// 通过这个类来处理 /blog 的对应请求
WebServlet(/blog)
public class BlogServlet extends HttpServlet {private ObjectMapper objectMapper new ObjectMapper();// 这个方法用来获取到数据库中的博客列表Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 从数据库中查询到博客列表转成JSON格式 然后直接返回即可BlogDao blogDao new BlogDao();ListBlog blogs blogDao.selectAll();// 把 blogs 对象转成JSON格式String respJson objectMapper.writeValueAsString(blogs);resp.setContentType(application/json;charsetutf8);resp.getWriter().write(respJson);}
} 发现连接成功了~
在数据库中插入点信息
-- 给博客表中插入数据方便测试。
insert into blog values(null, 这是第一篇博客, 从今天开始我要认真学 Java, 1, now());
insert into blog values(null, 这是第二篇博客, 从昨天开始我要认真学 Java, 1, now());
insert into blog values(null, 这是第三篇博客, 从前天开始我要认真学 Java, 1, now());
insert into blog values(null, 这是第一篇博客, 从今天开始我要认真学 C, 2, now());此处得到的响应和预期的效果有一点点差别时间格式~ 预期得到的是一个格式化时间实际上得到一个毫秒时间戳 此处就需要把时间戳转成格式化时间(可以在前端来做也可以在后端来做) 这里在后端来改 在Blog类中
public String getPostTime() {// 使用 SimpleDateFormat 来完成时间戳到格式化日期的转换// 这个转换过程需要在构造方法中指定要转换格式然后调用format来进行转换SimpleDateFormat simpleDateFormat new SimpleDateFormat(yyyy-MM-dd HH:mm:ss);return simpleDateFormat.format(postTime);}这里就看到我们的格式改变了~
编写客户端代码
在页面加载的时候让页面通过ajax访问服务器获取到数据库中的博客数据并且填写到页面中
处理服务器响应的逻辑:
script srchttp://libs.baidu.com/jquery/2.0.0/jquery.min.js/scriptscript// 在页面加载的时候通过 ajax 给服务器发送数据获取到博客列表信息并且显示在页面上function getBlogList() {$.ajax({type: get,url: blog,success: function(body) {// 获取到的 body 就是一个 js 对象数组每个元素就是一个 js 对象,根据这个对象来构造一个div// 1. 先把 .right 里原有的内容清空let rightDiv document.querySelector(.right);rightDiv.innerHTML ;// 2. 遍历 body构造出一个个的 blogDivfor (let blog of body) {let blogDiv document.createElement(div);blogDiv.className blog;let titleDiv document.createElement(div);titleDiv.className title;titleDiv.innerHTML blog.title;blogDiv.appendChild(titleDiv);// 构造发布时间let dateDiv document.createElement(div);dateDiv.className date;dateDiv.innerHTML blog.postTime;blogDiv.appendChild(dateDiv);// 构造博客的摘要let descDiv document.createElement(div);descDiv.className desc;descDiv.innerHTML blog.content;blogDiv.appendChild(descDiv);// 构造查看全文let a document.createElement(a);a.innerHTML 查看全文 gt;gt;;// 此处希望点击之后能够跳转博客详情页// 这个跳转过程需要告知服务器要访问哪个博客的详情页a.href blog_detail.html?blogId blog.blogId;blogDiv.appendChild(a);// 把 blodDiv 挂到 dom树上rightDiv.appendChild(blogDiv);}},error: function() {alert(获取博客列表失败)}});}getBlogList();/script完整代码
!DOCTYPE html
html langen
headmeta charsetUTF-8meta http-equivX-UA-Compatible contentIEedgemeta nameviewport contentwidthdevice-width, initial-scale1.0title博客列表页/titlelink relstylesheet hrefcss/common.csslink relstylesheet hrefcss/blog_list.css
/head
body!-- 这是导航栏 --div classnavimg srcimage/log.jpg altspan我的博客系统/span!-- 空白元素用来占位置 --div classspacer/diva hrefblog_list.html主页/aa hrefblog_edit.html写博客/aa href#注销/a/divdiv classcontainer!-- 左侧个人信息 --div classleft!-- 表示整个用户信息区 --div classcardimg srcimage/2.jpg alth3摸鱼王胖嘟嘟/h3a href#gitee 地址/adiv classcounterspan文章/spanspan分类/span/divdiv classcounterspan2/spanspan1/span/div/div/div!-- 右侧个人信息 --div classright!-- .blog就对应一个博客 --div classblog!-- 博客标题 --div classtitle我的第一篇博客/div!-- 博客发布时间 --div classdate2023-02-11 18:26:00/div!-- 博客摘要 --div classdesc从今天起我要认真敲代码. Lorem, ipsum dolor sit amet consectetur adipisicing elit. Neque exercitationem, ut doloribus blanditiis, eveniet earum culpa accusamus rem eum cum deleniti, quisquam expedita distinctio tempora quaerat adipisci officia esse reprehenderit./diva href#查看全文 gt;/a/div!-- .blog就对应一个博客 --div classblog!-- 博客标题 --div classtitle我的第一篇博客/div!-- 博客发布时间 --div classdate2023-02-11 18:26:00/div!-- 博客摘要 --div classdesc从今天起我要认真敲代码. Lorem, ipsum dolor sit amet consectetur adipisicing elit. Neque exercitationem, ut doloribus blanditiis, eveniet earum culpa accusamus rem eum cum deleniti, quisquam expedita distinctio tempora quaerat adipisci officia esse reprehenderit./diva href#查看全文 gt;/a/div/divscript srchttp://libs.baidu.com/jquery/2.0.0/jquery.min.js/scriptscript// 在页面加载的时候通过 ajax 给服务器发送数据获取到博客列表信息并且显示在页面上function getBlogList() {$.ajax({type: get,url: blog,success: function(body) {// 获取到的 body 就是一个 js 对象数组每个元素就是一个 js 对象,根据这个对象来构造一个div// 1. 先把 .right 里原有的内容清空let rightDiv document.querySelector(.right);rightDiv.innerHTML ;// 2. 遍历 body构造出一个个的 blogDivfor (let blog of body) {let blogDiv document.createElement(div);blogDiv.className blog;let titleDiv document.createElement(div);titleDiv.className title;titleDiv.innerHTML blog.title;blogDiv.appendChild(titleDiv);// 构造发布时间let dateDiv document.createElement(div);dateDiv.className date;dateDiv.innerHTML blog.postTime;blogDiv.appendChild(dateDiv);// 构造博客的摘要let descDiv document.createElement(div);descDiv.className desc;descDiv.innerHTML blog.content;blogDiv.appendChild(descDiv);// 构造查看全文let a document.createElement(a);a.innerHTML 查看全文 gt;gt;;// 此处希望点击之后能够跳转博客详情页// 这个跳转过程需要告知服务器要访问哪个博客的详情页a.href blog_detail.html?blogId blog.blogId;blogDiv.appendChild(a);// 把 blodDiv 挂到 dom树上rightDiv.appendChild(blogDiv);}},error: function() {alert(获取博客列表失败)}});}getBlogList();/script
/body
/htmlBlogDao.java逻辑的处理
package model;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;// 用于去封装博客表的基本操作
public class BlogDao {// 1. 往博客表里插入一个博客.public void insert(Blog blog) {// JDBC 基本代码~Connection connection null;PreparedStatement statement null;try {// 1) 和数据库建立连接connection DBUtil.getConnection();// 2) 构造 SQL 语句String sql insert into blog values(null, ?, ?, ?, now());statement connection.prepareStatement(sql);statement.setString(1, blog.getTitle());statement.setString(2, blog.getContent());statement.setInt(3, blog.getUserId());// 3) 执行 SQLstatement.executeUpdate();} catch (SQLException e) {throw new RuntimeException(e);} finally {// 4) 关闭连接释放资源DBUtil.close(connection, statement, null);}}// 2. 能够获取到博客表中的所有博客表的信息(用于博客列表页),此处每篇博客不一定获取到完整的正文public ListBlog selectAll() {ListBlog blogs new ArrayList();Connection connection null;PreparedStatement statement null;ResultSet resultSet null;try {connection DBUtil.getConnection();String sql select * from blog;statement connection.prepareStatement(sql);resultSet statement.executeQuery();while (resultSet.next()) {Blog blog new Blog();blog.setBlogId(resultSet.getInt(blogId));blog.setTitle(resultSet.getString(title));blog.setContent(resultSet.getString(content));blog.setUserId(resultSet.getShort(userId));blog.setPostTime(resultSet.getTimestamp(postTime));blogs.add(blog);}} catch (SQLException e) {throw new RuntimeException(e);} finally {DBUtil.close(connection, statement, resultSet);}return blogs;}// 3. 能够根据博客 id 获取到指定的博客内容(用于博客详情页)public Blog selectOne(int blogId) {Connection connection null;PreparedStatement statement null;ResultSet resultSet null;try {connection DBUtil.getConnection();String sql select * from blog where blogId ?;statement connection.prepareStatement(sql);statement.setInt(1, blogId);resultSet statement.executeQuery();// 此处我们是使用 主键 来作为查询条件的,查询结果要么是1要么是0if (resultSet.next()) {Blog blog new Blog();blog.setBlogId(resultSet.getInt(blogId));blog.setTitle(resultSet.getString(title));// 这里需要针对内容进行截断(太长了就去掉后面)String content resultSet.getString(content);if (content.length() 50) {content content.substring(0, 50) ...;}blog.setContent(content);blog.setUserId(resultSet.getShort(userId));blog.setPostTime(resultSet.getTimestamp(postTime));return blog;}} catch (SQLException e) {throw new RuntimeException(e);} finally {DBUtil.close(connection, statement, resultSet);}return null;}// 4. 从博客表中根据博客id删除博客.public void delete(int blogId) {Connection connection null;PreparedStatement statement null;try {connection DBUtil.getConnection();String sql delete from blog where blogId ?;statement connection.prepareStatement(sql);statement.setInt(1, blogId);statement.executeUpdate();} catch (SQLException e) {throw new RuntimeException(e);} finally {DBUtil.close(connection, statement, null);}}// 注意上述操作是增删查没有改~
}
问题一
当前这个博客列表页还有点小问题~ 当前拿到的博客列表顺序是不太科学的 需要保证我们最新的博客得在最上面才行~
如果在sql中没有指定 order by ~ 此时查询到的结果是不确定的~
因此在sql中不加 order by 之前是不应该依赖查询结果的顺序的~
在BlogDao.java中找到此处 可以看到是降序排序的了 问题二刷新页面的时候发现一哆嗦~
旧的内容(测试时写死的数据) 新的内容(从数据库里查的数据) 是通过网络交互的花个几十 ms 都是很正常的人眼是能捕捉到这个变化的~
直接把旧的测试数据删了就行了~ 像这样~~ 博客详情页
约定前后端交互接口
请求
GET /blog?blogId 1
响应
HTTP/1.1 200 OK
Content-Type: application/json;{blogId: 1,title: “第一篇博客”,content: 这是正文,userId: 1,postTime: 2023-03-18 15:58:00
}和获取博客列表的区别~ 1.请求里面带有参数blogId 2.响应结果不是json数组了而只是单一的对象 3.响应的博客正文不再截断了。
获取博客列表的请求 GET/ blog 不带参数的~
package controller;import com.fasterxml.jackson.databind.ObjectMapper;
import model.Blog;
import model.BlogDao;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;
import java.util.List;// 通过这个类来处理 /blog 的对应请求
WebServlet(/blog)
public class BlogServlet extends HttpServlet {private ObjectMapper objectMapper new ObjectMapper();// 这个方法用来获取到数据库中的博客列表Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType(application/json;charsetutf8);BlogDao blogDao new BlogDao();// 先尝试获取到 req 中的 blogId参数如果该参数存在说明要请求博客详情// 如果不存在就说明要请求博客列表String pararm req.getParameter(blogId);if (pararm null) {// 不存在参数获取博客列表// 从数据库中查询到博客列表转成JSON格式 然后直接返回即可ListBlog blogs blogDao.selectAll();// 把 blogs 对象转成JSON格式String respJson objectMapper.writeValueAsString(blogs);resp.getWriter().write(respJson);} else {// 存在参数获取博客详情int blogId Integer.parseInt(pararm);Blog blog blogDao.selectOne(blogId);String respJson objectMapper.writeValueAsString(blog);resp.getWriter().write(respJson);}}
} 后端代码实现和博客列表页的获取基本相同就直接放到一个方法中来实现了使用blogId参数来区分是获取博客列表还是详情~
前端代码
修改blog_detail.html让这个页面加载的时候能够调用上述接口来从服务器获取到博客数据 在前端代码这边要想构造一个请求获取博客详情就得知道当前用户点击的博客的id 这个id就已经包含在 当前的 blog_detail.html页面的url里面了~
希望在博客详情页拿到博客的具体内容。 就需要构造一个请求 /blog?blogId5 这个请求是 blog_detail.html通过ajax发送的 blog_detail.html就需要构造出blogId(5)的这个参数 关键问题就是这个参数(5)从哪里来的呢 其实当来到blog_detail.html这个页面的时候url里就已经带上了这个参数~
通过location.search就能够拿到?blogId5这段内容从而构造出/blog?blogId5这样的请求。 既然如此那应该如何处理能够让我们这里的markdown文本内容被渲染成带有特定样式的html片段呢 仍然要使用edlitor.md这个库来实现。 这个库不仅提供了markdown编辑器也提供了渲染功能~
当我们在进行程序验证的时候要时刻牢记浏览器缓存可能会影响到结果. 之前已经访问过blog_detail页面了因此浏览器就可能把这个页面给保存到本地. 下次再尝试访问的时候就直接访问本地内容了。 !DOCTYPE html
html langen
headmeta charsetUTF-8meta http-equivX-UA-Compatible contentIEedgemeta nameviewport contentwidthdevice-width, initial-scale1.0title博客详情页/titlelink relstylesheet hrefcss/common.csslink relstylesheet hrefcss/blog_detail.css!-- 引入 editor.md 的依赖 --link relstylesheet hrefeditor.md/css/editormd.min.css /script srcjs/jquery.min.js/scriptscript srceditor.md/lib/marked.min.js/scriptscript srceditor.md/lib/prettify.min.js/scriptscript srceditor.md/editormd.js/script
/head
body!-- 这是导航栏 --div classnavimg srcimage/log.jpg altspan我的博客系统/span!-- 空白元素用来占位置 --div classspacer/diva hrefblog_list.html主页/aa hrefblog_edit.html写博客/aa href#注销/a/divdiv classcontainer!-- 左侧个人信息 --div classleft!-- 表示整个用户信息区 --div classcardimg srcimage/2.jpg alth3摸鱼王胖嘟嘟/h3a href#gitee 地址/adiv classcounterspan文章/spanspan分类/span/divdiv classcounterspan2/spanspan1/span/div/div/div!-- 右侧个人信息 --div classright!-- 使用这个 div 来包裹整个博客内容详情 --div classblog-content!-- 博客标题 --h3/h3!-- 博客的时间 --div classdate/div!-- 博客的正文内容 --div idcontent styleopacity: 80%/div/div/divscriptfunction getBlogDetail() {$.ajax({type: get,// location.search 拿到了形如 ?blogId5 这样的一段内容url: blog location.search,success: function(body) {// 根据 body 中的内容来构造页面// 1. 构造博客标题let h3 document.querySelector(.blog-contenth3);h3.innerHTML body.title;// 2. 构造博客发布时间let dateDiv document.querySelector(.date);dateDiv.innerHTML body.postTime;// 3. 构造博客正文// 如果直接把content设为 innerHtML此时展示在界面上的内容是原始的markdown字符串// 咱们需要的是渲染后的带有格式的效果// let content document.querySelector(#content);// content.innerHTML body.content;// 第一个参数对应 idcontent 的html标签渲染后得到的HTML片段就会放到这个标签下editormd.markdownToHTML(content, {markdown: body.content});}});}getBlogDetail();/script/div
/body
/html当前已经把博客列表页和详情页搞出来了 接下来来写登录页
登录页
约定前后端交互接口
请求
POST /login
Content-Type: application/x-ww-form-urlencoded
这里的逻辑可以直接使用form表单来进行提交没必要非要使用ajax~
usernamezhangsanpassword123
响应
HTTP/1.1 302
Location:blog.list.html
当登录成功之后就自动跳转到主页(博客列表页)1)给这部分代码套上一层form标签 给input加上name属性 2)usernamezhangsanpassword123 3)把button按钮换成input标签
在前端页面开发的过程中html页面结构是非常重要的后续的CSS和JS很多都依赖了这个页面的结构 一旦页面结构发生了调整就可能导致CSS或者js失效
!DOCTYPE html
html langen
headmeta charsetUTF-8meta http-equivX-UA-Compatible contentIEedgemeta nameviewport contentwidthdevice-width, initial-scale1.0title登录页面/titlelink relstylesheet hrefcss/common.csslink relstylesheet hrefcss/blog_login.css
/head
body!-- 这是导航栏 --div classnavimg srcimage/log.jpg altspan我的博客系统/span!-- 空白元素用来占位置 --div classspacer/diva hrefblog_list.html主页/aa hrefblog_edit.html写博客/a!-- 注销页面没必要在登陆页面展示 --!-- a href#注销/a --/divdiv classlogin-containerform actionlogin methodpostdiv classlogin-dialogh3登录/h3div classrowspan用户名/spaninput typetext id username nameusername/divdiv classrowspan密码/spaninput typepassword idpassword namepassword/divdiv classrow!-- button提交/button --input typesubmit idsubmit value提交/div/div/form/div
/body
/html/* 登录页面的专用样式文件 */.login-container {width: 100%;height: calc(100% - 50px);/* 需要让里面的子元素垂直水平居中 */display: flex;align-items: center;justify-content: center;
}.login-dialog {width: 400px;height: 350px;background-color: rgba(255, 255, 255, 0.8);border-radius: 10px;
}.login-dialog h3 {text-align: center;padding: 50px 0;
}.login-dialog .row {height: 50px;width: 100%;display: flex;align-items: center;justify-content: center;
}.login-dialog .row span {/* 把span转换成块级元素方便设置尺寸 */display: block;width: 100px;font-weight: 700;
}#username, #password {width: 200px;height: 40px;font-size: 22px;line-height: 40px;padding-left: 10px;border-radius: 10px;/* 去掉边框 */border: none;/* 去掉轮廓线 */outline: none;
}.row #submit {width: 300px;height: 50px;border-radius: 10px;color: white;background-color: rgb(0,128,0);border: none;outline: none;margin-top: 50px;
}.row #submit:active {background-color: #666;
} 后端逻辑
此处约定的路径是/login这是一个新的路径就需要使用一个新的servlet来处理 使用getPararmeter读取参数的时候如果参数的值是纯英文那还好一般没啥问题。 如果参数的值是中文此时直接读取参数很容易出现乱码
中文就涉及到乱码。 但是Servlet默认并不是按照utf8来解析的~
package controller;import model.User;
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 javax.servlet.http.HttpSession;
import java.io.IOException;WebServlet(/login)
public class LoginServlet extends HttpServlet {Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setCharacterEncoding(utf8);resp.setCharacterEncoding(utf8);// 1. 获取到请求中的参数String username req.getParameter(username);String password req.getParameter(password);System.out.println(username username , password password);if (username null || .equals(username) || password null || .equals(password)) {// 请求的内容缺失肯定是登录失败~resp.setContentType(text/html; charsetutf8);resp.getWriter().write(当前的用户名或密码为空);return;}// 2. 和数据库中的内容进行比较UserDao userDao new UserDao();User user userDao.selectByName(username);if (user null || !user.getPassword().equals(password)) {// 用户没有查到或者密码不匹配也是登录失败resp.setContentType(text/html; charsetutf8);resp.getWriter().write(用户名或密码错误);return;}// 3. 如果比较通过就创建会话.HttpSession session req.getSession(true);// 把刚才的用户信息存储到会话中session.setAttribute(user, user);// 4. 返回一个重定向报文跳转到博客列表页resp.sendRedirect(blog_list.html);}
}
当登录功能完成之后就需要调整一下之前的两个页面(博客列表和博客详情) 让这两个页面必须登录后才能访问~~ 此处就做出这样的限制~(这样限制一下后面实现一些其他功能会更简单) 在进入博客列表页/详情页的时候先检查一下用户的登录状态~如果用户当前已经是登录状态才能继续使用如果是未登录状态则强制跳转到login页面
如何实现上述功能 可以在博客列表页/详情页加载的时候通过ajax访问一下服务器获取当前的登录状态~看能不能获取到如果获取到了就说明当前确实是已经登录了。此时就可以留在这个页面了。 如果没有获取到说明就未登录。就需要跳转到登录页面~
约定前后端交互接口 请求GET /login响应HTTP/1.1 200 OKContent-Type:application/json{userId: 1,username: zhangsan,}登录了就直接返回当前登录的用户信息. 未登录则直接返回一个userId为0的对象
{userId: 0,username: ,}此处只是一种典型的约定方式完全也可以采用其他的方式来约定~ 比如使用403表示当前未登录~
package controller;import com.fasterxml.jackson.databind.ObjectMapper;
import model.User;
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 javax.servlet.http.HttpSession;
import java.io.IOException;WebServlet(/login)
public class LoginServlet extends HttpServlet {private ObjectMapper objectMapper new ObjectMapper();Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setCharacterEncoding(utf8);resp.setCharacterEncoding(utf8);// 1. 获取到请求中的参数String username req.getParameter(username);String password req.getParameter(password);System.out.println(username username , password password);if (username null || .equals(username) || password null || .equals(password)) {// 请求的内容缺失肯定是登录失败~resp.setContentType(text/html; charsetutf8);resp.getWriter().write(当前的用户名或密码为空);return;}// 2. 和数据库中的内容进行比较UserDao userDao new UserDao();User user userDao.selectByName(username);if (user null || !user.getPassword().equals(password)) {// 用户没有查到或者密码不匹配也是登录失败resp.setContentType(text/html; charsetutf8);resp.getWriter().write(用户名或密码错误);return;}// 3. 如果比较通过就创建会话.HttpSession session req.getSession(true);// 把刚才的用户信息存储到会话中session.setAttribute(user, user);// 4. 返回一个重定向报文跳转到博客列表页resp.sendRedirect(blog_list.html);}// 这个方法用来让前端检测当前的登录状态Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType(application/json;charsetutf8);HttpSession session req.getSession(false);if (session null) {// 检测下会话是否存在不存在说明未登录User user new User();resp.getWriter().write(objectMapper.writeValueAsString(user));return;}User user (User) session.getAttribute(user);if (user null) {// 虽然有会话但是会话里没有 user 对象也视为未登录user new User();resp.getWriter().write(objectMapper.writeValueAsString(user));return;}// 已经登录的状态// 注意此处不要把密码返回到前端user.setPassword();resp.getWriter().write(objectMapper.writeValueAsString(user));}
}
前端代码
!DOCTYPE html
html langen
headmeta charsetUTF-8meta http-equivX-UA-Compatible contentIEedgemeta nameviewport contentwidthdevice-width, initial-scale1.0title博客列表页/titlelink relstylesheet hrefcss/common.csslink relstylesheet hrefcss/blog_list.css
/head
body!-- 这是导航栏 --div classnavimg srcimage/log.jpg altspan我的博客系统/span!-- 空白元素用来占位置 --div classspacer/diva hrefblog_list.html主页/aa hrefblog_edit.html写博客/aa href#注销/a/divdiv classcontainer!-- 左侧个人信息 --div classleft!-- 表示整个用户信息区 --div classcardimg srcimage/2.jpg alth3摸鱼王胖嘟嘟/h3a href#gitee 地址/adiv classcounterspan文章/spanspan分类/span/divdiv classcounterspan2/spanspan1/span/div/div/div!-- 右侧个人信息 --div classright!-- .blog就对应一个博客 --!-- div classblog --!-- 博客标题 --!-- div classtitle我的第一篇博客/div --!-- 博客发布时间 --!-- div classdate2023-02-11 18:26:00/div --!-- 博客摘要 --!-- div classdesc从今天起我要认真敲代码. Lorem, ipsum dolor sit amet consectetur adipisicing elit. Neque exercitationem, ut doloribus blanditiis, eveniet earum culpa accusamus rem eum cum deleniti, quisquam expedita distinctio tempora quaerat adipisci officia esse reprehenderit./div --!-- a href#查看全文 gt;/a --!-- /div --!-- .blog就对应一个博客 --/div/divscript srchttp://libs.baidu.com/jquery/2.0.0/jquery.min.js/scriptscript// 在页面加载的时候通过 ajax 给服务器发送数据获取到博客列表信息并且显示在页面上function getBlogList() {$.ajax({type: get,url: blog,success: function(body) {// 获取到的 body 就是一个 js 对象数组每个元素就是一个 js 对象,根据这个对象来构造一个div// 1. 先把 .right 里原有的内容清空let rightDiv document.querySelector(.right);rightDiv.innerHTML ;// 2. 遍历 body构造出一个个的 blogDivfor (let blog of body) {let blogDiv document.createElement(div);blogDiv.className blog;let titleDiv document.createElement(div);titleDiv.className title;titleDiv.innerHTML blog.title;blogDiv.appendChild(titleDiv);// 构造发布时间let dateDiv document.createElement(div);dateDiv.className date;dateDiv.innerHTML blog.postTime;blogDiv.appendChild(dateDiv);// 构造博客的摘要let descDiv document.createElement(div);descDiv.className desc;descDiv.innerHTML blog.content;blogDiv.appendChild(descDiv);// 构造查看全文let a document.createElement(a);a.innerHTML 查看全文 gt;gt;;// 此处希望点击之后能够跳转博客详情页// 这个跳转过程需要告知服务器要访问哪个博客的详情页a.href blog_detail.html?blogId blog.blogId;blogDiv.appendChild(a);// 把 blodDiv 挂到 dom树上rightDiv.appendChild(blogDiv);}},error: function() {alert(获取博客列表失败)}});}getBlogList();// 加上一个逻辑通过 GET /login 这个接口来获取下当前的登录状态~function getUserInfo() {$.ajax({type: get,url: login,success: function(body) {// 判定此处的body是不是一个有效的user对象if (body.userId body.userId 0) {// 登录成功// 不做处理console.log(当前用户登录成功用户名 body.username);} else {// 登录失败// 让前端页面跳转到 login.hmtlalert(当前您尚未登录请登录后再访问博客列表);location.assign(blog_login.html);}},error: function() {location.assign(blog_login.html);}});}getUserInfo();/script
/body
/html!DOCTYPE html
html langen
headmeta charsetUTF-8meta http-equivX-UA-Compatible contentIEedgemeta nameviewport contentwidthdevice-width, initial-scale1.0title博客详情页/titlelink relstylesheet hrefcss/common.csslink relstylesheet hrefcss/blog_detail.css!-- 引入 editor.md 的依赖 --link relstylesheet hrefeditor.md/css/editormd.min.css /script srcjs/jquery.min.js/scriptscript srceditor.md/lib/marked.min.js/scriptscript srceditor.md/lib/prettify.min.js/scriptscript srceditor.md/editormd.js/script
/head
body!-- 这是导航栏 --div classnavimg srcimage/log.jpg altspan我的博客系统/span!-- 空白元素用来占位置 --div classspacer/diva hrefblog_list.html主页/aa hrefblog_edit.html写博客/aa href#注销/a/divdiv classcontainer!-- 左侧个人信息 --div classleft!-- 表示整个用户信息区 --div classcardimg srcimage/2.jpg alth3摸鱼王胖嘟嘟/h3a href#gitee 地址/adiv classcounterspan文章/spanspan分类/span/divdiv classcounterspan2/spanspan1/span/div/div/div!-- 右侧个人信息 --div classright!-- 使用这个 div 来包裹整个博客内容详情 --div classblog-content!-- 博客标题 --h3/h3!-- 博客的时间 --div classdate/div!-- 博客的正文内容 --div idcontent styleopacity: 80%/div/div/divscriptfunction getBlogDetail() {$.ajax({type: get,// location.search 拿到了形如 ?blogId5 这样的一段内容url: blog location.search,success: function(body) {// 根据 body 中的内容来构造页面// 1. 构造博客标题let h3 document.querySelector(.blog-contenth3);h3.innerHTML body.title;// 2. 构造博客发布时间let dateDiv document.querySelector(.date);dateDiv.innerHTML body.postTime;// 3. 构造博客正文// 如果直接把content设为 innerHtML此时展示在界面上的内容是原始的markdown字符串// 咱们需要的是渲染后的带有格式的效果// let content document.querySelector(#content);// content.innerHTML body.content;// 第一个参数对应 idcontent 的html标签渲染后得到的HTML片段就会放到这个标签下editormd.markdownToHTML(content, {markdown: body.content});}});}getBlogDetail();// 加上一个逻辑通过 GET /login 这个接口来获取下当前的登录状态~function getUserInfo() {$.ajax({type: get,url: login,success: function(body) {// 判定此处的body是不是一个有效的user对象if (body.userId body.userId 0) {// 登录成功// 不做处理console.log(当前用户登录成功用户名 body.username);} else {// 登录失败// 让前端页面跳转到 login.hmtlalert(当前您尚未登录请登录后再访问博客详情);location.assign(blog_login.html);}},error: function() {location.assign(blog_login.html);}});}getUserInfo();/script/div
/body
/html正确显示用户信息 当下用户信息显示的是”摸鱼王胖嘟嘟“实际登录的用户显示”zhangsan“。
针对博客列表页 !DOCTYPE html
html langen
headmeta charsetUTF-8meta http-equivX-UA-Compatible contentIEedgemeta nameviewport contentwidthdevice-width, initial-scale1.0title博客列表/titlelink relstylesheet hrefcss/common.csslink relstylesheet hrefcss/blog_list.css
/head
body!-- 这是导航栏 --div classnavimg srcimage/log.jpg altspan我的博客系统/span!-- 空白元素, 用来占位置 --div classspacer/diva hrefblog_list.html主页/aa hrefblog_edit.html写博客/aa href#注销/a/div!-- 这里的 .container 作为页面的版心 --div classcontainer!-- 左侧个人信息 --div classleft!-- 表示整个用户信息区域. --div classcardimg srcimage/2.jpg alth3/h3a href#github 地址/adiv classcounterspan文章/spanspan分类/span/divdiv classcounterspan2/spanspan1/span/div/div/div!-- 右侧内容详情 --div classright!-- .blog 就对应一个博客 --!-- div classblogdiv classtitle我的第一篇博客/divdiv classdate2022-05-05 20:52:00/divdiv classdesc从今天起, 我要认真敲代码. Lorem ipsum dolor sit amet consectetur adipisicing elit. Nulla alias tenetur ut velit ex voluptatibus consequatur quam exercitationem, assumenda ea blanditiis repudiandae? Repellendus tenetur nostrum asperiores molestias doloremque cupiditate maiores./diva href#查看全文 gt;gt; /a/div --/div/div/divscript srchttp://libs.baidu.com/jquery/2.0.0/jquery.min.js/scriptscript// 在页面加载的时候, 通过 ajax 给服务器发送数据, 获取到博客列表信息, 并且显示在界面上. function getBlogList() {$.ajax({type: get,url: blog,success: function(body) {// 获取到的 body 就是一个 js 对象数组, 每个元素就是一个 js 对象, 根据这个对象构造 div// 1. 先把 .right 里原有的内容给清空let rightDiv document.querySelector(.right);rightDiv.innerHTML ;// 2. 遍历 body, 构造出一个个的 blogDivfor (let blog of body) {let blogDiv document.createElement(div);blogDiv.className blog;// 构造标题let titleDiv document.createElement(div);titleDiv.className title;titleDiv.innerHTML blog.title;blogDiv.appendChild(titleDiv);// 构造发布时间let dateDiv document.createElement(div);dateDiv.className date;dateDiv.innerHTML blog.postTime;blogDiv.appendChild(dateDiv);// 构造博客的摘要let descDiv document.createElement(div);descDiv.className desc;descDiv.innerHTML blog.content;blogDiv.appendChild(descDiv);// 构造 查看全文let a document.createElement(a);a.innerHTML 查看全文 gt;gt;;// 此处希望点击之后能够跳转到 博客详情页 !!// 这个跳转过程需要告知服务器要访问的是哪个博客的详情页. a.href blog_detail.html?blogId blog.blogId;blogDiv.appendChild(a);// 把 blogDiv 挂到 dom 树上!rightDiv.appendChild(blogDiv);}}, error: function() {alert(获取博客列表失败!);}});}getBlogList();/script!-- 在这里引入上述的 js 文件, 就可以执行到里面的代码, 也就进行了登录状态的监测了 --script srcjs/common.js/scriptscript// 针对博客列表页, 调用的时候传入参数getUserInfo(blog_list.html);/script
/body
/html针对博客详情页
当前看到的是博客详情页用户名也是成了zhangsan了此处就需要处理一下让博客列表页和详情页能够做出一些区分~
让服务器提供一个新的接口~这个接口可以让客户端指定blogId获取到指定blogId的作者信息
请求:
GET/authorInfo?blogId6响应
{userId:2,username: lisi,
}!DOCTYPE html
html langen
headmeta charsetUTF-8meta http-equivX-UA-Compatible contentIEedgemeta nameviewport contentwidthdevice-width, initial-scale1.0title博客详情页/titlelink relstylesheet hrefcss/common.csslink relstylesheet hrefcss/blog_detail.css!-- 引入 editor.md 的依赖 --link relstylesheet hrefeditor.md/css/editormd.min.css /script srcjs/jquery.min.js/scriptscript srceditor.md/lib/marked.min.js/scriptscript srceditor.md/lib/prettify.min.js/scriptscript srceditor.md/editormd.js/script
/head
body!-- 这是导航栏 --div classnavimg srcimage/log.jpg altspan我的博客系统/span!-- 空白元素, 用来占位置 --div classspacer/diva hrefblog_list.html主页/aa hrefblog_edit.html写博客/aa href#注销/a/div!-- 这里的 .container 作为页面的版心 --div classcontainer!-- 左侧个人信息 --div classleft!-- 表示整个用户信息区域. --div classcardimg srcimage/2.jpg alth3/h3a href#github 地址/adiv classcounterspan文章/spanspan分类/span/divdiv classcounterspan2/spanspan1/span/div/div/div!-- 右侧内容详情 --div classright!-- 使用这个 div 来包裹整个博客的内容详情 --div classblog-content!-- 博客标题 --h3/h3!-- 博客的时间 --div classdate/div!-- 博客的正文内容 --div idcontent styleopacity: 80%/div/div/div/divscriptfunction getBlogDetail() {$.ajax({type: get,// location.search 拿到了形如 ?blogId5 这样的一段内容url: blog location.search,success: function(body) {// 根据 body 中的内容来构造页面// 1. 构造博客标题let h3 document.querySelector(.blog-contenth3);h3.innerHTML body.title;// 2. 构造博客发布时间let dateDiv document.querySelector(.date);dateDiv.innerHTML body.postTime;// 3. 构造博客正文// 如果直接把 content 设为 innerHTML, 此时展示在界面上的内容, 是原始的 markdown 字符串// 咱们需要的是渲染后的, 带有格式的效果// let content document.querySelector(#content);// content.innerHTML body.content;// 第一个参数对应 idcontent 的 html 标签. 渲染后得到的 html 片段就会被放到这个 标签下. editormd.markdownToHTML(content, {markdown: body.content});}});}getBlogDetail();// 加上一个逻辑, 通过 GET /login 这个接口来获取下当前的登录状态~function getUserInfo(pageName) {$.ajax({type: get,url: login,success: function(body) {// 判定此处的 body 是不是一个有效的 user 对象(userId 是否非 0)if (body.userId body.userId 0) {// 登录成功!// 不做处理!console.log(当前用户登录成功! 用户名: body.username);// 在 getUserInfo 的回调函数中, 来调用获取作者信息getAuthorInfo(body);} else {// 登录失败!// 让前端页面, 跳转到 login.htmlalert(当前您尚未登录! 请登录后再访问博客列表!);location.assign(blog_login.html);}},error: function() {alert(当前您尚未登录! 请登录后再访问博客列表!);location.assign(blog_login.html);}});}// 判定用户的登录状态getUserInfo(blog_detail.html);// 从服务器获取一下当前博客的作者信息, 并显示到界面上. // 参数 user 就是刚才从服务器拿到的当前登录用户的信息function getAuthorInfo(user) {$.ajax({type: get,url: authorInfo location.search,success: function(body) {// 此处的 body, 就是服务器返回的 User 对象, 是文章的作者信息if (body.username) {// 如果响应中的 username 存在, 就把这个值设置到页面上. changeUserName(body.username);if (body.username user.username) {// 作者和登录的用户是一个人, 则显示 删除按钮let navDiv document.querySelector(.nav);let a document.createElement(a);a.innerHTML 删除;// 期望点击删除, 构造一个形如 blogDelete?blogId6 这样的请求a.href blogDelete location.search;navDiv.appendChild(a);}} else {console.log(获取作者信息失败! body.reason);}}});}function changeUserName(username) {let h3 document.querySelector(.cardh3);h3.innerHTML username;}/script
/body
/html实现注销功能
退出当前登录的状态 在导航栏中安排一个“注销”按钮 当用户点击注销之后就会在服务器上取消登录状态并且跳转到登录页面。
约定前后端交互接口
请求
GET / logout
响应
HTTP/1.1 302
Location:login.htmlpackage controller;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 javax.servlet.http.HttpSession;
import java.io.IOException;SuppressWarnings({all})
WebServlet(/logout)
public class LogoutServlet extends HttpServlet {Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 先找到当前用户的会话HttpSession session req.getSession(false);if (session null) {// 用户没有登录谈不上注销resp.getWriter().write(当前用户尚未登录无法注销1);return;}// 然后把这个用户的会话中的信息删掉session.removeAttribute(user);resp.sendRedirect(blog_login.html);}
}
发布博客功能
在博客编辑页中当用户输入了博客标题和正文之后点击发布~ 此时就会把博客数据提交到服务器由服务器存储到数据库中~
约定前后端交互接口
请求
POST/blog
Content-Type:application/x-www-form-urlencodedtitle这是标题content这是正文....
响应
HTTP/1.1 302
Location:blog_list.htmlpackage controller;import com.fasterxml.jackson.databind.ObjectMapper;
import model.Blog;
import model.BlogDao;
import model.User;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 javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;// 通过这个类来处理 /blog 的对应请求
WebServlet(/blog)
public class BlogServlet extends HttpServlet {private ObjectMapper objectMapper new ObjectMapper();// 这个方法用来获取到数据库中的博客列表Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType(application/json;charsetutf8);BlogDao blogDao new BlogDao();// 先尝试获取到 req 中的 blogId参数如果该参数存在说明要请求博客详情// 如果不存在就说明要请求博客列表String pararm req.getParameter(blogId);if (pararm null) {// 不存在参数获取博客列表// 从数据库中查询到博客列表转成JSON格式 然后直接返回即可ListBlog blogs blogDao.selectAll();// 把 blogs 对象转成JSON格式String respJson objectMapper.writeValueAsString(blogs);resp.getWriter().write(respJson);} else {// 存在参数获取博客详情int blogId Integer.parseInt(pararm);Blog blog blogDao.selectOne(blogId);String respJson objectMapper.writeValueAsString(blog);resp.getWriter().write(respJson);}}Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {HttpSession session req.getSession(false);if (session null) {// 当前用户未登录不能提交博客resp.setContentType(text/html;charsetutf8);// 直接告诉客户端请求参数不对resp.getWriter().write(当前用户未登录不能提交博客);return;}User user (User) session.getAttribute(user);if (user null) {// 当前用户未登录不能提交博客resp.setContentType(text/html;charsetutf8);// 直接告诉客户端请求参数不对resp.getWriter().write(当前用户未登录不能提交博客);return;}// 一定哟啊先指定好请求按照哪种编码来解析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/html;charsetutf8);// 直接告诉客户端请求参数不对resp.getWriter().write(提交博客失败缺少必要的参数);return;}// 构造 Blog 对象把当前的信息填进去并插入数据库中// 此处要给 Blog 设置的属性主要是 titlecontentuserId// postTime 和 blogId 都不需要手动指定都是插入数据库的时候自动生成的Blog blog new Blog();blog.setTitle(title);blog.setContent(content);// 作者 id 就是当前提交这个博客的用户的身份信息blog.setUserId(user.getUserId());BlogDao blogDao new BlogDao();blogDao.insert(blog);// 重定向到博客列表页resp.sendRedirect(blog_list.html);}
}
这里就需要整一个form表单把这里的内容给套上
删除博客 服务器处理 用户点击删除按钮触发一个HTTP请求HTTP请求就会让服务器删除指定的博客~
完整代码
完整代码我放gitee仓库了需要的可以去博客系统仓库找。