目录
🚩发布博客
🚩部署页面需求
博客系统,基本情况,主要是四个页面
- 博客列表页,显示出当前网站上都有哪些博客
- 博客详情页,点击列表上的某个博客,就能进入对应详情页(显示出博客的具体内容)
- 博客编辑页,让用户输入博客内容,并且发送到服务器
这个部分是一个markdown编辑器,markdown(md)是程序员常用的一种用来写文档的语言。
- 登录页
当下要完成的任务:
基于上述页面,编写服务器/前后端交互代码l,通过这些代码,完成博客系统完整的功能。
📝实现博客列表页
让页面从服务器拿到博客数据(数据库)
📝实现博客详情页
让页面从服务器拿到博客数据(数据库)
📝实现登录功能
📝实现强制要求登录
(当前处于未登录的状态下,其他的页面,博客列表,博客详情,博客编辑.....就会强制跳转到登录页)要求用户登录之后才能使用
📝实现显示用户信息
从服务器获取到,博客列表页,拿到的是当前登录的用户的信息,博客详情页,拿到的是文章作者的信息。
📝实现退出登录
📝发布博客
博客编辑页,输入文章标题和内容之后,点击发布,就能把这个数据上传到服务器上并保存
这些功能搞定,就基本上的博客系统搞定
🚩准备工作
写一个复杂一些的代码,往往需要先理清楚思路,相对于细节来说,理清思路是更复杂的。(为了实现这个代码,要写哪些类,有哪些方法(方法的具体细节,我们先不用写))
1.创建项目,引入依赖,把当前的这些前端页面也导入到项目中
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>blog_system</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.0</version> </dependency> </dependencies> </project>
- 2.数据库设计
设计好对应的表结构,并且把数据库相关代码,也进行封装
a>找到实体
博客(blog表) 用户(user表)
b>确认实体之间的关系
一对多 一个博客,只能属于一个用户,一个用户,可以发布多个博客
create table blog ( blogId int primary key auto_increment, title varchar(1024), content varchar(4096), postTime datetime, userId int ); create table user ( userId int primary key auto_increment, username varchar(50) unique, -- 用户名也要求是不能重复的. password varchar(50) );
- 3.把数据操作的代码进行一些封装
进行网站开发的过程中,一种常见的代码组织结构,MVC结构
M model:操作数据的代码
V view:操作/构造界面的代码
C controller:业务逻辑,处理前端请求
- 📝DBUtil完成对于数据库建立连接和关闭连接的实现(这里需要用到懒汉模式)懒汉模式是不安全的,当前sevlet本身就是在多线程环境下执行的,tomcat收到多个请求的是,就会使用多线程的方式,执行不同的servlet代码
package model; 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; // 通过这个类, 封装数据库建立连接的操作. // 由于接下来代码中, 有多个 Servlet 都需要使用数据库. 就需要有一个单独的地方来把 DataSource 这里的操作进行封装. // 而不能只是放到某个 Servlet 的 init 中了. // 此处可以使用 单例模式 来表示 dataSource public class DBUtil { private volatile static DataSource dataSource = null; private static DataSource getDataSource() { if (dataSource == null) { synchronized (DBUtil.class) { if (dataSource == null) { dataSource = new MysqlDataSource(); ((MysqlDataSource) dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java_blog_system3?characterEncoding=utf8&useSSL=false"); ((MysqlDataSource) dataSource).setUser("root"); ((MysqlDataSource) dataSource).setPassword("105528clzyf."); } } } return dataSource; } public static Connection getConnection() throws SQLException { return getDataSource().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(); } } } }
- 📝创建实体类,每个表都需要搞一个专门的类来表示,表里的一条数据,就会对应到这个类的一个对象。这样数据库中的数据和代码联系在一起了
Blog类对应blog表,User类对应user表
package model; import java.sql.Timestamp; import java.text.SimpleDateFormat; // Blog 对象就是对应到 blog 表中的一条记录. // 表里有哪些列, 这个类里就应该有哪些属性 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 String getPostTime() { // Java 标准库提供了一个 SimpleDateFormat 类, 完成时间戳到格式化时间的转换. // 这个类的使用, 千万不要背!!! 都要去查一下!! 背大概率会背错!!! SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return simpleDateFormat.format(postTime); } public void setPostTime(Timestamp postTime) { this.postTime = postTime; } public int getUserId() { return userId; } public void setUserId(int userId) { this.userId = userId; } @Override public String toString() { return "Blog{" + "blogId=" + blogId + ", title='" + title + '\'' + ", content='" + content + '\'' + ", postTime=" + postTime + ", userId=" + userId + '}'; } }
package model; // 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; } @Override public String toString() { return "User{" + "userId=" + userId + ", username='" + username + '\'' + ", password='" + password + '\'' + '}'; } }
- 📝还需要创建两个类,来完成针对博客表和用户表的增删改查操作 BlogDao UserDao(后续写)
BlogDao进行对博客表增删查改
对博客进行新增操作,查询操作,根据博客id查询指定的博客,根据博客id删除博客。
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; // 通过 BlogDao 来完成针对 blog 表的操作 public class BlogDao { // 1. 新增操作 (提交博客就会用到) public void insert(Blog blog) { 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. 执行 SQL statement.executeUpdate(); } catch (SQLException e) { throw new RuntimeException(); } finally { DBUtil.close(connection, statement, null); } } // 2. 查询博客列表 (博客列表页) // 把数据库里所有的博客都拿到. public List<Blog> getBlogs() { List<Blog> blogList = new ArrayList<>(); Connection connection = null; PreparedStatement statement = null; ResultSet resultSet = null; try { connection = DBUtil.getConnection(); String sql = "select * from blog order by postTime desc"; statement = connection.prepareStatement(sql); resultSet = statement.executeQuery(); while (resultSet.next()) { Blog blog = new Blog(); blog.setBlogId(resultSet.getInt("blogId")); blog.setTitle(resultSet.getString("title")); // 此处读到的正文是整个文章内容. 太多了. 博客列表页, 只希望显示一小部分. (摘要) // 此处需要对 content 做一个简单截断. 这个截断长度 100 这是拍脑门出来的. 具体截取多少个字好看, 大家都可以灵活调整. 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")); blogList.add(blog); } } catch (SQLException e) { throw new RuntimeException(e); } finally { DBUtil.close(connection, statement, resultSet); } return blogList; } // 3. 根据博客 id 查询指定的博客 public Blog getBlog(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(); // 由于此处是拿着 blogId 进行查询. blogId 作为主键, 是唯一的. // 查询结果非 0 即 1 , 不需要使用 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 throwables) { throwables.printStackTrace(); } 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 throwables) { throwables.printStackTrace(); } finally { DBUtil.close(connection, statement, null); } } }
UserDao进行对用户表增删查改
根据userId和username查到对应的i信息
package model; import java.lang.ref.PhantomReference; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; // 通过 UserDao 完成针对 user 表的操作 public class UserDao { // 新增暂时不需要. 不需要实现 "注册" 功能. // 删除暂时不需要. 不需要实现 "注销帐户" 功能. // 1. 根据 userId 来查到对应的用户信息 (获取用户信息) public User getUserById(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(); 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 throwables) { throwables.printStackTrace(); } finally { DBUtil.close(connection, statement, resultSet); } return null; } // 2. 根据 username 来查到对应的用户信息 (登录) public User getUserByName(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(); 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 throwables) { throwables.printStackTrace(); } finally { DBUtil.close(connection, statement, resultSet); } return null; } }
Dao=>Data Access Object 数据访问对象,通过这两个类的对象,来完成针对数据库表的操作
实现逻辑
- 1>约定好前后端交互接口
- 2>编写前端代码,构造http请求(form/ajax)
- 3>编写后端代码,处理这个请求,返回响应
- 4>编写前端代码,解析http请求,构造页面
🚩获取博客列表页
核心操作:我们首先看看前端发出的请求,前端发出ajax请求,服务器处理该请求,服务器通过数据库获取博客表中的博客信息(博客标题,博客部分内容),然后返回响应成功之后就调用创建函数页面格式。
客户端发出http请求:
服务器处理请求并做出响应
public ObjectMapper objectMapper=new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//博客列表页(我们需要通过blogDao类中进行查询到所有的博客信息)
BlogDao blogDao=new BlogDao();
List<Blog>blogList=blogDao.getBlogs();//获取到博客信息
//对象转成json,用objectMapper进行写数据
String respJson=objectMapper.writeValueAsString(blogList);
//设置json格式
resp.setContentType("application/json;charset=utf8");
//返回响应
resp.getWriter().write(respJson);
}
因为前端代码中,我们设定了,如果没有用户登录那么就回到登录页面,所以 最终的也页面必须得等登录页面写好,我们就能看到。
我们接下来给博客详情页,也就是我们显示在博客列表只是简单的100个文字,如果一些博客超过了100个文字,我们就需要知道具体的发布时间,具体的文章内容和标题。
🚩博客详情页
核心操作:点不同的博客,跳转过去之后, 都会带有不同的blogId的query string(在url地址后面有个queryString),就像下面的图解。
后续在博客详情页中,就可以也给服务器发起ajax请求,根据blogId来查询数据库中对应博客的具体内容,服务器成功返回响应之后,前端进行构造页面。并显示出给客户端。
客户端发出http请求:
location.search:
- 1.我们在前端页面控制台上进行输入location.search,我们就可以就可以知道blogId是什么了。
- 2.url:'blog'+location.search,带上blogId,就和博客列表页进行区分,一个路径对应一个servlet,当前使用一个servlet处理两个请求,博客列表页,不带query string,博客详情页,带有query string,就可以根据query string是否存在的情况,来区分是哪种请求分别返回不同的数据即可 。(使用两个servlet处理两个请求,或者使用一个servlet处理一个请求,都是可以 ,我们只需要约定不同的路径即可)
所以我们详情页中的url中query string中包含blogId。服务器就可以通过blogId进行对博客信息的查询。
根据BlogDao对象中getBlog方法进行根据博客id查询指定的博客。
服务器解析请求并响应
由于博客列表页和博客详情页都是针对博客来进行前后端交互信息的,所以两者页面是公用同一个servlet类,都发出了get请求,但是不同的是,博客列表页请求中没有给blogId而博客详情页给出了blogId,我们需要分情况讨论。blogId为空的情况下,服务器对博客列表页解析请求并做出响应,blogId不为空的情况下,服务器对博客详情页解析请求并做出响应。
.url:'blog'+location.search,带上blogId,就和博客列表页进行区分,一个路径对应一个servlet,当前使用一个servlet处理两个请求,博客列表页,不带query string,博客详情页,带有query string,就可以根据query string是否存在的情况,来区分是哪种请求分别返回不同的数据即可 。(使用两个servlet处理两个请求,或者使用一个servlet处理一个请求,都是可以 ,我们只需要约定不同的路径即可)
public ObjectMapper objectMapper=new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//博客列表页(我们需要通过blogDao类中进行查询到所有的博客信息
BlogDao blogDao=new BlogDao();
//详情页中提供blogId进行获取博客信息
//首先获取请求中的blogId
String blogId=req.getParameter("blogId");
if(blogId==null){
//由于博客列表页没有给出blogId,我们就进入该判断中
List<Blog>blogList=blogDao.getBlogs();//获取到博客信息
String respJson=objectMapper.writeValueAsString(blogList);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(respJson);
return;
}
//详情页拿到blogId
//我们可以通过blogId来查询到博客信息
Blog blog=blogDao.getBlog(Integer.parseInt(blogId));
String respJson=objectMapper.writeValueAsString(blog);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(respJson);
}
最终的效果等登录之后即可看见。
🚩实现登录页面
核心操作:在登录页面中,在输入框中填写用户名和密码,点击登录,就会给服务器发起HTTP请求(可以使用ajax,也可以使用form)。服务器处理登录请求,读取用户名密码,在数据库查询,匹配,如果正确就登录成功,创建会话,跳转到博客列表页。由于这里,登录成功,直接进行重定向跳转,这种重定向操作,就不需要浏览器额外写代码处理,直接浏览器就自动跳转了。
前端发出http请求:
服务器处理请求并做出响应
服务器处理前端发来的请求,获取密码和用户名,然后进行一系列的判断操作(判断是否为空,不为空了,看看用户是否存在,用户存在了看看是否密码正确),用户名和密码都正确后,我们需要创建会话,存储该用户的信息,点击登录之后,我们就可以跳转到博客列表页了。
@WebServlet("login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//将请求中的内容改成utf8zifuji
req.setCharacterEncoding("utf8");
//获取请求发来的密码和用户名
String password=req.getParameter("password");
String username=req.getParameter("username");
//判断密码或者用户是否为空
if(password==null||password.length()==0||username==null||username.length()==0){
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("你的用户名或者密码为空");
return;
}
//这里说明用户名和密码都不为空
//我们可以通过username来查一下是否有这个人
UserDao userDao=new UserDao();
User user=userDao.getUserByName(username);
if(user==null){
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("你输入的用户不存在");
return;
}
//用户存在,我们就看看密码是不是正确的
if(!password.equals(user.getPassword())){
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("你输入的密码错误");
return;
}
//此时我们的用户名和密码都正确了,我们就需要创建会话
HttpSession session=req.getSession(true);//如果没有,就自动创建true
session.setAttribute("user",user);
//我们直接跳转到博客列表页
resp.sendRedirect("blog_list.html");
}
}
此时我们就可以登录页面了,但是在登录页面之后(密码和用户都正确存在的情况下),页面又跳转到登录页面了,这是为什么呢?
每个页面都会调用到app.js中的方法,所以如果没有得到200登录状态(也就是服务器在用户登录的时候给客户端返回响应登录状态,登录状态就是判断会话,sessin如果==null那么就表示登录状态异常发出403,session不为空那么就表示登录状态成功发出200)
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session= req.getSession(false);
//会话为空
if(session==null){
resp.setStatus(403);
return;
}
//因为session中存储了用户,会话有,但是用户不存在也会存在问题
User user=(User) session.getAttribute("user");
if(user==null){
resp.setStatus(403);
return;
}
resp.setStatus(200);
}
此时如果我们用户名和密码正确并且存在的情况下,我们是基本上是可以看到主要页面(博客列表页,博客详情页,博客登录页)。
🍭效果展示
登录页面
博客列表页
博客详情页
从这里我们已经基本完成主要的内容了,但是也有个别的地方就是,我们在博客列表页中没有显示用户名,博客详情页中没有显示作者,这是需要改进的地方。
🎈显示用户信息
- 博客列表页:显示的是当前登录的用户的信息
- 博客详情页:显示的是当前文章的作者信息
在页面加载的时候,给服务器发起ajax请求,服务器返回对应的用户数据,根据发起请求的不同页面,服务器返回不同的信息即可。
客户端发起请求(博客列表页和博客详情页)
服务器处理请求并返回响应
📝博客列表页:
显示用户信息,我们就可以想到当初登录的时候,我们创建了一个会话,存储用户信息,我们得到用户之后直接返回即可,因为前端得到成功的响应之后就直接会使用username。
博客列表页
@WebServlet("/userInfo")
public class UserInfoServlet extends HttpServlet {
ObjectMapper objectMapper=new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session=req.getSession(false);
if(session==null) {
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("该用户未登录");
return;
}
User user=(User) session.getAttribute("user");
if(user==null) {
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("该用户未登录");
return;
}
String respJson=objectMapper.writeValueAsString(user);
user.setPassword("");
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(respJson);
}
}
📝博客详情页:
我们知道博客详情页会提供query string中blogId,我们依旧通过blogId来查到对应的博客信息,博客信息里面有userId,我们通过userId查到User对象,进而返回响应给客户端。
博客详情页
@WebServlet("/getAuthorInfo")
public class AuthorInfoServlet extends HttpServlet {
ObjectMapper objectMapper=new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String blogId=req.getParameter("blogId");
//获得Blog对象(从blogDao中)
BlogDao blogDao=new BlogDao();
Blog blog=blogDao.getBlog(Integer.parseInt(blogId));
//从blog表中获得userId
int userId=blog.getUserId();
//通过userId获取user对象(在userDao中)
UserDao userDao=new UserDao();
User user=userDao.getUserById(userId);
String respJson=objectMapper.writeValueAsString(user);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(respJson);
}
}
🚩退出登录
博客列表/博客详情/博客编辑 导航栏中,都有一个“注销”按钮,让用户点击“注销”的时候,就能够触发一个HTTP请求(GET请求),服务器收到这个GET请求的时候,就会把会话里的user这个Attribute给删了,由于判定用户是否是登录状态的逻辑中,需要同时验证,会话存在,且这里的user Attribute也存在,只要破坏一个,就可以使登录状态发生改变了。
为啥不直接删除session本身??主要因为,servlet没有提供,删除session方法。虽然有间接的方式(session可以设置过期时间,设置一个非常短的过期时间),也可以起到删除的效果。
session提供了removeAttribute这样的方法,可以把user这个Attribute给删了。
这个东西是a标签,可以有一个href属性,点击就会触发一个http请求,并且可能会引起浏览器跳转到另一个页面。
📝客户端发送请求
不用写ajax,直接给a标签设置href属性即可
📝编写后端代码,处理这个请求,完成退出登录的操作
先拿到会话(也就是保存的用户信息),如果会话是空,说明用户未登录,如果会话不为空,那么我们就删除会话中的参数,然后跳转到到登录页面。方法跳转页面方法默认发出的都是get请求
@WebServlet("/logout")
public class LoginOutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//先拿到会话
HttpSession session=req.getSession(false);
if(session==null){
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("用户未登录");
return;
}
session.removeAttribute("user");//删除会话中的参数
resp.sendRedirect("login.html");
}
}
🚩发布博客
当点击提交的时候,就需要构造HTTP请求,把此时的页面中的标题和正文都传输到服务器这边,服务器把这个数据存入数据库即可。此时这里的http请求,可以使用ajax,也可以使用form(这种填写输入框,提交数据的场景,使用form会更方便)
📝客户端发送请求
客户端发出post的请求,客户端输入标题以及在markdown编辑器中输入内容,这些都是客户端发出的请求。
display:none让这个textarea隐藏起来。
<script>
var editor = editormd("editor", {
// 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉.
width: "100%",
// 设定编辑器高度
height: "calc(100% - 50px)",
// 编辑器中的初始内容
markdown: "# 在这里写下一篇博客",
// 指定 editor.md 依赖的插件路径
path: "editor.md/lib/",
saveHTMLToTextarea: true
});
getLoginStatus();
</script>
saveHTMLToTextarea:true 会把用户在编译器中输入内容,自动也保存到textarea里一份。
📝编写服务器代码,处理刚才的请求
服务器接收到客户端发来的请求,首先是获取请求(title和content),如果请求的内容没有问题,那么我们就需要将客户端写的这篇博客存储到数据库中。创建Blog对象,由于发布时间是now()默认的,blogId的自增的,所以我们就需要给blog中设置标题,内容,userId这三个内容即可,然后创建BlogDao对象,给Blog对象进行insert操作。
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf8");
//获取请求
String title=req.getParameter("title");
String content=req.getParameter("content");
//判断内容和标题是否为空
if(title.length()==0||title==null||content==null||content.length()==0){
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前传过来的标题或正文为空! 无法新增博客!");
return;
}
//看会话中是否有用户信息
HttpSession session=req.getSession(false);
if(session==null){
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("用户未登录");
return;
}
//用户存在 通过user对象获取userId
User user=(User) session.getAttribute("user");
int userId=user.getUserId();
//Blog对象中需要有 userId title content
Blog blog=new Blog();
blog.setContent(content);
blog.setTitle(title);
blog.setUserId(userId);
//创建BlogDao 插入blog
BlogDao blogDao=new BlogDao();
blogDao.insert(blog);
//跳转成功后,重新回到博客列表页看到所有的博客
resp.sendRedirect("blog_list.html");
}
直接跳转页面到博客列表页。新的内容显示。
其实这个系统还有很多很多地方要改进的地方。
比如:
- 问题一:
用户chlorine但是,为什么会显示别的用户写的博客信息呢?
- 问题二:
当前虽然登录过了,一旦重新启动服务器,此时仍然会判定为未登录状态。
登录状态是通过服务器这里的session来存储的,session这里服务器内存中类似于hashmap这样的结构,一旦服务器重启了,hashmap里面原有的内容就没了
其实这种设定,严格的说,并不科学。相比之下有更好的解决方案
- 可以把会话进行持久性保存(文件数据库,redis...)
- 可以使用令牌的方式(把用户信息,在服务器加密,还是保存在浏览器这边)相当于服务器没有在内存中存储当前用户的身份
问题三:
只有我自己能访问这个网站,我想要更多的用户能看到这个网站,那么我就需要将该内容打包部署到linux上(云服务器),让更多的用户进行写查博客操作。后面会学到。
javaEE进阶会学到。学无止境。
我们不怕掉眼泪,但要值得。