小小项目-博客系统 - 服务器版本 - javaEE初阶 - 细节狂魔 ...

打印 上一主题 下一主题

主题 523|帖子 523|积分 1569

文章目录



前言

   没有看过这篇博客系统的页面设计,,建议先去看这篇文章。
因为你接下来的内容,我不会在去讲页面是怎么去设计的,默认你是懂得其中的逻辑。
而这篇文章是在博客系统的页面设计的成果上,进行 改进 / 升级 的结果。

另外,这篇Servlet - JavaEE初阶博文中,实战案例“表白墙” 需要看一看!!!

     因为这个 表白墙的实现,就是基于 前后端分离的方式来开发的。
前后端分离:
让后端只是给前端返回数据,数据往往就是以 json 格式来组织的。
后端这边彻底不必关心页面的结构和样式了。
只要说开始的时候,前端和后端各自约定好前后端交互接口,然后再分别开发就行了。
前后端分离带来的好处:
除了能够各自独立的进行开发,还能各自独立的进行测试。
前端写完了代码,可以自己创建一个 假的服务器(mock server),来“吐出”一些构造好的假数据,来验证前端写的内容的正确性。
后端写完代码了,也可以自己构造一些 HTTP 请求,来验证服务器“吐出”的数据对不对。
这也就是postman做的事情。

    我们再来稍微回顾一下前面讲的表白墙的实现步骤:
     1、把前端页面给做出来
2、约定前后端交互接口
3、实现服务器代码来处理请求,返回 json 格式的数据。
4、实现客户端代码来构造发送请求,处理响应。
其实这短短4个步骤,就涉及到了前后端分离的手段。
其实我们仔细想一下:我们最开始写前端代码的时候,完全没有依赖服务器是吧。
比如:写表白墙页面的时候,就完全没有考虑到后端的问题。
看过我前面写的博客的读友们,我来帮你们回忆一下,是那篇博客。
前端三剑客之 JavaScript的最后一个案例。
而在我讲解 servlet的时候,我给它加入了 服务器元素。使其可以通过 IP地址来访问。(仅限=与主机所在的局域网内部设备)
当然要想做到全网都能访问,我们还需要买一个云服务器来获取外网 IP,这样才可以做到全网都能访问我们制作的页面。

如果看过上篇博文的朋友,就知道:我们在完成服务器代码之后,并没有关注前端是怎么去写的,直接使用postman取构造一个HTTP请求,来观察我们服务器代码返回数据,是否达到我们的预期。
也就是说我们写后端代码的时候,并不关注前端代码是什么样子的。

最后我们把服务器和客户端的代码都写完之后,再放在一起进行调试,看看有什么问题存在。

这种 前后端分离 的开发方式,在日常开发中是属于主流的开发方式。

   

博客系统

    在前面已经完成了博客系统的设计。
博客:前端 - 博客系统(页面设计)

     而且我们在设计博客系统页面的时候,也没有关心服务器的问题。
直接就把页面显示的内容给写死了。
此时,再把它领出来,就是为了给它加入服务器。
使其成为一个 动态的页面,显示的内容随着用户的输入(操作)而改变。
另一个原因是 前端知识 和 后端服务器知识,我们都有了。
我们就具有实现一个简单网站的能力了。
当然,就需要来实践了。

   

准备工作

   还是那熟悉配方:七个步骤
  1、创建项目

   打开你们的idea,跟着我开始搞事情!

  

2、引入依赖

   既然涉及到服务器,那么 servlet 肯定是要引入的,另外,有涉及到前后端交互,那么Jackson 也是必要的。
因为数据的组织方式需要使用到 JSON 形式。
最后文章是需要存储的,那么 数据库MySQL 是必不可少的!
中央仓库地址:https://mvnrepository.com/

  

3、创建必要目录

  1. <!DOCTYPE web-app PUBLIC
  2.    
  3.     "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  4.    
  5.     "http://java.sun.com/dtd/web-app_2_3.dtd" >
  6. <web-app>
  7.     <display-name>
  8.    
  9.     Archetype Created Web Application
  10.     </display-name>
  11. </web-app>
复制代码



4、编写代码

   先随便写一个代码

  

5 和 6、打包和部署:配置Smart Tomcat




7、验证程序




下面,开始正式编写服务器代码了

MVC 中的 V(view)【页面的显示】

   因为 MVC 中的 V 已经完成了。
     没完成的,看看这博客博客系统(页面设计)先把 页面设计出来。
    下面我们就把前面写的博客页面给拷贝到当前项目的 webapp 路径下面

记住!千万不要放错位置了!!!
一定要是 webapp 目录底下!!!!

     另外,这些文件都可以理解成静态的资源。
后续打包部署的时候,这些静态资源也会被一并“打包部署”,后续也就可以在浏览器中访问到了。

   

MVC 中的 M(Model)【操作数据存取的逻辑】

   也就是实现数据库相关的代码。
     此处的代码都是我之前讲过的MySQL最后一讲:JDBC 编程
虽然代码写起来没有难度,但是比较繁琐。
先使用这种最朴素的写法,找找感觉。
后面有机会的话,我会给大家带来更简单的方法。
   

1、创建数据库/表结构 【数据库设计】

   设计数据库,需要根据当前的需求来进行设计。
     首先,我们来回忆一下:我们的博客系统中有几个页面
1、博客列表页:显示博客的列表
2、博客详情页:点击博客列表页上面 列出的博客条目,跳转到的页面,这个页面显示博客的完整内容。
3、博客登录页
4、博客编辑页:基于 markdown的插件 editor.md 文件夹,搞了一个markdown编译器。
然后我们就可以基于这个页面来发布博客了。

下面,我们就可以基于上述的页面,提出一些需求!
1、存储博客:
当我们点击发布按钮的时候,博客被发布到 服务器上,就要被存储起来。【MySQL的作用就在于此】

2、获取博客:在博客列表页 和 详情页中,能够拿到博客的内容。

3、还能进行登录校验:用户名,密码等是否在服务器存储数据中存在。

    现在我们需要基于上面的需求来思考:需要设计那些表呢?
     其实在设计表的时候,我们需要抓住需求中“实体”。
“实体”:可参考这篇文章对数据表进行“增删查改”的进阶操作
实体:关键性的名词

    在main目录底下,创建一个sql文件,用于编写 建库建表的 SQL。
     MySQL中的数据类型,可参考这篇文章MySQL第二讲 - 数据表简单操作 与 “增删查改的开头部分- 增”

先来创建 博客表

下面我们再来创建 用户表

    为了后面测试方便,我们来给用户表插入几条数据。
     
    下面我们来打开MySQL来看一下效果
     
    mysql代码就在这里,不想写就在这里拷贝

  1. -- 双减号是注释符
  2. -- 通过这文件来编写建库建表的 sql
  3. -- 如果 java_blog 数据库不存在,则创建把它创建出来;反之不创建。
  4. create database if not exists java_blog;
  5. -- 选中数据库
  6. use java_blog;
  7. -- 此时我们已经进入 数据库java_blog内部了
  8. -- 下面我们来创一个博客表
  9. -- 在创建之前,先判断一下数据库中是否已经存在 blog 数据表
  10. -- 如果存在,删除它
  11. drop table if exists blog;
  12. create table blog(
  13.     -- 将博客序号设置为一个自增主键
  14.   blogId int primary key auto_increment,
  15.   -- 博客标题
  16.   title varchar(1024),
  17.   -- 博客正文:注意博客内容是非常长的,因此varchar是不够的,故使用 mediumtext.
  18.   -- mediumtext 可以表示几个G 的数据。大概16兆
  19.   content mediumtext,
  20.   -- 博客作者ID
  21.   userId int,
  22.   -- 文章的发布时间
  23.   postTime datetime
  24. );
  25. -- 用户表
  26. drop table  if exists user ;
  27. create table user(
  28.     userId int primary key auto_increment,
  29.     -- 去重
  30.     username varchar(128) unique,
  31.     password varchar(128)
  32. );
  33. -- 因为 用户id 为主键,直接置为null,默认从1开始分配编号
  34. -- userId=1
  35. insert into user values(null,"zhangsan","123");
  36. -- userId=2
  37. insert into user values(null,"lisi","456");
复制代码


2、 封装数据库操作

2.1、先创建DButil 类 来封装 数据库连接操作

   就是上一篇博客Servlet - JavaEE初阶中实现 服务器版本的表白墙中创建的DButil类,用来简化我们代码量。

这里代码与上篇博文中 DButi类l 中的代码,是一样的!
这里就不再赘述了。
关于多线程,你可以参考多线程基础篇

  1. import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
  2. import javax.sql.DataSource;
  3. import java.sql.Connection;
  4. import java.sql.PreparedStatement;
  5. import java.sql.ResultSet;
  6. import java.sql.SQLException;
  7. public class DButil {
  8.     private static final String URL = "jdbc:mysql://127.0.0.1:3306/java_blog?characterEncoding=utf8&useSSL=false";
  9.     private static final String User = "root";
  10.     private static final String Password = "123456";
  11.     // 加上 volatile 防止编译器对其进行优化。
  12.     private static volatile DataSource dataSource = null;// 数据源对象
  13.     // 创建数据源,描述服务器的所在地址,用户名,以及用户密码
  14.     private static  DataSource getDataSource(){
  15.     // 提升效率
  16.    
  17.     if(dataSource == null){
  18.    
  19.     // 线程安全
  20.    
  21.         synchronized (DButil.class){
  22.    
  23.         
  24.     if(dataSource == null){
  25.    
  26.         
  27.         dataSource = new MysqlDataSource();
  28.    
  29.         
  30.         ((MysqlDataSource)dataSource).setURL(URL);
  31.    
  32.         
  33.         ((MysqlDataSource)dataSource).setUser(User);
  34.    
  35.         
  36.         ((MysqlDataSource)dataSource).setPassword(Password);
  37.    
  38.         
  39.     }
  40.    
  41.         }
  42.    
  43.     }
  44.    
  45.     return dataSource;
  46.     }
  47.     // 获取练剑
  48.     public static Connection getConnection() throws SQLException {
  49.    
  50.     return getDataSource().getConnection();
  51.     }
  52.     //资源回收
  53.     public static void close(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet){
  54.    
  55.     if (resultSet != null){
  56.    
  57.         try {
  58.    
  59.         
  60.     resultSet.close();
  61.    
  62.         } catch (SQLException e) {
  63.    
  64.         
  65.     e.printStackTrace();
  66.    
  67.         }
  68.    
  69.     }
  70.    
  71.     if (preparedStatement != null){
  72.    
  73.         try {
  74.    
  75.         
  76.     preparedStatement.close();
  77.    
  78.         } catch (SQLException e) {
  79.    
  80.         
  81.     e.printStackTrace();
  82.    
  83.         }
  84.    
  85.     }
  86.    
  87.     if (connection != null){
  88.    
  89.         try {
  90.    
  91.         
  92.     connection.close();
  93.    
  94.         } catch (SQLException e) {
  95.    
  96.         
  97.     e.printStackTrace();
  98.    
  99.         }
  100.    
  101.     }
  102.     }
  103. }
复制代码


2.2、创建实体类

   进一步来说:就是使用实体类来表示数据库中的一条记录
     
   

2.3、封装针对数据的增删改查

   此时,我们要真正开始写 JDBC 代码了!
     我们把提供了增删改查这样的类,称为 DAO - 数据访问对象(Data Access Object)
下面我们就来创建两个这样的类 来提供 对应内容的增删查改的功能。

   

BlogDao:这个类用于封装博客表的基本操作(增删查改)




BlogDao 总程序

  1. import java.sql.Connection;
  2. import java.sql.PreparedStatement;
  3. import java.sql.ResultSet;
  4. import java.sql.SQLException;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. // 这个类用于封装博客表的基本操作(增删查改)
  8. public class BlogDao {
  9. //    1、往博客表里,插入一条记录(一篇博客)
  10.     public static void insert(Blog blog){
  11.    
  12.     Connection connection = null;
  13.    
  14.     PreparedStatement preparedStatement = null;
  15.    
  16.     // JDBC 基本的代码
  17.    
  18.     try {
  19.    
  20.         //1、和数据库建立连接
  21.    
  22.         connection = DButil.getConnection();
  23.    
  24.         //2、构造sql语句
  25.    
  26.         // 这里的 now() 表示取得是当前系统的时间
  27.    
  28.         String sql = "insert into blog values(null,?,?,?,now())";
  29.    
  30.         preparedStatement = connection.prepareStatement(sql);
  31.    
  32.         preparedStatement.setString(1,blog.getTitle());
  33.    
  34.         preparedStatement.setString(2, blog.getContent());
  35.    
  36.         preparedStatement.setInt(3,blog.getUserId());
  37.    
  38.         //3、执行SQL语句
  39.    
  40.         preparedStatement.executeUpdate();
  41.    
  42.     } catch (SQLException e) {
  43.    
  44.         e.printStackTrace();
  45.    
  46.     }finally {
  47.    
  48.         //4、关闭连接,回收资源
  49.    
  50.         DButil.close(connection,preparedStatement,null);
  51.    
  52.     }
  53.     }
  54. //    2、能够获取到博客表中的所有博客的信息【用于博客列表页显示博客有多少】
  55. //    另外,我们的博客列表,会从每篇博文中截取一段内容,因此截取的内容不一定是截取到完整的博客内容。
  56. //    因为截取到完整的博客的内容,占用的空间会很大!两三行还行,多了就不利于显示更多的博文信息
  57. public static List<Blog> selectAll(){
  58.     List<Blog> blogs = new ArrayList<>();
  59.     Connection connection = null;
  60.     PreparedStatement preparedStatement = null;
  61.     ResultSet resultSet = null;
  62.     // JDBC 基本的代码
  63.     try {
  64.    
  65.     //1、和数据库建立连接
  66.    
  67.     connection = DButil.getConnection();
  68.    
  69.     //2、构造sql语句
  70.    
  71.     String sql = "select * from blog";
  72.    
  73.     preparedStatement = connection.prepareStatement(sql);
  74.    
  75.     //3、执行SQL语句
  76.    
  77.     resultSet = preparedStatement.executeQuery();
  78.    
  79.     // 4、 遍历结果集
  80.    
  81.     while(resultSet.next()){
  82.    
  83.         Blog blog = new Blog();
  84.    
  85.         blog.setBlogId(resultSet.getInt("blogId"));
  86.    
  87.         blog.setTitle(resultSet.getString("title"));
  88.    
  89.         blog.setContent(resultSet.getString("content"));
  90.    
  91.         blog.setUserId(resultSet.getInt("userId"));
  92.    
  93.         blog.setTimestamp(resultSet.getTimestamp("postTime"));
  94.    
  95.         blogs.add(blog);
  96.    
  97.     }
  98.     } catch (SQLException e) {
  99.    
  100.     e.printStackTrace();
  101.     }finally {
  102.    
  103.     //5、关闭连接,回收资源
  104.    
  105.     DButil.close(connection,preparedStatement,resultSet);
  106.     }
  107.     return blogs;
  108. }
  109. //    3、能够根据 博客id 读取到指定的博客内容【用于博客详情页显示一篇博客的详情内容】
  110.     public static Blog selectOne(int blogId){
  111.    
  112.     Connection connection = null;
  113.    
  114.     PreparedStatement preparedStatement = null;
  115.    
  116.     ResultSet resultSet = null;
  117.    
  118.     // JDBC 基本的代码
  119.    
  120.     try {
  121.    
  122.         //1、和数据库建立连接
  123.    
  124.         connection = DButil.getConnection();
  125.    
  126.         //2、构造sql语句
  127.    
  128.         String sql = "select * from blog where blogId = ?";
  129.    
  130.         preparedStatement = connection.prepareStatement(sql);
  131.    
  132.         preparedStatement.setInt(1,blogId);
  133.    
  134.         //3、执行SQL语句
  135.    
  136.         resultSet = preparedStatement.executeQuery();
  137.    
  138.         // 4、 遍历结果集:此处我们是使用主键 作为查询条件
  139.    
  140.         // 因此查询的结果要么是一条,要么是零条。
  141.    
  142.         if(resultSet.next()){
  143.    
  144.         
  145.     Blog blog = new Blog();
  146.    
  147.         
  148.     blog.setBlogId(resultSet.getInt("blogId"));
  149.    
  150.         
  151.     blog.setTitle(resultSet.getString("title"));
  152.    
  153.         
  154.     blog.setContent(resultSet.getString("content"));
  155.    
  156.         
  157.     blog.setUserId(resultSet.getInt("userId"));
  158.    
  159.         
  160.     blog.setTimestamp(resultSet.getTimestamp("postTime"));
  161.    
  162.         
  163.     return blog;
  164.    
  165.         }
  166.    
  167.     } catch (SQLException e) {
  168.    
  169.         e.printStackTrace();
  170.    
  171.     }finally {
  172.    
  173.         //5、关闭连接,回收资源
  174.    
  175.         DButil.close(connection,preparedStatement,resultSet);
  176.    
  177.     }
  178.    
  179.     return null;
  180.     }
  181. //    4、从博客列表中,根据博客Id 来删除博客
  182.     public static void delete(int blogId){
  183.    
  184.     Connection connection = null;
  185.    
  186.     PreparedStatement preparedStatement = null;
  187.    
  188.     // JDBC 基本的代码
  189.    
  190.     try {
  191.    
  192.         //1、和数据库建立连接
  193.    
  194.         connection = DButil.getConnection();
  195.    
  196.         //2、构造sql语句
  197.    
  198.         String sql = "delete from blog where blogId = ?";
  199.    
  200.         preparedStatement = connection.prepareStatement(sql);
  201.    
  202.         preparedStatement.setInt(1,blogId);
  203.    
  204.         //3、执行SQL语句
  205.    
  206.          preparedStatement.executeUpdate();
  207.    
  208.         // 4、 遍历结果集:此处我们是使用主键 作为查询条件
  209.    
  210.         // 因此查询的结果要么是一条,要么是零条。
  211.    
  212.     } catch (SQLException e) {
  213.    
  214.         e.printStackTrace();
  215.    
  216.     }finally {
  217.    
  218.         //5、关闭连接,回收资源
  219.    
  220.         DButil.close(connection,preparedStatement,null);
  221.    
  222.     }
  223.     }
  224. //    注意上述的4个操作只涉及到增删查,不涉及到 改。
  225. //    改,这个操作可以被实现,但是不在这里实现。
  226. //    换句话来说:实现该这个操作 和 前面的增删查,并没有什么区别。
  227. //    所以在这里只是显示几个经典的功能,如果你们想要实现 改,加油!
  228. //    我偷个懒。
  229. }
复制代码


UserDao:这个类用于封装用户表的基本操作(增删查改)




UserDao 总程序

  1. import java.sql.Connection;
  2. import java.sql.PreparedStatement;
  3. import java.sql.ResultSet;
  4. import java.sql.SQLException;
  5. // 这个类用于封装用户表的基本操作(增删查改)
  6. public class UserDao {
  7.     //针对这个类来说,我们就简化的写就行了
  8.     // 注册/注销 的功能,我们就不实现了
  9.     // 主要实现:
  10.     // 1、根据 用户名 来查询用户信息
  11.     //    这个操作会在登录逻辑中使用。
  12.     public static User selectByName(String username){
  13.    
  14.     Connection connection = null;
  15.    
  16.     PreparedStatement statement = null;
  17.    
  18.     ResultSet resultSet = null;
  19.    
  20.     try {
  21.    
  22.         connection = DButil.getConnection();
  23.    
  24.         String sql = "select * from user where username=? ";
  25.    
  26.         statement = connection.prepareStatement(sql);
  27.    
  28.         statement.setString(1,username);
  29.    
  30.         resultSet = statement.executeQuery();
  31.    
  32.         if (resultSet.next()){
  33.    
  34.         
  35.     User user = new User();
  36.    
  37.         
  38.     user.setUserId(resultSet.getInt("userId"));
  39.    
  40.         
  41.     user.setUsername(resultSet.getString("username"));
  42.    
  43.         
  44.     user.setPassword(resultSet.getString("password"));
  45.    
  46.         
  47.     return user;
  48.    
  49.         }
  50.    
  51.     } catch (SQLException e) {
  52.    
  53.         e.printStackTrace();
  54.    
  55.     } finally {
  56.    
  57.         DButil.close(connection,statement,resultSet);
  58.    
  59.     }
  60.    
  61.     return null;
  62.     }
  63.     // 2、根据 用户id 来查询用户信息
  64.     //   博客详情页,就可以根据用户id 来查询作者的名字,将其显示出来
  65.     public static User selectById(int userId){
  66.    
  67.     Connection connection = null;
  68.    
  69.     PreparedStatement statement = null;
  70.    
  71.     ResultSet resultSet = null;
  72.    
  73.     try {
  74.    
  75.         connection = DButil.getConnection();
  76.    
  77.         String sql = "select * from user where userId=? ";
  78.    
  79.         statement = connection.prepareStatement(sql);
  80.    
  81.         statement.setInt(1,userId);
  82.    
  83.         resultSet = statement.executeQuery();
  84.    
  85.         if (resultSet.next()){
  86.    
  87.         
  88.     User user = new User();
  89.    
  90.         
  91.     user.setUserId(resultSet.getInt("userId"));
  92.    
  93.         
  94.     user.setUsername(resultSet.getString("username"));
  95.    
  96.         
  97.     user.setPassword(resultSet.getString("password"));
  98.    
  99.         
  100.     return user;
  101.    
  102.         }
  103.    
  104.     } catch (SQLException e) {
  105.    
  106.         e.printStackTrace();
  107.    
  108.     } finally {
  109.    
  110.         DButil.close(connection,statement,resultSet);
  111.    
  112.     }
  113.    
  114.     return null;
  115.     }
  116. }
复制代码

小结

   经过上述的逻辑,终于把数据库操作都给准备好了。
接下来,就可以实现服务器中的后续代码了

     换句话来说:
Model 就搞定了,下面要去写 controller 了。

   

MVC 中的 C (Controller)【处理请求之后的关键逻辑】

在完成 controller 代码之前,我们需要 前后端的交互接口。

   究竟前端给后端发送什么样的请求,后端给前端返回什么样的响应。
必须把这件事确定好了,我们才可以进行后续的操作。

     需要注意的是:我们在约定前后端接口的时候,会涉及到好几个页面。
我们一个一个页面来写:
针对这里的4个页面,分别去约定前后端交互接口,编写服务器代码,编写客户端代码。

   

先从 博客列表页 / 主页 入手

   这个页面需要能够展示出数据库中的博客列表
     我们先看一下博客列表页长什么样子,不知道看过博客页面设计博文的读友们是否还有印象?
   

针对获取博客列表这件事情,我们就可以约定前后端的交互接口了。

   
  约定好了这件事之后,我们就可以按照这样的约定来编写服务器代码。
  

博客列表页 - 服务器代码 && 客户端之间的代码

服务器代码部分


   下面我们再来看下执行效果。
通过postman来测试服务器代码,返回的数据是否正确。

但是还没完!还有一个小细节。

     那就是发布时间的表现形式。

   

服务器总代码

  1. package controller;
  2. import com.fasterxml.jackson.databind.ObjectMapper;
  3. import model.Blog;
  4. import model.BlogDao;
  5. import javax.servlet.ServletException;
  6. import javax.servlet.annotation.WebServlet;
  7. import javax.servlet.http.HttpServlet;
  8. import javax.servlet.http.HttpServletRequest;
  9. import javax.servlet.http.HttpServletResponse;
  10. import java.io.IOException;
  11. import java.util.List;
  12. @WebServlet("/blog")
  13. public class blogServlet extends HttpServlet {
  14.     private ObjectMapper objectMapper = new ObjectMapper();
  15.     // 这个方法用来获取到数据库中的博客列表
  16.     @Override
  17.     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  18.    
  19.     // 从数据数据中查询到博客列表,将格式转化成json格式,然后直接返回即可
  20.    
  21.     List<Blog> blogs = BlogDao.selectAll();
  22.    
  23.     // 把 blogs 转化成json数组的形式
  24.    
  25.     String respJson = objectMapper.writeValueAsString(blogs);
  26.    
  27.     resp.setContentType("application/json; charset=utf8");
  28.    
  29.     resp.getWriter().write(respJson);
  30.     }
  31. }
复制代码


客户端代码部分

   我们需要在页面加载的时候,让页面通过 ajax 访问到数据库中的博客数据,并且填入页面中。

  

客户端总代码

   不要老想着拷贝,上面讲解代码的时候还涉及到 blogDao 和 Blog,两个类的改动。
     另外你没有实现 博客的页面设计,也是不行的。
我这里只是提供 前端发送请求,与处理服务器返回的响应 的 逻辑代码。
具体的 博客页面设计,还是需要你看 博客页面设计的文章。
而且接下来的所有代码都是这样给你们的。
不要想着偷油,你就能看得懂这里的逻辑

   
  1.    
  2.    
复制代码

效果图 - 附带解决博客显示问题

   1、博客列表中 博客显示顺序问题

    2、页面刷新时,即重新加载页面时,会涉及到清空右侧区域内容,填充新的内容,在此之间,会存在会卡顿一下的情况。
其中的原因就是: 加载原先写死的数据,并清空原先数据的时候,需要时间来执行。

  

博客博客详情页

   我们来看一下博客详情页长什么样子。

看到上面我划线标出的 查询字符串 了吗?它将起到至关重要的作用!!!

     整个链接(URL)就是一个湖区博客详情页的请求。
此处,我们希望:点击一篇博客的 查看全文的链接之后,所得到的页面,能显示出当前这个博客的正文内容。
怎么去获取对应的正文内容呢?就是我在 URL 中 划出的 blogId 键值对,来查询并获取到对应的内容。

至于方法,还是通过 ajax 来进行获取。
具体来说:
在 blog_detail 页面加载的时候,触发 ajax 请求来访问服务器,获取到博客内容。
再次填充到博客详情页里面!

   

下面我们就可以约定前后端的交互接口了。

   
  

博客博客详情页 - 服务器代码 && 客户端之间的代码

服务器部分

   约定的请求里面,不是带有 blog 嘛?

还是使用 原来的 BlogServlet 类,来实现对 博客详情页请求 的处理。

     那么问题来了:
博客详情页 和 博客列表页 的 请求,方法都是 GET.
此时的doGet方法中,已经用于实现了 博客列表页 的逻辑。
因此,我们要想把 详情页请求的处理,填入其中。
那么,代码上又如何区别 那个部分是 详情页,那个是列表页 的 代码呢?
还是那个详情页 URL 中 划出 的 那个 blogId 的键值对,它是区分两者请求的关键。

此时我们就只用了一个服务器代码完成了两个页面的逻辑。

   
服务器总代码 - blog

  1. package controller;import com.fasterxml.jackson.databind.ObjectMapper;import model.Blog;import model.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 {   
  2.     resp.setContentType("application/json; charset=utf8");   
  3.     // 先尝试获取到 req 中的 blogId 参数   
  4.     // 如果参数存在,说明接下来要处理的详情页的逻辑;反之,接下来就要处理列表页的逻辑   
  5.     String str = req.getParameter("blogId");   
  6.     if(str == null){   
  7.         // 如果 str 为 null,说明:接下来就要处理列表页的逻辑   
  8.         // 从数据数据中查询到博客列表,将格式转化成json格式,然后直接返回即可   
  9.         List blogs = BlogDao.selectAll();   
  10.         // 把 blogs 转化成json数组的形式   
  11.         String respJson = objectMapper.writeValueAsString(blogs);//   
  12.         resp.setContentType("application/json; charset=utf8");   
  13.         resp.getWriter().write(respJson);   
  14.     }else{   
  15.         // 反之,接下来就要处理列表页的逻辑   
  16.         // 将获取到的 blogId 转换成 整形   
  17.         int blogId = Integer.parseInt(str);   
  18.         // 调用我们之前书写 selectOne 方法,来查询指定的博客信息   
  19.         Blog blog = BlogDao.selectOne(blogId);   
  20.         // 将 blog对垒 转换成 json格式的字符串   
  21.         String respJson = objectMapper.writeValueAsString(blog);//   
  22.         resp.setContentType("application/json; charset=utf8");   
  23.         resp.getWriter().write(respJson);   
  24.     }    }}
复制代码
   下面我们来进行阶段性测试,看看服务器代码 是否将两者的逻辑区分开来。

  

客户端部分




客户端总代吗

   记住!渲染显示的内容,需要你们引入 markdown的第三方库。
具体配置的步骤,还是需要你看 博客页面设计的文章。

  1.    
  2.         
复制代码


效果图


   为了突出 markdown的渲染的效果,我们再来插入一组数据。

  

博客登录页

   目的:就是实现用户登录逻辑。
     能够说登录用户咋输入正确的用户名和密码之后,页面登录成功。

   

下俩我们就来约定前后端交互接口




这回我们先来写客户端,把登录页面进行调整




客户端总代码

  1.    
  2.         
  3.     博客登录页   
  4.         
  5.         
  6.         [img]https://blog.csdn.net/./image/2.jpg[/img]   
  7.     我的博客系统   
  8.         
  9.         
  10.     [url=https://blog.csdn.net/bloghome.html]主页[/url]   
  11.     [url=https://blog.csdn.net/writingblog.html]写博客[/url]   
  12.         
  13.         
  14.         
  15.         
  16.         
  17.         
  18.         
  19.     [size=4]登陆[/size]
  20.    
  21.         
  22.         
  23.         
  24.         用户名   
  25.         
  26.         
  27.         
  28.         
  29.         
  30.         
  31.         
  32.         
  33.     密码   
  34.         
  35.         
  36.         
  37.         
  38.         
  39.         
  40.         
  41.         
  42.         
  43.         
  44.         
  45.         
  46.            
复制代码


后端部分




后端总代码 - login

  1. package controller;import model.User;import model.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 {    @Override    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {   
  2.     req.setCharacterEncoding("utf8");// 设置字符集   
  3.     resp.setCharacterEncoding("utf8");   
  4.     //1、获取到请求中的参数   
  5.     String username = req.getParameter("username");   
  6.     String password = req.getParameter("password");   
  7.     //2、和数据库的内容进行比较   
  8.     if(username == null || "".equals(username) || password == null || "".equals(password)){   
  9.         resp.setContentType("text/html; charset=utf8");//告诉浏览器当前返回一个 静态页面,读取数据形式是utf8   
  10.         resp.getWriter().write("当前用户名或密码不能为空!");   
  11.     }else{   
  12.         // 用户名 和 密码 不为空的情况   
  13.         User user = UserDao.selectByName(username);   
  14.         if(user == null || !user.getPassword().equals(password)){   
  15.         
  16.     resp.setContentType("text/html; charset=utf8");   
  17.         
  18.     resp.getWriter().write("当前用户名,或者密码不正确!");   
  19.         }else{   
  20.         
  21.     //3、如果比较通过,就创建会话   
  22.         
  23.     HttpSession session = req.getSession(true);   
  24.         
  25.     // 把刚才的用户信息,存储在会话中   
  26.         
  27.     session.setAttribute("user", user);   
  28.         
  29.     //4、返回一个重定向报文,跳转到博客列表页   
  30.         
  31.     resp.sendRedirect("bloghome.html");   
  32.         }   
  33.     }    }}
复制代码


效果图


   我们再来看加上汉字的情况

  

代码整改 - 完善页面跳转逻辑

   当登录功能完成之后,就需要调整一下之前定的两个页面【博客列表 和 博客详情】
     让这两个页面,必须在登录后,才能访问!!!
此处我们就做出了这样的限制。
而且这样限制一下,后面实现其它功能会更简单!


在我们进入 博客 列表页 / 详情页 的时候,先检查一下用户的登录状态。
如果用户当前已经是登录状态,才能继续使用。
反之,用户处于位登录状态,则强制跳转到 博客登录页。

    那么如何实现上述功能?
     可以在 博客 列表 / 详情 页,加载的时候,通过 ajax 访问一下服务器,来获取当前的登录状态。
看看能不能获取到 状态:
如果获取到了,就说明当前确实已经登录了。
此时就可以留在这个页面了。

如果没有获取到,说明用户处于未登录状态。
既然是未登录,那么就不能滞留在当前页面,强制跳转到登录页面,完成登录之后,才能继续访问。
   

约定一下,前后端交互接口




下面我们来书写服务器代码




服务器总代码 - login

   在当前服务器这里拿到了 session,并且拿到了里面的 user,视为登录成功!
     如果登录成功的话,服务器会给客户端返回 sessionId。
浏览器就会保存这个sessionId。
并且在下次方式请求的时候就会带上这个 id。
服务器拿到了 这个Id,就可以去一个“哈希表”中查询,从而获取对应的信息。
这样就知道了 当前的 sessionId 对象是谁。

   
  1. package controller;import com.fasterxml.jackson.databind.ObjectMapper;import model.User;import model.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 {   
  2.     req.setCharacterEncoding("utf8");// 设置字符集   
  3.     resp.setCharacterEncoding("utf8");   
  4.     //1、获取到请求中的参数   
  5.     String username = req.getParameter("username");   
  6.     String password = req.getParameter("password");   
  7.     //2、和数据库的内容进行比较   
  8.     if(username == null || "".equals(username) || password == null || "".equals(password)){   
  9.         resp.setContentType("text/html; charset=utf8");//告诉浏览器当前返回一个 静态页面,读取数据形式是utf8   
  10.         resp.getWriter().write("当前用户名或密码不能为空!");   
  11.     }else{   
  12.         // 用户名 和 密码 不为空的情况   
  13.         User user = UserDao.selectByName(username);   
  14.         if(user == null || !user.getPassword().equals(password)){   
  15.         
  16.     resp.setContentType("text/html; charset=utf8");   
  17.         
  18.     resp.getWriter().write("当前用户名,或者密码不能正确!");   
  19.         }else{   
  20.         
  21.     //3、如果比较通过,就创建会话   
  22.         
  23.     HttpSession session = req.getSession(true);   
  24.         
  25.     // 把刚才的用户信息,存储在会话中   
  26.         
  27.     session.setAttribute("user", user);   
  28.         
  29.     //4、返回一个重定向报文,跳转到博客列表页   
  30.         
  31.     resp.sendRedirect("bloghome.html");   
  32.         }   
  33.     }    }    // 这个方法用来让前端检测当前的登录状态    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {   
  34.     resp.setContentType("application/json; charset=utf8");   
  35.     HttpSession session =  req.getSession(false);//获取当前会话   
  36.     if (session == null){   
  37.         // 检测会话是否存在:不存在则说明未登录。   
  38.         User user = new User();   
  39.         resp.getWriter().write(objectMapper.writeValueAsString(user));   
  40.         return;   
  41.     }   
  42.     // 如果会话不为空,就来查询会话中是否存在user 对象   
  43.     // 如果存在,返回的就是 一个非空的 user 对象   
  44.     // 如果不存在,返回的是一个 null   
  45.     User user = (User) session.getAttribute("user");   
  46.     // user 不存在   
  47.     if(user == null){   
  48.         user = new User();   
  49.         resp.getWriter().write(objectMapper.writeValueAsString(user));   
  50.         return;   
  51.     }   
  52.     // user存在 / 处于已登录的状态   
  53.     // 注意:此处不要把密码也给返回去了【避免密码泄露】   
  54.     user.setPassword("");   
  55.     resp.getWriter().write(objectMapper.writeValueAsString(user));    }}
复制代码


前端部分




博客列表页部分




效果图

   我们重启Tomcat,直接通过浏览器的地址栏输入URL,进行访问博客列表页

  

博客详情页

   这里和列表页是一样的。
因此即将刚才写的前端代码拷贝过去就行了。

  

博客详情页 && 博客列表页 - 前端检查登录逻辑代码

  1.     function getUserInfo(){   
  2.     $.ajax({   
  3.         type: "get",   
  4.         url: "login",   
  5.         success: function(body){   
  6.         
  7.     // 判定此处的 body 对象,是不是一个有效的 user 对象(userId 是否 非0 )   
  8.         
  9.     // 判断body.userId 是否存在,并且大于0   
  10.         
  11.     if(body.userId && body.userId>0){   
  12.         
  13.         // 进入 if 语句中,说明用户:处于登录状态   
  14.         
  15.         console.log("当前用户登录成功! 用户名:" + body.username);   
  16.         
  17.     }else{   
  18.         
  19.         // 登录失败   
  20.         
  21.         // 通过 前端重定向方法 location.assign 方法 来进行页面的跳转   
  22.         
  23.         alert("当前您尚未登录!请在登录之后,再来访问博客列表!");   
  24.         
  25.         location.assign('bloglogin.html');   
  26.         
  27.     }   
  28.         },   
  29.         error: function(){   
  30.         
  31.     alert("当前您尚未登录!请在登录之后,再来访问博客列表!");   
  32.         
  33.     location.assign('bloglogin.html');   
  34.         }   
  35.     });    }    getUserInfo();
复制代码


效果图

   还是跟前面测试一样,重启Tomcat,直接通过浏览器的地址栏输入URL,进行访问博客列表页。
     注意!
虽然前面登录过,但是会话信息是保存在服务器内存中的(HttpSession的hash表,是内存的)
因此,我才会要求你们重启Tomcat,就是为了清空会话信息。
这样你们才能更清晰的看到效果。

另外,有的朋友可能会想法:我们能不能给 会话 设置一个 “过期时间”?
就比如:
我们在登录 腾讯视频等等其它网站的时候,登录过一次。但是过一段时间就需要我们重新登录了。
答案是课可以的!
但是,服务器一旦重启,不管 你的 “过期时间” 有没有到,这个会话一样会被删除。
然后,你再次访问也还是会强制 重新登录的!



有的朋友可能会说:使用 cookie
其实 cookie,我们一直都在用。另外 cookie 也确实是存储 浏览器所在机器的硬盘上。
cookie中存储的是 sessionId,也就是 key。
但是键值对的本体,还是在服务器上的。

重启服务器,这个键值对本体,还是会被删除。

举个例子:网吧购置会员卡
cookie 就相当于是一张网卡,sessionId 就是 你的会员id(key)。
key 所对应的 value 值,就是你在这张卡上存的钱。
但是网吧倒闭了,你再拿着这张卡,去上网,或者退钱。
尽管你是知道自己的会员id的,你也是上不了网,退不了钱的!!!
网吧都没了,你上哪去上网???上哪去退钱??
人家老板都跑路了!!!
或者说:网吧换主人了。
没有那个老板,会买前任的单。
人家是不会认你这张卡的!
这都是套路!!
一波会员换个老板,懂的都懂。

如果想要持久化存储,还是需要 数据库来实现。
也就是说:你要自己敲代码来实现。

另外,我们 代码并没有把往cookie里存,而只是在内存中用了一下

JS 虽然也能操作cookie,但是这里就不讲了。
但是如果你不去操作,返回的值 也就是在内存中,随着你页面的 关闭 / 刷新,内存中的值也就没了。

也就是说:不是说随便使用 ajax,ajax点什么,都会往cookie中存储。

   

代码整改2 - 完善页面逻辑


   也就是说:
代码整改2 完善的页面逻辑:让左侧用户栏显示信息,根据实际情况来显示。
博客主页,也就博客列表页,左侧显示的是登录用户的信息。

而它的右侧博客,可能是登录用户写的,也可能不是登录用户写的。
因此,点击任意任意一篇博客,左侧显示的信息,是当前博客的作者信息。
这就是我们 整改代码2 的 最终目的。
  

约定前后端接口 && 前后端程序



1、针对博客列表页

      这里其实在前已经处理过了,在 检查登录状态的时候。

下面我们来验证一下程序的效果

有的人可能会有问题:

而且,当初设计数据库的时候,就没有考虑开辟一个专门数据表来存储这些 图片,文章数目等等。。。字段的信息。

   

2、博客详情页 - 约定 - 服务器代码部分

      这个就有些特别了!
它需要创建 一个新的 服务器接口。

    我们先来书写后端代码

  

2、博客详情页 - 服务器总代吗 - authorInfo

  1. package controller;import com.fasterxml.jackson.databind.ObjectMapper;import model.Blog;import model.BlogDao;import model.User;import model.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 AuthorInfoServlet extends HttpServlet {    private ObjectMapper objectMapper = new ObjectMapper();    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {   
  2.     resp.setContentType("application/json; charset=utf8");   
  3.     // 通过这个方法,来获取到指定的博客作者信息   
  4.     String blogId = req.getParameter("blogId");   
  5.     if(blogId == null || "".equals(blogId)){   
  6.         // 参数绝少   
  7.         resp.getWriter().write("{ "ok": false,"reason": "参数缺失!"}");   
  8.         return;   
  9.     }   
  10.     // 根据 blogId 在数据库中进行查找,找到对应的 Blog 对象   
  11.     // 然后,在进一步根据 Blog 对象 找到作者信息   
  12.     Blog blog = BlogDao.selectOne(Integer.parseInt(blogId));   
  13.     if(blog == null){   
  14.         resp.getWriter().write("{ "ok": false,"reason": "要查找的博客不存在!"}");   
  15.         return;   
  16.     }   
  17.     //根据 blog 对象,查询到用户对象   
  18.     User author = UserDao.selectById(blog.getUserId());   
  19.     if (author == null){   
  20.         resp.getWriter().write("{ "ok": false,"reason": "要查找的用户不存在!"}");   
  21.         return;   
  22.     }   
  23.     // 把 author 对象返回到浏览器这边   
  24.     author.setPassword("");   
  25.     String respJson = objectMapper.writeValueAsString(author);   
  26.     resp.getWriter().write(respJson);    }}
复制代码
  下面我们来使用 postman来测试一下服务器代码,返回的数据是否符合我们的预期
      记得重启Tomcat

   

2、博客详情页 - 前端部分




前端代码部分

  1.    
  2.         //通过这个方法,从服务器获取当前博客的作者信息,并显示在封面上   
  3.     function getAuthorInfo(){   
  4.         $.ajax({   
  5.         
  6.     type: "get",   
  7.         
  8.     url:'authorInfo'+ location.search,   
  9.         
  10.     // 此处的body,就是是一个  user 对象   
  11.         
  12.     success: function(body){   
  13.         
  14.         if(body.username){   
  15.         
  16.         
  17.     // 如果响应中的 username 存在,就把这个值设置到网页面上   
  18.         
  19.         
  20.     changeUserName(body.username);   
  21.         
  22.         }else{   
  23.         
  24.         
  25.     console.log("获取作者信息失败" + body.reason);   
  26.         
  27.         }   
  28.         
  29.     }   
  30.         });   
  31.     }   
  32.     getAuthorInfo();
复制代码


效果图




阶段性总结

   通过上述的开发,就能有一个感受
页面和服务器之间的交互,不一定只要一次,而且大概率是有很多次交互。

     博客列表页(博客主页)
这里涉及到了两次交互:
1、从服务器拿到博客列表数据
2、从服务器拿到当前用户登录的信息

博客详情页
这里涉及到了三次交互:
1、从服务器拿到博客的详细内容
2、从服务器拿到当前用户登录的信息
3、从服务器拿到当前文章的作者信息

其实想咱们当前这种简单的页面,有个两三次交互,一点都不多!
有些复杂的页面,可能和服务器有 十次 或者二十次的交互,都是很正常的!

   

实现 “注销”功能

   简单来说‘:就是退出用户的登录状态。
返回到登录页,来切换账号。
或者就是单纯的为了退出登录状态。

我们的导航栏中,不是有一个注销按钮吗?
我们期望:当用户点击 注销按钮 之后,就会在服务器上取消登录的状态,并且能够跳转回到登录页面。

当下 注销按钮 这里并没有赋予效果。

     等于就点了之后没有反应
下面代码中的 # 号相当于是一个占位符,并没有实际意义。

    我们希望在点击之后,能够给服务器发送一个 HTTP 请求,从而触发注销操作。
     这个操作具体就是把会话中信息给删除就行了。
一旦会话中的消息被删除,就会触发 检查登录状态 的逻辑,从而强制回到登录页面。
   

约定前后端交互接口




后端部分




后端总代码 - logout

   我们需要明白:什么样子的情况才算是登录?
     用户有一个 session,同时 session 有一个 user属性。
这个属性是用户登录成功的时候,会创建一个会话,并且在会话中存储当前登录用户的信息对象

也就是说: session 和 user 对象 同时具备,才能叫登录状态。

即:注销功能,只需破坏上面两者中的任意一个即可。
这里我们选择的是 破坏 第二个条件:把会话中的user属性删除。

有的人可能会问:为什么不优先删除会话?这样不是更直接,删得也更干净??
原因很简单:Servlet 没有提供 删除会话 的 API。
那么就只能删除属性了。

   
  1. package controller;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 {   
  2.     // 先找到当前用户的会话   
  3.     HttpSession session = req.getSession(false);   
  4.     if (session == null){   
  5.         // 如果 session 为 null,会话不存在,说明用户压根就没有登录过   
  6.         // 既然没有登录,也就谈不上注销   
  7.         resp.getWriter().write("当前用户尚未登录,无法注销!");   
  8.         return;   
  9.     }   
  10.     // 已登录状态   
  11.     // 然后把这个用户的会话中的信息给删除掉   
  12.     session.removeAttribute("user");   
  13.     resp.sendRedirect("bloglogin.html");// 发送一个重定向的响应    }}
复制代码


客户端部分




效果图

   这里我只展示一个页面的注销功能,其他的页面的小姑都一样
     记得重启Tomcat!!!

   

博客列表页 - 发布博客

   我们先来回顾一下博客编辑页的页面长什么样子


我们预期的效果如下:

  

约定前后端交互接口




后端总代码 - blog

    doPost 是我们处理发布博客的逻辑代码
doPost 是我们用来获取到数据库中的博客列表

  1. package controller;import com.fasterxml.jackson.databind.ObjectMapper;import model.Blog;import model.BlogDao;import model.User;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 {   
  2.     resp.setContentType("application/json; charset=utf8");   
  3.     // 先尝试获取到 req 中的 blogId 参数   
  4.     // 如果参数存在,说明接下来要处理的详情页的逻辑;反之,接下来就要处理列表页的逻辑   
  5.     String str = req.getParameter("blogId");   
  6.     if(str == null){   
  7.         // 如果 str 为 null,说明:接下来就要处理列表页的逻辑   
  8.         // 从数据数据中查询到博客列表,将格式转化成json格式,然后直接返回即可   
  9.         List blogs = BlogDao.selectAll();   
  10.         // 把 blogs 转化成json数组的形式   
  11.         String respJson = objectMapper.writeValueAsString(blogs);//   
  12.         resp.setContentType("application/json; charset=utf8");   
  13.         resp.getWriter().write(respJson);   
  14.     }else{   
  15.         // 反之,接下来就要处理列表页的逻辑   
  16.         // 将获取到的 blogId 转换成 整形   
  17.         int blogId = Integer.parseInt(str);   
  18.         // 调用我们之前书写 selectOne 方法,来查询指定的博客信息   
  19.         Blog blog = BlogDao.selectOne(blogId);   
  20.         // 将 blog对垒 转换成 json格式的字符串   
  21.         String respJson = objectMapper.writeValueAsString(blog);//   
  22.         resp.setContentType("application/json; charset=utf8");   
  23.         resp.getWriter().write(respJson);   
  24.     }    }    @Override    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {   
  25.     HttpSession session = req.getSession(false);   
  26.     if(session == null){   
  27.         // 当前用户处于未登录状态,不能发布博文!   
  28.         resp.setContentType("text/html; charset=utf8");   
  29.         resp.getWriter().write("前用户处于未登录状态,不能发布博文!");   
  30.         return;   
  31.     }   
  32.     User user = (User) session.getAttribute("user");   
  33.     if(user == null){   
  34.         // 当前用户处于未登录状态,不能发布博文!   
  35.         resp.setContentType("text/html; charset=utf8");   
  36.         resp.getWriter().write("前用户处于未登录状态,不能发布博文!");   
  37.         return;   
  38.     }   
  39.     // 只要上面两个条件都满足,才能算是处于登录状态   
  40.     // 也只有处于登录的用户,才能编写博客。   
  41.     // 如果别人写了半天,点击发布,你提示别人未登录,这不是消遣别人嘛!   
  42.     // 因此 判断用户登录状态的代码 必须置前!才能执行下面的逻辑!   
  43.     req.setCharacterEncoding("utf8");// 一定要先指定好请求按哪种编码方式来解析   
  44.     // 先从请求中,取出参数(博客的标题和正文)   
  45.     String title = req.getParameter("title");   
  46.     String content = req.getParameter("content");   
  47.     if(title == null || content == null || "".equals(title)||"".equals(content)){   
  48.         // 我们认为质押出现上述情况之一,就是违法数据   
  49.         // 直接告诉客户端,请求参数不对   
  50.         resp.setContentType("text/html; charset=utf8");   
  51.         resp.getWriter().write("发布博客失败!缺少必要的参数!");   
  52.         return;   
  53.     }   
  54.     // 如果没有出错的话,我们就构造一个 Blog 对象,把当前的数据存入数据库   
  55.     Blog blog = new Blog();   
  56.     // 此处我们蛀牙给 Blog 设置的属性,主题是 title,content,userId(作者信息)   
  57.     // postTime【now()方法】 和 blogId(自增主键) 这两个不需要手动指定,都是插入数据库的时候,自动生成的。   
  58.     blog.setTitle(title);   
  59.     blog.setContent(content);   
  60.     // 作者 id 就是当前提交这个博客的用户的身份信息!!!   
  61.     blog.setUserId(user.getUserId());   
  62.     BlogDao.insert(blog);   
  63.     // 新增博客之后,我们就需要跳转到博客列表页   
  64.     resp.sendRedirect("bloghome.html");    }}
复制代码


前部部分

   我们就需要整一个 form表单,把一些关键信息“包裹起来”。
     与登录页面所做的事情是一样的。
还没完哦!我们还需要修改 发布按钮的样式。
原先是针对 button 进行设置样式的,而现在换成input标签。
因此原先的针对button设置的样式也就不起租用了!

   

前端总代码

  1.    
  2.         
  3.     博客编辑页   
  4.         
  5.         
  6.         
  7.         
  8.         
  9.         [img]https://blog.csdn.net/./image/2.jpg[/img]   
  10.     我的博客系统   
  11.         
  12.         
  13.     [url=https://blog.csdn.net/bloghome.html]主页[/url]   
  14.     [url=https://blog.csdn.net/writingblog.html]写博客[/url]   
  15.     [url=https://blog.csdn.net/logout]注销[/url]   
  16.         
  17.         
  18.         
  19.         
  20.         
  21.         
  22.         
  23.         
  24.         
  25.         
  26.         
  27.         
  28.         
  29.         
  30.         
  31.         
  32.         
  33.         
  34.         
  35.         
  36.         
  37.         
  38.         
  39.         
  40.         
  41.         
  42.         
  43.         
  44.         
  45.         
  46.         
  47.         
  48.         
  49.         
  50.         
  51.         
  52.         
  53.    
复制代码


效果图


   下面我们来发布一个博客,看看是否达到了我们想要的效果

  

博客列表页 - 删除博客 【最后一项功能】

   删除博客,肯定不是随随便便就可以删除的。
     博客,只能删除自己写的!!!!
如果每个人都能删除别人的博客,不就乱套了嘛!

   

界面上 / 前端 的处理:

   在博客详情页这里,就去进行判定
     判定看当前这个博客的作者,是否就是登陆用户。
如果是,就在导航栏显示一个删除键、
如果不是,导航栏里就不显示 删除按钮

   

前端总代码

  1.    
  2.     // 通过 GET /login 这个接口,来获取当前的登录状态   
  3.     function getUserInfo(){   
  4.         $.ajax({   
  5.         
  6.     type: "get",   
  7.         
  8.     url: "login",   
  9.         
  10.     success: function(body){   
  11.         
  12.         // 判定此处的 body 对象,是不是一个有效的 user 对象(userId 是否 非0 )   
  13.         
  14.         // 判断body.userId 是否存在,并且大于0   
  15.         
  16.         if(body.userId && body.userId>0){   
  17.         
  18.         
  19.     // 进入 if 语句中,说明用户:处于登录状态   
  20.         
  21.         
  22.     console.log("当前用户登录成功! 用户名:" + body.userName);   
  23.         
  24.         
  25.     //在 getUserInfo 的回调函数中,来调用 getAuthorInfo 函数   
  26.         
  27.         
  28.     getAuthorInfo(body);// 将获取到用户信息,作为参数发送给  getAuthorInfo 函数   
  29.         
  30.         }else{   
  31.         
  32.         
  33.     // 登录失败   
  34.         
  35.         
  36.     // 通过 前端重定向方法 location.assign 方法 来进行页面的跳转   
  37.         
  38.         
  39.     alert("当前您尚未登录!请在登录之后,再来访问博客列表!");   
  40.         
  41.         
  42.     location.assign('bloglogin.html');   
  43.         
  44.         
  45.         
  46.         
  47.         }   
  48.         
  49.     },   
  50.         
  51.     error: function(){   
  52.         
  53.         alert("当前您尚未登录!请在登录之后,再来访问博客列表!");   
  54.         
  55.         location.assign('bloglogin.html');   
  56.         
  57.     }   
  58.         });   
  59.     }   
  60.     getUserInfo();   
  61.     //通过这个方法,从服务器获取当前博客的作者信息,并显示在封面上   
  62.     function getAuthorInfo(user){//此时的参数user 就是刚才从服务器拿到的当前用户登录的信息   
  63.         $.ajax({   
  64.         
  65.     type: "get",   
  66.         
  67.     url:'authorInfo'+ location.search,   
  68.         
  69.     // 此处的body,就是是一个  user 对象【这是文章作者的信息】   
  70.         
  71.     success: function(body){   
  72.         
  73.         if(body.username){   
  74.         
  75.         
  76.     // 如果响应中的 username 存在,就把这个值设置到网页面上   
  77.         
  78.         
  79.     changeUserName(body.username);   
  80.         
  81.         
  82.     // 然后,我们就可以开始进行判断了   
  83.         
  84.         
  85.     if(body.username == user.username){   
  86.         
  87.         
  88.         // 如果相同,说明作者 和 登录用户 就是同一个人。   
  89.         
  90.         
  91.         // 此时就可以显示出 删除按钮   
  92.         
  93.         
  94.         // 获取导航栏对象   
  95.         
  96.         
  97.         let navDIV = document.querySelector('.navigation');   
  98.         
  99.         
  100.         // 创建一个新的 a 标签   
  101.         
  102.         
  103.         let a =  document.createElement('a');   
  104.         
  105.         
  106.         a.innerHTML= "删除";   
  107.         
  108.         
  109.         //期望点击删除按钮,构造一个形如 blogDelete?blogId=6 这样的请求   
  110.         
  111.         
  112.         a.href = "blogDelete"+ location.search;   
  113.         
  114.         
  115.         navDIV.appendChild(a);// 江删除按钮接入导航栏中   
  116.         
  117.         
  118.     }   
  119.         
  120.         }else{   
  121.         
  122.         
  123.     console.log("获取作者信息失败" + body.reason);   
  124.         
  125.         }   
  126.         
  127.     }   
  128.         });   
  129.     }   
  130.     // getAuthorInfo();   
  131.     function changeUserName(username){   
  132.         let h3 = document.querySelector('.card>h3');   
  133.         h3.innerHTML = username;   
  134.     }
复制代码
  细心的人就会发现:当前这个代码,就是使用了套娃操作。
     一个ajax中调用另外一个ajax。
当前我们只是两个ajax,感觉还好。即使是在回调里嵌套调用 ajax,也勉强能够接受。
但如果我们要进行的操作有 十个八个的。
而且一直都是前一个 ajax 回调 下一个 ajax 的时候,代码写起来就会非常之丑陋!
这种情况,在JS中被称为 “回调地狱”

那么,我们该怎么避免这个问题呢?
在 JS 的 ES6 版本中 引入了 Promise,就能解决这个问题。

Promise:能有效的解决 “回调地狱” 的问题。
它其实也是能够让 这种异步执行的代码,去按照异步的规则来执行。
但是它不再是通过回调的方式来执行科,而是通过一些 “语法糖” 把我们已有的回调函数进行封装。
以至于代码写起来很简便,代码看起来很优雅。

但是后来 人们又觉得 Promise 还是差点意思。
在 ES7 版本中,又引入 async 和 await,能够更加优雅的解决上述问题了。
这些 Promise,async 和 await等等。。。都是 语法糖。
通过这些语法,能够简化代码,让代码看起来没有那么复杂,那么丑陋。
执行的效果任然是一样的。
慌个说法:通过调用一些简洁的方法,来完成复杂执行效果。
既降低了代码的复杂度,又达到了预期的效果。

      下面我们测试一下:博客作者 和 登录用户 是同一个人的情况下。
导航栏中是否存在 删除按钮。

  

服务器处理:

   用户点击删除按钮,触发一个 HTTP 请求,HTTP请求就会让服务器删除指定的博客。
      服务器收到这个请求之后,就会把这个博客从数据库中删除
约定一下接口:在讲前端的时候,已经制定了 Servlet Path 和 queryString。

   
  1. package controller;import model.Blog;import model.BlogDao;import model.User;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 {   
  2.     // 1、检查当前用户是否登录   
  3.     HttpSession session = req.getSession(false);   
  4.     if (session == null){   
  5.         resp.setContentType("text/html; charset=utf8");   
  6.         resp.getWriter().write("当前您处于未登录状态,无法进行删除操作!");   
  7.         return;   
  8.     }   
  9.     User user = (User) session.getAttribute("user");   
  10.     if(user == null){   
  11.         resp.setContentType("text/html; charset=utf8");   
  12.         resp.getWriter().write("当前您处于未登录状态,无法进行删除操作!");   
  13.         return;   
  14.     }   
  15.     // 2、获取到参数中的 blogId   
  16.     String blogId = req.getParameter("blogId");   
  17.     if (blogId == null || "".equals(blogId)){   
  18.         resp.setContentType("text/html; charset=utf8");   
  19.         resp.getWriter().write("参数残缺,无法进行删除操作!");   
  20.         return;   
  21.     }   
  22.     // 3、获取需要删除的博客信息   
  23.     Blog blog = BlogDao.selectOne(Integer.parseInt(blogId));   
  24.     if (blog == null){   
  25.         resp.setContentType("text/html; charset=utf8");   
  26.         resp.getWriter().write("您要删除的博客不存在,无法进行删除操作!");   
  27.         return;   
  28.     }   
  29.     // 4、再次校验:当前的用户是否是博客的作者   
  30.     if(user.getUserId() != blog.getUserId()){   
  31.         // 虽然在前端中处理过这个问题,但是再稳一波,twin check 更保险!   
  32.         resp.setContentType("text/html; charset=utf8");   
  33.         resp.getWriter().write("非法删除操作!");   
  34.         return;   
  35.     }   
  36.     // 5、删除指定博客   
  37.     BlogDao.delete(Integer.parseInt(blogId));   
  38.     // 6、返回一个重定向响应   
  39.     resp.sendRedirect("bloghome.html");    }}
复制代码


效果图




整个项目各部部分的代码



model :数据存储相关代码

数据库访问代码:DButil

  1. 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 static final String URL = "jdbc:mysql://127.0.0.1:3306/java_blog?characterEncoding=utf8&useSSL=false";    private static final String User = "root";    private static final String Password = "123456";    // 加上 volatile 防止编译器对其进行优化。    private static volatile DataSource dataSource = null;    // 创建数据源,描述服务器的所在地址,用户名,以及用户密码    private static  DataSource getDataSource(){   
  2.     if(dataSource == null){   
  3.         synchronized (DButil.class){   
  4.         
  5.     if(dataSource == null){   
  6.         
  7.         dataSource = new MysqlDataSource();   
  8.         
  9.         ((MysqlDataSource)dataSource).setURL(URL);   
  10.         
  11.         ((MysqlDataSource)dataSource).setUser(User);   
  12.         
  13.         ((MysqlDataSource)dataSource).setPassword(Password);   
  14.         
  15.     }   
  16.         }   
  17.     }   
  18.     return dataSource;    }    // 获取练剑    public static Connection getConnection() throws SQLException {   
  19.     return getDataSource().getConnection();    }    //资源回收    public static void close(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet){   
  20.     if (resultSet != null){   
  21.         try {   
  22.         
  23.     resultSet.close();   
  24.         } catch (SQLException e) {   
  25.         
  26.     e.printStackTrace();   
  27.         }   
  28.     }   
  29.     if (preparedStatement != null){   
  30.         try {   
  31.         
  32.     preparedStatement.close();   
  33.         } catch (SQLException e) {   
  34.         
  35.     e.printStackTrace();   
  36.         }   
  37.     }   
  38.     if (connection != null){   
  39.         try {   
  40.         
  41.     connection.close();   
  42.         } catch (SQLException e) {   
  43.         
  44.     e.printStackTrace();   
  45.         }   
  46.     }    }}
复制代码


实体类

User

  1. package model;// 每个User 对象,对应 model.User 数据表 里的 一条记录public class User {    private int userId = 0;    private String username = "";    private String password = "";    public int getUserId() {   
  2.     return userId;    }    public void setUserId(int userId) {   
  3.     this.userId = userId;    }    public String getUsername() {   
  4.     return username;    }    public void setUsername(String username) {   
  5.     this.username = username;    }    public String getPassword() {   
  6.     return password;    }    public void setPassword(String password) {   
  7.     this.password = password;    }}
复制代码


Blog

  1. package model;import java.sql.Timestamp;import java.text.SimpleDateFormat;// 每个 blog 对象,对应 blog数据表 里的 一条记录public class Blog {    private int blogId;    private String title;    private String content;    private int userId;    private Timestamp postTime;// 下面我们就来给他们设置 getter 和 setter 方法    public int getBlogId() {   
  2.     return blogId;    }    public void setBlogId(int blogId) {   
  3.     this.blogId = blogId;    }    public String getTitle() {   
  4.     return title;    }    public void setTitle(String title) {   
  5.     this.title = title;    }    public String getContent() {   
  6.     return content;    }    public void setContent(String content) {   
  7.     this.content = content;    }    public int getUserId() {   
  8.     return userId;    }    public void setUserId(int userId) {   
  9.     this.userId = userId;    }//    public Timestamp getPostTime() {//   
  10.     return postTime;//    }// 这里不再是返回一个时间戳对象,而是返回一个 String    // 这个 String 就是格式化好了的时间    public String getPostTime() {//    在Java中有一个类,叫做 SimpleDataFormat 类,中文意思就是 日期格式//     SimpleDateFormat 类 在转换时间格式的时候,需要在构造方法中指定转换的时间格式   
  11.     SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");   
  12.     // 具体转换的实现是通过 SimpleDateFormat 类中 format 方法来实现的。   
  13.     // 将 postTime 作为 format 方法的参数即可   
  14.     return simpleDateFormat.format(postTime);//返回的时间格式,就是我们所指定的时间格式    }    public void setPostTime(Timestamp postTime) {   
  15.     this.postTime = postTime;    }}
复制代码


提供 操作实体类方法 的类


BlogDao

  1. package model;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 {//    1、往博客表里,插入一条记录(一篇博客)    public static void insert(Blog blog){   
  2.     Connection connection = null;   
  3.     PreparedStatement preparedStatement = null;   
  4.     // JDBC 基本的代码   
  5.     try {   
  6.         //1、和数据库建立连接   
  7.         connection = DButil.getConnection();   
  8.         //2、构造sql语句   
  9.         // 这里的 now() 表示取得是当前系统的时间   
  10.         String sql = "insert into blog values(null,?,?,?,now())";   
  11.         preparedStatement = connection.prepareStatement(sql);   
  12.         preparedStatement.setString(1,blog.getTitle());   
  13.         preparedStatement.setString(2, blog.getContent());   
  14.         preparedStatement.setInt(3,blog.getUserId());   
  15.         //3、执行SQL语句   
  16.         preparedStatement.executeUpdate();   
  17.     } catch (SQLException e) {   
  18.         e.printStackTrace();   
  19.     }finally {   
  20.         //4、关闭连接,回收资源   
  21.         DButil.close(connection,preparedStatement,null);   
  22.     }    }//    2、能够获取到博客表中的所有博客的信息【用于博客列表页显示博客有多少】//    另外,我们的博客列表,会从每篇博文中截取一段内容,因此截取的内容不一定是截取到完整的博客内容。//    因为截取到完整的博客的内容,占用的空间会很大!两三行还行,多了就不利于显示更多的博文信息public static List selectAll(){    List blogs = new ArrayList();    Connection connection = null;    PreparedStatement preparedStatement = null;    ResultSet resultSet = null;    // JDBC 基本的代码    try {   
  23.     //1、和数据库建立连接   
  24.     connection = DButil.getConnection();   
  25.     //2、构造sql语句   
  26.     String sql = "select * from blog order by postTime desc";   
  27.     preparedStatement = connection.prepareStatement(sql);   
  28.     //3、执行SQL语句   
  29.     resultSet = preparedStatement.executeQuery();   
  30.     // 4、 遍历结果集   
  31.     while(resultSet.next()){   
  32.         Blog blog = new Blog();   
  33.         blog.setBlogId(resultSet.getInt("blogId"));   
  34.         blog.setTitle(resultSet.getString("title"));   
  35.         // 这里需要对内容进行操作   
  36.         // 如果内容很长,则对其进行截取操作;反之则不进行   
  37.         String content = resultSet.getString("content");   
  38.         if(content.length()>50){   
  39.         
  40.     // 如果代码超过了50个字,进行截取   
  41.         
  42.     // 这里为什么是50,因为我愿意!   
  43.         
  44.     content = content.substring(0,50) + ".......";   
  45.         }   
  46.         blog.setContent(content);   
  47.         blog.setUserId(resultSet.getInt("userId"));   
  48.         blog.setPostTime(resultSet.getTimestamp("postTime"));   
  49.         blogs.add(blog);   
  50.     }    } catch (SQLException e) {   
  51.     e.printStackTrace();    }finally {   
  52.     //5、关闭连接,回收资源   
  53.     DButil.close(connection,preparedStatement,resultSet);    }    return blogs;}//    3、能够根据 博客id 读取到指定的博客内容【用于博客详情页显示一篇博客的详情内容】    public static Blog selectOne(int blogId){   
  54.     Connection connection = null;   
  55.     PreparedStatement preparedStatement = null;   
  56.     ResultSet resultSet = null;   
  57.     // JDBC 基本的代码   
  58.     try {   
  59.         //1、和数据库建立连接   
  60.         connection = DButil.getConnection();   
  61.         //2、构造sql语句   
  62.         String sql = "select * from blog where blogId = ?";   
  63.         preparedStatement = connection.prepareStatement(sql);   
  64.         preparedStatement.setInt(1,blogId);   
  65.         //3、执行SQL语句   
  66.         resultSet = preparedStatement.executeQuery();   
  67.         // 4、 遍历结果集:此处我们是使用主键 作为查询条件   
  68.         // 因此查询的结果要么是一条,要么是零条。   
  69.         if(resultSet.next()){   
  70.         
  71.     Blog blog = new Blog();   
  72.         
  73.     blog.setBlogId(resultSet.getInt("blogId"));   
  74.         
  75.     blog.setTitle(resultSet.getString("title"));   
  76.         
  77.     blog.setContent(resultSet.getString("content"));   
  78.         
  79.     blog.setUserId(resultSet.getInt("userId"));   
  80.         
  81.     blog.setPostTime(resultSet.getTimestamp("postTime"));   
  82.         
  83.     return blog;   
  84.         }   
  85.     } catch (SQLException e) {   
  86.         e.printStackTrace();   
  87.     }finally {   
  88.         //5、关闭连接,回收资源   
  89.         DButil.close(connection,preparedStatement,resultSet);   
  90.     }   
  91.     return null;    }//    4、从博客列表中,根据博客Id 来删除博客    public static void delete(int blogId){   
  92.     Connection connection = null;   
  93.     PreparedStatement preparedStatement = null;   
  94.     // JDBC 基本的代码   
  95.     try {   
  96.         //1、和数据库建立连接   
  97.         connection = DButil.getConnection();   
  98.         //2、构造sql语句   
  99.         String sql = "delete from blog where blogId = ?";   
  100.         preparedStatement = connection.prepareStatement(sql);   
  101.         preparedStatement.setInt(1,blogId);   
  102.         //3、执行SQL语句   
  103.          preparedStatement.executeUpdate();   
  104.         // 4、 遍历结果集:此处我们是使用主键 作为查询条件   
  105.         // 因此查询的结果要么是一条,要么是零条。   
  106.     } catch (SQLException e) {   
  107.         e.printStackTrace();   
  108.     }finally {   
  109.         //5、关闭连接,回收资源   
  110.         DButil.close(connection,preparedStatement,null);   
  111.     }    }//    注意上述的4个操作只涉及到增删查,不涉及到 改。//    改,这个操作可以被实现,但是不在这里实现。//    换句话来说:实现该这个操作 和 前面的增删查,并没有什么区别。//    所以在这里只是显示几个经典的功能,如果你们想要实现 改,加油!//    我偷个懒。}
复制代码


UserDao

  1. package model;import java.sql.Connection;
  2. import java.sql.PreparedStatement;
  3. import java.sql.ResultSet;
  4. import java.sql.SQLException;
  5. // 这个类用于封装用户表的基本操作(增删查改)
  6. public class UserDao {
  7.     //针对这个类来说,我们就简化的写就行了
  8.     // 注册/注销 的功能,我们就不实现了
  9.     // 主要实现:
  10.     // 1、根据 用户名 来查询用户信息
  11.     //    这个操作会在登录逻辑中使用。
  12.     public static User selectByName(String username){
  13.    
  14.     Connection connection = null;
  15.    
  16.     PreparedStatement statement = null;
  17.    
  18.     ResultSet resultSet = null;
  19.    
  20.     try {
  21.    
  22.         connection = DButil.getConnection();
  23.    
  24.         String sql = "select * from user where username=? ";
  25.    
  26.         statement = connection.prepareStatement(sql);
  27.    
  28.         statement.setString(1,username);
  29.    
  30.         resultSet = statement.executeQuery();
  31.    
  32.         if (resultSet.next()){
  33.    
  34.         
  35.     User user = new User();
  36.    
  37.         
  38.     user.setUserId(resultSet.getInt("userId"));
  39.    
  40.         
  41.     user.setUsername(resultSet.getString("username"));
  42.    
  43.         
  44.     user.setPassword(resultSet.getString("password"));
  45.    
  46.         
  47.     return user;
  48.    
  49.         }
  50.    
  51.     } catch (SQLException e) {
  52.    
  53.         e.printStackTrace();
  54.    
  55.     } finally {
  56.    
  57.         DButil.close(connection,statement,resultSet);
  58.    
  59.     }
  60.    
  61.     return null;
  62.     }
  63.     // 2、根据 用户id 来查询用户信息
  64.     //   博客详情页,就可以根据用户id 来查询作者的名字,将其显示出来
  65.     public static User selectById(int userId){
  66.    
  67.     Connection connection = null;
  68.    
  69.     PreparedStatement statement = null;
  70.    
  71.     ResultSet resultSet = null;
  72.    
  73.     try {
  74.    
  75.         connection = DButil.getConnection();
  76.    
  77.         String sql = "select * from user where userId=? ";
  78.    
  79.         statement = connection.prepareStatement(sql);
  80.    
  81.         statement.setInt(1,userId);
  82.    
  83.         resultSet = statement.executeQuery();
  84.    
  85.         if (resultSet.next()){
  86.    
  87.         
  88.     User user = new User();
  89.    
  90.         
  91.     user.setUserId(resultSet.getInt("userId"));
  92.    
  93.         
  94.     user.setUsername(resultSet.getString("username"));
  95.    
  96.         
  97.     user.setPassword(resultSet.getString("password"));
  98.    
  99.         
  100.     return user;
  101.    
  102.         }
  103.    
  104.     } catch (SQLException e) {
  105.    
  106.         e.printStackTrace();
  107.    
  108.     } finally {
  109.    
  110.         DButil.close(connection,statement,resultSet);
  111.    
  112.     }
  113.    
  114.     return null;
  115.     }
  116. }
复制代码


View:前端页面显示 与 构造请求部分代码

   引入 jQuery 和 引入 第三方库markdown,需要你们自己配置,反正我都讲了。
在博客页面设计的博文中

   
CSS 公共样式代码

  1. /* 放置一些各个页面都会用到的公共样式 */
  2. /* 导航栏就是属于每个页面都会用到的公共样式 */
  3. /* 首先,我们需要去掉浏览器样式 */
  4. *{
  5.     margin: 0;
  6.     padding: 0;
  7.     box-sizing: border-box;
  8. }
  9. /* 给整个页面加上背景 */
  10. html,body{
  11.     /* html 的父元素就是浏览器窗口 */
  12.     /* 此处的100%,意思是 html 元素的高度 和 浏览器一样高*/
  13.     /* body 的元素是 html */
  14.     /* 其意思不言而喻,就是 “继承父亲的财产”  */
  15.     height: 100%;
  16.     /* 加上背景图 */
  17.     /* .. 是指 当前 common 文件的父级目录 */
  18.     /* 我存放的图片的地方,就与它的父级目录是同一级 */
  19.     background-image: url(../image/preview.jpg);
  20.     /* 拒绝平铺 */
  21.     background-repeat: no-repeat;
  22.     /* 图片覆盖整个页面 */
  23.     background-size: cover;
  24.     /* 图片处于剧痛位置 */
  25.     background-position: center;
  26. }
  27. /* 导航栏样式 */
  28. .navigation{
  29.     width: 100%;
  30.     /* 一般这里的尺寸都是设计稿规定好了的 */
  31.     /* 但是这里并没有,所以这里的尺寸,自己决定 */
  32.     height: 50px;
  33.     /* 由于导航栏的背景颜色属于一种半透明的状态 */
  34.     /* 所以我们要使用 rgba 的方式来进行处理 */
  35.     /* 颜色你们自己发挥 */
  36.     /* 需要注意的 透明度alpha 是一个 0 - 1 之间数字*/
  37.     background-color: rgba(55,20,11, 0.6);
  38.     /* 由于里面的logo,标题,连接之类的都是水平一行来进行排列的 */
  39.    /* 所以,这里我们就需要用到弹性布局 */
  40.     display: flex;
  41.     /* 实现元素 垂直居中的效果。 */
  42.     align-items: center;
  43.     color: orange;
  44. }
  45. .navigation img{
  46.     /* 将图片缩小一段 */
  47.     /* 上期上下左右都留一点空位 */
  48.     width: 40px;
  49.     height: 40px;
  50.     /* 将图片设置为原型 */
  51.     border-radius: 50%;
  52.     /* 设置 内/外边距 */
  53.     margin-left: 30px;
  54.     margin-right: 10px;
  55. }
  56. .navigation .spacer{
  57.     /* 相对于父元素的宽度,占比百分之70 */
  58.     width: 80%;
  59. }
  60. .navigation a{
  61.     /* 去掉下划线 */
  62.     text-decoration: none;
  63.     /* 设置标签之间的间距 */
  64.     /* 上下内边距0px,左右10px */
  65.     padding: 0 10px;
  66.     color: orange;
  67. }
  68. /* 接下来是 版心相关的样式 */
  69. .container{
  70.     /* 注意!既然要留有空白,那么宽度就不能是100% */
  71.     width: 1000px;
  72.     /* 版心的理想高度:页面高度 减去 导航栏的高度 */
  73.     height: calc(100% - 50px);
  74.     /* 水平居中 */
  75.     /* 上下外边距为 0,左右边距由浏览器自动调整 */
  76.     margin: 0 auto;
  77.     display: flex;
  78.     justify-content: space-between;
  79. }
  80. /* 版心的左侧部分 */
  81. .container .left{
  82.     height: 100%;
  83.     width: 200px;
  84.     /* background-color: red; */
  85. }
  86. /* 版心的右侧部分 */
  87. .container .right{
  88.     height: 100%;
  89.     /* 父类container 的宽度为1000px */
  90.     /* 左侧栏占了200px */
  91.     width: 795px;
  92.     /* background-color: blue; */
  93.     /* 背景颜色 */
  94.     background-color: rgba(255, 255, 255,0.75);
  95.     /* 边框圆角 */
  96.     border-radius: 20px;
  97.     /* 处理溢出问题 */
  98.     /* 溢出了就滚动内容,没溢出就不滚动 */
  99.     overflow: auto;
  100. }
  101. /* 接下来实现 card 部分的样式 */
  102. .card{
  103.     /* 背景颜色 */
  104.     background-color: rgba(255,255,255, 0.75);
  105.     /* 背景区域圆角 */
  106.     border-radius: 20px;
  107.     /* 通过这里的内边距,就可以让图片水平居中 */
  108.     /* 这里设置的30像素,意思是指4个方向,都是30px */
  109.     /* 因为我们的图片长宽都是140px,而card的宽为200px */
  110.     /* 200 -140 == 60px,两边一平摊刚好 30 px */
  111.     /* 刚好能水平居中,而且上下空出30px */
  112.     padding: 30px;
  113. }
  114. .card img{
  115.     /* card 的宽度为 200px 【默认与父类 left 宽度相同】*/
  116.     /* 先把图片的尺寸速效 */
  117.     width: 140px;
  118.     height: 140px;
  119.     /* 将图片变成圆形形 */
  120.     border-radius: 50%;
  121. }
  122. .card h3{
  123.     text-align: center;
  124.     padding: 10px;
  125. }
  126. .card a{
  127.     /* 首先,先把 a 标签转化成 块级元素 */
  128.     /* 因为 a 默认是行内元素,行内元素的很多边距是没有效果的 */
  129.     display: block;
  130.     /* 文本居中 */
  131.     text-align: center;
  132.     /* 去掉下划线 */
  133.     text-decoration: none;
  134.     /* 字体颜色 */
  135.     color: rgb(64, 55, 55);
  136.     /* 间距 */
  137.     padding: 10px;
  138. }
  139. .card .counter{
  140.     /* 弹性布局,目的:为了更好的水平排列 */
  141.     display: flex;
  142.     /* 通过 justify-content: aroumd */
  143.     /* 来使用它们左右进行分离排列 */
  144.     justify-content: space-around;
  145.     padding: 5px;
  146. }
复制代码


博客登录页

网页框架代码 与 动态显示代码 部分

  1.    
  2.         
  3.     博客登录页   
  4.         
  5.         
  6.         [img]https://blog.csdn.net/./image/2.jpg[/img]   
  7.     我的博客系统   
  8.         
  9.         
  10.     [url=https://blog.csdn.net/bloghome.html]主页[/url]   
  11.     [url=https://blog.csdn.net/writingblog.html]写博客[/url]   
  12.         
  13.         
  14.         
  15.         
  16.         
  17.         
  18.         
  19.     [size=4]登陆[/size]
  20.    
  21.         
  22.         
  23.         
  24.         用户名   
  25.         
  26.         
  27.         
  28.         
  29.         
  30.         
  31.         
  32.         
  33.     密码   
  34.         
  35.         
  36.         
  37.         
  38.         
  39.         
  40.         
  41.         
  42.         
  43.         
  44.         
  45.         
  46.            
复制代码


页面样式(CSS 代码)

  1. /* 登录页面的专用样式文件 */
  2. .login-container{
  3.     /* 页面宽度 */
  4.     width: 100%;
  5.     /* 减去导航的高度 */
  6.     height: calc(100% - 50px);
  7.     /* 需要让里面的子元素,垂直水平居中,就会用到 flex */
  8.     display: flex;
  9.     /* 垂直居中 */
  10.     align-items: center;
  11.     /* 水平居中 */
  12.     justify-content: center;
  13.    
  14. }
复制代码


博客列表页(主页)


前端框架代码 与 JS代码

  1.    
  2.         
  3.     博客主页   
  4.         
  5.         
  6.         [img]https://blog.csdn.net/./image/2.jpg[/img]   
  7.     我的博客系统   
  8.         
  9.         
  10.     [url=https://blog.csdn.net/bloghome.html]主页[/url]   
  11.     [url=https://blog.csdn.net/writingblog.html]写博客[/url]   
  12.     [url=https://blog.csdn.net/logout]注销[/url]   
  13.         
  14.         
  15.         
  16.         
  17.         
  18.         
  19.         
  20.         [img]https://blog.csdn.net/./image/1.jpg[/img]   
  21.         
  22.         
  23.         
  24.     [size=4] [/size]
  25.    
  26.         
  27.     [url=https://blog.csdn.net/#]github 地址[/url]   
  28.         
  29.         
  30.         
  31.         文章   
  32.         
  33.         分类   
  34.         
  35.         
  36.         
  37.         
  38.         
  39.         2   
  40.         
  41.         1   
  42.         
  43.         
  44.         
  45.         
  46.         
  47.         
  48.         
  49.         
  50.         
  51.         
  52.         
  53.         
  54.         
  55.         
  56.         
  57.         
  58.         
  59.         
  60.         
  61.         
  62.         
  63.         
  64.         
  65.         
  66.         
  67.         
  68.         
  69.         
  70.         
  71.         
  72.         
  73.         
  74.         
  75.         
  76.         
  77.         
  78.         
  79.         
  80.         
  81.      
复制代码


CSS 样式代码部分

  1. /* 这个文件中专门写和博客列表页相关的样式 */
  2. .blog{
  3.     /* blog的宽度 和 父元素right是一样的 */
  4.     width: 100%;
  5.     /* 高度如果不设置,就取决于内容高度的总和 */
  6.     /* 所以,我们这里不去设置 */
  7.     /* 设置 每一篇博客的间距,通过 设置内边距 */
  8.     padding: 20px;
  9. }
  10. /* 标题 */
  11. .blog .title{
  12.     /* 居中 */
  13.     text-align: center;
  14.     /* 字体大小 */
  15.     font-size: 22px;
  16.     /* 字体粗细 */
  17.     font-weight: bold;
  18.     /* 设置边距 */
  19.     /* 上边边距 10px,左右0px(因为blog里面已经设置了啦20px) */
  20.     padding: 10px 0;
  21. }
  22. /* 日期 */
  23. .blog .date{
  24.     /* 文本居中 */
  25.     text-align: center;
  26.     /* 字体颜色 */
  27.     color: rgb(204, 33, 204);
  28.     /* 边距 */
  29.     padding: 10px;
  30. }
  31. /* 自然段 */
  32. .blog .desc{
  33.     text-indent: 2em;
  34. }
  35. /* 查看按钮 */
  36. .blog a{
  37.     /* 将 a标签 设置为 块级元素,方便设置尺寸 和 边距 */
  38.     display: block;
  39.     /* 尺寸 */
  40.     width: 140px;
  41.     height: 40px;
  42.     /* 去掉下划线 */
  43.     text-decoration: none;
  44.     /* 字体颜色 */
  45.     color: black;
  46.     /* 文本内容居中 */
  47.     text-align: center;
  48.     /* 文本框垂直居中 */
  49.     line-height: 40px;
  50.     /* 文本框水平居中位置 */
  51.     margin: 10px auto;
  52.     /* 边框: 边框线条粗细2px,颜色:黑色,边框线条:实线 */
  53.     border: 2px black solid;
  54.     /* 如果想让变化的过程变得柔和一些,就可以加上过渡效果 */
  55.     transition: all 0.5s;
  56. }
  57. /* 伪类选择器 */
  58. .blog a:hover{
  59.     background-color: orange;
  60. }
复制代码


博客详情页


前端框架代码 与 JS代码

  1.    
  2.         
  3.     博客详情页   
  4.         
  5.         
  6.         
  7.         
  8.         
  9.         [img]https://blog.csdn.net/./image/2.jpg[/img]   
  10.     我的博客系统   
  11.         
  12.         
  13.     [url=https://blog.csdn.net/bloghome.html]主页[/url]   
  14.     [url=https://blog.csdn.net/writingblog.html]写博客[/url]   
  15.     [url=https://blog.csdn.net/logout]注销[/url]   
  16.         
  17.         
  18.         
  19.         
  20.         
  21.         
  22.         
  23.         [img]https://blog.csdn.net/./image/1.jpg[/img]   
  24.         
  25.         
  26.         
  27.     [size=4]  [/size]
  28.    
  29.         
  30.     [url=https://blog.csdn.net/#]github 地址[/url]   
  31.         
  32.         
  33.         
  34.         文章   
  35.         
  36.         分类   
  37.         
  38.         
  39.         
  40.         
  41.         
  42.         2   
  43.         
  44.         1   
  45.         
  46.         
  47.         
  48.         
  49.         
  50.         
  51.         
  52.         
  53.         
  54.         
  55.         
  56.         
  57.         [size=4]   
  58.         
  59.     [/size]
  60.    
  61.         
  62.         
  63.         
  64.         
  65.         
  66.         
  67.         
  68.         
  69.         
  70.         
  71.         
  72.         
  73.         
  74.         
  75.         
  76.         
  77.         
  78.         
  79.         
复制代码


CSS 样式代码部分

  1. /* 给博客详情页使用的样式文件 */
  2. /* 首先,我们需要给 blog-content 设置内边距*/
  3. /* 因为你们仔细看看模板,就会知道内容与边框是有一定的距离的 */
  4. .blog-content{
  5.     padding: 30px;
  6. }
  7. /* 标题 */
  8. .blog-content h3{
  9.     /* 标题居中 */
  10.     text-align: center;
  11.     /* 设置边距,因为标题与下面的时间是有距离的 */
  12.     /* .blog-content设置的边距 和这个没有关系!!! */
  13.     /* .blog-content 是针对整体,.blog-content h3 是针对内容中的标题 */
  14.     /* 上下 20 px,左右0px */
  15.     padding: 20px 0;
  16. }
  17. /* 日期 */
  18. .blog-content .date{
  19.     /* 文本居中 */
  20.     text-align: center;
  21.     /* 字体颜色 */
  22.     color: rgb(204, 33, 204);
  23.     /* 针对日期设置间距 */
  24.     padding: 20px;
  25. }
  26. /* 自然段 */
  27. .blog-content p{
  28.     /* 每个自然段首行缩进 2个字符 */
  29.     text-indent: 2em;
  30.     /* 给每个自然段设置边距,使其每个自然段隔开 */
  31.     padding: 10px 0;
  32. }
复制代码


博客编辑页


前端框架代码 与 JS代码

  1.    
  2.         
  3.     博客编辑页   
  4.         
  5.         
  6.         
  7.         
  8.         
  9.         [img]https://blog.csdn.net/./image/2.jpg[/img]   
  10.     我的博客系统   
  11.         
  12.         
  13.     [url=https://blog.csdn.net/bloghome.html]主页[/url]   
  14.     [url=https://blog.csdn.net/writingblog.html]写博客[/url]   
  15.     [url=https://blog.csdn.net/logout]注销[/url]   
  16.         
  17.         
  18.         
  19.         
  20.         
  21.         
  22.         
  23.         
  24.         
  25.         
  26.         
  27.         
  28.         
  29.         
  30.         
  31.         
  32.         
  33.         
  34.         
  35.         
  36.         
  37.         
  38.         
  39.         
  40.         
  41.         
  42.         
  43.         
  44.         
  45.         
  46.         
  47.         
  48.         
  49.         
  50.         
  51.         
  52.         
  53.    
复制代码


CSS 样式代码部分

  1. /* 这是博客编辑页专用的样式文件 */
  2. /* 容器 */
  3. .blog-edit-container {
  4.     /* 尺寸 */
  5.     width: 1000px;
  6.     /* 页面高度 - 导航栏高度 */
  7.     height: calc(100% - 50px);
  8.     /* 水平居中 */
  9.     margin: 0 auto;
  10. }
  11. /* 先针对标题区 的 div 下手 */
  12. .blog-edit-container .title{
  13.     /* 与分类元素 .blog-edit-container 一样:1000px */
  14.     width: 100%;
  15.     /* 标题区域高度 */
  16.     height: 50px;
  17.     /* 为了方便调节尺寸,我们使用 弹性布局 */
  18.     display: flex;
  19.     /* 垂直居中 */
  20.     align-items: center;
  21.     /* 使子元素 输入框 和 按钮 靠两边放置,中间留个缝 */
  22.     justify-content: space-between;
  23. }
  24. .blog-edit-container .title #title{
  25.     /* 尺寸 */
  26.     /* 宽度我们要考虑一下 */
  27.     /* 总长度是1000px,我打算给按钮设置 100px 的宽度*/
  28.     /* 那么 输入的宽度就应该是 900px */
  29.     /* 但是模板中,两者是不能紧挨着的 */
  30.     /* 所以我给 890px */
  31.     width: 890px;
  32.     height: 40px;
  33.     /* 边框圆角 */
  34.     border-radius: 15px;
  35.     /* 去掉边框线 */
  36.     border: none;
  37.     /* 去掉轮廓线 */
  38.     outline: none;
  39.     /* 背景颜色 和 透明度 */
  40.     background-color: rgba(255, 255, 255, 0.75);
  41.     font-size: 20px;
  42.     padding-left: 10px;
  43. }
  44. .blog-edit-container .title #submit{
  45.     width: 100px;
  46.     height: 40px;
  47.     /* 背景颜色 */
  48.     background-color: rgb(240, 163, 21);
  49.     /* 字体颜色 */
  50.     color: white;
  51.     /* 去掉边框线 */
  52.     border: none;
  53.     /* 去掉轮廓线 */
  54.     outline: none;
  55.     /* 边框圆角 */
  56.     border-radius: 15px;
  57. }
  58. /* 点击时,按钮背景颜色为黑色 */
  59. .blog-edit-container .title #submit:active{
  60.     background-color: black;
  61. }
  62. #editor{
  63.     border-radius: 15px;
  64.     /* background-color 只是针对当前元素进行设置,不会影响到子元素 */
  65.     /* background-color: rgba(255, 255, 255, 0.75); */
  66.     /* opacity 会影响到子元素 */
  67.     /* 给最外面的父元素设置了透明,里面的元素也会一起半透明 */
  68.     opacity: 75%;
  69. }
复制代码


Controller : 处理请求逻辑代码补码


博客登录页/博客列表页/博客详情页/博客编辑页 - 服务器代码

    doPost :用户登录逻辑
doGet:验证登录状态逻辑

  1. package controller;import com.fasterxml.jackson.databind.ObjectMapper;import model.User;import model.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 {   
  2.     req.setCharacterEncoding("utf8");// 设置字符集   
  3.     resp.setCharacterEncoding("utf8");   
  4.     //1、获取到请求中的参数   
  5.     String username = req.getParameter("username");   
  6.     String password = req.getParameter("password");   
  7.     //2、和数据库的内容进行比较   
  8.     if(username == null || "".equals(username) || password == null || "".equals(password)){   
  9.         resp.setContentType("text/html; charset=utf8");//告诉浏览器当前返回一个 静态页面,读取数据形式是utf8   
  10.         resp.getWriter().write("当前用户名或密码不能为空!");   
  11.     }else{   
  12.         // 用户名 和 密码 不为空的情况   
  13.         User user = UserDao.selectByName(username);   
  14.         if(user == null || !user.getPassword().equals(password)){   
  15.         
  16.     resp.setContentType("text/html; charset=utf8");   
  17.         
  18.     resp.getWriter().write("当前用户名,或者密码不能正确!");   
  19.         }else{   
  20.         
  21.     //3、如果比较通过,就创建会话   
  22.         
  23.     HttpSession session = req.getSession(true);   
  24.         
  25.     // 把刚才的用户信息,存储在会话中   
  26.         
  27.     session.setAttribute("user", user);   
  28.         
  29.     //4、返回一个重定向报文,跳转到博客列表页   
  30.         
  31.     resp.sendRedirect("bloghome.html");   
  32.         }   
  33.     }    }    // 这个方法用来让前端检测当前的登录状态    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {   
  34.     resp.setContentType("application/json; charset=utf8");   
  35.     HttpSession session =  req.getSession(false);//获取当前会话   
  36.     if (session == null){   
  37.         // 检测会话是否存在:不存在则说明未登录。   
  38.         User user = new User();   
  39.         resp.getWriter().write(objectMapper.writeValueAsString(user));   
  40.         return;   
  41.     }   
  42.     // 如果会话不为空,就来查询会话中是否存在user 对象   
  43.     // 如果存在,返回的就是 一个非空的 user 对象   
  44.     // 如果不存在,返回的是一个 null   
  45.     User user = (User) session.getAttribute("user");   
  46.     // user 不存在   
  47.     if(user == null){   
  48.         user = new User();   
  49.         resp.getWriter().write(objectMapper.writeValueAsString(user));   
  50.         return;   
  51.     }   
  52.     // user存在 / 处于已登录的状态   
  53.     // 注意:此处不要把密码也给返回去了【避免密码泄露】   
  54.     user.setPassword("");   
  55.     resp.getWriter().write(objectMapper.writeValueAsString(user));    }}
复制代码


博客列表页 / 博客编辑页 - 服务器代码

    doGet:获取数据库存储的博客列表信息
doPost:发布博客

  1. package controller;import com.fasterxml.jackson.databind.ObjectMapper;import model.Blog;import model.BlogDao;import model.User;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 {   
  2.     resp.setContentType("application/json; charset=utf8");   
  3.     // 先尝试获取到 req 中的 blogId 参数   
  4.     // 如果参数存在,说明接下来要处理的详情页的逻辑;反之,接下来就要处理列表页的逻辑   
  5.     String str = req.getParameter("blogId");   
  6.     if(str == null){   
  7.         // 如果 str 为 null,说明:接下来就要处理列表页的逻辑   
  8.         // 从数据数据中查询到博客列表,将格式转化成json格式,然后直接返回即可   
  9.         List blogs = BlogDao.selectAll();   
  10.         // 把 blogs 转化成json数组的形式   
  11.         String respJson = objectMapper.writeValueAsString(blogs);//   
  12.         resp.setContentType("application/json; charset=utf8");   
  13.         resp.getWriter().write(respJson);   
  14.     }else{   
  15.         // 反之,接下来就要处理详情页的逻辑   
  16.         // 将获取到的 blogId 转换成 整形   
  17.         int blogId = Integer.parseInt(str);   
  18.         // 调用我们之前书写 selectOne 方法,来查询指定的博客信息   
  19.         Blog blog = BlogDao.selectOne(blogId);   
  20.         // 将 blog对垒 转换成 json格式的字符串   
  21.         String respJson = objectMapper.writeValueAsString(blog);//   
  22.         resp.setContentType("application/json; charset=utf8");   
  23.         resp.getWriter().write(respJson);   
  24.     }    }// 发布博客    @Override    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {   
  25.     HttpSession session = req.getSession(false);   
  26.     if(session == null){   
  27.         // 当前用户处于未登录状态,不能发布博文!   
  28.         resp.setContentType("text/html; charset=utf8");   
  29.         resp.getWriter().write("前用户处于未登录状态,不能发布博文!");   
  30.         return;   
  31.     }   
  32.     User user = (User) session.getAttribute("user");   
  33.     if(user == null){   
  34.         // 当前用户处于未登录状态,不能发布博文!   
  35.         resp.setContentType("text/html; charset=utf8");   
  36.         resp.getWriter().write("前用户处于未登录状态,不能发布博文!");   
  37.         return;   
  38.     }   
  39.     // 只要上面两个条件都满足,才能算是处于登录状态   
  40.     // 也只有处于登录的用户,才能编写博客。   
  41.     // 如果别人写了半天,点击发布,你提示别人未登录,这不是消遣别人嘛!   
  42.     // 因此 判断用户登录状态的代码 必须置前!才能执行下面的逻辑!   
  43.     req.setCharacterEncoding("utf8");// 一定要先指定好请求按哪种编码方式来解析   
  44.     // 先从请求中,取出参数(博客的标题和正文)   
  45.     String title = req.getParameter("title");   
  46.     String content = req.getParameter("content");   
  47.     if(title == null || content == null || "".equals(title)||"".equals(content)){   
  48.         // 我们认为质押出现上述情况之一,就是违法数据   
  49.         // 直接告诉客户端,请求参数不对   
  50.         resp.setContentType("text/html; charset=utf8");   
  51.         resp.getWriter().write("发布博客失败!缺少必要的参数!");   
  52.         return;   
  53.     }   
  54.     // 如果没有出错的话,我们就构造一个 Blog 对象,把当前的数据存入数据库   
  55.     Blog blog = new Blog();   
  56.     // 此处我们蛀牙给 Blog 设置的属性,主题是 title,content,userId(作者信息)   
  57.     // postTime【now()方法】 和 blogId(自增主键) 这两个不需要手动指定,都是插入数据库的时候,自动生成的。   
  58.     blog.setTitle(title);   
  59.     blog.setContent(content);   
  60.     // 作者 id 就是当前提交这个博客的用户的身份信息!!!   
  61.     blog.setUserId(user.getUserId());   
  62.     BlogDao.insert(blog);   
  63.     // 新增博客之后,我们就需要跳转到博客列表页   
  64.     resp.sendRedirect("bloghome.html");    }}
复制代码


博客详情页 - 服务器代码

获取作者信息

  1. package controller;import com.fasterxml.jackson.databind.ObjectMapper;import model.Blog;import model.BlogDao;import model.User;import model.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 AuthorInfoServlet extends HttpServlet {    private ObjectMapper objectMapper = new ObjectMapper();    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {   
  2.     resp.setContentType("application/json; charset=utf8");   
  3.     // 通过这个方法,来获取到指定的博客作者信息   
  4.     String blogId = req.getParameter("blogId");   
  5.     if(blogId == null || "".equals(blogId)){   
  6.         // 参数绝少   
  7.         resp.getWriter().write("{ "ok": false,"reason": "参数缺失!"}");   
  8.         return;   
  9.     }   
  10.     // 根据 blogId 在数据库中进行查找,找到对应的 Blog 对象   
  11.     // 然后,在进一步根据 Blog 对象 找到作者信息   
  12.     Blog blog = BlogDao.selectOne(Integer.parseInt(blogId));   
  13.     if(blog == null){   
  14.         resp.getWriter().write("{ "ok": false,"reason": "要查找的博客不存在!"}");   
  15.         return;   
  16.     }   
  17.     //根据 blog 对象,查询到用户对象   
  18.     User author = UserDao.selectById(blog.getUserId());   
  19.     if (author == null){   
  20.         resp.getWriter().write("{ "ok": false,"reason": "要查找的用户不存在!"}");   
  21.         return;   
  22.     }   
  23.     // 把 author 对象返回到浏览器这边   
  24.     author.setPassword("");   
  25.     String respJson = objectMapper.writeValueAsString(author);   
  26.     resp.getWriter().write(respJson);    }}
复制代码

删除博客操作

  1. package controller;import model.Blog;import model.BlogDao;import model.User;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 {   
  2.     // 1、检查当前用户是否登录   
  3.     HttpSession session = req.getSession(false);   
  4.     if (session == null){   
  5.         resp.setContentType("text/html; charset=utf8");   
  6.         resp.getWriter().write("当前您处于未登录状态,无法进行删除操作!");   
  7.         return;   
  8.     }   
  9.     User user = (User) session.getAttribute("user");   
  10.     if(user == null){   
  11.         resp.setContentType("text/html; charset=utf8");   
  12.         resp.getWriter().write("当前您处于未登录状态,无法进行删除操作!");   
  13.         return;   
  14.     }   
  15.     // 2、获取到参数中的 blogId   
  16.     String blogId = req.getParameter("blogId");   
  17.     if (blogId == null || "".equals(blogId)){   
  18.         resp.setContentType("text/html; charset=utf8");   
  19.         resp.getWriter().write("参数残缺,无法进行删除操作!");   
  20.         return;   
  21.     }   
  22.     // 3、获取需要删除的博客信息   
  23.     Blog blog = BlogDao.selectOne(Integer.parseInt(blogId));   
  24.     if (blog == null){   
  25.         resp.setContentType("text/html; charset=utf8");   
  26.         resp.getWriter().write("您要删除的博客不存在,无法进行删除操作!");   
  27.         return;   
  28.     }   
  29.     // 4、再次校验:当前的用户是否是博客的作者   
  30.     if(user.getUserId() != blog.getUserId()){   
  31.         // 虽然在前端中处理过这个问题,但是再稳一波,twin check 更保险!   
  32.         resp.setContentType("text/html; charset=utf8");   
  33.         resp.getWriter().write("非法删除操作!");   
  34.         return;   
  35.     }   
  36.     // 5、删除指定博客   
  37.     BlogDao.delete(Integer.parseInt(blogId));   
  38.     // 6、返回一个重定向响应   
  39.     resp.sendRedirect("bloghome.html");    }}
复制代码



总结

    学到了这里,就就是一种检验,检验前面博客讲的知识是不是学扎实了。
至少你得能看懂代码。

     当前博客系统,可以认为是一个比较简单的程序。
像这样的程序,你写道简历上,当做一个小小项目也行。
其作用 聊胜于无,不然也不会说是小小项目。
但是别看简单,要动手!知道嘛!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

前进之路

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表