⭐️前言⭐️
因文章篇幅较长,所以整个流程分两篇文章来完成。
🍉博客主页: 🍁【如风暖阳】🍁
🍉精品Java专栏【JavaSE】、【备战蓝桥】、【JavaEE初阶】、【MySQL】、【数据结构】
🍉欢迎点赞 👍 收藏 ⭐留言评论 📝私信必回哟😁🍉本文由 【如风暖阳】 原创,首发于 CSDN🙉
🍉博主将持续更新学习记录收获,友友们有任何问题可以在评论区留言
🍉博客中涉及源码及博主日常练习代码均已上传码云(gitee)、GitHub
在前端共涉及的四个页面,都需要分别完成“约定前后端交互接口”、“编写服务器代码”、“编写客户端代码”等任务流程。
这个页面需要展示出数据库中的博客列表,按以下开发流程来进行操作
1)约定接口
2)编写服务器代码
//通过这个类来处理 /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();List blogs=blogDao.selectAll();//把blogs对象转成JSON格式String respJson=objectMapper.writeValueAsString(blogs);resp.setContentType("application/json;charset=utf8");resp.getWriter().write(respJson);}
}
postman
测试成功:
3)编写客户端代码
这一步需要我们在之前写好的前端代码中进行调整,加入ajax
请求,使得前端页面能够与服务器交互,并从数据库中获取到博客列表。
博客列表
1)约定前后端交互接口
可以发现在博客详情页的请求中,与博客列表页不同的是多了?blogId=1
这样的查询字符串;
该查询字符串在blog_list.html
的a
链接标签中就加入了
2)编写客户端代码
加入ajax
请求,使得博客详情页的内容,可以通过请求,从服务器中根据查询字符串中的信息,找到对应的博客,展示在前端页面上。
该操作还需要注意,博客正文要用markdown
格式渲染,所以要引入editor.md
依赖
核心代码块:
完整前端代码:
博客详情页
3)编写服务器代码,在BlogServlet
类中修改doGet
方法,使得浏览器发来的请求中,如果有查询字符串,就返回博客详情页json
数据;如果没有查询字符串,就返回博客列表页json
数据。
//通过这个类来处理 /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;charset=utf8");BlogDao blogDao=new BlogDao();//先尝试获取到req中的blogId参数,如果该参数存在,说明是要求请求博客详情//如果该参数不存在,说明是要请求博客的列表String parm=req.getParameter("blogId");if(parm==null) {//不存在参数,获取博客列表List blogs=blogDao.selectAll();//把blogs对象转成JSON格式String respJson=objectMapper.writeValueAsString(blogs);resp.getWriter().write(respJson);}else {//存在参数,获取博客详情int blogId=Integer.parseInt(parm);Blog blog=blogDao.selectOne(blogId);String respJson=objectMapper.writeValueAsString(blog);resp.getWriter().write(respJson);}}
}
效果预览:
1)约定接口
登录成功后,跳转到博客列表页。
在前端登录实现部分中,我们使用的是input
标签,所以通过form
表单的方式来构造登录请求更方便。
2)编写前端代码
主要是加入form
表单,并把提交按钮类型改为submit
,还需要改提交按钮对应的css
样式
blog_login.html
:
Document
blog_login.css
:
.login-container {width: 100%;/* 注意减号两边有空格 */height: calc(100% - 50px);/* 需要让里面的子元素, 垂直水平居中, 需要用到 flex 布局 */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;
}.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;
}
3)编写服务器代码
约定的路径是/login
,需要新建一个Servlet
类来处理这里的登录请求
@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; charset=utf8");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; charset=utf8");resp.getWriter().write("用户名或密码错误!");return;}//3.如果比较通过,就创建会话HttpSession session=req.getSession(true);//把刚才的用户信息,存储到会话中session.setAttribute("user",user);//4.返回一个重定向报文,跳转到博客列表页resp.sendRedirect("blog_list.html");}
}
效果预览:
在我们完成了登录功能后,需要对前边两个页面(博客列表页和博客详情页)进行调整,使得这两个页面必须登录后才能访问。
要想实现上述的功能,就需要在博客列表页/详情页加载的时候,通过ajax
访问一下服务器,获取当前的登录状态,看看能不能获取到,如果获取到了,就说明当前确实是已经登录了,此时就可以留在这个页面了;如果没有获取到,说明未登录,就需要跳转到登录页面。
1)约定接口
如果登录了就返回当前登录的用户信息,未登录,就直接返回一个userId=0
的对象。
2)编写服务器代码
在LoginServlet
类中加上doGET
方法
//这个方法用来让前端检测当前的登录状态@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("application/json;charset=utf8");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));}
3)编写前端代码
在博客详情页和博客列表页中加入ajax
请求,在页面一加载出来的时候就向服务器发送请求来判定登录状态。
因为两个页面都需要进行判断,所以把判断逻辑单独出一个js
文件中,让这两个前端代码引入即可。
//这个文件放一些页面公共的代码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);}else {//登录失败!//让前端页面跳转到login.htmlalert("当前您尚未登录!请登录后再访问博客列表!");location.assign('blog_login.html');}},error: function() {alert("当前您尚未登录! 请登录后再访问博客列表!");location.assign('blog_login.html');}});
}
该步骤需要注意两点:
因为在数据库中并没有存储个人的头像和文章数量等信息,所以只能对用户名做出变动,因为之前在1.4中完成了登录状态检测的步骤后,也就完成了后端代码,这一步只需要在前端进行微调,在页面是博客列表页时,能够将用户名做出修改即可。
//这个文件放一些页面公共的代码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);if(pageName=='blog_list.html') {changeUserName(body.username);}}else {//登录失败!//让前端页面跳转到login.htmlalert("当前您尚未登录!请登录后再访问博客列表!");location.assign('blog_login.html');}},error: function() {alert("当前您尚未登录! 请登录后再访问博客列表!");location.assign('blog_login.html');}});
}function changeUserName(username) {let h3=document.querySelector('.card>h3');h3.innerHTML=username;
}
效果预览:
要想完成博客详情页中用户名的更改,还需要重新约定接口、编写前后端代码。
1)约定接口
2)编写前端代码
关键代码:
前端代码中的两个ajax
请求,是异步并发执行的,并不能确定先后顺序,所以在一个函数的回调函数中,调用另一个函数,让其变为串行执行,才能控制顺序。
3)编写服务器代码
@WebServlet("/authorInfo")
public class AuthorServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("application/json; charset=utf8");// 通过这个方法, 来获取到指定的博客的作者信息.String param = req.getParameter("blogId");if (param == null || "".equals(param)) {// 参数缺少了.resp.getWriter().write("{ \"ok\": false, \"reason\": \"参数缺失!\" }");return;}// 根据当前 blogId 在数据库中进行查找, 找到对应的 Blog 对象, 再进一步的根据 blog 对象, 找到作者信息.BlogDao blogDao = new BlogDao();Blog blog = blogDao.selectOne(Integer.parseInt(param));if (blog == null) {resp.getWriter().write("{ \"ok\": false, \"reason\": \"要查询的博客不存在!\" }");return;}// 根据 blog 对象, 查询到用户对象UserDao userDao = new UserDao();User author = userDao.selectById(blog.getUserId());if (author == null) {resp.getWriter().write("{ \"ok\": false, \"reason\": \"要查询的用户不存在!\" }");return;}// 把 author 返回到浏览器这边// 注意要把密码给干掉!author.setPassword("");resp.getWriter().write(objectMapper.writeValueAsString(author));}
}
在导航栏中有一个“注销”按钮,当用户点击注销以后,就会在服务器上取消登录状态,并且能够跳转到登录页面。
1)约定前后端交互接口
2)注销逻辑的服务器代码
@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("当前用户尚未登录!无法注销!");return;}//把这个用户的会话中的信息给删掉即可session.removeAttribute("user");resp.sendRedirect("blog_login.html");}
}
用户有一个session
,同时session中有一个user
属性,两者同时兼备时,就是登陆状态,注销只要把其中一个条件破坏掉即可。
3)客户端代码修改
把博客列表页、博客详情页、博客编辑页中的导航栏中的注销按钮中的herf属性,都做出修改,改成“logout”这个路径。
在博客编辑页中,当用户输入了博客标题和正文之后,点击发布,此时就会把博客数据提交到服务器,由服务器存储到数据库中。
1)约定前后端交互接口
2)实现服务器代码
在BlogServlet里面添加一个doPost方法,来处理上述的post请求;
核心操作,就是读取请求中的标题和正文,构造blog
,构造Blog
对象,并插入数据库
@Overrideprotected void doPost(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;}//一定要先指定好请求,按照哪种编码来解析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;charset=utf8");resp.getWriter().write("提交博客失败!缺少必要的参数!");return;}//构造Blog对象,把当前的信息填进去,并插入数据库中//此处要给Blog设置的属性,主要是title,content,userId(作者信息)//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");}
3)调整客户端代码
将整个文章内容用form
表单套住,提交form
表单
Document
注意对css
样式的选择器进行调整,保证提交按钮的样式不丢。
只有自己能够删除自己的博客,不能够删除别人的博客。
1)约定接口
2)调整前端代码
我们需要在博客详情页中进行判定,当前博客的作者,是否就是登录的用户;
如果是,就在导航栏里显示一个删除按钮,如果不是,就不显示删除按钮。
3)编写服务器代码
@WebServlet("/blogDelete")
public class BlogDeleteServlet extends HttpServlet {@Overrideprotected void doGet(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.获取到参数中的blogIdString blogId=req.getParameter("blogId");if(blogId==null||"".equals(blogId)) {resp.setContentType("text/html;charset=utf8");resp.getWriter().write("当前blogId参数不对!");return;}//3.获取要删除的博客信息BlogDao blogDao=new BlogDao();Blog blog=blogDao.selectOne(Integer.parseInt(blogId));if(blog==null) {resp.setContentType("text/html;charset=utf8");resp.getWriter().write("当前要删除的博客不存在!");return;}//4.再次校验,当前的用户是否是博客的作者if(user.getUserId()!=blog.getUserId()) {resp.setContentType("text/html;charset=utf8");resp.getWriter().write("当前登录的用户不是作者,没有删除权限");return;}//5.确认无误,开始删除blogDao.delete(Integer.parseInt(blogId));//6.重定向到博客列表resp.sendRedirect("blog_list.html");}
}
⭐️最后的话⭐️
总结不易,希望uu们不要吝啬你们的👍哟(^U^)ノ~YO!!如有问题,欢迎评论区批评指正😁