2021-08-13

本文详细介绍了JavaWeb尚硅谷网上书城项目的实现过程,涵盖MVC设计模式、图书管理、分页功能的重点内容。包括图书列表的展示、添加和删除功能的实现,特别是使用重定向避免表单重复提交,以及添加确认提示和分页的优化。此外,还涉及了验证码的使用以防止恶意操作。
摘要由CSDN通过智能技术生成

JavaWeb尚硅谷网上书城项目总结(中)

第四阶段——图书模块

4.1 MVC概念

  • MVC 全称:Model 模型、 View 视图、 Controller 控制器。
  • MVC 最早出现在 JavaEE 三层中的 Web 层,它可以有效的指导 Web 层的代码如何有效分离,单独工作。
  • View 视图:只负责数据和界面的显示,不接受任何与显示数据无关的代码,便于程序员和美工的分工合作——JSP/HTML。
  • Controller 控制器:只负责接收请求,调用业务层的代码处理请求,然后派发页面,是一个“调度者”的角色——Servlet。转到某个页面。或者是重定向到某个页面。
  • Model 模型:将与业务逻辑相关的数据封装为具体的 JavaBean 类,其中不掺杂任何与数据处理相关的代码——JavaBean/domain/entity/pojo。

MVC是一种思想
MVC 的理念是将软件代码拆分成为组件,单独开发,组合使用( 目的还是为了降低耦合度)。
在这里插入图片描述
MVC 的作用还是为了降低耦合。让代码合理分层。方便后期升级和维护。

4.2 图书列表管理页面的实现

如果访问jsp无法直接得到数据,name可以先让程序访问Servlet程序,再请求转发
在这里插入图片描述
BookServlet 程序中添加list方法:

 //查询
    protected void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1通过BookService 查询全部图书
        List<Book> books = bookService . queryBooks();
        //2把全部图书保存到Request域中
        req.setAttribute("books",books);
        //3,请求转发到/pages/manager/book_ manager. jsp页面
    //    req.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(req,resp);
        resp.sendRedirect(req.getContextPath()+"/manager/bookServlet?action=page");
    }

pages/manager/book_manager.jsp页面的数据遍历输出:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>图书管理</title>
	<%--静态包含 base标签,css样式,jQuery--%>
	<%@ include file="../common/head.jsp" %>

	<script type="text/javascript">
		$(function(){
			// 给删除的a标签绑定单击事件,用于删除的确认提示操作
			$("a.deleteClass").click(function(){
			// 在事件的function函数中,有一个this对象。这个this对象,是当前正在响应事件的dom对象。
			/**
			 * confirm是确认提示框函数
			 * 参数是它的提示内容
			 * 它有两个按钮,一个确认,一个是取消。
			 * 返回true表示点击了,确认,返回false表示点击取消。
			 */
			return confirm("你确定要删除【"+$(this).parent().parent().find("td:first").text()+"】");
			// return false// 阻止元素的默认行为===不提交请求
			});
		});
	</script>

</head>
<body>
	
	<div id="header">
			<img class="logo_img" alt="" src="static/img/logo.gif" >
			<span class="wel_word">图书管理系统</span>
		<%--静态包含manager管理模块的菜单--%>
		<%@ include file="../common/manager_menu.jsp" %>
	</div>
	
	<div id="main">
		<table>
			<tr>
				<td>名称</td>
				<td>价格</td>
				<td>作者</td>
				<td>销量</td>
				<td>库存</td>
				<td colspan="2">操作</td>
			</tr>		

			<c:forEach items="${requestScope.page.items}" var="book">
				<tr>
					<td>${book.name}</td>
					<td>${book.price}</td>
					<td>${book.author}</td>
					<td>${book.sales}</td>
					<td>${book.stock}</td>
					<td><a href="manager/bookServlet?action=getBook&id=${book.id}&pageNo=${requestScope.page.pageNo}">修改</a></td>
					<td><a class="deleteClass" href="manager/bookServlet?action=delete&id=${book.id}&pageNo=${requestScope.page.pageNo}">删除</a></td>
				</tr>
			</c:forEach>

			<tr>
				<td></td>
				<td></td>
				<td></td>
				<td></td>
				<td></td>
				<td></td>
				<td><a href="pages/manager/book_edit.jsp?pageNo=${requestScope.page.pageTotal}">添加图书</a></td>
			</tr>	
		</table>


		<%--静态包含分页条--%>
		<%@include file="/pages/common/page_nav.jsp" %>
	</div>

	<%--静态包含页脚内容--%>
	<%@ include file="../common/footer.jsp" %>
</body>
</html>

4.3 添加图书的功能——跳转使用重定向(重点)

图解分析:
在这里插入图片描述
问题说明:如果使用请求转发会出现表单重复提交
当用户提交完请求,浏览器会记录下最后一次请求的全部信息。当用户按下功能键 F5,就会发起浏览器记录的最后一次请求。
解决办法:使用重定向
在这里插入图片描述

4.4 给删除添加确认提示操作:

给删除添加class属性
在这里插入图片描述
javaScript语法:

<script type="text/javascript">
	$(function(){
		// 给删除的a标签绑定单击事件,用于删除的确认提示操作
		$("a.deleteClass").click(function(){
		// 在事件的function函数中,有一个this对象。这个this对象,是当前正在响应事件的dom对象。
		/**
		 * confirm是确认提示框函数
		 * 参数是它的提示内容
		 * 它有两个按钮,一个确认,一个是取消。
		 * 返回true表示点击了,确认,返回false表示点击取消。
		 */
		return confirm("你确定要删除【"+$(this).parent().parent().find("td:first").text()+"】");
		// return false// 阻止元素的默认行为===不提交请求
		});
	});
</script>

4.5 修改图书功能的实现

在这里插入图片描述
在这里插入图片描述

4.6 同一页面如何即执行添加操作又执行修改操作(重点)

book_edit.jsp页面,即要做添加操作,又要做修改操作。所以如何动态修改隐藏域让它的值即可以实现添加,又可以实现修改操作
1.解决方案一
可以发请求发起时,附带上当前要操作的值,并注入到隐藏域中。
在这里插入图片描述

2.解决方案二
可以通过判断当前请求参数中是否包含有id参数。如果有说明修改操作。如果没有说明是添加操作。${empty param.id ? “add”:“update”}
在这里插入图片描述
3.解决方案三
可以通过判断,Request域中是否包含有修改的图书信息对象,如果没有说明是添加操作,如果有说明是修改操作。
在这里插入图片描述

第五阶段——图书分页(重点)

分页模块的分析:
在这里插入图片描述

5.1 分页的初步实现

1.抽取分页模型Page

/**
 * page是分页的模型对象
 * @param <T>是具体的模块的JavaBean类
 */
public class Page<T> {
    public static final Integer PAGE_SIZE=2;
    //当前页码
    private Integer pageNo;
    //总页码
    private Integer pageTotal;
    //当前页显示数量
    private Integer pageSize=PAGE_SIZE;
    //总记录数
    private Integer pageTotalCount;
    //当前页数据
    private List <T> items;

2.BookDao代码:

public interface BookDao {

    // 设置增删改查的方法
    public int addBook(Book book);
    // 删除
    public int deleteBookById(Integer id);
    // 修改
    public int updateBook(Book book);
    // 各种花式查询
    // 通过id查书
    public Book queryBookById(Integer id);
    // 查询所有书
    public List<Book> queryBooks();

    Integer queryForPageToalCount();

    List<Book> queryForPageItems(int begin, int pageSize);
}

4.BookDaoTest代码:

public class BookDaoTest {

    private BookDao bookDao=new BookDaoImpl();
    @Test
    public void addBook() {
        bookDao.addBook(new Book(null,"雇工","1234",
                new BigDecimal(9999),111234,0,null));
    }

    @Test
    public void deleteBookById() {
        bookDao.deleteBookById(17);

    }

    @Test
    public void updateBook() {
        bookDao.updateBook(new Book(17,"平凡的世界","1234",
                new BigDecimal(9999),111234,0,null));
    }

    @Test
    public void queryBookById() {
        System.out.println(bookDao.queryBookById(17));
    }

    @Test
    public void queryBooks() {
        for (Book queryBook : bookDao.queryBooks()) {
            System.out.println(queryBook);
        }
    }
        @Test
        public void queryForPageToalCount() {
          /*  String sql="select count(*) from t_book";
            Number count= (Number) queryForSingleValue(sql);
            return count.intValue();*/

            System.out.println(bookDao.queryForPageToalCount());
        }

        @Test
        public void queryForPageItems() {
           for (Book book:bookDao.queryForPageItems(8, Page.PAGE_SIZE)){
               System.out.println(book);
           }
        }
}

5.BookService代码:

    @Override
    public Page page(int pageNo, int pageSize) {
        Page<Book> page = new Page();

        // 设置每页显示的数量
        page.setPageSize(pageSize);
        // 求总记录数
        int pageTotalCount = bookDao.queryBookTotalCount();
        // 设置总记录数
        page.setPageTotalCount(pageTotalCount);
        // 求总页码
        int pageTotal = pageTotalCount/pageSize + (pageTotalCount%pageSize==0?0:1);
        // 设置总页码
        page.setPageTotal(pageTotal);
        // 设置当前页码
        page.setPageNo(pageNo);

        // 求当前页数据的开始索引
        int begin = (page.getPageNo()-1)*pageSize;
        // 求当前页数据
        List<Book> items = bookDao.queryPageItems(begin,pageSize);
        // 设置当前页数据
        page.setItems(items);
        return page;
    }

6.BookServlet 程序的代码:

    protected void page(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1 获取请求的参数 pageNo 和 pageSize
        int pageNo = WebUtils.parseInt(req.getParameter("pageNo"),1);
//        int pageSize = WebUtils.parseInt(req.getParameter("pageSize"), Page.PAGE_SIZE);
        int pageSize = Page.PAGE_SIZE;
        //2 调用BookService.page(pageNo,pageSize):Page对象
        Page<Book> page = bookService.page(pageNo,pageSize);

        page.setUrl("manager/bookServlet?action=page");

        //3 保存Page对象到Request域中
        req.setAttribute("page",page);
        //4 请求转发到pages/manager/book_manager.jsp页面
        req.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(req,resp);
    }

5.2 首页、上一页、下一页、末页和跳转到指定页面的实现

  • 首页、上一页、下一页、末页的实现
<div id="page_nav">
	<%-- 大于首页,才显示 --%>
	<c:if test="${requestScope.page.pageNo > 1}">
		<a href="manager/bookServlet?action=page&pageNo=1">首页</a>
		<a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo-1}">上一页</a>
	</c:if>
	<a href="#">3</a>
	【${ requestScope.page.pageNo }<a href="#">5</a>
	<%-- 如果已经 是最后一页,则不显示下一页,末页 --%>
	<c:if test="${requestScope.page.pageNo < requestScope.page.pageTotal}">
		<a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo+1}">下一页</a>
		<a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageTotal}">末页</a>
	</c:if>
	共${ requestScope.page.pageTotal }页,${ requestScope.page.pageTotalCount }条记录
	到第<input value="4" name="pn" id="pn_input"/><input type="button" value=" 确定">
</div>

  • 实现跳转到指定页面
    在这里插入图片描述
  • 注意:Page 对象中的修改:
public void setPageNo(Integer pageNo) {
	/* 数据边界的有效检查 */
	if (pageNo < 1) {
		pageNo = 1;
	}
	if (pageNo > pageTotal) {
		pageNo = pageTotal;
	}
	this.pageNo = pageNo;
}

5.3 分页模块中 ,页码处理

需求:显示 5 个连续的页码,而且当前页码在中间。除了当前页码之外,每个页码都可以点击跳到指定页。

情况1 :如果总页码小于等于 5  的情况,页码的范围是:1- 总页码
	11
	212
	3123
	41234
	512345
情况 2 :总页码大于 5  的情况。假设一共 10 页 页
	小情况 1 :当前页码为前面 3  个:123  的情况,页码范围是:1-5.12345
		12345
		12345
	小情况 2 :当前页码为最后 3  个,8910 ,页码范围是:总页码减 4 -  总页码
		678910
		678910
		678910】
	小情况 34567 ,页码范围是:当前页码减 2 -  当前页码加 2
		23456
		34567
		45678
		56789

解决代码:

<%--页码输出的开始--%>
<c:choose>
	<%--情况1:如果总页码小于等于5的情况,页码的范围是:1-总页码--%>
	<c:when test="${requestScope.page.pageTotal <= 5}">
		<c:set var="begin" value="1"/>
		<c:set var="end" value="${requestScope.page.pageTotal}"/>
	</c:when>
	<%--情况2:总页码大于5的情况--%>
	<c:when test="${requestScope.page.pageTotal>5}">
		<c:choose>
			<%--小情况1:当前页码为前面3个:123的情况,页码范围是:1-5.--%>
			<c:when test="${requestScope.page.pageNo<=3}">
				<c:set var="begin" value="1"/>
				<c:set var="end" value="5"/>
			</c:when>
			<%--小情况2:当前页码为最后3个,8910,页码范围是:总页码减4 - 总页码--%>
			<c:when test="${requestScope.page.pageNo > requestScope.page.pageTotal-3}">
				<c:set var="begin" value="${requestScope.page.pageTotal-4}"/>
				<c:set var="end" value="${requestScope.page.pageTotal}"/>
			</c:when>
			<%--小情况34567,页码范围是:当前页码减2 - 当前页码加2--%>
			<c:otherwise>
				<c:set var="begin" value="${requestScope.page.pageNo-2}"/>
				<c:set var="end" value="${requestScope.page.pageNo+2}"/>
			</c:otherwise>
		</c:choose>
	</c:when>
</c:choose>
<c:forEach begin="${begin}" end="${end}" var="i">
	<c:if test="${requestScope.page.pageNo == i}">
		【${i}</c:if>
	<c:if test="${requestScope.page.pageNo != i}">
		<a href="manager/bookServlet?action=page&pageNo=${i}">${i}</a>
	</c:if>
</c:forEach>

5.4 修改分页后,增加,删除,修改图书信息的回显页面

以修改图书为示例:
1.在修改的请求地址上追加当前页码参数:
在这里插入图片描述
2.在 book_edit.jsp 页面中使用隐藏域记录下 pageNo 参数
在这里插入图片描述
3.在服务器重定向的时候,获取当前页码追加上进行跳转
在这里插入图片描述

5.5 分页条的抽取(难点)

1.在page对象中添加 url
在这里插入图片描述

2.在Servlet 程序的page分页方法中设置 url
在这里插入图片描述
3.修改分页条中请求地址为url变量输出,并抽取一个单独的 jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--分页条的开始--%>
<div id="page_nav">
    <%--大于首页,才显示--%>
    <c:if test="${requestScope.page.pageNo > 1}">
        <a href="${requestScope.page.url}&pageNo=1">首页</a>
        <a href="${requestScope.page.url}&pageNo=${requestScope.page.pageNo-1}">上一页</a>
    </c:if>

    <%--页码输出的开始--%>
    <c:choose>
        <%--情况1:如果总页码小于等于5的情况,页码的范围是:1-总页码--%>
        <c:when test="${requestScope.page.pageTotal <= 5}">
            <c:set var="begin" value="1"/>
            <c:set var="end" value="${requestScope.page.pageTotal}"/>
        </c:when>
        <%--情况2:总页码大于5的情况--%>
        <c:when test="${requestScope.page.pageTotal>5}">
            <c:choose>
                <%--小情况1:当前页码为前面3个:123的情况,页码范围是:1-5.--%>
                <c:when test="${requestScope.page.pageNo<=3}">
                    <c:set var="begin" value="1"/>
                    <c:set var="end" value="5"/>
                </c:when>
                <%--小情况2:当前页码为最后3个,8910,页码范围是:总页码减4 - 总页码--%>
                <c:when test="${requestScope.page.pageNo > requestScope.page.pageTotal-3}">
                    <c:set var="begin" value="${requestScope.page.pageTotal-4}"/>
                    <c:set var="end" value="${requestScope.page.pageTotal}"/>
                </c:when>
                <%--小情况34567,页码范围是:当前页码减2 - 当前页码加2--%>
                <c:otherwise>
                    <c:set var="begin" value="${requestScope.page.pageNo-2}"/>
                    <c:set var="end" value="${requestScope.page.pageNo+2}"/>
                </c:otherwise>
            </c:choose>
        </c:when>
    </c:choose>
    <c:forEach begin="${begin}" end="${end}" var="i">
        <c:if test="${requestScope.page.pageNo == i}">
            【${i}</c:if>
        <c:if test="${requestScope.page.pageNo != i}">
            <a href="${requestScope.page.url}&pageNo=${i}">${i}</a>
        </c:if>
    </c:forEach>
    <%--如果已经是最后一页,则不显示下一页,末页--%>
    <c:if test="${requestScope.page.pageNo < requestScope.page.pageTotal}">
        <a href="${requestScope.page.url}&pageNo=${requestScope.page.pageNo+1}">下一页</a>
        <a href="${requestScope.page.url}&pageNo=${requestScope.page.pageTotal}">末页</a>
    </c:if>

    共${requestScope.page.pageTotal}页,${requestScope.page.pageTotalCount}条记录
    到第<input value="${requestScope.page.pageNo}" name="pn" id="pn_input"/><input type="button" id="searchPageBtn" value="确定" >
    <%--跳转到指定页数功能--%>
    <script type="text/javaScript">
        $(function(){
            $("#searchPageBtn").click(function(){
                var pageNo = $("#pn_input").val();
                <%--var pageTotal = ${requestScope.page.pageTotal};--%>
                <%--alert(pageTotal);--%>

                // javaScript语言中提供了一个location地址栏对象
                // 它有一个属性叫href.它可以获取浏览器地址栏中的地址
                // href属性可读,可写,
                // 赋值操作,location.href=”/url” 表示当前页面打开URL页面
                // 读操作,表示获取当前地址栏的url
                location.href = "${requestScope.basePath}${requestScope.page.url}&pageNo="+pageNo;
            });
        });
    </script>
</div>
<%--分页条的结束--%>

第六阶段——验证码

6.1 显示登录的用户信息

登录成功之后将用户登录的信息保存到session中
UserServlet 程序中保存用户登录的信息:
在这里插入图片描述
页面由登录注册改为显示登录的用户信息
在这里插入图片描述
在这里插入图片描述

6.2 登出——注销用户

1、销毁 Session 中用户登录的信息(或者销毁 Session)
2、重定向到首页(或登录页面)

UserServlet 程序中添加 logout 方法:

    /**
     * 注销
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    protected void logout(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1、销毁 Session 中用户登录的信息(或者销毁 Session)
        req.getSession().invalidate();
        //2、重定向到首页(或登录页面)
        resp.sendRedirect(req.getContextPath());
    }

6.3 表单重复提交之——验证码(重点)

表单重复提交有三种常见的情况:

一:提交完表单。服务器使用请求转来进行页面跳转。这个时候,用户按下功能键 F5,就会发起最后一次的请求。造成表单重复提交问题。解决方法:使用重定向来进行跳转
在这里插入图片描述
二:用户正常提交服务器,但是由于网络延迟等原因,迟迟未收到服务器的响应,这个时候,用户以为提交失败,就会着急,然后多点了几次提交操作,也会造成表单重复提交。
三:用户正常提交服务器。服务器也没有延迟,但是提交完成后,用户回退浏览器。重新提交。也会造成表单重复提交。
在这里插入图片描述

6.4 谷歌 kaptcha 图片验证码的使用

谷歌验证码 kaptcha 使用步骤如下:
1.导入谷歌验证码的 jar 包
kaptcha-2.3.2.jar
在这里插入图片描述
2.在 web.xml 中去配置用于生成验证码的 Servlet 程序
com.google.code.kaptcha.servlet.KaptchaServlet,生成一个图片。同时将生成的验证码字符串放到HttpSession中
3.在表单中使用 img 标签去显示验证码图片并使用它

<form  method="get" action="http://localhost:8080/tmp/loginServlet">
    用户名:<input type="text" name="username" /><br/>
    验证码:<input type="text" name="code" style="width:50px"/>
    <img src="/tmp/kaptcha.jpg" style="width:100px;height:28px;"><br/>
    <input type="submit" value="登录"/>
</form>

4.在服务器获取谷歌生成的验证码和客户端发送过来的验证码比较使用

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取Session中的验证码
        String token = (String)req.getSession().getAttribute("KAPTCHA_SESSION_KEY");
        //删除Session中的验证码,防止重复提交
        req.getSession().removeAttribute("KAPTCHA_SESSION_KEY");
        
        String code = req.getParameter("code");
        //获取用户名
        String username = req.getParameter("username");
        if(token != null && token.equalsIgnoreCase(code)){
            System.out.println("保存到数据库"+username);
            resp.sendRedirect("/tmp/ok.jsp");
        }else{
            System.out.println("请不要重复提交表单");
        }
    }

  • 切换验证码的实现(重点)
//给验证码的图片,绑定单击事件
$("#code_img").click(function(){
	// 在事件响应的 function 函数中有一个 this 对象。这个 this 对象,是当前正在响应事件的 dom 对象
	// src 属性表示验证码 img 标签的 图片路径。它可读,可写
	// alert(this.src);
	this.src = "kaptcha.jpg?d="+new Date();//在后面加上一个每次都不同的值
});

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值