目录
准备工作
(1) 创建项目
(2) 引入依赖(servlet、Jackson、mysql)
(3)创建必要的目录
(4)编写代码
(5、6) 打包部署(基于smart Tomcat)
(7)在浏览器中验证
编写服务器代码
View层:
编写数据库的操作代码
(1)创建数据库/表结构(数据库设计)
博客页面
1.博客列表页,显示博客的列表
2.博客详情页,点击博客列表页,上面列出的博客条目,跳转到的页面,显示博客的完整内容
3.登录页
4.博客编辑页,基于editor.md实现Markdown编辑器
存储博客:点击发布的时候,博客被发布到服务器上,就要被存起来
获取博客:在博客列表页和博客详情页,能够拿到博客的内容
还能够进行登录校验
博客表:用来存储所有的博客数据
用户表:用户登录
(2)封装 数据库
1.创建DBUtil封装数据库连接操作
2. 创建实体类
使用实体类表示数据库中的一条记录。
此处主要创建了Blog类和User类
3. 封装针对数据的增删改查
约定前后端交互接口
博客列表页
约定交互接口
这个页面要能够展示出数据库中的博客的列表
编写服务器代码
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
private ObjectMapper objectMapper=new ObjectMapper();
//这个方法用来获取到数据库中的博客列表
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//从数据库中查询到博客列表,转成json,然后返回
BlogDao blogDao=new BlogDao();
List<Blog> blogs=blogDao.selectAll();
//把blogs对象转成json格式
String respJson=objectMapper.writeValueAsString(blogs);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(respJson);
}
}
编写客户端编码
在页面加载的时候,让页面通过ajax访问服务器,获取到数据库中的博客数据,并且填到页面中。
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>
//在页面加载的时候,通过ajax给服务器发送数据,获取到博客列表信息,并且显示在界面上
function getBlogList(){
$.ajax({
type:'get',
url:'blog',
success:function(body){
//获取到的body就是一个JS对象数组,每个元素就是一个JS
//1.先把.right里原有的内容给清空
let rightDiv=document.querySelector('.right');
rightDiv.innerHTML='';
//2.遍历body,构造出一个个的blogDiv
for(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='data';
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='查看全文>>';
//此处希望点击之后,跳转到博客详情页
//这个跳转需要告知服务器要访问的是哪个博客的详情页
a.href='blog_detail.html?blogId='+blog.blogId;
blogDiv.appendChild(a);
//吧blogDiv挂到dom树上
rightDiv.appendChild(blogDiv);
}
},
error:function(){
alert("获得博客列表失败")
}
});
}
getBlogList();
</script>
博客详情页
约定交互接口
后端代码实现
后端代码实现和博客列表页的获取,基本相同,就直接放在一个方法中来实现。用blogId参数来区分是获取博客列表还是详情
int blogId=Integer.parseInt(param);
Blog blog=blogDao.selectOne(blogId);
String respJson=objectMapper.writeValueAsString(blog);
resp.getWriter().write(respJson);
实现前端代码
<script>
function getBlogDetail() {
$.ajax({
type:'get',
url:'blog'+location.search,
success:function(body) {
//根据body中的内容来构造页面
//1.构造博客标题
let h3=document.querySelector(".blog-content>h3");
h3.innerHTML=body.title;
//2.构造博客发布时间
let dataDiv=document.querySelector(".data");
dataDiv.innerHTML=body.postTime;
//3.构造博客正文
//如果直接把content设为innerHTML,此时展示在界面上的内容,是原始的Markdown字符串
//需要的是渲染后的效果
// let content=document.querySelector("#content");
// content.innerHTML=body.content;
editormd.markdownToHTML('content',{
markdown:body.content
});
}
});
}
getBlogDetail();
</script>
博客登录页
约定交互接口
前端代码
后端代码
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected 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访问一下服务器,获取到当前的登录状态,看能否获取到。如果获取到了,就说明当前确实是已经登录了,此时就可以留在这个页面了,如果没有获取到,说明未登录,就需要跳转到登录页面。
约定前后端交互接口。
前端代码:
//通过 GET/login 这个接口来获取下当前的登录状态
function getUserInfo(){
$.ajax({
type:'get',
url:'login',
success: function(body) {
//判断此处的body是不是一个有效的user对象(userId是否为0)
if(body.userId && body.userId > 0){
//登录成功
//不做处理
console.log("当前用户登录成功!用户名:" + body.username);
}else {
//登录失败
//让前端页面跳转到html
alert("当前您尚未登录,请登录后在访问博客列表");
location.assign('login.html');
}
},
error: function() {
alert("当前您尚未登录,请登录后在访问博客列表");
location.assign('login.html');
}
});
}
后端代码:
//这个方法就让前端来检测当前的登录状态
@Override
protected 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));
}
正确显示用户信息
(1)在博客列表页,正确显示用户信息是实际登录的用户。
登录用户的信息,在检测用户是否登录的接口中,就已经拿到了。只需要把拿到的用户信息,显示到界面上即可。
(2)在博客详情页,正确显示的信息是当前文章的作者信息。
让服务器提供一个接口,这个接口可以让客户端指定blogId,获取到指定blogId的作者信息。
实现“注销”功能
退出当前登录的状态。
当用户点击注销之后,就会在服务器上取消登录状态,并且能够跳转到登录页面。
约定前后端交互接口
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
@Override
protected 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("login.html");
}
}
如何理解已经登录?
用户有一个session,同时session有一个user属性,两者同时具备,才是登录状态。
注销,只要破坏掉上面任意一个条件就行。此处选择的是破坏第二个条件
发布博客
在博客编辑页中,当用户输入了博客标题和正文之后,点击发布,此时就会把博客数据提交到服务器,由服务器存储到数据库中。
约定前后端交互接口
服务器代码
在BlogServlet里面添加一个dopost方法,来处理上述post请求
核心操作,就是读取请求中的标题和正文,构造Blog对象,并插入数据库。
@Override
protected 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);
blog.setUserId(user.getUserId());
BlogDao blogDao=new BlogDao();
blogDao.insert(blog);
//重定向到博客列表页
resp.sendRedirect("blog_list.html");
}
客户端代码
<form action="blog" method="post">
<div class="title">
<input type="text" placeholder="在此处输入标题" name="title">
<!-- <button>发布文章</button> -->
<input type="submit" value="发布文章">
</div>
<!-- 放置md编辑器 -->
<div id="editor">
<!-- 为了进行form的提交,此处添加一个textarea多行编辑框,借助这个编辑框来实现表单的提交 -->
<textarea name="content" style="display:none"></textarea>
</div>
</form>
删除博客
只有自己能删自己的博客,不能删别人的博客。
界面
界面上的处理,在博客详情页这里去判断,看当前这个博客的作者,是否就是登录的用户,如果是,就在导航栏里显示一个“删除按钮”,如果不是,就不显示删除按钮。
在博客详情页这块,其实既从服务器获取了当前用户的登录信息,又获取了博客的作者信息。
服务器
用户点击删除按钮,触发一个HTTP请求,就会让服务器删除掉指定的博客,服务器收到请求后,就会把这个博客从数据库里给删掉。
@WebServlet("/blogDelete")
public class BlogDeleteServlet extends HttpServlet {
@Override
protected 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.获取到参数中的blogId
String 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");
}
}