当前位置:网站首页>实现一个博客系统----使用模板引擎技术
实现一个博客系统----使用模板引擎技术
2022-07-05 14:27:00 【学不会二叉树的小比特】
主要思路:
1)服务器渲染,基于模板引擎(我们在这里实现第一种)
2)前后端分离,基于AJAX,服务器给前端返回json格式的数据,前端自己进行拼接HTML片段;
1)博客列表页(动态页面)<---------渲染页面
1.1提供所有的博客信息(渲染右侧每一个div博客):OperatorBlog中的SelectAll()方法------------来自于数据库
1.2提供登陆者的个人信息:---------->获取登录者的用户身份信息------>HttpSession中的键值对当点击文章的标题,给服务器发送GET请求,带上BlogID,访问博客详情页
2)博客详情页(动态页面)<---------渲染页面
1.1提供具体的博客数据------>根据blogID通过OperatorBlog中的SelectOne方法来查询到博客进行渲染------>来源于数据库
1.2获取文章中的作者信息---->根据blogID通过OperatorBlog中的SelectorOne方法查询到blog,在进行获取到blog中的userID,再根据调用OperatorUser中的selectByUserID来查询到文章的作者----->来源于数据库
3)实现博客编辑页(静态页面)------->把用户编写好的博客标题和内容交给服务器
先进行构造一个Blog对象,在想数据库里面进行插入数据-------->MYSQL
4)实现博客登录页(静态页面)------->把输入框中的用户名和密码提交给服务器
4.1根据用户名来进行查询用户信息(查询到User对象)------>MYSQL
4.2把用户信息(User对象)写到HttpSession里面
5)实现注销功能(没有单独的界面,只是在导航栏里面)
根据a标签向服务器发送一个Http的Get请求,直接进行删除了会话中保存的用户的身份信息
6)实现删除博客的功能(没有单独的界面,在博客详情页这里会多出一个删除按钮,前提是当前登录的作者就是文章的作者)
通过超链接像服务发送请求,请求中带上blogID这样的参数,然后实现删除博客------>操作数据库
1.创建maven项目:
1)我们进行引入Servlet的依赖,引入Thymeleaf和MYSQL的依赖
2)在main目录下创建webapp目录,在webapp目录里面创建web.xml文件,在webapp里面创建WEB-INF目录,再从WEB-INF里面创建template目录,把我们之前写的博客系统的前端页面全部拷贝过来
3)使用Smart-Tomact进行打包部署
2.设计数据库
我们要把博客信息和用户的身份信息给储存到数据库里面
1)我们进行创建博客表
先创建数据库----Java200,博客列表:博客ID,博客标题,博客正文(在这里面使用mediumetext表示的数据范围要比varchar要长一些),博客发布时间,一篇博客对应的作者(用一个ID来进行标识)
创建博客表
create table blog(
blogID int primary key auto_increment,//每一篇博客的身份标识
title varchar(1024),//每一篇博客的文章
content mediumtext,//每一篇博客的正文
postTime datetime,//每一篇博客的发布时间
userID int);//每一篇博客的作者
2)我们进行创建用户表
用户的身份标识ID,用户的姓名,用户的密码,况且我们的用户表里面的用户身份标识ID和博客表中的userID也是可以用外键来进行关联的;
create table user(
userID int,
name varchar(20),
password varchar(30));
3)封装数据库操作,JDBC代码,下面我们来写一些MYSQL中的常用方法
我们在Java里面创建一个包叫做MYSQL
1)我们创建第一个类,叫做ConnectionDatabase,里面存放JDBC链接MYSQL的代码,之前都写过
package MYSQL;
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 ConnectionDatabase {
private static final String url="jdbc:mysql://127.0.0.1:3306/java200?characterEncoding=utf-8&userSSL=true";
private static final String password="12503487";
private static final String user="root";
public static DataSource datasource=null;
public static DataSource getDataSource()
{
if(datasource==null)
{
synchronized(Object.class)
{
if(datasource==null)
{
datasource=new MysqlDataSource();
((MysqlDataSource)datasource).setURL(url);
((MysqlDataSource)datasource).setUser(user);
((MysqlDataSource)datasource).setPassword(password);
}
}
}
return datasource;
}
public static Connection getConnection() throws SQLException {
return getDataSource().getConnection();
}
public void close(ResultSet resultSet, PreparedStatement statement, Connection connection) throws SQLException {
resultSet.close();
statement.close();
connection.close();
}
}
2. 创建实体类,通过每一个实体类对象,来进行代表表中的一条记录数据;我们分别进行创建Blog这样的类和User这样的类来进行代表;类中的各种属性和数据库中的各种属性是相对应的,并针对里面的属性提供Set和Get方法-----他们都是根据数据库中的内容来进行创建的类;我们在MYSQL这个包中,创建Blog和User类;
1)注意在java当中表示时间可以用timestamp类型,还可以使用java.sql.Date,但是这个Date类型只能表示日期,是不可以进行表示时,分,秒的
2)在MYSQL中表示时间可以用String,但是不确定当前表示的时间是否是合法时间,我们还可以使用datetime和timestamp类型(到了2038年恐怕就不够用了)
3.在进行创建一些类,来进行通过一些JDBC代码来实现数据库的一些操作;
1.我们进行创建第一个类,来实现对Blog对象的一些操作;
1)新增一篇博客----我们在前端代码里面有发布文章这样的功能
2)获取到所有博客列表页-----就对应我们的博客列表页,作用是显示所有博客;
3)根据博客ID可以查找到对应的博客,主要是为了在博客列表页里面点击查看全文之后,可以看到一篇博客的具体文章详情;
4)根据博客ID来进行删除博客;
5)我们可以写一些测试用例来验证我们上面的操作;
package MYSQL;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class OperateBlog {
public void insert(Blog blog) throws SQLException, SQLException {
System.out.println("进行新增一篇博客");
//1与数据库建立连接
Connection connection=null;
connection=ConnectionDatabase.getConnection();
String SQL="insert into blog values(null,?,?,?,?)";
//2创建SQL语句
PreparedStatement 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执行SQL语句
int ret=statement.executeUpdate();
if(ret==1)
{
System.out.println("新增博客成功");
}else{
System.out.println("新增博客失败");
}
ConnectionDatabase.close(null,statement,connection);
}
public List<Blog> SelectAll() throws SQLException {
System.out.println("开始查找所有博客");
List<Blog> list=new ArrayList<>();
Connection connection=ConnectionDatabase.getConnection();
String SQL="select * from blog";
PreparedStatement statement=connection.prepareStatement(SQL);
ResultSet set=statement.executeQuery();
//遍历里面的结构集合
while(set.next())
{
Blog blog=new Blog();
blog.setBlogID(set.getInt("blogID"));
blog.setTitle(set.getString("title"));
blog.setContent(set.getString("content"));
blog.setPostTime(set.getTimestamp("postTime"));
blog.setUserID(set.getInt("userID"));
list.add(blog);
}
ConnectionDatabase.close(set,statement,connection);
return list;
}
public Blog SelectOne(int blogID) throws SQLException {
System.out.println("开始查找指定博客");
Connection connection=null;
PreparedStatement statement=null;
connection=ConnectionDatabase.getConnection();
String SQL="select * from blog where blogID = ?";
statement=connection.prepareStatement(SQL);
statement.setInt(1,blogID);
ResultSet set=statement.executeQuery();
if(set.next()) {//条件不要写错 if(set!=null)
Blog blog = new Blog();
//遍历结果集和,此处是基于自增主键来进行查询的,要么是1条记录要么是0条记录;
blog.setBlogID(set.getInt("blogID"));
blog.setTitle(set.getString("title"));
blog.setContent(set.getString("content"));
blog.setPostTime(set.getTimestamp("postTime"));
blog.setUserID(set.getInt("userID"));
ConnectionDatabase.close(set, statement, connection);
return blog;
}
return null;
}
public void DeleteBlog(int blogID) throws SQLException {
Connection connection=null;
PreparedStatement statement=null;
connection=ConnectionDatabase.getConnection();
String SQL="delete from blog where blogID = ?";
statement=connection.prepareStatement(SQL);
statement.setInt(1,blogID);
int len=statement.executeUpdate();
if(len==1)
{
System.out.println("删除成功");
}
}
public static void main(String[] args) throws SQLException {
//1测试插入功能
Blog blog=new Blog();
blog.setUserID(1);
blog.setBlogID(1);
blog.setTitle("ABCDEFG");
blog.setContent("I am a boy");
blog.setPostTime(new Timestamp(System.currentTimeMillis()));//必须是java.sql里面的包
//setPostTime里面需要一个Timestamp类型,里面就需要new一个Timestamp对象,Timestamp里面参数是一个时间戳
// statement设置下标的时候是从0下标开始进行设置的
OperateBlog blogdemo=new OperateBlog();
blogdemo.insert(blog);
//2.测试查找所有博客功能
List<Blog> list=blogdemo.SelectAll();
// System.out.println(list);
//3查找指定博客
Blog blog1=blogdemo.SelectOne(1);
System.out.println(blog1);
}
}
2.我们进行创建第二个对象,来进行对user来进行操作
1)实现新增用户,来进行实现注册功能(向数据库中插入数据)
2)根据用户名来查找到用户对象,登陆的时候要根据用户名来查找用户对象,来判断进行对比,输入框输入的密码和查找到的用户密码进行相对比,从而是否是正确的;
3)根据用户ID(根据博客列表页就可以)来进行查找用户对象,当我们点击查看全文的时候,在博客页面上显示作者的具体身份信息(点击博客详情页之后);
我们要知道,在博客列表页显示的是登陆者的身份信息,但是点击博客列表页之后,显示的是这篇文章对应作者的身份信息;
4)注册的时候,一个用户名只有一个user对象,用户名是不重复的;
package MYSQL;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class OperateUser {
public void insert(User user) throws SQLException {
Connection connection=null;
PreparedStatement statement=null;
connection=ConnectionDatabase.getConnection();
String SQL="insert into user values(?,?,?)";
statement=connection.prepareStatement(SQL);
statement.setInt(1,user.getUserID());
statement.setString(2, user.getName());
statement.setString(3, user.getPassword());
int len=statement.executeUpdate();
if(len==1)
{
System.out.println("插入成功");
}else{
System.out.println("插入失败");
}
}
public User selectByName(String username) throws SQLException {
Connection connection=null;
PreparedStatement statement=null;
connection=ConnectionDatabase.getConnection();
String SQL="select * from user where name = ?";
statement= connection.prepareStatement(SQL);
statement.setString(1,username);
ResultSet set=statement.executeQuery();
if(set.next())
{
User user=new User();
user.setPassword(set.getString("password"));
user.setName(username);
user.setUserID(set.getInt("userID"));
return user;
}
return null;
}
public User selectByUserID(int userID) throws SQLException {
Connection connection=null;
PreparedStatement statement=null;
connection=ConnectionDatabase.getConnection();
String SQL="select * from user where userID = ?";
statement=connection.prepareStatement(SQL);
statement.setInt(1,userID);
ResultSet set=statement.executeQuery();
if(set.next())
{
User user=new User();
user.setUserID(userID);
user.setName(set.getString("name"));
user.setPassword(set.getString("password"));
return user;
}
return null;
}
}
4.我们此时开始写实现页面的操作,我们让服务器基于模板引擎,来把从数据库中查找到的数据,返回到网页上面
1)先进行初始化Thymeleaf
2)实现博客列表页
3)实现博客详情页
4)实现博客登录页
5)实现博客编辑页
1.进行初始化模板引擎
我们要把把初始化代码放到ServletContextListener中,然后把TemplateEngine初始化好了之后放到ServletContext里面;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener//一定要加注释
public class StartThymeleaf implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("开始进行初始化模板引擎");
//1创建TemplateEngine对象和resolver对象
ServletContext context=servletContextEvent.getServletContext();
TemplateEngine engine=new TemplateEngine();
ServletContextTemplateResolver solver=new ServletContextTemplateResolver(context);
solver.setPrefix("/WEB-INF/template/");
solver.setSuffix(".html");
solver.setCharacterEncoding("utf-8");
//2将resolver对象和engine对象进行关联
engine.setTemplateResolver(solver);
//3将我们创建好的engine对象放到ServletContext对象里面
context.setAttribute("engine",engine);
System.out.println("初始化模板引擎的操作结束");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
2.实现博客列表页
1)在博客列表页里面,我们在页面上面写的每一篇博客就都不能是一个写死的数据,而是应该在数据库中进行查找得到的每一条记录,每一篇博客都是一个class=blog的div,况且没一篇博客里面还有标题,发布时间,内容,这些属性都是和我们的数据库中的blog表中对应的字段是相同的;我们在前端页面就可以用th:each,根据服务器传过来的blog数组,依次遍历到每一个blog,再把它构建到我们的列表页面上;
2)我们详情页的链接就不能全叫blog2.html,在href属性里面加上blogID,这样我们的服务器就知道当前访问的是哪一篇博客列表了;
3)我们把每一篇博客对应的查看全文的a标签换成了,这个blogID在QueryString进行设置
<a href="'blog2.html?blogID='+blog.blogID" class="detail" target="_blank">查看全文>></a>
当我们进行访问每一篇博客的博客详情页的时候,都带上一个blogID,这样服务器就知道了我们具体访问的是哪一篇博客了;
4)我们在写后端Servlet代码的时候,WebServlet中所进行关联的路径,是可以包含点的,后面再去浏览器访问blog1.html的时候,就不是一个静态的页面了,而是一个动态生成的页面
5)如果内容太多了导致溢出,就自动加上一个滚动条overflow:auto;如果说博客正文太长,我们就从博客列表页进行截取一段,一部分(直接截取前面的若干个字就可以了),我们直接可以在数据库操作Blog的那个类中,进行获取到所有博客的时候,判断博客内容长度是否超过90,超过90个字符,就调用subString方法进行截取;在拼接字符串".......";
我们再进行查询数据库的时候,就直接获取到正文之后就进行判定,如果太长,直接进行截断
<!-- 右侧是博客列表页 -->
<div class="right">
<!-- 在右侧,我们表示的是一篇一篇的博客 -->
<div class="blog" th:each="blog : ${blogs}">
<!-- 文章标题 -->
<h2 class="title" th:text="${blog.title}"></h2>
<!-- 发布日期 -->
<div class="date" th:text="${blog.postTime}"></div>
<!-- 文章内容 -->
<div class="text" th:text="${blog.content}"></div>
<!-- 查看全文按钮 -->
<a href="${'blog2.html?blogID'+blog.blogID}" class="detail" target="_blank">查看全文>></a>
</div>
</div>
这里面的内容都是依靠模板渲染的,后端传递过来一个blogs数组或者list,我们就会自动遍历到这里面的每一个blog,取出里面的每一个属性,然后再去进行渲染操作;
OperateBlog operateBlog=new OperateBlog();
//1从数据库拿到所有的博客列表
List<Blog> list= null;
try {
list = operateBlog.SelectAll();
} catch (SQLException e) {
e.printStackTrace();
}
resp.setContentType("text/html;charset=utf-8");
//2拿到模板引擎对象,进行页面渲染操作
ServletContext context=req.getServletContext();
TemplateEngine engine=(TemplateEngine)context.getAttribute("engine");
//3创建WebContext对象,进行设置键值对
WebContext webContext=new WebContext(req,resp,context);
webContext.setVariable("blogs",list);
//4进行渲染
String html= engine.process("blog1",webContext);
resp.getWriter().write(html);
3.实现博客详情页
1)在博客列表页,我们是显式指定所有博客,进行获取到所有博客就可以了,但是在博客详情页里面,我们要根据a标签中的请求中的BlogID来进行指定到底是哪一篇博客,再根据blogID来进行查询数据库,从而我们就得到了博客的详细情况;这个blogID是被设置在QueryString里面的
2)当前我们还差一个地方,那就是说在当前的博客详情页,里面显示的内容,不是按照Markdown的语法来进行渲染的,例如说此处的三级标题还是不能正常的进行显示;
<!-- 右侧是博客详情页,这个div表示右侧版新的整个背景-->
<div class="right">
<!--这里面不用写th:each-->
<div class="blog">
<!-- 文章标题 -->
<h3 th:text="${blog.title}"></h3>
<!-- 博客发布时间 -->
<div class="date" th:text="${blog.postTime}">2022年12月3日</div>
<P th:text="${blog.content}"></P>
</div>
</div>
我们希望在博客详情页里面,能够传过来一个blog对象,找到对应的属性进行渲染
resp.setContentType("text/html;charset=utf-8");
OperateBlog operateBlog=new OperateBlog();
String blogID=req.getParameter("blogID");
if(blogID==""||blogID.equals(""))
{
String html="<h3>blogID字段缺失</h3>";
resp.getWriter().write(html);
}
Blog blog= null;
try {
blog = operateBlog.SelectOne(Integer.parseInt(blogID));
} catch (SQLException e) {
e.printStackTrace();
}
if(blog==null)
{
String html="<h3>根据博客ID获取到的当前博客不存在</h3>";
resp.getWriter().write(html);
}
ServletContext context=req.getServletContext();
TemplateEngine engine=(TemplateEngine)context.getAttribute("engine");
WebContext webContext=new WebContext(req,resp,context);
webContext.setVariable("blog",blog);
String html=engine.process("blog2",webContext);
resp.getWriter().write(html);
}
4.实现博客登陆界面
1)登陆页面的HTML是不用进行动态替换的,也就是说这里面的内容是固定的,不需要模板进行动态替换,页面里面没有需要进行动态生成的内容
2)实现一个Servlet来进行实现登录请求(静态页面直接放到webapp里面,不用放到模板引擎的文件里面)
3)我们还需要对页面进行微调,当我们进行点击按钮的时候,可以发送一个HTTP请求,里面的input标签加上name属性,只能用一个form表单来进行包裹;
4)再进行实现服务器的代码的时候,我们写的逻辑是这样的,直接用loginServlet来进行实现
一:先从请求中读取出用户名和密码
二:再从数据库中根据用户名读出User,再对比密码是否正确,输入正确,那么就登录成功,把当前的user对象放到HttpSession中,以备后用
三:登录成功之后,我们直接302重定向到博客列表页
5)我们要针对博客列表页,博客详情页,是否已经进行登录进行一个校验,此处就约定,如果当前处于未登录状态,就不可以进行访问博客列表页和博客详情页,此时如果进行访问,就要重定向到博客登录页;此事后要修改我们之前写的,两个Servlet;
我们进行性判断用户是否进行登陆的标准就是看看当前能不能拿到一个合适的session,并且能够拿到这里面的user对象,就说明用户正在处于登录状态;
6)把下面代码放到blog1Servlet和blog2Servlet里面,在我们进行重定向操作的时候,一定要结束当前的程序;
7)当我们进行重启服务器之后,直接访问blog1.html或者blog2.html,都会跳转到博客登录页,让当前用户进行登录;
8)我们为什么在登陆之后可以拿到登陆的user对象呢?那是因为我们在实现博客登录页之后,我们自己在httpsession存放了一个user对象在里面;
实现具体登陆页面的代码:
import MYSQL.OperateUser;
import MYSQL.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.sql.SQLException;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取请求中的参数
resp.setContentType("text.html;charset=utf-8");
req.setCharacterEncoding("utf-8");
String username=req.getParameter("name");
String password=req.getParameter("password");
System.out.println(username);
System.out.println(password);
if(username==null||password==null||username.equals("")||password.equals(""))
{ String html="<h3>当前请求中的用户名或者密码缺失</h3>";
resp.getWriter().write(html);
return;
}
OperateUser operateUser=new OperateUser();
//2从数据库中查询用户
User user=null;
try {
user=operateUser.selectByName(username);
} catch (SQLException e) {
e.printStackTrace();
}
//3进行校验,不存在直接重定向到登陆界面,下面是登陆失败的几种情况,查不到user或者密码不匹配
if(user==null)
{
String html="<h3>用户名错误或者密码错误</h3>";
resp.getWriter().write(html);
return;
}
if(!user.getPassword().equals(password))
{
String html="<h3>用户名错误或者密码错误</h3>";
resp.getWriter().write(html);
return;
}
//4把user对象寸放大HttpSession里面
HttpSession httpSession=req.getSession(true);
httpSession.setAttribute("user",user);
resp.sendRedirect("blog1.html");
}
}
再进行访问每一个页面之前,进行判断用户是否进行登陆的代码:
//判断当前用户是否登录
HttpSession session=req.getSession(false);
if(session==null||session.equals(""))
{
System.out.println("当前用户未登录");
//未登录状态
resp.sendRedirect("blog3.html");
return;
}
User userx= (User) session.getAttribute("user");
if(userx==null)
{
System.out.println("当前用户未登录");
//未登录状态
resp.sendRedirect("blog3.html");
return;
}
1)上面的HttpSession对象是根据同一个SessionID进行找到的对象,当我们执行到req.getSession()的时候,我们就会根据SessionID来进行查找Session对象了;
2)但是在当前我们还是发现了一些问题,在我们进行登陆成功之后,我们在左边的个人信息列表中的数据是写死的,当我们进入到博客详情页,显示的文章作者也是写死的;前面之所以这些信息是写死的那是因为我们之前没有实现登录功能,也就没有办法拿到用户信息,现在已经可以登录,接下来就可以把当前页面上的身份信息,基于模板引擎来进行动态替换;
如果是在博客列表页,那么此处的用户信息就显示当前登陆者的用户信息;
如果实在博客详情页,那么此时的用户信息就显示当前文章作者的用户信息;
当前还没有进行实现修改头像以及gitup这样的功能,这些实现和动态替换用户名没啥区别,只不过是需要在数据库里面扩充字段来保存这些信息;
例如说我们想要保存头像,可以在数据库里面进行存放一些一个图片的路径,到时候就可以把这个图片路径返回到前端页面了;img标签里面的src属性即可
我们先进行修改博客列表页;
此处的所设置的userx是我们在刚才检查登陆状态的时候,从session里面中读的user,这个userx正是当前用户进行登陆的用户的信息;
我们每一次服务器进行重启的时候,服务器内存中的那一个保存会话的hash表也就没了,所以之前的登陆状态也就没有了(因为这个哈希表是保存在内存里面的),当下一次我们的客户端访问服务器的时候Cookie字段的中的sessionID服务器也不会认识,就只能认定是未登录状态;
但是在实际上不是所有的会话信息都在内存里面,Servlet默认的会话是在内存里面的,想要把hash表存放到文件或者数据库里面,就要通过代码进行实现;
修改博客详情页:
前端修改方式是一样的:
后端代码:
此处我们就不可以在webContext里面设置成从session里面读取到的user,而是根据我们之前再去之前在博客列表页传过来的blogID来进行查询用户user,这个user才是我们需要进行模板渲染的user;
上面写法是错误的,怎么可以根据blogID来进行查询到User呢?下面是正确的:
OperateUser operateUser=new OperateUser();
try {
User user=operateUser.selectByUserID(blog.getUserID());
webContext.setVariable("user",user);
} catch (SQLException e) {
e.printStackTrace();
}
5.实现注销功能
1)我们在这里面期望,当我们进行点击注销按钮之后,就可以退出登录状态了,我们在之前判断用户是否进行登录的时候,现根据SessionID来进行查找出HttpSession对象,再取出里面的user;
2)我们必须取到一个user才算登陆成功,取user取不到,或者说取不到session都是登陆失败;
3)当前我们的注销按钮是一个a标签,里面有一个路径,咱们可以写一个Servlet来进行处理这里面的请求,在代码中我们可以根据SessionID来进行找到储存在Session里面的User对象,然后把这个User对象给删了;在HttpSession中提供了一个removeAttribute()这样的方法;然后直接重定向到登陆界面即可;
4)我们在服务器这边在java目录里面创建deleteServlet来进行处理注销请求;
<a href="delete" target="_blank">注销</a>
import MYSQL.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;
@WebServlet("/delete")
public class deleteServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//判断当前用户是否登录
HttpSession session=req.getSession(false);
if(session==null||session.equals(""))
{
System.out.println("当前用户未登录");
//未登录状态
resp.sendRedirect("blog3.html");
return;
}
User userx= (User) session.getAttribute("user");
if(userx==null)
{
System.out.println("当前用户未登录");
//未登录状态
resp.sendRedirect("blog3.html");
return;
}
resp.setContentType("text/html;charset=utf-8");
session.removeAttribute("user");
//重定向登陆界面
resp.sendRedirect("blog3.html");
}
}
6.实现博客发布功能
1)针对博客编辑页进行调整,当前我们的博客编辑页,是不需要进行动态替换的,就和之前的登陆界面差不多,只需要加上form表单,只需要最后点击提交按钮,触发HTTP请求,来进行提交;
我们要在input标签里面加上name属性,方便与后面服务器可以获取到这个文章的标题,然后提交按钮要进行修改;
2)另外我们还需要自己写一个Servlet来针对提交博客的请求,来进行处理;
3)每一次进行修改的功能,开发新的逻辑,都是需要前后端进行搭配进行的,这就导致前后端进行交互不能彻底地进行分离开
4)但是Markdown编译器中的内容如何进行提交呢?
我们可以在id="editor"的div标签里面加上一个隐藏的textarea标签,里面加上name属性,这个东西不需要进行显示,只是为了后面的表单来进行构造数据
5)我们还需要进行一步操作,把我们markDown编译器里面的内容放到textarea里面,方便进行提交;我们只需要在初始化editor的时候,在script标签里面加上saveHTMLToTextarea:true,就是直接可以让editor.md实时的将编辑框里面的内容给存到用户自己写的textarea里面了;于是进一步的进行提交表单就可以带上博客的正文了;
前端代码:
<!-- 这就是博客编辑页的版心 -->
<form action="edit" method="post">
<div class="edit">
<div class="writetitle">
<input type="text" id="title" name="title">
<input type="submit" value="点击发布文章" id="submit">
</div>
<div id="editor">
<textarea name="content" style="display:none"></textarea>
</div>
</form>
</div>
<script>
//首先要初始化编译器
//先创建出一个textinner对象
//editormd相当于是一个函数,生成一个editor对象
//第一个参数是要指定放到哪一个html标签中,元一个html标签关联,参数就是对应的ID;
var editor=editormd("editor", {
//这里的尺寸必须在这里设置,不能依靠CSS进行设置
width:"100%",
// 这是计算makdown正文的高度
height:"1000px",
//编辑器的初始内容
markdown:"#在这里写一下第一篇博客",
//编译器所用的插件所在的路径
path:"editor.md/lib/",
saveHTMLToTextarea:true
});
后端代码:
import MYSQL.Blog;
import MYSQL.OperateBlog;
import MYSQL.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.sql.SQLException;
import java.sql.Timestamp;
@WebServlet("/edit")
public class editServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//判断当前用户是否登录
HttpSession session=req.getSession(false);
if(session==null||session.equals(""))
{
System.out.println("当前用户未登录");
//未登录状态
resp.sendRedirect("blog3.html");
return;
}
User userx= (User) session.getAttribute("user");
if(userx==null)
{
System.out.println("当前用户未登录");
//未登录状态
resp.sendRedirect("blog3.html");
return;
}
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
//2获取请求中的文章标题和文章内容
String content=req.getParameter("content");
String title=req.getParameter("title");
if(content==null||content.equals("")||title==null||title.equals(""))
{
String html="<h3>发布博客失败,文章标题或者文章内容是空</h3>";
resp.getWriter().write(html);
return;
}
//3构建一个blog对象,插入到数据库里面
Blog blog=new Blog();
blog.setContent(content);
//注意当前的文章作者正是当前的登陆者
blog.setUserID(userx.getUserID());
blog.setTitle(title);
blog.setPostTime(new Timestamp(System.currentTimeMillis()));
OperateBlog operateBlog=new OperateBlog();
try {
operateBlog.insert(blog);
} catch (SQLException e) {
e.printStackTrace();
}
//4重定向到博客列表页
resp.sendRedirect("blog1.html");
}
}
7.当前我们发现,博客详情页显示的是博客的原始的Markdown内容,而不是渲染后的markdown,一级标题,二级标题都没有进行显示出来
一个#会被渲染成一级标题,三个#会被渲染成三级标题(左侧写文字,右边显示具体渲染后的结果)
我们此时要针对我们的博客详情页进行调整,同时也需要在这个页面中引入editor.md这个库,然后在这个库里面有一个操作markdownHTML,这个方法的作用是把原始的markDown文本给进行渲染成一个HTML
<script>
//首先要初始化编译器
//先创建出一个textinner对象
//editormd相当于是一个函数,生成一个editor对象
//第一个参数是要指定放到哪一个html标签中,元一个html标签关联,参数就是对应的ID;
var editor=editormd("editor", {
//这里的尺寸必须在这里设置,不能依靠CSS进行设置
width:"100%",
// 这是计算makdown正文的高度
height:"1000px",
//编辑器的初始内容
markdown:"#在这里写一下第一篇博客",
//编译器所用的插件所在的路径
path:"editor.md/lib/",
saveHTMLToTextarea:true
});
</script>
在博客详情页里面,有一个代码:
<P th:text="${blog.content}"></P>这个div里面就包含了markDown的原始报文
markdown的原始内容,就在这个div里面,我们就可以把这个div里面的内容给取出来,然后再通过刚才的markdownToHTML方法(这个方法在script标签里面)进行转换,然后再把转换的结果给些回到刚才的这个div中;
<div id="content"th:text="${blog.content}"></div>
<script>
function rendMo(){
//1先取出div里面的内容
let markDownDiv=document.querySelector("#content");
let markdown=markDownDiv.innerHTML;
console.log(markdown);
//2把div里面的内容设成空
markDownDiv.innerHTML="";
//3进行替换
editormd.markdownToHTML('content',{
markdown:markdown
});//第一个参数表示ID
}
rendMo();
</script>
8.我们完成了上述的调整之后,就发现当前这个代码确实可以运行了也可以进行渲染出来了,但是这里面的背景和之前的半透明效果,显得格格不入;
此处可以给刚才的div设置一个纯透明背景(back-groundcolor里面的rgbc是可以进行设置透明度的;
<div id="content"th:text="${blog.content}" style="background-color:transparent;"></div>
这个style属性表示完全透明,看到的背景就是一个父亲元素的背景;
之前我们的博客列表这里面不太科学,因为新的博客出现在最下边,一般是新的博客在上边,我们就修改了数据库中的查找所有博客的查询语句,加上order by postTime desc属性
9.我们进行实现删除博客的功能
1)你只能删除自己写的博客,例如说A用户进行登陆之后是不可以进行删除B作者的博客的
2)我们在博客详情页中的导航栏加上一个删除博客的按钮,我们已进行点击这个删除博客,就会给后端的服务器发送一个请求,写一个Servlet,服务器就会把这篇博客进行删除了;
3)如果当前这个博客的作者是当前登陆的用户本身,那么就显示这个删除按钮,否则不显示
<a target="_blank" th:if="${showdeleteButton}"th:href="${'deleteblog?blogID='+blog.blogID}"></a>
1)我们在前端加上这样的代码,首先我们肯定要进行判断当前进行登录的用户和文章作者的用户是不是相同的,就用th:if来进行判断,如果相同,就把showdeleteButton设成true,并显示在页面上;否则就会设置成false;
2)当我们进行点击a标签的时候,传递过来的参数肯定要带上blogID这样我们才可以在后面的服务器上面根据blogID来进行删除;
我们在blog2Servlet中修改的代码:
webContext.setVariable("showdeleteButton",user.getUserID()==userx.getUserID());
我们用的deleteblogServlet中的代码
import MYSQL.Blog;
import MYSQL.OperateBlog;
import MYSQL.OperateUser;
import MYSQL.User;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import javax.servlet.ServletContext;
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.sql.SQLException;
@WebServlet("/blog2.html")
public class Blog2Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
//判断当前用户是否登录
HttpSession session=req.getSession(false);
if(session==null||session.equals(""))
{
System.out.println("当前用户未登录");
//未登录状态
resp.sendRedirect("blog3.html");
return;
}
User userx= (User) session.getAttribute("user");
if(userx==null)
{
System.out.println("当前用户未登录");
//未登录状态
resp.sendRedirect("blog3.html");
return;
}
resp.setContentType("text/html;charset=utf-8");
OperateBlog operateBlog=new OperateBlog();
String blogID=req.getParameter("blogID");
if(blogID==""||blogID.equals(""))
{
String html="<h3>blogID字段缺失</h3>";
resp.getWriter().write(html);
}
Blog blog= null;
try {
blog = operateBlog.SelectOne(Integer.parseInt(blogID));
} catch (SQLException e) {
e.printStackTrace();
}
if(blog==null)
{
String html="<h3>根据博客ID获取到的当前博客不存在</h3>";
resp.getWriter().write(html);
}
ServletContext context=req.getServletContext();
TemplateEngine engine=(TemplateEngine)context.getAttribute("engine");
WebContext webContext=new WebContext(req,resp,context);
OperateUser operateUser=new OperateUser();
try {
User user=operateUser.selectByUserID(blog.getUserID());
webContext.setVariable("user",user);
webContext.setVariable("showdeleteButton",user.getUserID()==userx.getUserID());
} catch (SQLException e) {
e.printStackTrace();
}
webContext.setVariable("blog",blog);
String html=engine.process("blog2",webContext);
resp.getWriter().write(html);
}
}
10.项目总结:
一:已经实现的功能:
1)实现了博客列表的展示
2)实现了博客详情的展示
3)实现了登录,用户注销,针对登陆状态的验证,用户信息的显示,发布博客,博客详情页能够显示渲染后的markdown效果
4)实现了删除博客的功能
二:仍然还没有实现的功能
1)用户的头像功能(上传头像,不同用户显示不同的头像)
2)用户的gitup显示,当前是写死的
3)支持文章分类,针对数据库创建一个分类表,允许用户发布文章的时候指定一个分类
4)实现编辑博客,修改博客内容,在博客详情页里面加上修改这样的按钮,点击之后进入到博客编辑页,把刚才博客里面的内容给带过去
5)评论.......
先修改前端代码,再进行修改后端代码;
边栏推荐
- 矩阵链乘 - 动态规划实例
- mysql 自定义函数 身份证号转年龄(支持15/18位身份证)
- Mysql database installation tutorial under Linux
- Security analysis of Web Architecture
- LeetCode_ 3 (longest substring without repeated characters)
- 软件测试人在深圳有哪些值得去的互联网公司【软件测试人员专供版】
- Qingda KeYue rushes to the science and Innovation Board: the annual revenue is 200million, and it is proposed to raise 750million
- Principle and performance analysis of lepton lossless compression
- Tidb DM alarm DM_ sync_ process_ exists_ with_ Error troubleshooting
- R語言ggplot2可視化:可視化折線圖、使用theme函數中的legend.position參數自定義圖例的比特置
猜你喜欢
Principle and performance analysis of lepton lossless compression
ASP. Net large takeout ordering system source code (PC version + mobile version + merchant version)
Thymeleaf th:with use of local variables
Interpretation of tiflash source code (IV) | design and implementation analysis of tiflash DDL module
Shenziyu, the new chairman of Meizu: Mr. Huang Zhang, the founder, will serve as the strategic adviser of Meizu's scientific and technological products
乌卡时代下,企业供应链管理体系的应对策略
Oneconnect listed in Hong Kong: with a market value of HK $6.3 billion, ye Wangchun said that he was honest and trustworthy, and long-term success
循环不变式
Loop invariant
Thymeleaf th:classappend属性追加 th:styleappend样式追加 th:data-自定义属性
随机推荐
R语言ggplot2可视化:gganimate包基于transition_time函数创建动态散点图动画(gif)、使用shadow_mark函数为动画添加静态散点图作为动画背景
Sharing the 12 most commonly used regular expressions can solve most of your problems
Loop invariant
基于 TiDB 场景式技术架构过程 - 理论篇
mysql 自定义函数 身份证号转年龄(支持15/18位身份证)
Use the word "new" to attract curious people
R language ggplot2 visual bar graph: visualize the bar graph through the two-color gradient color theme, and add label text for each bar (geom_text function)
LeetCode_ 3 (longest substring without repeated characters)
Show strength. In this way, the mobile phone will not be difficult to move forward
开挖财上的证券账户可以吗?安全吗?
关于memset赋值的探讨
软件测试人在深圳有哪些值得去的互联网公司【软件测试人员专供版】
C语言中限定符的作用
Lepton 无损压缩原理及性能分析
Shen Ziyu, nouveau Président de Meizu: M. Huang Zhang, fondateur de Meizu, agira comme conseiller stratégique pour les produits scientifiques et technologiques de Meizu
LeetCode_ 67 (binary sum)
Thymeleaf th:with局部变量的使用
03_ Dataimport of Solr
Tidb DM alarm DM_ sync_ process_ exists_ with_ Error troubleshooting
C - Divisors of the Divisors of An Integer Gym - 102040C