准备工作,创建项目
引入依赖
1.servlet
2.mysql
3.jackson
导入前端代码
1.博客列表页
2.博客详情页
3.登录页
4.博客编辑页
接下来要进行的操作就是两大方面
1.前端和服务器的交互
2.服务器和数据库的交互
进行数据库设计创建数据库和数据表
一把需要把建库建表的操作写错sql文件然后保存下来
这时我们使用一个表表示博客 另一个表表示用户(创建表是根据需求来 博客系统中能够管理博客 也要进行登录等用户操作)
--编写SQL完成建库建表操作create database if not exists blog_system charset utf8;use blog_system;drop table if exists user;
drop table if exists blog;create table blog (blogId int primary key auto_increment,-- 博客标题title varchar(256),-- 博客内容content varchar(4096),-- 博客作者userId int,-- 博客发布时间postTime datetime
);
create table user(userId int primary key auto_increment,-- 用户名 指定用户名不能重复username varchar(64) unique,-- 密码password varchar(64)-- user 还可以设置其他数据属性 github链接 头像链接
);-- 构造一些数据
insert into user values(1,'zhangsan','123'),(2,'lisi','123');
insert into blog values(1,'第一篇博客','学习java第一天',1,'2024-01-01 12:00:00');
insert into blog values(2,'第二篇博客','学习java第二天',1,'2024-01-01 12:00:00');
insert into blog values(3,'第三篇博客','学习java第三天',1,'2024-01-01 12:00:00');
通过封装JDBC代码 来实现一些基础的数据库操作
在这个项目中 针对blog表和user表进行一些增删查改
DAO(Data Access Object)数据访问对象 写一些类 通过这些类里的方法封装了数据库操作 此时数据库就是通过这样的对象来访问的
package dao;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 {//单例模式编写//并不会在外部使用所以使用static修饰方法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("jdbc:mysql://127.0.0.1:3306/blog_system?useSSL=false&characterEncoding=utf8");((MysqlDataSource)dataSource).setUser("root");((MysqlDataSource)dataSource).setPassword("123456");}}}return dataSource;}//提供一个方法和数据库建立连接public static Connection getConnection(){try {return getDataSource().getConnection();} catch (SQLException e) {e.printStackTrace();}return null;}//提供一个方法和数据库断开连接 进行资源释放public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet){//如果把三个close放到一个try中一旦前面的close出现异常就会导致后面的close执行不到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();}}}}
}
封装数据库代码
1.设计数据库/设计表
2.封装DBUtil,实现建立连接和断开连接
3.创建实体类 后续数据库操作是围绕实体类展开的
实体类:数据库设计
根据需求找出有那些实体
在梳理清楚实体和实体之间的关系(一对一 一对多 多对多)
此处的实体类是要和数据库的表有对应关系的每个表都需要有实体类使用类的对象表示这个表的一条记录
此处的实体类一个是对应表User 一个是Blog
4.编写Dao类 来进行进一步的封装数据库操作
package dao;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;//通过这个类 进行封装针对blog表的增删改查
public class BlogDao {//1.新增一个博客//调用insert的时候 需要先构造一个blog对象//作为参数传递给insert 再由insert内部完成数据库插入操作public void insert(Blog blog) {Connection connection = null;PreparedStatement statement = null;try{//1.和数据库建立连接connection = DButil.getConnection();//2.构造sql语句//此处的博客发部时间正好是sql执行的时候 使用sql里的now()函数完成获取当前时间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){e.printStackTrace();}finally {//4.关闭连接释放资源DButil.close(connection,statement,null);}}//2.查询表里所有的博客// 正常开发中 一把不会直接把整个表里的数据全部查询 一般都是条件查询或者分页查询public List<Blog> getBlogs(){Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;List<Blog> blogs = new ArrayList<>();try {//1.和数据库建立连接connection = DButil.getConnection();//2.构造SQL语句String sql = "select * from blog";statement = connection.prepareStatement(sql);//3.执行SQLresultSet = statement.executeQuery();//4.遍历结果集合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.getInt("userId"));blog.setPostTime(resultSet.getTimestamp("postTime"));blogs.add(blog);}}catch (SQLException e){e.printStackTrace();}finally {DButil.close(connection,statement,resultSet);}//如果前面的查询出现问题blogs就会得到一个null的listreturn blogs;}//3.指定一个blogid 查询某一个博客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又是主键//查询到结果要么是1条记录要么是0条记录 所以可以直接进行判定if (resultSet.next()){Blog blog = new Blog();blog.setBlogId(resultSet.getInt("blogId"));blog.setTitle(resultSet.getString("title"));blog.setContent(resultSet.getString("content"));blog.setUserId(resultSet.getInt("userId"));blog.setPostTime(resultSet.getTimestamp("postTime"));return blog;}}catch (SQLException e){e.printStackTrace();}return null;}//4.指定博客进行删除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){e.printStackTrace();}finally {DButil.close(connection, statement, null);}}
}
此处的JDBC代码是高度同质化的后面会使用Mybatis给数据库操作代码消除掉
下面是关于UserDao的JDBC代码
package dao;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;//通过这个类 进行封装针对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 e){e.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("passoword"));return user;}}catch(SQLException e){e.printStackTrace();} finally{DButil.close(connection,statement,resultSet);}return null;}}
上述代码 完成类数据库相关的工作
接下来就可以进行前后端交互逻辑的实现了 以功能为维度展开分别进行"设计前后端交互接口" “后端开发代码” “前端开发代码” “调试”
功能一:实现博客列表页 让博客列表页能够加载博客列表
之前显示的内容都是在html中写死的 此处应该从数据库中获取
之后我们要做的就是 让博客列表在加载的时候发起一个ajax的http请求 请求发到服务器上 就能获取到博客列表的数据 进一步把博客数据展示到页面上(这就涉及前端和后端交互)
1.前端发一个HTTP请求向后端索要博客列表数据
2.后端收到请求之后查询数据库获取到数据库中的博客列表返回给前端
3.前端拿到响应之后就依据这里的内容构造出html片段最终显示出来
在进行这三个操作之前 还需要先约定前后端交互接口后端和前端要进行很多种不同的数据交互 每一种数据交互 都需要发送不同的请求返回不同的响应
这里这么约定都行 下面是一种典型的约定方法
此处是获取博客列表 此处不需要加上一些额外的参数
后续实现获取博客详情就需要指定blogId 就可以通过query string 或者 body传递给后端
请求 GET blog
响应
HTTP/1.1 200 OK
Content-Type: applicattion/json [
{
blogId:1
title:
content:
userId:
postTime:
}
{
blogId:2
title:
content:
userId:
postTime:
}
]
编写后端代码
后端代码要做的事就是 当收到上述请求之后 就会构造并返回一个约定响应的数据
package api;import com.fasterxml.jackson.databind.ObjectMapper;
import dao.Blog;
import dao.BlogDao;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;public class BlogServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {BlogDao blogDao = new BlogDao();List<Blog> blogs = blogDao.getBlogs();//这里jackson看到blogs是一个数组就会构造出一个json数组 //然后针对List中的每个Blog对象 分别构造出json对象//具体构造过程就是根据Blog的属性 属性的名字 就是json都key 属性的值就是json的valueString respJson = objectMapper.writeValueAsString(blogs);resp.setContentType("application/json; charset=utf8");resp.getWriter().write(respJson);}
}
{
blogId:1
title:
content:
userId:
postTime:
}
编写前端代码
让页面通过js ajax的发起http请求 来获取到刚才服务器这里的数据
<script src = "https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script><script>// 编写js代码// 构造http请求 获取到博客列表数据 并展示到页面上function getBlogs() {$.ajax({type : 'get',url : 'blog',success : function(body) {// 根据响应的内容 构造出 html 片段 展示到页面上// 由于服务器响应已经设置了Content-Type为application/json内容所以此时就会自动把此处的响应内容解析成js对象数组let container = document.querySelector('.container-right');for(let blog of body){// 根据当前blog构造一个html片段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.href = 'blog_datail.html?blogId=' + blog.blogId;a.innerHTML = '查看全文 >>';blogDiv.appendChild(a);//最后把拼好的blogDiv添加在contationer的后面container.appendChild(blogDiv);} }});}getBlogs();</script>
然后我们访问清单网页就会导入我们数据库的内容
打开一个博客列表此处涉及到两个http请求 一个请求时获取博客列表页这个静态 一个时获取博客数据的
当前我们显示的是时间戳所以要对其进行转化
这里需要明确问题是在前端还是在后端
1.后端返回时间戳
2.后端返回是格式化时间 但是前端显示了时间戳
对此问题我们可以通过抓包来了解
可以看到抓到两个http请求第一个请求是获取blog_list.html这个页面
一个是后端代码请求
我们可以看到后端的代码就已经是一个时间戳了 所以此时我们后端就要做一个格式化的时间 在后端解决问题
1.看到blogs是一个list 生成结果就是json数组
2.数组的每个元素(blog对象)分别转换成json字符串
3.转换成的字符串根据blog对象的getter方法来完成的
jackson就会自动调用这里的getPostime 把得到的结果作为json字符串中postTime属性的值
此处就回返回一个Timstamp这个类型就被jackson当成时间戳来显示
时间戳=>格式化时间(Java标准库中提供了一个SimpleDateFormat类 完成这个工作)
1.先构造一个对象构造的时候指定具体的格式
2.使用format方法得到格式化
实现博客详情页
此处的步骤和之前的类似
1.约定前后端交互接口
请求: GET/blog?blogId=1
响应: HTTP/1.1 200 OK
Content-Type:application/json
{ blogId:1 title:“学习java第一天”
content:“今天学习情况”
userId:1
postTime:“时间”
}
2.实现后端代码
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {BlogDao blogDao = new BlogDao();//现尝试获取下blogId这个参数 看看能不能获取到String blogId = req.getParameter("blogId");if(blogId == null){//此时就是获取博客列表 没有blogId参数//这里jackson看到blogs是一个数组就会构造出一个json数组//然后针对List中的每个Blog对象 分别构造出json对象//具体构造过程就是根据Blog的属性 属性的名字 就是json都key 属性的值就是json的valueList<Blog> blogs = blogDao.getBlogs();String respJson = objectMapper.writeValueAsString(blogs);//此处把java对象数组 转成了json字符串resp.setContentType("application/json; charset=utf8");resp.getWriter().write(respJson);}else {//此时就说明获取的是博客详情 有blogId参数//这里获取的id得到的是一个字符串 所以要把它转成int类型Blog blog = blogDao.getBlog(Integer.parseInt(blogId));if(blog == null){//返回一个id为0的blog对象 前端再根据这里进行进行判定//id不为空 但是输入的query不存在 例如输入的id是100000000blog = new Blog();}String respJson = objectMapper.writeValueAsString(blog);resp.setContentType("application/json; charset=utf8");resp.getWriter().write(respJson);}}
}
3.实现前端代码
文档对象模型(DOM)
HTML上的每个元素 都会对应到一个js的对象
这个对象是已经创造的
通过querSelector就能把这个对象找到
这里是先找到有没有container-right标签 再看看标签里有没有h3标签
一旦把js对象内容进行修改了 界面内容就会进行改变
引入依赖的顺序 editor.md依赖了jquery 所以我们在引入editor之前要先引入jquery依赖
实现登录详情页
在页面输入用户名 和 密码
点击登录之后 就会触发登录逻辑 如果登录成功 就会重定向到 博客列表
1.前后端交互接口
POST/login Content-Type: application/x-www-form-urlencoded
(使用form表单的方式提交)
username=zhangsan&password=123
响应 HTTP/1.1 302
Location:blog_list.html(登录之后就会重定向到列表页面)
2.编写后端代码
新写一个servlet处理login这个请求路径
每个servlet都会关联一个路径 前面博客列表博客详情页都属于blog这个路径 一个servlet就行
此处需要一个login的路径
package api;import dao.User;
import dao.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 {//1.获取请求中用户名和密码//给请求对象设置字符集使其请求中username或者password是中文也能处理req.setCharacterEncoding("utf8");String username = req.getParameter("username");String password = req.getParameter("password");if(username == null || password == null || username.equals("") || password.equals("")){resp.setContentType("text/html; charset=utf8");resp.getWriter().write("输入的用户名或者密码为空");return;}//2.和数据库验证 看当前用户名和密码是否匹配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;}//3.创建会话HttpSession session = req.getSession(true);}
}
创建会话方法参数true表示不存在就创建 存在就查询 并且生成键值对(key就是sessionId
value就是HttpSession对象) 通过sessionId通过set-Cookie返回到浏览器
3.写前端代码
<div class="login-container"><!-- 登录对话框 --><div class="login-dialog"><h3>登录</h3><!-- 使用 form 包裹一下下列内容, 便于后续给服务器提交数据 --><form action="login" method="post"><div class="row"><span>用户名</span><input type="text" id="username" 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></div>
</body>
</html>
实现强制检查功能
此处要求系统必须要登录才能使用 用户在未登录博客详情页列表页就会自动跳转到登录页
1.约定前后端交互接口
在博客详情页列表页 发起一个get 的 ajax 询问服务器是否登录
请求:
GET login
响应:
HTTP/1.1 200 OK(已登录)
HTTP/1.1 403 Forbidden(未登录)
2.编写后端代码
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 = (User) session.getAttribute("user");if(user==null){resp.setStatus(403);return;}//若是会话存在而且用户存在就返回 200 状态码表示已登录resp.setStatus(200);}
3.编写前端代码
在每段前端html文件上加上一个js方法
function checklogin(){$.ajax({type : 'get',url: 'login',success: function(body){},error: function(body){location.assign("login.html");}});
}
实现显示博客详情页
(如果使用zhangsan这个用户列表页就应该是张三用户信息)
1.约定前后端交互接口
博客列表页:
请求: GET/user
响应: HTTP/1.1 200 OK
{ userId:1,username:‘zhangsan’} 博客详情页
请求: GET/user?blogId=1
响应: HTTP/1.1 200 OK
{ userId:1 username:“zhangsan” }
2.编写后端代码
package api;import com.fasterxml.jackson.databind.ObjectMapper;
import dao.Blog;
import dao.BlogDao;
import dao.User;
import dao.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("/user")
public class UserServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String blogId = req.getParameter("blogId");if(blogId == null){//从博客列表页//从seeion中拿到user对象HttpSession session = req.getSession(false);if(session == null){User user = new User();String respJson = objectMapper.writeValueAsString(user);resp.setContentType("application/json;charset=utf8");resp.getWriter().write(respJson);return;}User user = (User) session.getAttribute("user");if(user == null){user = new User();String respJson = objectMapper.writeValueAsString(user);resp.setContentType("application/json; charset=utf8");resp.getWriter().write(respJson);return;}String respJson = objectMapper.writeValueAsString(user);resp.setContentType("application/json; charset=utf8");resp.getWriter().write(respJson);}else {//博客详情页//需要查询数据库BlogDao blogDao = new BlogDao();Blog blog = blogDao.getBlog(Integer.parseInt(blogId));if(blog == null){User user = new User();String respJson = objectMapper.writeValueAsString(user);resp.setContentType("application/json; charset=utf8");resp.getWriter().write(respJson);return;}UserDao userDao = new UserDao();User user = userDao.getUserById(blog.getUserId());if(user == null){user = new User();String respJson = objectMapper.writeValueAsString(user);resp.setContentType("application/json; charset=utf8");resp.getWriter().write(respJson);return;}String respJson = objectMapper.writeValueAsString(user);resp.setContentType("application/json; charset=utf8");resp.getWriter().write(respJson);}}
}
先从请求中拿到blogId然后如果blogid为空 就从请求中拿到一个会话session 如果会话为空就返回一个respJson格式字符串 这个json格式字符串中里面具体内容是{userId:0,username:“”…}
为什么返回的是一个空的user而不是null?
这是因为ajax要求返回的类型是一个userjson的格式
如果返回的是一个null就会报错
退出登录(注销)
判定登陆状态逻辑有两点
1.会话存在
2.会话中存储user对象两个条件同时具备才认为用户已经登录
我们只要破坏上诉任何一个条件就可以达成注销效果
在此我们可以使用Servlet中的api删除user(Attribute)
还是按照之前的三个步骤展开
1.约定前后端交互接口
请求
GET/logout
响应
HTTP/1.1 302
Location:login.html
2.编写后端代码
package api;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("/logout")
public class LogoutServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//先获取到请求中的sessionHttpSession session = req.getSession(false);//如果会话为空就是未登录的状态 返回到登录界面if(session == null){resp.sendRedirect("login.html");return;}//如果之前登录成功了就会给session中存储一个user这样的会话Attribute//我们把这个user删除就会 判定未登录了session.removeAttribute("user");resp.sendRedirect("login.html");}
}
3.编写前端代码
我们可以通过一个a标签href属性 指定要访问的路径就可以了
点击a标签就能触发http get请求
发布博客
本质上和登录类似
核心是通过form表单 把页面用户输入内容提交到服务器这边服务器就可以把内容保存到数据库中即可
1.约定前后端交互接口
使用form提交数据
请求
POST/blog
Content-Type:application/x-www-from-urlencoded
title=xxx&content=xxxx
响应
HTTP/1.1 302
Location:blog_list.html
提交之后就能看到新发布的博客了
2.编写后端代码
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.获取到登录用户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;}//2.获取到请求中传递的内容//记得设置编码req.setCharacterEncoding("utf8");String title = req.getParameter("title");String content = req.getParameter("content");if(title == null || content == null || "".equals(title) || "".equals(content)){resp.setContentType("text/html; charset=utf8");resp.getWriter().write("标题或正文为空");return;}//3.构造Blog对象 并且插入数据库之中Blog blog = new Blog();blog.setTitle(title);blog.setContent(content);blog.setUserId(user.getUserId());BlogDao blogDao = new BlogDao();blogDao.insert(blog);//4.反转到博客列表resp.sendRedirect("blog_list.html");}
3.编写前端代码
把from表单补齐