博客项目设计文档

博客设计文档

设计详情

  1. filter 拦截器来进行会话管理
  2. 多线程的单例模式来控制对数据库的连接
  3. 通过json来进行前后端的交接 用序列化和反序列化前后端数据
  4. 创建一个单独的类来进行异常处理 并且将异常原因返回给前端
  5. 设计一个统一的前后端响应交接抽象类,通过其他servlet来重写抽象类的方法,提高代码的复用率
  6. 设计一个JSONResponse类作为后端响应数据的模板,从而更好的实现前后端数据的交接

前端设计

  1. Json + ajax
  2. success为true则返回正确的页面 为false则返回我们设置的报错信息

调用一个接口来统一返回成功或者失败的接口

成功
  1. 没有报错,返回为true,响应正确的前端页面
失败
  1. 返回为false, 响应报错的后端的信息
  2. 报错信息就是我们自己所设置的报错信息
用户登录接口
请求
  1. POST “/login”
  2. Content-Type: x-www-form-urlencoded
  3. username=abc&password=123
响应
  1. Content-Type : application/json
  2. 跳转页面的内容
获取文章列表
请求
  1. GET “/articleList”
  2. Content-Type: text/html; charset=UTF-8
  3. 获取到Session,强转user,获取到user的id
响应
  1. Content-Type: text/html; charset=UTF-8
  2. 获取到文章列表
发表文章
请求
  1. GET “/articleAdd”
  2. Content-Type: text/html; charset=UTF-8
  3. 获取到文章标题和文章内容
响应
  1. Content-Type: text/html; charset=UTF-8
  2. 发表文章成功
获取文章详情
请求
  1. GET “/articleDetail”
  2. Content-Type: text/html; charset=UTF-8
  3. 根据文章id 获取文章详情
响应
  1. Content-Type: text/html; charset=UTF-8
  2. 返回文章
修改文章
请求
  1. POST “/articleUpdate”
  2. Content-Type: text/html; charset=UTF-8
响应
  1. Content-Type: text/html; charset=UTF-8
  2. 返回文章
删除文章
请求
  1. GET “/articleUpdate”
  2. Content-Type: text/html; charset=UTF-8
响应
  1. Content-Type: text/html; charset=UTF-8
  2. 刷新文章列表

代码设计

1.数据库设计
drop database if exists servlet_blog;
create database servlet_blog character set utf8mb4;

use servlet_blog;

create table user (
   id int primary key  auto_increment,
   username varchar(20) not null unique,
   password varchar(20) not null,
   nickname varchar(20),
   sex bit,
   birthday date ,
   head varchar(20)
);

create table article(
   id int primary key auto_increment,
   title varchar(20) not null ,
   content mediumtext not null,

   create_time timestamp default now(),
   view_count int default 0,
   user_id int,
   foreign key(user_id) references user(id)
);

-- 主外键关联的表,默认创建的表的主外键是restrict严格模式,具有从连关系
-- 如果从表有数据关联到某一行数据,那x不可以删
-- 真实的设计上是不删除物理,在每一个表上设计上一个字段,表示是否有效


insert into user(username, password) values ('a', '1');
insert into user(username, password) values ('b', '2');
insert into user(username, password) values ('c', '3');


insert into article(title, content, user_id) values ('堆排序', 'public****', 2);
insert into article(title, content, user_id) values ('冒泡排序', 'public****', 2);
insert into article(title, content, user_id) values ('选择排序', 'public****', 2);
insert into article(title, content, user_id) values ('插入排序', 'public****', 1);
insert into article(title, content, user_id) values ('归并排序', 'public****', 1);
insert into article(title, content, user_id) values ('快速排序', 'public****', 1);

2. 数据库类

JSONResponse 这个类不会被添加到数据库中,只是为了做前后端数据的交接

package org.example.model;


/**
 * http响应json数据, 前后端统一约定的json格式
 * 响应状态码都是200  进入ajax 的success来使用
 *  { success : true, data:xxx }
 *  { success : false, code : xxx}
 * */



// 使用注解的方式获取 get 和 set 方法
public class JSONResponse {
    // 业务操作是否成功
    private boolean success;
    // 业务操作的消息码  一般来说 出现错误时的错误码才有意义,用于定位问题
    private  String code;
    // 业务操作的错误消息 给用户看的信息
    private String message;
    // 业务数据, 业务操作成功时,给前端ajax success 方法使用,解析行用json数据,渲染网页
    private Object data;

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}

3.数据库连接设置
package org.example.servlet;

import org.example.dao.ArticleDao;
import org.example.model.Article;
import org.example.model.User;
import org.example.util.JSONUtil;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.InputStream;

@WebServlet("/articleAdd")
public class ArticleAddServlet extends AbstractBaseServlet {

    @Override
    protected Object process(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        HttpSession session = req.getSession(false);
        User user = (User) session.getAttribute("user");

        // 请求数据类型是application/json 需要反序列化
        InputStream inputStream = req.getInputStream();
        Article article = JSONUtil.deserialize(inputStream, Article.class);
        article.setUser_id(user.getId());

        return ArticleDao.insert(article);
    }
}

4.关于dao层代码的设计

对用户的登录

package org.example.dao;

import org.example.MyException.AppException;
import org.example.model.User;
import org.example.util.DBUtil;

import java.sql.*;
import java.util.Date;

public class LoginDAO {
    public User query (String username) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;

        connection = DBUtil.getConnection();
        String sql = "select * from user where username=?";
        try {

            statement = connection.prepareStatement(sql);
            statement.setString(1, username);

            resultSet = statement.executeQuery();

            if (resultSet.next()) {
                User user = new User();
                user.setId(resultSet.getInt("id"));
                user.setName(resultSet.getString("username"));
                user.setPassword(resultSet.getString("password"));
                user.setNickname(resultSet.getString("nickname"));


                //返回值是 时分秒 java.sql.Date
                //getTimestamp java.sql.Timestamp 年月日时分秒
                //获取的时数据库中时间类型 sql.Date
                java.sql.Date birthday = resultSet.getDate("birthday");

                if (birthday != null) {
                    // 需要转化类型 将sql.Date 装换成 util.Date
                    user.setBirthday(new Date(birthday.getTime()));
                }

                user.setSex(resultSet.getBoolean("sex"));
                user.setHead(resultSet.getString("head"));
                return user;
            }
        } catch (SQLException e) {
            throw new AppException("DAOLOG001", "查询用户错误", e);
        } finally {
            DBUtil.Close(connection, statement, resultSet);
        }
        return null;
    }

}

对文章的获取 添加 删除 修改

对文章列表的获取

package org.example.dao;

import org.example.MyException.AppException;
import org.example.model.Article;
import org.example.util.DBUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class ArticleDao {

    public static int insert(Article article) {
        Connection connection = null;
        PreparedStatement statement = null;

        connection = DBUtil.getConnection();
        String  sql = "insert into article (title, content, user_id)" +
                "values (?, ?, ?)";
        try {
            statement = connection.prepareStatement(sql);
            statement.setString(1, article.getTitle());
            statement.setString(2, article.getContent());
            statement.setInt(3, article.getUser_id());
            return statement.executeUpdate();
        } catch (SQLException throwables) {
            throw new  AppException("ARTINSERT005", "新增出错", throwables);
        } finally {
            DBUtil.Close(connection, statement, null);
        }
    }

    public static Article query(int parseInt) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;

        connection = DBUtil.getConnection();
        String sql = "select id,title,content from article where id = ?";
        try {
            statement = connection.prepareStatement(sql);
            statement.setInt(1, parseInt);
            resultSet = statement.executeQuery();
            if (resultSet.next()) {
              Article article = new Article();
              article.setId(parseInt);
              article.setTitle(resultSet.getString("title"));
              article.setContent(resultSet.getString("content"));
              return article;
            }
            return null;
        } catch (SQLException throwables) {
            throw new AppException("ARTDETAIL", "查询文章详情失败", throwables);
        } finally {
            DBUtil.Close(connection, statement, resultSet);
        }

    }

    public static int updateArticle(Article article) {
        Connection connection = null;
        PreparedStatement statement = null;

        connection = DBUtil.getConnection();
        String sql = "update article set content = ? ,title = ?  where id = ? ;";
        try {
            statement = connection.prepareStatement(sql);
            statement.setString(1, article.getContent());
            statement.setString(2, article.getTitle());
            statement.setInt(3, article.getId());
            
            int num = statement.executeUpdate();
            return num;
        } catch (SQLException throwables) {
            throw new AppException("UPDATEART007", "修改失败", throwables);
        } finally {
            DBUtil.Close(connection, statement, null);
        }

    }

    public List<Article> queryByuUser_id(Integer user_id) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        List<Article> list = new ArrayList<>();
        connection = DBUtil.getConnection();
        String sql = "select * from article where user_id = ?";
        //String sql  = "select id, title from article where user_id=?";
        try {
            statement = connection.prepareStatement(sql);
            statement.setInt(1, user_id);
            resultSet = statement.executeQuery();
            while (resultSet.next()) {
                Article article = new Article();
                article.setId(resultSet.getInt("id"));
                article.setContent(resultSet.getString("content"));

                article.setTitle(resultSet.getString("title"));
                article.setUser_id(resultSet.getInt("user_id"));
                article.setView_count(resultSet.getInt("view_count"));
                java.sql.Date date = resultSet.getDate("create_time");
                if (date != null) {
                    article.setCreate_time(new Date(date.getTime()));
                }
                list.add(article);
            }
            return list;
        } catch (SQLException e) {
            throw new AppException("ART001", "获取文章列表失败");
        } finally {
            DBUtil.Close(connection, statement, resultSet);
        }
    }

    public static int delete(String[] split) {
        Connection connection = null;
        PreparedStatement statement = null;

        connection = DBUtil.getConnection();
        StringBuilder sql = new StringBuilder("delete from article where id in (");
        sql.append("?");
        for (int i = 1; i < split.length; i++) {
            sql.append(",");
            sql.append("?");
        }
        sql.append(")");
        try {
            statement = connection.prepareStatement(sql.toString());
            for (int i = 0; i < split.length; i++) {
                statement.setInt(i+1, Integer.parseInt(split[i]));
            }
            return statement.executeUpdate();
        } catch (SQLException throwables) {
            throw new AppException("ARTDELE001", "文章删除出错", throwables);
        } finally {
            DBUtil.Close(connection, statement, null);
        }
    }
}

5.对于前后端数据的序列化和反序列化的设计
package org.example.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.io.InputStream;

// JSON 是前后端传输请求的数据格式
// 表单数据类型   x-www-form-urlencoded
public class JSONUtil {

    private static final ObjectMapper MAPPER = new ObjectMapper();
    // 静态方法
    /**
     * JSON序列化 ,将Java对象序列化成JSON字符串
     * @param o java对象
     * @return  json字符串
    * */
    public static String serialize (Object o) {
        try {
            // 将java对象序列化后写入
            return MAPPER.writeValueAsString(o);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new RuntimeException("json序列化失败" + o);
        }
    }

    /**
     * 反序列化 将JSON字符串转化成JAVA对象
     * 使用泛型 来确定JAVA对象
     * @param is 输入流
     * @param tClass 确定要反序列化的类型
     * @return 反序列化的对象
     */
    public static <T> T deserialize (InputStream is, Class<T> tClass) {
        try {
            // 读取内容后将其反序列化成java对象
            return MAPPER.readValue(is, tClass);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("json反序列化失败" + tClass.getName());
        }
    }
    

}

6. Servelt类需要继承的抽象类
package org.example.servlet;

import org.example.MyException.AppException;
import org.example.model.JSONResponse;
import org.example.util.JSONUtil;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;


public abstract class AbstractBaseServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置请求体的编码格式
        req.setCharacterEncoding("UTF-8");
        // 设置响应体的编码格式
        resp.setCharacterEncoding("UTF-8");
        // 设置响应体的数据类型  浏览器采取那种方式去执行
        resp.setContentType("application/json");

        //Session会话管理:除登录和注册接口,其他都需要登录后访问
        //req.getServletPath(); 获取请求的服务路径

        JSONResponse JSresp = new JSONResponse();

        try {

            //调用子类重写的方法
            Object data = process(req, resp);

            // 子类的process 方法操作成功,没有抛异常,表示业务执行成功
            JSresp.setSuccess(true);
            JSresp.setData(data);

        } catch (Exception e){
            //异常如何处理 JDBC的异常SQLException JSON处理的异常
            //自定义异常来返回异常处理的方式
            // 传入前端的异常信息

            e.printStackTrace();
            String code = "UNKNOWN";
            String s = "服务器未知错误";
            if (e instanceof AppException) {
                code = ((AppException) e).getCode();
                s = e.getMessage();
            }

            // 可以不用设置  boolean的基本数据类型false
            JSresp.setSuccess(false);
            JSresp.setCode(code);
            JSresp.setMessage(s);
        }
        PrintWriter p = resp.getWriter();
        // 调用自己写的序列化方法  序列化JSresp对象
        p.println(JSONUtil.serialize(JSresp));
        p.flush();
        p.close();
    }

    // protected : 子类可以用
    protected abstract Object process(HttpServletRequest req,
                                    HttpServletResponse resp) throws Exception;

}

7.servelt层代码

1.登录

package org.example.servlet;

import org.example.MyException.AppException;
import org.example.dao.LoginDAO;
import org.example.model.User;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;


@WebServlet("/login")
public class LoginServlet extends AbstractBaseServlet{

    @Override
    protected Object process(HttpServletRequest req, HttpServletResponse resp) throws Exception {

        String username = req.getParameter("username");
        String password = req.getParameter("password");
        LoginDAO dao = new LoginDAO();
        User user = dao.query(username);

        if (user == null) {
            throw new AppException("LOG002", "用户不存在");
        }

        if (!user.getPassword().equals(password)) {
            throw new AppException("LOG002", "用户名或者密码错误");

        }
        /*if ("abc".equals(username)) {
            // 模拟登录成功
            return null;
            //resp.sendRedirect("jsp/articleList.jsp");
        } else {
            throw new AppException("LOGIN001", "用户名或密码错误");
        }*/

        HttpSession session = req.getSession();
        session.setAttribute("user", user);
        return null;
    }
}

2.获取文章列表

package org.example.servlet;

import org.example.MyException.AppException;
import org.example.dao.ArticleDao;
import org.example.model.Article;
import org.example.model.User;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.List;

@WebServlet("/articleList")
public class ArticleListServlet extends AbstractBaseServlet{

    @Override
    protected Object process(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        // 获取session
        HttpSession  session = req.getSession(false);
        // 如果没有 返回null

        if (session == null) {
            throw new AppException("ART002", "session为空, 用户未登录");
        }
        User user = (User) session.getAttribute("user");

        if (user == null) {
            throw new AppException("ART002", "session不为空, 但没有user");
        }
        Integer user_id = user.getId();
        ArticleDao dao = new ArticleDao();
        List<Article> articles = dao.queryByuUser_id(user_id);

        return articles;
    }
}

  1. 获取文章详情
package org.example.servlet;

import org.example.dao.ArticleDao;
import org.example.model.Article;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/articleDetail")
public class ArticleDetail extends AbstractBaseServlet{
    @Override
    protected Object process(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        String id = req.getParameter("id");
        Article article = ArticleDao.query(Integer.parseInt(id));
        return article;
    }
}

  1. 发布文章
package org.example.servlet;

import org.example.dao.ArticleDao;
import org.example.model.Article;
import org.example.model.User;
import org.example.util.JSONUtil;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.InputStream;

@WebServlet("/articleAdd")
public class ArticleAddServlet extends AbstractBaseServlet {

    @Override
    protected Object process(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        HttpSession session = req.getSession(false);
        User user = (User) session.getAttribute("user");

        // 请求数据类型是application/json 需要反序列化
        InputStream inputStream = req.getInputStream();
        Article article = JSONUtil.deserialize(inputStream, Article.class);
        article.setUser_id(user.getId());

        return ArticleDao.insert(article);
    }
}

  1. 修改文章
package org.example.servlet;

import org.example.dao.ArticleDao;
import org.example.model.Article;
import org.example.util.JSONUtil;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;


@WebServlet("/articleUpdate")
public class ArticleUpdateServlet extends AbstractBaseServlet {

    @Override
    protected Object process(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        // 获取请求中的输入流
        InputStream inputStream = req.getInputStream();
        // 反序列化
        Article article = JSONUtil.deserialize(inputStream, Article.class);
        int num = ArticleDao.updateArticle(article);
        return num;
    }
}

  1. 删除文章
package org.example.servlet;

import org.example.dao.ArticleDao;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/articleDelete")
public class ArticleDeleteServlet extends  AbstractBaseServlet{

    @Override
    protected Object process(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        // 未进行会话管理
        String strid = req.getParameter("ids");
        int num = ArticleDao.delete(strid.split(","));
        return num;
    }
}

8.自定义异常对异常的处理类
package org.example.MyException;



/**
 * 自定义异常类: 业务代码抛自定义异常和其他异常
 *  自定义异常返回给的错误码,其他异常返回其他异常状态码
 * */
public class AppException extends RuntimeException {

    private String code;

    public AppException (String code,String message) {
      //  super(message);
      //  this.code = code;
        this(code, message, null);
    }

    public AppException (String code, String message, Throwable cause) {
        super(message, cause);
        this.code = code;
    }

    public String getCode() {
        return code;
    }
}

9.fitter拦截器

利用fitter拦截器来做用户的会话管理

package org.example.filter;

import org.example.model.JSONResponse;
import org.example.util.JSONUtil;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;

// 匹配路径 指的是匹配所有方法路径
// 进行会话管理
/**
 * 配置用户统一会话管理的过滤器,配配置有请求路径
 * 服务器 资源: /login 不用session检验 其他都需要 返回401
 * 前端资源: /jsp/校验Session ,不通过重定向到登录页面
 *           /js/, /static/, /view/ 全都不校验
* */

@WebFilter("/*")
public class LoginFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    /**
     * 每次http请求匹配到过滤器路径时,会执行过滤器的doFilter
     * 如果往下执行,是调用FilterChain.doFilter(request, response)
     * 否则自行处理响应
     * */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        String servletPath = req.getServletPath();
        // 不需要登录允许访问:
        if (servletPath.startsWith("/js/") || servletPath.startsWith("/static/")
        || servletPath.startsWith("/view/") || servletPath.equals("/login")) {
            chain.doFilter(request, response);
        } else {
            // 获取session对象
            HttpSession session = req.getSession();
            // 验证用户是否登录 , 如果没有登录, hi需要根据前端或后端做不同的处理
            if (session == null || session.getAttribute("user") == null) {
                // 前端重定向到登录页面
                // 后端返回401状态码

                if (servletPath.startsWith("/jsp/")) {
                    // 重定向到登陆页面
                    // 绝对路径
                    resp.sendRedirect(basePath(req) + "/view/login.html");

                } else {
                    // 401 状态码
                    resp.setStatus(401);
                    resp.setContentType("application/json; charset=UTF-8");
                    JSONResponse jsonResponse = new JSONResponse();
                    jsonResponse.setCode("NOLOGIN");
                    jsonResponse.setMessage("用户没有登录");
                    PrintWriter pw = resp.getWriter();
                    // 序列化
                    pw.println(JSONUtil.serialize(jsonResponse));
                    pw.flush();
                    pw.close();
                }

            } else {
                chain.doFilter(request, response);
            }
        }
    }

    /**
     * 根据http请求,动态获取访问路径
     * */
    public static String basePath(HttpServletRequest request) {
        // http
        String schema = request.getScheme();
        // 主机ip或域名
        String host = request.getServerName();
        // 端口
        int port = request.getServerPort();
        String contextPath = request.getContextPath();
        return schema + "://" + host + ":" + port + contextPath;
    }

    @Override
    public void destroy() {

    }
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值