博客系统
一。博客系统的基本情况:
1.四个页面:
(1)博客列表页,显示列表页上有哪些博客
(2)博客详情页,点击某个博客可以观看对应的详情页
(3)博客编辑页,用户可以在这里进行博客的编写
(4)登陆页
2。博客系统要实现的功能
(1)实现博客列表:让页面从服务器上拿到博客数据(数据库)
(2)实现博客详情页:点击想看的博客,让其从服务器上拿到完整的数据
(3)实现登陆的页面
(4)实现强制执行登陆的页面:当用户在列表页,详情页,编辑页的时候,发现用户没有登陆,那么就进行强制执行登陆,跳转到登陆页
(5)实现显示用户信息,从服务器上获取;博客列表页上要有用户的信息;博客详情页上要有作者的详细信息
(6)实现退出登陆
(7)实现发布博客
二。准备工作:
(1)创建项目,引入依赖,创建目录结构
依赖(servlet,mysql,Jackson)
目录结构:在main路径下创建一个webapp文件夹,在webapp这个文件夹中创建WEB-INF文件夹,在WEB-INF文件夹中创建web.xml,一般将前端的代码都放在webapp这个文件夹之中
(2)数据库的设计
Step1:找到实体:博客(blog表),用户(user表)
Step2:确定实体之间的关系:一个博客只能属于一个用户,一个用户可以创建多个博客;因此就要在博客表中引入一个userId这样的属性
blig表(blogid,title,content,postTime,userid)
user表(userid, username,password)
在main路径下创建一个db.sql的文件,这个文件中存放的是对数据库的操作(创库,创表,增数据...)
sql
create database if not exits java666_blog_system charset=utf8;
use java666_blog_system;
drop table if exits blog;
drop table if exits user;
create table blog(blogid int primary key auto-increment,title varchar(1024),content varchar(4096),postTime datatime, userid int);
create table user(userid int primary key auto-increment,username varchar(50) unique,password varchar(50));
//插入一些数据,方便后面进行
insert into blog values(1,"第一篇博客","今天",now(),1);
insert into blog values(2,"第二篇博客","昨天",now(),1);
insert into blog values(3,"第三篇博客","前天",now(),1);
insert into user values(1,"zhangsan",'123');
insert into user values(2,'lisi','123');
(3)对数据库操作的代码进行封装
首先要在main文件夹中创建一个model文件夹,在model中创建一个文件叫做DBUtil.java,在这个文件中创建一个类(DBUtil类),通过这个类封装数据库建立连接的操作
由于接下来的很多servlet代码都要用到数据库,因此就要有单独的地方吧datasource这里的操作进行封装,而不是只放到某个servlet的init之中,此处就可以使用单例模式来表示,单例模式线程是不安全的,当前servlet本身就是在多线程环境下完成的,Tomcat收到多个请求的时候,就会用多线程的方式执行不同的servlet代码
java
package model;
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 volatile static 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:3301/java109_blog_system");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("2222");
}
}
}
return dataSource;
}
public static Connection getConnection() throws SQLException {
return getDataSource().getConnection();
}
public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet){
try {
resultSet.close();
statement.close();
resultSet.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
DBUtil完成了数据的创建和关闭的过程
下一步:创建实体类
每个表都要用一个专门的类来表示,表里的一条数据就会对应到这个类的一个对象,这样就把数据库中的数据和代码连接起来
在model文件夹中创建Blog.java文件
java
public class Blog{
private int blogid;
private String title;
private String content;
private TimeStamp postTime;
private int userid;
//下面的getter和setter让idea自动生成一下
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 Timestamp getPostTime() {
return postTime;
}
public String getPostTime() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return format.format(postTime);
}
public void setPostTime(Timestamp postTime) {
this.postTime = postTime;
}
}
同样在model文件夹中创建User.java文件
java
public class User{
private int userid;
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;
}
}
下面就要创建两个类(BlogDao,UserDao),来完成针对博客表和用户表的增删查改操作
Dao:Data Access Object 数据访问对象,通过这个类的对象,来完成针对数据表的操作
通过BlogDao完成对Blog表的操作,在model文件夹中创建BlogDao.java文件
在这个BlogDao类中,要实现四种功能:(1)新增数据(提交博客时会用到)(2)查询博客列表(博客列表页),把数据库中所有的博客都拿到 (3)根据博客id查询指定的博客 (4)根据博客id删除博客
(1)新增数据:
java
public void insert(Blog blog){
Connection connection=DBUtil.getConnection();//建立连接
PreparedStatement statement=null;
try{
String sql="insert into blog values(null,?,?,now(),?)";//构建sql
statement=connection.preparedStatement(sql);
statement.setString(1,blog.getTitle());
statement.setString(2,blog.getContent());
statement.setString(3,blog.getUserid());
statement.executeUpdate();//执行sql
}catch(SQLException){
throw new RuntimeException();
}finally{
DBUtil.close(connection,statement,null);
}
}
(2)查询博客列表:
java
public List<Blog> SelectAll(){
List<Blog> blogList=new ArrayList<>();
Connection connection=null;
PreparedStatement statement=null;
ResultSet resultSet=null;
//上面这么写是为了便于最后进行释放资源
try{
connection=DBUtil.getConnection();
String sql="select * from blog";
statement=connection.preparedStatement(sql);
resultSet=statement.executeQuery();
while(resultSet.next()){
Blog blog=new Blog();
blog.setBlogId(resultSet.getInt("blogid"));
blog.setTitle(resultSet.getString("title"));
//由于此处正文是整个文章的内容,太多了,因此在列表页中,只希望出现一小部分(摘要),对此content只需要进行一个小小的截断,这个截断长度是可以自定的
String content=resultSet.getString("content");
if(content.length()>100){
content=content.subString(0,100)+"......";
}
blog.setContent(content);
blog.setPostTime(result.setTimestamp("postTime"));
blog.setUserid(result.setInt("userid"));
blogList.add(blog);
}
}catch(SQLException e){
throw new RuntimeException();
}finally{
DBUtil.close(connection,statement,resultSet);
}
return blogList;
}
(3).根据博客id查询指定的博客
java
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.preparedStatemnt(sql);
statement.setInt(1,blogid);
resultSet=statement.executeQuery();
//由于这里的resultSet非0即1,所以不用使用while(此处拿着blogid进行查询,blogid是主键,是唯一的)
if(resultSet.next()){
Blog blog=new Blog();
blog.setBlogId(resultSet.getInt("blogid"));
blog.setTitle(resultSet.getString("title"));
blog.setContent(resultSet.getString("content"));
blog.setPostTime(resultSet.getTimestamp("postTime"));
blog.setUserid(resultSet.getInt("userid"));
return blog;
}catch(SQLException e){
throw new RuntimeException();
}finally{
DBUtil.close(connection,statement,resultSet);
}
return null;
}
}
java
public void delete(int blogid){
Connection connection=null;
PreparedStatement statement=null;
try{
connection=DBUtil.getConnection();
String sql="delete from blog where blogid = ?";
statement=connection.preparedStatement(sql);
statement.setInt(1,blogid);
statement.executeUpdate();
}catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection, statement, null);
}
}
通过UserDao完成User表的操作:
UserDao要实现的操作:(1)根据userid查询到对应的用户信息(获取用户信息) (2)根据username来查询到对应的用户信息(登陆)
(1)根据userid查询对应的用户信息
java
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.PreparedStaement(sql);
statement.setInt(1,userid);
resultSet=statement.executeQuery();
if(resultSet.next()){
User user=new User();
user.setUserid(resultSet.getInt("userid"));
user.setUserName(resultSet.getString("useName"));
user.setUserPassword(resultSet.getString("password"));
return user;
}catch(SQLException e){
throw new RuntimeException();
}finally{
DBUtil.close(connection,statement,resultSet);
}
return null;
}
}
(2)根据username来查询到对应的用户信息(登陆)
java
public User getUserByUserName(String username){
Connection connection=null;
PreparedStatement Statement=null;
ResultSet resultset=null;
try{
connection=DBUtil.getConnection();
String sql="select * from user where username = ?";
statement=connection.preparedStatement(sql);
statement.setUserName(1,username);
resultSet=statement.executeQuery();
if(resultSet.next()){
User user=new User();
user.setUserid(resultSet.getInt("userid"));
user.setUserName(resultSet.getString("userName"));
user.setUserPassword(resultSet.getString("userPassword"));
return user;
}catch(SQLException e){
throw new RuntimeException();
}finally{
DBUtil.close(connection,statement,resultSet);
}
return null;
}
}
以上就是所有的准备工作,下面就要进行前后端交互的代码,从而实现四个页面和7个功能
三。前后端交互:
1.获取博客列表页
在博客列表加载的时候,发送ajax给服务器发送请求,从服务器(数据库)中拿到博客列表数据,并显示到页面上
(1)约定前后端交互接口:
请求:GET /blog
响应:HTTP/1.1 200 ok
Content---Type:application/json
Body:
[
{
blogid=1,
title="这是标题",
content="这时文章",
postTime="......",
userid=1
}
]
(2)编写请求代码(前端)
让浏览器给服务器发送请求,在前端中新加入script标签(注:前端代码已经把部分写好了,没有写与前后端交互的部分,其他部分见博客最后)
html
<script>
function getblogs(){//把ajax封装到函数之中,js中定义函数,使用function关键字,不需要返回值类型
$.ajax({
type:'get',
url:'blog',
success:function(body){
//.....当服务器成功响应之后,调用的回调函数,根据返回的响应数据,构造页面片段
}
})
grtblogs();//定义完函数一定要在最后面调用一下
}
</script>
(3)让服务器处理请求,返回响应数据(查询数据库)
java
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
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.getWritter().write(respjson);
}
}
(4)让前端代码处理上述响应数据
构造html片段,显示到页面上,这里构造的过程还是之前的api
querySelector:获取页面已有的元素
createElement:创建新的元素
.innerHtml:设置元素里的内容
.className:设置元素的class属性
appendChild:把这个元素加到另一个元素的末尾
由这个例子就可以大概知道下面要怎么构造这个响应
html
<!-- 右侧信息 -->
<div class="container-right">
<!-- 这个 div 表示一个 博客 -->
<!-- <div class="blog">
<div class="title">我的第一篇博客博客博客博客</div>
<div class="date">2023-05-11 20:00:00</div>
<div class="desc">
从今天起, 我要认真敲代码.
</div>
<a href="blog_detail.html?blogId=1">查看全文 >> </a>
</div> -->
</div>
javascript
success:function(body){
let containerDiv=document.querySelector('container-right');//这句话后面有解释
for(int i=0;i<body.length;i++){
let blog=body[i];//blog就是形如{blogid='1',title='xxx',......}
let blogDiv=document.createElement('div');//构造整个博客
blogdiv.className='blog';
let titleDiv=document.createElement('div')//构建标题
titleDiv.className='title';
titleDiv.innerHTML=blog.title;
let dateDiv=document.createElement('div')
dateDiv.className='date';
dateDiv.innerHTML=blog.postTime;
let descDiv=document.createElement('div');
descDiv.className='desc';
descDiv.innerHTML=blog.content;
let a=document.createElement('a');
a.innerHTML="查看全文";
a.href="blog_detil.html?blogid="+blog.blogid;//点击不同的博客跳转到不同的博客详情页
//上面的操作完成了每个零件的创建,还要往往一起进行组装
blogDiv.appendChild(TitleDiv);
blogDiv.appendChild(dateDiv);
blogDiv.appendChild(descDiv);
blogDiv.appendChild(a);
//最后要在循环外边获取container-right,所以加上let containerDiv=document.querySelector('container-right');
containerDiv.appendChild(blogDiv);
}
}
通过运行就可以发现存在两个问题:
(1)页面显示的postTime是一个时间戳,我们无法看懂,所以要把这里的上次登陆时间转换成人们能看懂的形式
解决方法:要在Blog类中找到PostTime的getter方法,由于标准库提供了一个SimpleDateFormat类,完成时间戳到格式化的转换(这个不用背,用的时候查一下就行)
java
public String getPostTime() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return format.format(postTime);
}
(2)顺序问题:
正常来说,咱们希望新的博客在上面,老的博客在下面
解决办法,在BlogDao类中找到getBlogs方法,然后改sql语句就行
java
String sql="select * from blog order by postTime desc";
2.博客详情页:
点不同的博客跳转过去都会带有不同的blogid的queryString,后续在博客详情页中,就可以给服务器发ajax请求,根据这里的blogid查询数据库,博客的具体内容再返回,前端还是把得到的数据构造到页面上
点击查看全文就会跳转到这个网址:127.0.0.1:8080/blog_system/blog_detail.html?blogid=4
(1)约定前后端接口:
请求:GET/blog?blogid=1
响应:HTTP/1.1 200 ok
Content_Type: application/json
{
blogid:1,
title:"这时一篇博客",
content:"这时博客正文",
postTime:"20231-11-07 10:20:00",
userid:1
}
(2)让前端代码通过ajax发起请求
在前端代码中找到blog_detail.html文件,在这个文件中添加script标签
html
<script>
function getBlog(){
$.ajax({
type:'get',
url:'blog'+location.search
success:function(body){
//当服务器成功传回响应
}
})
}
get getBlog();
</script>
此处有个问题,发起ajax请求时要带有blogid,blogid当前处于博客详情页的url之中,这里通过loaction.search来获取到url中的queryString
通过观察,我们可以发现博客列表页和博客详情页可以通过是否有queryString进行区分,博客列表页没有queryString,博客详情页有queryString,因此可以把这两个需求写在一个servlet之中,通过是否包含queryString进行判断进行哪个逻辑代码
(3)后端接收处理请求,并给出响应
java
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//从queryString中查询一下是否有内容,如果有就查指定博客,如果没有就查询所以博客
BlogDao blogDao=new BlogDao();
String blogid=req.getParameter("blogid");
if(blogid==null){
List<Blog> blogs=blogDao.selectAll();
//以json的形式返回给客户端
String respJson=objectMapper.writeValueAsString(blogs);
resp.setContentType("application/json;charset=utf8");
resp.getWritter.write(respJson);
}else{
Blog blog=blogDao.selectOne(Integer.parseInt(blogId));
//以json的形式返回给客户端
String respJson=objectMapper.writeValueAsString(blog);
resp.setContentType("application/json;charset=utf8");
resp.getWritter.write(respJson);
}
}
}
上述代码就将博客列表页和博客详情页进行了结合,让他们放在一个servlet的doget请求之中,通过是否包含queryString进行判断进行哪个代码逻辑
(4)前端拿到响应之后,把响应数据构造成html片段
写之前,blog_detail.html文件中给了页面的html代码
html
<!-- 右侧信息 -->
<div class="container-right">
<h3></h3>
<div class="date"></div>
<div id="content">
</div>
</div>
下面就要在上面图片中已经写好的html代码中加入要填入的东西,但是不能直接填,要让代码变得灵活,实现代码的复用
querySelector是获取页面已有元素
java
success function(blog){//因为后端返回的就是blog,所以这里的body可以写成blog
let h3=document.querySelector('.container-right h3');//不是div标签,前面要加点,后面h3不用加点
h3.innerHTML=blog.Title;
let dateDiv=document.querySelector('container-right .date')//是div标签,前面不要加点,后面date要加点
dadteDiv.innerHTML=blog.PostTime;
let contentDiv=document.querySelector('.container-right .content');
contentDiv.innerHTML=blog.content;
}
此时经过运行就会出现问题:
(1)刷新发现内容没变,和上一次没改的内容一样,这是因为浏览器缓存的原因,只需要按ctrl+f5进行强制刷新即可
(2)显示效果是markdown的原始情况,但是我们希望得到的是mackdown渲染之后的情景
Step1:确保引入了jquery依赖(在前端)
html
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
Step2:引入editor.md依赖(在前端)
html
<link rel="stylesheet" href="editor.md/css/editormd.min.css" />
<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>
Step3:进行渲染
editormd.markdownToHTML('content',{markdown:blog.content});
这里的editormd是库里给的一个全局变量,把依赖正确饮用,这个变量就可以使用
小括号里的第一个是一个html标签,可以有很多属性,class属性往往是用来和css样式配合使用的,id属性则是一个身份标识,要求一个页面上,id必须唯一
小括号第二个是要渲染的对象,这里是要对content正文进行渲染,所以对象就是content
3.实现登陆:
在登陆页面,在输入框中填写用户名和密码,点击登陆,就会给服务器发起HTTP请求(可以使用ajax,也可使用form)
服务器处理登陆请求,读取用户名密码,在数据库中查询,匹配如果成功创建会话,跳转到博客列表页
(1)约定前后端交互接口
请求:POST/login
content-Type:application/www-form-urlencoded
username=zhangsan %ipassword=lisi
响应:HTTP/1.1 302
Location: blog_list.html
(2)让前端发送请求(login.html)
html
<!-- 登录页的版心 -->
<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>
(3)让服务器处理请求并返回响应,新建一个loginServlet文件
java
@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");
// 1. 从请求中, 获取到用户名和密码.
String username=req.getParameter("username");
String password=req.getParameter("password");
//2.读取数据库,看看这里的用户名和密码是否和数据库中的匹配
UserDao userDao=new UserDao();
User user=userDao.selectUserByName(username);
if(user==null){
String html="<h3>用户名或密码错误! </h3>";
resp.setContentType("test/html;charset=utf8");
resp.getWriter().write(html);
return;
}
if(!password.equals(user.getPassword())){
String html = "<h3>用户名或密码错误! </h3>";
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write(html);
return;
}
//用户名和密码都正确,此时要创建一个对话
HttpSession session=req.getSession(true);
//此时要把用户对象放入一个回话之中,下次访问其他对象就可以直接进入
session.setAttribute("user",user);
//4.返回一个重定向响应,能跳转到博客列表页
resp.sendRedirect("blog_list.html");
}
}
4.强制要求登陆:
在博客列表页,详情页,编辑页,判定当前用户是否登陆,如果没有登陆,则强制跳转到登陆页
在这几个页面中,在页面加载是,给服务器发起ajax请求,从服务器上获取登陆状态
(1)约定前后端交互接口
GET/login
登陆成功HTTP/1.1 200
登陆失败HTTP/1.1 403
也有一种方法来区分成功和失败,都返回200,但是两者的body不同
(2)前端部分
在blog_list.html文件中的script标签中加上一下代码
html
function getLoginSatus(){
$.ajax({
type:'get',
url:'login',
success:function(body){
console.log("已经登陆了");
}
error:function(body){
location.assign('login.html');//前端页面跳转的写法
}
})
}
getLoginStatus();
一个页面通常会触发多个ajax,这些ajax之间是并发执行的,彼此之间互不干预
这里不使用后端的跳转是因为只有判断为没登陆的时候才能触发error,就是因为error了,后端无法发送响应,才需要前端去进行跳转
(3)后端部分处理请求
找到后端代码中的loginServlet.java文件,加入doget方法
java
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session=req.getSession(false);//如果有session就直接使用,如果没有session就为空,不创建
if(session==null){
resp.setStatus(403);
return;
}
User user=(User)session.getAttribute("user");
if(user==null){
resp.setStatus(403);
return;
}
resp.setStatus(200);
}
上述这些代码是用在博客列表页中的,同样的道理,只需把前端代码复制粘贴进详情页和编辑页中的前端script标签之中即可
5.显示用户信息
博客列表页:显示当前登陆的用户信息
博客详情页:显示当前文章作者的信息
在页面加载的时候给服务器发ajax请求,服务器返回对应的用户数据,根据发起请求不同的页面,服务器返回不同的信息即可
(1)约定前后端接口
博客列表页:获取当前登陆的用户信息
请求:GET/userInfo
响应:HTTP/1.1 200 ok application/json
{
userid:1
username:'zhangsan'
}
博客详情页:获取当前博客的作者信息
请求:GET/authorInfo?blogid=1
响应:HTTP/1.1 200 ok application/json
{
userid:1
username:'zhangsan'
}
(2)博客列表页的前后端代码
在blog_list.html文件的script标签中加入一下代码
javascript
function getUserInfo(){
$.ajax({
type:'get',
url:'userInfo',
success:function(body){
//.....
}
})
}
后端代码:
java
@WebServlet("/userInfo")
public class UserInfoServlet etends HttpServlet{
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;
}
resp.setContentType("application/json;charset=utf8");//把user对象转换成json,并返回给浏览器
user.setPassword("");//这么写的目的是user中有password,如果把password传给客户很不安全,所以设置成空
string respJson=objectMapper.writeValueAsString(user);
resp.getWriter.write(respJson);
}
}
前端代码处理响应:
把响应的数据放到对应页面位置上
javascript
success:function(body){
let h3=document.querySelector('.card h3');//把拿到的响应数据,取出其中usename,设置到页面的h3标签之中
h3.innerHTML=user.username;
}
(3)博客详情页的前后端代码
在blog_detail.html文件中写入一下内容
javascript
function getAuthorInfo(){
$.ajax({
type:'get',
url:'getAuthorInfo'+location.search
success:function(user){
//.....
}
})
}
后端代码:
创建AuthorInfoServlet文件
java
@WebServlet("/getAuthorInfo")
public class AuthorInfoServlet etends HttpServlet{
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
String blogid=req.getParameter('blogid');
if(blogid==null){
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write("请求中缺少blogid");
return;
}
BlogDao blogDao=new BlogDao();//在blog表中查询对应的blog对象
Blog blog=blogDao.getBlog(Integer.parseInt(blogid));
if(blog==null){
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write("blogid没找到");
return;
}
UserDao userDao=new UserDao();
User user=userDao.getUserByid(blog.getuserid());
if(user==null){
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write("userid没找到");
return;
}
//把这个对象返回给浏览器
user.setPassword("");
String respJson=objectMappe.writeValueAsString(user);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(respJson);
}
原理就是先从请求中获取到blogid,然后用blogid找到对应的blog对象,然后拿到blog对象中的userid,根据这个userid获取到user表中的对象,最后作为响应传给前端
前端处理响应:
javascript
success:function(user){
let h3=document.querySelector('.card h3');
h3.innerHTML=user.username;
}
注意:在博客列表页的前端代码中,使用了appendChild,在详情页以及之后的功能就没有使用过,主要是因为要看处理响应的代码中是否要创建html标签,如果不创建html标签,那么就用上面这个方式,把东西传到标签之中,如果要创建html标签,那么就要使用appendChild的方式进行写前端代码
6.退出登陆:
博客列表页,博客详情页,博客编辑页导航栏中都有一个"注销"的按钮
让用户点击"注销"的时候,就会发送一个HTTP请求(GET请求),服务器收到这个GET请求的时候,就会把user这个Attribute删除,由于判断用户是否登陆的代码的逻辑中,同时要判断session是否存在,和user Attribute是否存在,所以只要破坏一个,那么就会让登陆状态发生改变
那为什么不直接删除session本身呢?
主要是因为servlet没提供删除session的方法,session提供了removeAttribute这样的方法,就可以把user这个Attribute给删除了
(1)约定前后端交互接口
请求:GET/logout
响应:直接重定向到登陆页 HTTP/1.1 302 Location:login.html
(2)编写前端,发送请求:
不用写ajax,直接给a标签设置href属性即可,博客列表页,博客详情页,博客编辑页都要加上这个标签
html
<a href="logout">注销</a>
(3)后端代码处理请求,给出响应
创建logout文件
java
@WebServlet("/logout")
public class LogoutServlet etends HttpServlet{
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("当前尚未登陆");
}
session.removeAttribute("user");//把会话中的user属性删除
resp.sendRedirect("login.html");
}
四。原初的前端代码:
(1)blog_detail.html
html
<!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">
</head>
<body>
<!-- 导航栏. nav 是 导航 这个词的缩写 -->
<div class="nav">
<!-- logo -->
<img src="image/logo2.jpg" alt="">
<div class="title">我的博客系统</div>
<!-- 只是一个空白, 用来把后面的链接挤过去 -->
<!-- 这是一个简单粗暴的写法~~ -->
<div class="spacer"></div>
<a href="blog_list.html">主页</a>
<a href="blog_edit.html">写博客</a>
<!-- 这里的地址回头再说 -->
<a href="">注销</a>
</div>
<!-- 页面的主体部分 -->
<div class="container">
<!-- 左侧信息 -->
<div class="container-left">
<!-- 这个 div 表示整个用户信息的区域 -->
<div class="card">
<!-- 用户的头像 -->
<img src="image/kun.jpg" alt="">
<!-- 用户名 -->
<h3>比特汤老湿</h3>
<!-- github 地址 -->
<a href="https://www.github.com">github 地址</a>
<!-- 统计信息 -->
<div class="counter">
<span>文章</span>
<span>分类</span>
</div>
<div class="counter">
<span>2</span>
<span>1</span>
</div>
</div>
</div>
<!-- 右侧信息 -->
<div class="container-right">
<h3>这是我的第一篇博客</h3>
<div class="date">2023-05-11 20:00:00</div>
<div class="content">
<p>从今天开始我要认真写代码, Lorem ipsum dolor sit amet consectetur adipisicing elit. Ducimus quas soluta ipsam, molestiae totam accusamus minus beatae nam quo suscipit doloribus corporis eius alias ab minima impedit ullam? Optio, magni.</p>
<p>从今天开始我要认真写代码, Lorem ipsum dolor sit amet consectetur adipisicing elit. Ducimus quas soluta ipsam, molestiae totam accusamus minus beatae nam quo suscipit doloribus corporis eius alias ab minima impedit ullam? Optio, magni.</p>
<p>从今天开始我要认真写代码, Lorem ipsum dolor sit amet consectetur adipisicing elit. Ducimus quas soluta ipsam, molestiae totam accusamus minus beatae nam quo suscipit doloribus corporis eius alias ab minima impedit ullam? Optio, magni.</p>
<p>从今天开始我要认真写代码, Lorem ipsum dolor sit amet consectetur adipisicing elit. Ducimus quas soluta ipsam, molestiae totam accusamus minus beatae nam quo suscipit doloribus corporis eius alias ab minima impedit ullam? Optio, magni.</p>
</div>
</div>
</div>
</body>
</html>
(2)blog_edit.html
html
<!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">
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<!-- 引入 editor.md 的依赖 -->
<link rel="stylesheet" href="editor.md/css/editormd.min.css" />
<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>
<!-- 导航栏. nav 是 导航 这个词的缩写 -->
<div class="nav">
<!-- logo -->
<img src="image/logo2.jpg" alt="">
<div class="title">我的博客系统</div>
<!-- 只是一个空白, 用来把后面的链接挤过去 -->
<!-- 这是一个简单粗暴的写法~~ -->
<div class="spacer"></div>
<a href="blog_list.html">主页</a>
<a href="blog_edit.html">写博客</a>
<!-- 这里的地址回头再说 -->
<a href="">注销</a>
</div>
<!-- 博客编辑页的版心 -->
<div class="blog-edit-container">
<form action="">
<!-- 标题编辑区 -->
<div class="title">
<input type="text" id="title-input">
<input type="submit" id="submit">
</div>
<!-- 博客编辑器 -->
<!-- 把 md 编辑器放到这个 div 中 -->
<div id="editor">
</div>
</form>
</div>
<script>
var editor = editormd("editor", {
// 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉.
width: "100%",
// 设定编辑器高度
height: "calc(100% - 50px)",
// 编辑器中的初始内容
markdown: "# 在这里写下一篇博客",
// 指定 editor.md 依赖的插件路径
path: "editor.md/lib/"
});
</script>
</body>
</html>
(3)blog_list.html
html
<!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>
<!-- 导航栏. nav 是 导航 这个词的缩写 -->
<div class="nav">
<!-- logo -->
<img src="image/logo2.jpg" alt="">
<div class="title">我的博客系统</div>
<!-- 只是一个空白, 用来把后面的链接挤过去 -->
<!-- 这是一个简单粗暴的写法~~ -->
<div class="spacer"></div>
<a href="blog_list.html">主页</a>
<a href="blog_edit.html">写博客</a>
<!-- 这里的地址回头再说 -->
<a href="">注销</a>
</div>
<!-- 页面的主体部分 -->
<div class="container">
<!-- 左侧信息 -->
<div class="container-left">
<!-- 这个 div 表示整个用户信息的区域 -->
<div class="card">
<!-- 用户的头像 -->
<img src="image/kun.jpg" alt="">
<!-- 用户名 -->
<h3>比特汤老湿</h3>
<!-- github 地址 -->
<a href="https://www.github.com">github 地址</a>
<!-- 统计信息 -->
<div class="counter">
<span>文章</span>
<span>分类</span>
</div>
<div class="counter">
<span>2</span>
<span>1</span>
</div>
</div>
</div>
<!-- 右侧信息 -->
<div class="container-right">
<!-- 这个 div 表示一个 博客 -->
<div class="blog">
<!-- 博客标题 -->
<div class="title">我的第一篇博客博客博客博客</div>
<!-- 博客的发布时间 -->
<div class="date">2023-05-11 20:00:00</div>
<!-- 博客的摘要-->
<div class="desc">
<!-- 使用 lorem 生成一段随机的字符串 -->
从今天起, 我要认真敲代码. Lorem ipsum dolor sit amet consectetur, adipisicing elit. Debitis repellendus voluptatum, reiciendis rem consectetur incidunt aspernatur eveniet excepturi magni quis sint, provident est at et pariatur dolorem aliquid fugit voluptatem.
</div>
<!-- html 中不能直接写 大于号, 大于号可能会被当成标签的一部分 -->
<a href="blog_detail.html?blogId=1">查看全文 >> </a>
</div>
<div class="blog">
<!-- 博客标题 -->
<div class="title">我的第一篇博客</div>
<!-- 博客的发布时间 -->
<div class="date">2023-05-11 20:00:00</div>
<!-- 博客的摘要-->
<div class="desc">
<!-- 使用 lorem 生成一段随机的字符串 -->
从今天起, 我要认真敲代码. Lorem ipsum dolor sit amet consectetur, adipisicing elit. Debitis repellendus voluptatum, reiciendis rem consectetur incidunt aspernatur eveniet excepturi magni quis sint, provident est at et pariatur dolorem aliquid fugit voluptatem.
</div>
<!-- html 中不能直接写 大于号, 大于号可能会被当成标签的一部分 -->
<a href="blog_detail.html?blogId=1">查看全文 >> </a>
</div>
<div class="blog">
<!-- 博客标题 -->
<div class="title">我的第一篇博客</div>
<!-- 博客的发布时间 -->
<div class="date">2023-05-11 20:00:00</div>
<!-- 博客的摘要-->
<div class="desc">
<!-- 使用 lorem 生成一段随机的字符串 -->
从今天起, 我要认真敲代码. Lorem ipsum dolor sit amet consectetur, adipisicing elit. Debitis repellendus voluptatum, reiciendis rem consectetur incidunt aspernatur eveniet excepturi magni quis sint, provident est at et pariatur dolorem aliquid fugit voluptatem.
</div>
<!-- html 中不能直接写 大于号, 大于号可能会被当成标签的一部分 -->
<a href="blog_detail.html?blogId=1">查看全文 >> </a>
</div>
</div>
</div>
</body>
</html>
(4)login.html
html
<!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/login.css">
</head>
<body>
<!-- 导航栏. nav 是 导航 这个词的缩写 -->
<div class="nav">
<!-- logo -->
<img src="image/logo2.jpg" alt="">
<div class="title">我的博客系统</div>
<!-- 只是一个空白, 用来把后面的链接挤过去 -->
<!-- 这是一个简单粗暴的写法~~ -->
<div class="spacer"></div>
<a href="blog_list.html">主页</a>
<a href="blog_edit.html">写博客</a>
</div>
<!-- 登录页的版心 -->
<div class="login-container">
<!-- 登录对话框 -->
<div class="login-dialog">
<h3>登录</h3>
<!-- 使用 form 包裹一下下列内容, 便于后续给服务器提交数据 -->
<form action="">
<div class="row">
<span>用户名</span>
<input type="text" id="username">
</div>
<div class="row">
<span>密码</span>
<input type="password" id="password">
</div>
<div class="row">
<input type="submit" id="submit" value="登录">
</div>
</form>
</div>
</div>
</body>
</html>