引言:
tomcat版本:8.5 开发工具:IDEA jdk版本:jdk1.8.0_161 此项目是对本博主之前编写的一个纯HTML页面的博客系统进行的一个改进
准备工作
1.创建maven项目
依次点击File→New→Project,选择左侧的Maven项目,点击Next,然后填写项目名称,点击Finish,至此一个maven项目就创建好了。
2. 引入依赖(servlet,jackson,mysql)
去中央仓库搜索下载即可,👉地址传送门(一般搜索关键字后第一个就是我们所需要的,注意版本)
<dependencies>
<!-- 加入 servlet 依赖 -->
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<!-- servlet 版本和 tomcat 版本有对应关系,切记 -->
<version>3.1.0</version>
<!-- 这个意思是我们只在开发阶段需要这个依赖,部署到 tomcat 上时就不需要了 -->
<scope>provided</scope>
</dependency>
<!--引入mysql驱动包-->
<!-- 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.12.6.1</version>
</dependency>
</dependencies>
引入依赖后记得手动刷新一下,以防万一。(第一次引入的时候时间可能会长一些,要耐心等待,有的时候可能因为网络问题导致引入失败,这时候不要慌,多刷新几次试试)
3.创建必要的目录
web.xml新建的时候选择File选项 web.xml里面填写的内容,直接复制下面的代码即可:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
</web-app>
4. 编写代码
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 java.io.IOException;
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("hello");
}
}
5/6. 打包部署程序——直接基于smart Tomcat
如何下载tomcat
tomcat直接在官网下载即可:👉官网下载 下载完解压即可,解压的时候最好不要解压到带中文的文件夹里。
如何下载smart Tomcat
然后就按照下面的步骤进行部署??????
7. 在浏览器中验证
点击上方小绿三角运行,然后在浏览器中输入:http://localhost(也可以是127.0.0.1):8080/blog_system/hello 到这里准备工作算是做完了,接下来就可以执行接下来的步骤了??😊
编写数据库的操作代码
1.创建数据库/表结构—数据库设计
设计数据库,需要根据当前的需求来进行设计,而我们这个博客页面,一共需要以下几个页面:
- 博客列表页,显示博客的列表
- 博客详情页,点击博客列表页,上面列出的博客条目,跳转到的页面,显示博客的完整内容
- 登录页面
- 博客编辑页,基于editor.md搞一个markdown编辑器,基于这个页面来发布博客
我们需要以下需求:
- 存储博客:当点击发布的时候,博客被发布到服务器上,就要被存起来
- 存取博客:在博客列表页和博客详情页,能够拿到博客的内容
- 能够进行登录校验
我们设计表,就需要抓住需求中的实体(关键性的名词)
- 博客表:用来存储所有的博客数据
- 用户表:用户登录时需要用到
-- 编写建库建表的 sql
create database if not exists blog_system;
use blog_system;
-- 创建一个博客表
drop table if exists blog;
create table blog (
blogId int primary key auto_increment,
title varchar(1024),
content mediumtext,
userId int, -- 文章作者的 id
postTime datetime -- 发布时间
);
-- 给博客插点数据,后面方便我们进行测试
insert into blog values(null,'第一篇博客','好好学习',1,now());
insert into blog values(null,'第二篇博客','天天向上',1,now());
insert into blog values(null,'第三篇博客','明天开始学Java',1,now());
insert into blog values(null,'第四篇博客','努力去见易烊千玺',2,now());
insert into blog values(null,'第五篇博客','累死自己,卷死同学',2,now());
-- 创建一个用户表
drop table if exists user;
create table user (
userId int primary key auto_increment,
username varchar(128) unique, -- 后续会使用用户名进行登录, 一般用于登录的用户名都是不能重复的.
password varchar(128)
);
insert into user values(null,'panpan','123');
insert into user values(null,'ajiao','123456');
2. 封装数据库操作
2.1 先创建 DBUtil 封装数据库连接的操作
package util;
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 {
private static final String URL="jdbc:mysql://127.0.0.1:3306/blog_system?characterEncoding=utf8&useSSL=false";
private static final String USERNAME="root";
private static final String PASSWORD="root";
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(URL);
((MysqlDataSource)dataSource).setUser(USERNAME);
((MysqlDataSource)dataSource).setPassword(PASSWORD);
}
}
}
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();
}
}
}
}
2.2 创建实体类(使用实体类来表示数据库中的一条记录)
使用实体类来表示数据库中的一条记录,根据数据库创建的表可知我们需要创建 Blog 类和 User 类
Blog 类
package bean;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
public class Blog {
private int blogId;
private String title;
private String content;
private int userId;
private Timestamp postTime;
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 int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getPostTime(){
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return simpleDateFormat.format(postTime);
}
public void setPostTime(Timestamp postTime) {
this.postTime = postTime;
}
}
User类
package bean;
public class User {
private int userId=0;
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;
}
}
2.3 封装针对数据的增删改查
我们把提供了增删改查这样的类,称为 DAO,所以我们可以把实现这类功能的类统一放在一个dao包里面。
BlogDao类
package dao;
import bean.Blog;
import util.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class BlogDao {
public void insert(Blog blog){
Connection connection=null;
PreparedStatement statement=null;
try {
connection= DBUtil.getConnection();
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());
statement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtil.close(connection,statement,null);
}
}
public List<Blog>selectAll(){
Connection connection=null;
PreparedStatement statement=null;
ResultSet resultSet=null;
List<Blog>blogs=new ArrayList<>();
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"));
String content=resultSet.getString("content");
if(content.length()>50){
content=content.substring(0,50)+"...";
}
blog.setContent(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);
}
return blogs;
}
public Blog selectOne(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();
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();
}finally {
DBUtil.close(connection,statement,resultSet);
}
return null;
}
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);
}
}
}
UserDao类
package dao;
import bean.User;
import util.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class UserDao {
public User selectByName(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 e) {
e.printStackTrace();
}finally {
DBUtil.close(connection,statement,resultSet);
}
return null;
}
public User selectById(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;
}
}
到此数据库操作都给准备好了,下面就开始编写前后端的代码了
约定前后端交互接口
在编写前后端代码之前,先把之前纯页面的博客系统全部拷贝到webapp里面 博客系统:码云地址
1. 实现博客列表
1.1约定前后端交互接口
[请求] GET/blog
[响应] [
{
blogId: 1,
title: "第一篇博客",
content: "博客正文",
userId: 1,
postTime: "2022-06-08 18:08:05"
},
{
blogId: 2,
title: "第二篇博客",
content: "博客正文",
userId: 2,
postTime: "2022-06-08 18:08:05"
},
...
]
约定浏览器给服务器发送一个 GET /blog 这样的 HTTP 请求, 服务器给浏览器返回了一个 JSON 格式的数据
1.2 实现服务器代码
package servlet;
import bean.Blog;
import com.fasterxml.jackson.databind.ObjectMapper;
import dao.BlogDao;
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 java.io.IOException;
import java.util.List;
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
private ObjectMapper objectMapper=new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
BlogDao blogDao=new BlogDao();
List<Blog>blogs=blogDao.selectAll();
String respJson= objectMapper.writeValueAsString(blogs);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(respJson);
}
}
1.3 编写客户端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>博客列表</title>
<link rel="stylesheet" href="css/common.css">
<link rel="stylesheet" href="css/blog_list.css">
</head>
<body>
<div class="nav">
<img src="img/yyqx.jpg" alt="">
<span >我的博客系统</span>
<!-- 空白元素,用来占位置 -->
<div class="spacer"></div>
<a href="blog_list.html">主页</a>
<a href="blog_edit.html">写博客</a>
<a href="logout">注销</a>
</div>
<!-- 这里的.container 作为页面的版心 -->
<div class="container">
<!-- 左侧个人信息 -->
<div class="left">
<div class="card">
<img src="./img/jackson.jpg" alt="">
<h3>同学潘</h3>
<a href="#">gitHub地址</a>
<div class="counter">
<span>文章</span>
<span>分类</span>
</div>
<div class="counter">
<span>2</span>
<span>1</span>
</div>
</div>
</div>
<!-- 右侧内容详情 -->
<div class="right">
<!-- .blog 就对应一篇博客 -->
<!-- <div class="blog">
<div class="title">
我的第一篇博客
</div>
<div class="data">
2022-5-11 23:07:25
</div>
<div class="desc">
从今以后,我要好好学习,天天向上。Lorem ipsum dolor sit amet consectetur adipisicing elit. Labore obcaecati tenetur itaque accusantium, commodi iste at aut ipsam ducimus temporibus culpa voluptatibus dolorem, exercitationem magnam. Molestiae ducimus dolore amet inventore?Lorem ipsum dolor sit amet consectetur adipisicing elit. Ex, saepe officia. Perferendis error pariatur hic numquam et recusandae reprehenderit cupiditate fugit? Facere officiis deserunt saepe iste eius iure modi cum.
</div>
<a href="blog_detail.html">查看全文>></a>
</div>
<div class="blog">
<div class="title">
我的第二篇博客
</div>
<div class="data">
2022-5-20 17:20:26
</div>
<div class="desc">
青年兴则国家兴,青年强则国家强。Lorem ipsum dolor sit amet consectetur adipisicing elit. Labore obcaecati tenetur itaque accusantium, commodi iste at aut ipsam ducimus temporibus culpa voluptatibus dolorem, exercitationem magnam. Molestiae ducimus dolore amet inventore?Lorem ipsum dolor sit amet consectetur adipisicing elit. Ex, saepe officia. Perferendis error pariatur hic numquam et recusandae reprehenderit cupiditate fugit? Facere officiis deserunt saepe iste eius iure modi cum.
</div>
<a href="#">查看全文>></a>
</div> -->
</div>
</div>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>
function getBlogList(){
$.ajax({
type:'get',
url:'blog',
success:function(body){
let rightDiv=document.querySelector('.right');
rightDiv.innerHTML='';
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 dataDiv=document.createElement('div');
dataDiv.className='data';
dataDiv.innerHTML=blog.postTime;
blogDiv.appendChild(dataDiv);
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);
rightDiv.appendChild(blogDiv);
}
},
error:function(body){
alert('获取博客列表失败!');
}
});
}
getBlogList();
</script>
</body>
</html>
效果图如下:
图中的用户名显示的是panpan,而上面的客户端代码中用户名给的是同学潘,这是因为后面我们要实现登录的是哪个用户就显示哪个用户名,因此这里有点差异。
2. 实现博客详情
由于目前点击博客列表页的 “查看全文” , 进入博客详情页后是写死的内容,所以 我们期望能够根据当前的 博客 id 从服务器动态获取博客内容。
2.1 约定前后端交互接口
[请求]
GET /blog?blogId=1
[响应] {
HTTP/1.1 200OK
Content-Type:application/json;
{
blogId: 2,
title: "第二篇博客",
content: "博客正文",
userId: 1,
postTime: "2022-06-08 18:08:05"
},
2.2 实现服务器代码
获取详情页跟获取列表基本相同,所以我们只需要更改一下代码判断是否带参数 blogId,以此来返回是列表页还是详情页。
package servlet;
import bean.Blog;
import com.fasterxml.jackson.databind.ObjectMapper;
import dao.BlogDao;
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.util.List;
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
private ObjectMapper objectMapper=new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
BlogDao blogDao=new BlogDao();
resp.setContentType("application/json;charset=utf8");
String param=req.getParameter("blogId");
if(param==null){
List<Blog>blogs=blogDao.selectAll();
String respJson= objectMapper.writeValueAsString(blogs);
resp.getWriter().write(respJson);
}else{
int blogId=Integer.parseInt(param);
Blog blog=blogDao.selectOne(blogId);
String respJson=objectMapper.writeValueAsString(blog);
resp.getWriter().write(respJson);
}
}
}
2.3 实现客户端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>博客详情页</title>
<link rel="stylesheet" href="css/common.css">
<link rel="stylesheet" href="css/blog_detail.css">
<!-- 引入editor.md的依赖 -->
<link rel="stylesheet" href="editor.md/css/editormd.min.css" />
<script src="js/jquery.min.js"></script>
<script src="editor.md/lib/marked.min.js"></script>
<script src="editor.md/lib/prettify.min.js"></script>
<script src="editor.md/editormd.js"></script>
</head>
<body>
<div class="nav">
<img src="img/yyqx.jpg" alt="">
<span >我的博客系统</span>
<!-- 空白元素,用来占位置 -->
<span class="spacer"></span>
<a href="blog_list.html">主页</a>
<a href="blog_edit.html">写博客</a>
<a href="logout">注销</a>
</div>
<div class="container">
<!-- 左侧个人信息 -->
<div class="left">
<div class="card">
<img src="./img/jackson.jpg" alt="">
<h3>同学潘</h3>
<a href="#">gitHub地址</a>
<div class="counter">
<span>文章</span>
<span>分类</span>
</div>
<div class="counter">
<span>2</span>
<span>1</span>
</div>
</div>
</div>
<!-- 右侧内容详情 -->
<div class="right">
<!-- 使用这个div来包裹整个博客的内容详情 -->
<div class="blog-content">
<!-- 博客标题 -->
<h3></h3>
<!-- 博客的发布时间 -->
<div class="date"></div>
<!-- 博客的正文内容 -->
<div id="content" style="opacity: 80%;">
</div>
</div>
</div>
</div>
<script>
function getBlogDetail(){
$.ajax({
type:'get',
url:'blog'+location.search,
success:function(body){
let h3=document.querySelector(".blog-content>h3");
h3.innerHTML=body.title;
let dateDiv=document.querySelector(".date");
dateDiv.innerHTML=body.postTime;
editormd.markdownToHTML('content',{
markdown:body.content
});
}
});
}
getBlogDetail();
</script>
</body>
</html>
效果图如下:
注意:当我们在进行程序验证的时候,要牢记一件事情,浏览器的缓存可能会影响到结果,你可能修改了之后再去同一个浏览器访问的时候,他并没有发生改变,因为浏览器可能把你上次访问的页面给保存到本地了,下次在尝试访问的时候访问的就是本地的内容,所以这个时候我们要按下: Ctrl + F5强制刷新一下
3. 实现登录
- 登陆页面提供一个 form 表单,通过 form 的方式把用户名密码提交给服务器
- 服务器端验证用户名密码是否正确
- 如果密码正确, 则在服务器端创建 Session,并把 sessionId 通过 Cookie 返回给浏览器
3.1 约定前后端交互接口
[请求]
POST /login
Content-Type: application/x-www-form-urlencoded
username=panpan&password=123
[响应]
HTTP/1.1 302
Location: blog_list.html
3.2 实现服务器代码
package servlet;
import bean.User;
import com.fasterxml.jackson.databind.ObjectMapper;
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 {
private ObjectMapper objectMapper=new ObjectMapper();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf8");
resp.setCharacterEncoding("utf8");
String username=req.getParameter("username");
String password=req.getParameter("password");
if(username==null||"".equals(username)||password==null||"".equals(password)){
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前的用户名或密码为空!");
return;
}
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;
}
HttpSession session= req.getSession(true);
session.setAttribute("user",user);
resp.sendRedirect("blog_list.html");
}
}
3.3 实现客户端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录页面</title>
<link rel="stylesheet" href="css/common.css">
<link rel="stylesheet" href="css/blog_login.css">
</head>
<body>
<div class="nav">
<img src="img/yyqx.jpg" alt="">
<span >我的博客系统</span>
<span class="spacer"></span>
<a href="blog_list.html">主页</a>
<a href="blog_edit.html">写博客</a>
</div>
<div class="login-container">
<form action="login" method="post">
<div class="login-dialog">
<h3>登录</h3>
<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>
</div>
</form>
</div>
</body>
</html>
实现强制要求登录
当用户访问 博客列表页 和 博客详情页 时, 如果用户当前尚未登陆, 就自动跳转到登陆页面
这个可以在博客列表页/详情页加载的时候,通过 ajax 访问一下服务器,获取当前的登录状态,看看能不能获取到,如果获取到了,就说明当前确实是已经登陆了,此时就可以留在这个页面;如果没有获取到,说明是未登录的,就会跳转到登录页面让你登录。
约定前后端交互接口
[请求]
GET/login
[响应]
HTTP/1.1 200OK
Content-Type:application/json
{
userId: 1,
username: 'panpan',
}
登陆了,就直接返回当前登录的用户信息;如果未登录,则直接返回一个userId 为 0 的对象
实现服务器代码
在LoginServlet中加入以下代码即可:
@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=new User();
resp.getWriter().write(objectMapper.writeValueAsString(user));
return;
}
user.setPassword("");
resp.getWriter().write(objectMapper.writeValueAsString(user));
}
实现客户端代码
在blog_list.html中加入以下代码即可:
function getUserInfo(){
$.ajax({
type:'get',
url:'login',
success:function(body){
if(body.userId&&body.userId>0){
console.log("当前用户登录成功!用户名:"+body.username);
}else{
alert("当前您尚未登录!请登录后再访问博客列表!");
location.assign('blog_login.html');
}
},
error:function(){
alert("当前您尚未登录!请登录后再访问博客列表!");
location.assign('blog_login.html');
}
});
}
getUserInfo();
博客详情页也可以有这样的功能,把这段代码添加到blog_detail.html中即可
function getUserInfo(){
$.ajax({
type:'get',
url:'login',
success:function(body){
if(body.userId&&body.userId>0){
console.log("当前用户登录成功!用户名:"+body.username);
}else{
alert("当前您尚未登录!请登录后再访问博客详情!");
location.assign('blog_login.html');
}
},
error:function(){
alert("当前您尚未登录!请登录后再访问博客列表详情!");
location.assign('blog_login.html');
}
});
}
getUserInfo();
效果图如下:
4. 实现显示用户信息
我们期望用户信息可以随着用户的登陆而发生改变,如果当前页面是博客列表页,则显示当前登陆用户的信息;如果当前页面是博客详情页,则显示该博客的作者用户信息。
4.1 约定前后端交互接口
在博客列表页,获取登录用户的用户信息:
[请求]
GET /user
[响应] {
userId: 1,
username: test
}
在博客详情页,获取当前文章作者的用户信息:
[请求]
GET /user?blogId=1
[响应] {
userId: 1,
username: test
}
4.2 实现服务器代码
主要针对博客详情页:
package servlet;
import bean.Blog;
import bean.User;
import com.fasterxml.jackson.databind.ObjectMapper;
import dao.BlogDao;
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 java.io.IOException;
@WebServlet("/authorInfo")
public class AuthorServlet extends HttpServlet {
private ObjectMapper objectMapper=new ObjectMapper();
@Override
protected 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;
}
BlogDao blogDao=new BlogDao();
Blog blog=blogDao.selectOne(Integer.parseInt(param));
if(blog==null){
resp.getWriter().write("{\"ok\":false,\"reason\":\"要查询的博客不存在!\"}");
return;
}
UserDao userDao=new UserDao();
User author= userDao.selectById(blog.getUserId());
if(author==null){
resp.getWriter().write("{\"ok\":false,\"reason\":\"要查询的用户不存在!\"}");
return;
}
author.setPassword("");
resp.getWriter().write(objectMapper.writeValueAsString(author));
}
}
4.3 实现客户端代码
对上述blog_list.html和blog_detail.html代码进行修改,实现登录成功过后显示相应的用户名
blog_list.html
function getUserInfo(){
$.ajax({
type:'get',
url:'login',
success:function(body){
if(body.userId&&body.userId>0){
console.log("当前用户登录成功!用户名:"+body.username);
ChangeUserName(body.username);
}else{
alert("当前您尚未登录!请登录后再访问博客列表!");
location.assign('blog_login.html');
}
},
error:function(){
alert("当前您尚未登录!请登录后再访问博客列表!");
location.assign('blog_login.html');
}
});
}
getUserInfo();
function ChangeUserName(username){
let h3=document.querySelector('.card>h3');
h3.innerHTML=username;
}
blog_detail.html
function ChangeUserName(username){
let h3=document.querySelector('.card>h3');
h3.innerHTML=username;
}
function getAuthorInfo(user){
$.ajax({
type:'get',
url:'authorInfo'+location.search,
success:function(body){
if(body.username){
ChangeUserName(body.username);
}else{
console.log("获取作者信息失败!"+body.reason);
}
}
});
}
效果图如下: 总结:
- 对于博客列表页,要显示登录用户信息,由于登录用户信息在检测用户是否登录的接口中,就已经拿到了,所以只需要把拿到的用户信息,显示到界面上即可(只需要动前端,后端不必修改)
- 对于博客详情页,要显示出文章作者的信息,需要提供一个新的 api ,让客户端传一个博客 id 过去,然后在服务器这里查询到用户信息,查到之后返回给页面
页面和服务器之间的交互,不一定只有一次,大概率是会有多次的。 我们这里写的博客列表页涉及到两次交互:
- 从服务器拿到博客列表数据
- 从服务器拿到当前的登录用户信息
博客详情页涉及到三次交互:
- 从服务器拿到博客的详细内容
- 从服务器拿到了当前的登录用户信息
- 从服务器拿到了当前文章的作者信息
我们以后可能还会遇到不止两次三次的,有些可能复杂的十几次也是正常的😅😅😅
5. 实现注销登录
就是退出登录状态,在导航栏安排一个"注销"按钮,当用户点击注销之后,就会在服务器上取消登录状态,并且跳回到登录页面,希望在点击之后,能够给服务器发送一个 HTTP 请求,从而触发注销动作(其实就是把会话中的信息给删除掉)
5.1 约定前后端接口
[请求]
GET /logout
[响应]
HTTP/1.1 302
Location: login.html
5.2 实现服务器代码
从 session 中删除掉保存的 User 对象,响应重定向到 login.html 页面
package servlet;
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 {
@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("blog_login.html");
}
}
5.3 实现客户端代码
只需要把blog_list.html,blog_detail.html,blog_edit.html中a标签中的href属性改为logout即可??
<a href="logout">注销</a>
6. 实现发布博客
在博客编辑页中,当用户输入博客标题和正文之后,点击发布,这个时候就会把博客数据提交到服务器,由服务器存储到数据库中。
6.1 约定前后端接口
[请求]
POST /blog
Content-Type: application/x-www-form-urlencoded
title=标题&content=正文...
[响应]
HTTP/1.1 302
Location: blog_list.html
6.2 实现服务器代码
在 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=new Blog();
blog.setTitle(title);
blog.setContent(content);
blog.setUserId(user.getUserId());
BlogDao blogDao=new BlogDao();
blogDao.insert(blog);
resp.sendRedirect("blog_list.html");
}
6.3 实现客户端代码
需要一个form表单,把这里的内容嵌套上:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>博客编辑页</title>
<link rel="stylesheet" href="css/common.css">
<link rel="stylesheet" href="css/blog_edit.css">
<!-- 引入editor.md的依赖 -->
<link rel="stylesheet" href="editor.md/css/editormd.min.css" />
<script src="js/jquery.min.js"></script>
<script src="editor.md/lib/marked.min.js"></script>
<script src="editor.md/lib/prettify.min.js"></script>
<script src="editor.md/editormd.js"></script>
</head>
<body>
<div class="nav">
<img src="img/yyqx.jpg" alt="">
<span >我的博客系统</span>
<!-- 空白元素,用来占位置 -->
<span class="spacer"></span>
<a href="blog_list.html">主页</a>
<a href="blog_edit.html">写博客</a>
<a href="logout">注销</a>
</div>
<!-- 包裹整个博客编辑页内容的顶级容器 -->
<div class="blog-edit-container">
<form action="blog" method="post" style="height: 100%;">
<div class="title">
<input type="text" placeholder="在此处输入标题" name="title" id="title">
<!-- <button>发布文章</button> -->
<input type="submit" value="发布文章" id="submit">
</div>
<!-- 放置md编辑器 -->
<div id="editor">
<!-- 为了进行form的提交,此处搞一个textarea多行编辑框,借助这个编辑框来实现表单的提交 -->
<!-- 可以设置editor.md,让编辑器把markdown内容也同步的保存到这个隐藏的textarea中,从而可以惊醒form提交 -->
<textarea name="content" style="display:none ;"></textarea>
</div>
</form>
</div>
<script>
var editor = editormd("editor", {
width: "100%",
height: "calc(100% - 90px)",
markdown: "# 在这里写下一篇博客",
path: "editor.md/lib/",
saveHTMLToTextarea:true
});
</script>
</body>
</html>
效果图如下:
7. 实现删除博客
删除博客,肯定不是可以随便删除的,约定只有自己能删除自己的博客,不能删除别人的博客?? (此处咱们暂时不考虑管理员)
界面上的处理: 在博客详情页这里,就去进行判定,判定看当前这个博客的作者,是否就是登录的用户。如果是,就在导航栏里显示一个“删除按钮";如果不是,就不显示删除按钮 服务器处理: 用户点击删除按钮,触发一个HTTP请求, HTTP请求就会让服务器删除指定的博客,服务器收到请求之后,就会把这个博客从数据库里给删除掉
7.1 约定前后端交互接口
[请求]
GET /blogDelete?blogId=1
[响应]
直接跳转到 博客列表页 即可
7.2 实现服务器代码
package servlet;
import bean.Blog;
import bean.User;
import dao.BlogDao;
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("/blogDelete")
public class BlogDeleteServlet 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;
}
User user=(User)session.getAttribute("user");
if(user==null){
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前尚未登录,不能删除!");
return;
}
String blogId=req.getParameter("blogId");
if(blogId==null||"".equals(blogId)){
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前blogId的参数不对!");
return;
}
BlogDao blogDao=new BlogDao();
Blog blog =blogDao.selectOne(Integer.parseInt(blogId));
if(blog==null){
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前要删除的博客不存在!");
return;
}
if(user.getUserId()!= blog.getUserId()){
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前登录的用户不是作者,没有权限删除!");
return;
}
blogDao.delete(Integer.parseInt(blogId));
resp.sendRedirect("blog_list.html");
}
}
7.3 实现客户端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>博客详情页</title>
<link rel="stylesheet" href="css/common.css">
<link rel="stylesheet" href="css/blog_detail.css">
<!-- 引入editor.md的依赖 -->
<link rel="stylesheet" href="editor.md/css/editormd.min.css" />
<script src="js/jquery.min.js"></script>
<script src="editor.md/lib/marked.min.js"></script>
<script src="editor.md/lib/prettify.min.js"></script>
<script src="editor.md/editormd.js"></script>
</head>
<body>
<div class="nav">
<img src="img/yyqx.jpg" alt="">
<span >我的博客系统</span>
<!-- 空白元素,用来占位置 -->
<span class="spacer"></span>
<a href="blog_list.html">主页</a>
<a href="blog_edit.html">写博客</a>
<a href="logout">注销</a>
</div>
<div class="container">
<!-- 左侧个人信息 -->
<div class="left">
<div class="card">
<img src="./img/jackson.jpg" alt="">
<h3></h3>
<a href="#">gitHub地址</a>
<div class="counter">
<span>文章</span>
<span>分类</span>
</div>
<div class="counter">
<span>2</span>
<span>1</span>
</div>
</div>
</div>
<!-- 右侧内容详情 -->
<div class="right">
<!-- 使用这个div来包裹整个博客的内容详情 -->
<div class="blog-content">
<!-- 博客标题 -->
<h3></h3>
<!-- 博客的发布时间 -->
<div class="date"></div>
<!-- 博客的正文内容 -->
<div id="content" style="opacity: 80%;">
</div>
</div>
</div>
</div>
<script>
function getBlogDetail(){
$.ajax({
type:'get',
url:'blog'+location.search,
success:function(body){
let h3=document.querySelector(".blog-content>h3");
h3.innerHTML=body.title;
let dateDiv=document.querySelector(".date");
dateDiv.innerHTML=body.postTime;
editormd.markdownToHTML('content',{
markdown:body.content
});
}
});
}
getBlogDetail();
function getUserInfo(){
$.ajax({
type:'get',
url:'login',
success:function(body){
if(body.userId&&body.userId>0){
console.log("当前用户登录成功!用户名:"+body.username);
getAuthorInfo(body);
}else{
alert("当前您尚未登录!请登录后再访问博客详情!");
location.assign('blog_login.html');
}
},
error:function(){
alert("当前您尚未登录!请登录后再访问博客列表详情!");
location.assign('blog_login.html');
}
});
}
getUserInfo();
function ChangeUserName(username){
let h3=document.querySelector('.card>h3');
h3.innerHTML=username;
}
function getAuthorInfo(user){
$.ajax({
type:'get',
url:'authorInfo'+location.search,
success:function(body){
if(body.username){
ChangeUserName(body.username);
if(body.username==user.username){
let navDiv=document.querySelector('.nav');
let a=document.createElement('a');
a.innerHTML='删除博客';
a.href='blogDelete'+location.search;
navDiv.appendChild(a);
}
}else{
console.log("获取作者信息失败!"+body.reason);
}
}
});
}
</script>
</body>
</html>
效果图如下:
目前实现的功能只有这么多,具体其他功能以后随缘实现???
|