1 BootStrap介绍
Bootstrap是由Twitter公司的工程师开发的最受欢迎的HTML、CSS和JavaScript框架(侧重点在CSS样式),用于开发响应式布局、移动设备优先的 WEB 项目。将资源导入工程内(WEB-INF目录下),引入到HTML/JSP页面中即可使用。
1.1 特点
响应式布局:相同的内容根据显示设备的不同的,显示不同的效果。
1.2 本项目使用了框架的哪些模块
后台框架的布局、栅格布局、模态框、表单按钮样式。
2 JavaWEB中的过滤器(javax.servlet.Filter接口)
过滤器是用来执行过滤任务的对象。通常过滤请求或者响应。过滤器在容器启动时就会被构造对象,并初始化,而Servlet默认在请求到达时构造对象并初始化。
当请求来到容器,容器发现调用servlet的service()方法前可以应用某些过滤器时,就会调用该过滤器的doFilter()方法。
doFilter()方法的执行就像一条链子,可以在doFilter()方法中进行service()方法的前置处理,而后决定是否调用下一个doFilter()方法。如果调用了FilterChain的doFilter()方法,就会运行下一个过滤器,如果没有下一个过滤器了,就会调用请求目标Servlet的service()方法。如果因为某个情况而没有调用FilterChain的doFilter(),则请求就不会继续交给接下来的过滤器或目标Servlet。
在陆续调用完Filter实例的doFilter()乃至Servlet的service()之后,流程会以堆栈顺序返回,所以在FilterChain的doFilter()运行完毕后(这行代码后面),可针对service()方法做后续处理。
以上文段摘自conkeyn@iteye,感谢大佬的分享!
2.1 方法列表(javax.servlet.Filter接口)
Filter | Servlet |
---|---|
init(FilterConfig) | init(ServletConfig) |
doFilter(request,response,chain) | service(request,response) |
destory | destroy |
每一个Servlet都有一个与之对应的ServletConfig对象,同理,每一个Filter都有一个与之对应的FilterConfig对象。
FilterConfig | ServletConfig |
---|---|
getInitParameter() | getInitParameter() |
getInitParameterNames() | getInitParameterNames() |
getServletContext() | getServletContext() |
getFilterName() | getServletName() |
3 文件上传(同步)
3.1 文件上传表单和普通表单的区别
- 普通表单 <form action=”” method=”” enctype=””>
- Method:可以是GET 也可以是POST
- enctype:默认的application/x-www-form-urlencoded,即底层浏览器将表单的参数组装成key=value的形式发送到服务器
- 文件上传的表单 <form action=”” method=”” enctype=””>
- Method:必须是POST
- enctype:必须是multipart/form-data,即底层浏览器把参数以分隔符分隔开,把文件以二进制的形式放到服务器
类似于
—WebKitFormBoundaryoo7HNoqzROQRNJZ4
Name=”name” …
Xxxx
—WebKitFormBoundaryoo7HNoqzROQRNJZ4
Name=”method”
Xfadfaf
—WebKitFormBoundaryoo7HNoqzROQRNJZ4
Name=”phone”
135…
—WebKitFormBoundaryoo7HNoqzROQRNJZ4
Name=”xxx.jpg”
Fddfadfasfadsf
—WebKitFormBoundaryoo7HNoqzROQRNJZ4
- form表单中不允许出现action属性
上传的具体代码片段(完整版链接)
//用户注册
protected void regAdmin(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
//使用SmartUpload的三个步骤
//S1.构造对象
SmartUpload su = new SmartUpload();
//S2.初始化
su.initialize(this, request, response);
//S3.上传,第三方组件解析请求,获取前端发来的参数并封装到自己的对象中
try {
su.upload();
Admin admin = new Admin();
//这里的Request及其方法均为被封装的SmartUpload的普通类及方法
//先将基本参数封装到自己的对象中
Request req = su.getRequest();
String username = req.getParameter("username");
String pwd = req.getParameter("pwd");
String phone = req.getParameter("phone");
String email = req.getParameter("email");
//基本参数存入实体类
admin.setCreatetime(new Timestamp(System.currentTimeMillis()));
admin.setEmail(email);
admin.setUsername(username);
admin.setPhone(phone);
admin.setPwd(pwd);
admin.setUpdatetime(new Timestamp(System.currentTimeMillis()));
//获得文件参数
Files fs = su.getFiles();
//获得其中第一个文件
File f = fs.getFile(0);
//获取文件名
String fName = f.getFileName();
//获取文件后缀
String fExt = f.getFileExt();
//还可以获取到上传这个文件的控件的名字
//如<input type="file" name="avatar">中的avatar
String fieldName = f.getFieldName();
//为了图片在数据库存储的独一性,图片存储到数据库的文件名需要修改,需要拼接文件后缀的名字
String newName = UUID.randomUUID().toString().replaceAll("-", "") + "." + fExt;
//把文件从第三方插件中保存到指定目录
//这里新建一个/myFiles目录
f.saveAs("/myFiles" + newName);
//文件在服务器中保存的路径
admin.setSavepath("/myFiles" + newName);
//文件原名
admin.setShowname(fName);
AdminService as = new AdminService();
PrintWriter out = response.getWriter();
if(as.regAdmin(admin) == 1){
session.setAttribute("admin", admin);
out.print("success");
}else out.print("fail");
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
3.2 服务器处理文件表单和普通表单的区别
- 普通表单:
服务器接收并解析请求,通过reqest.setHeader/setMethod/setProtocol/setParameter,我们可以在servlet中通过request获得参数信息和其他信息。 - 文件上传表单:
服务器解析请求,setHeader/setMethod不会setParameter,但提供了setInputStream方法,我们在Servlet中需要手动地从请求头中获得分隔符,通过分隔符手动解析表单的普通参数,再通过InputStream获得文件参数。一般该工作交给第三方组件来完成。我们直接从第三方插件中获得普通参数和文件参数。常用的第三方插件有jsp-smartUpload/apache-commons-fileupload。
3.3 常见错误 Files' name is invalid or does not exist
如果使用表单提交(button的type为submit),需要在form标签设置enctype为multipart/form-data。项目中使用ajax方式提交,详见我的Githubweb->reg.jsp。
4 踩坑记录
-
a标签是重定向,完全是浏览器的动作。
重定向的过程:
客户浏览器发送HTTP请求–>WEB服务器接受后发送302状态码响应及对应新的location给客户浏览器–>客户浏览器发现是302响应,则自动再发送一个新的http请求,请求url是新的location地址–>服务器根据此请求寻找资源并发送给客户
在这里location可以重定向到任意URL,
既然是浏览器重新发出了请求,则就没有什么request传递的概念了。
在客户浏览器路径栏显示的是其重定向的路径,客户可以观察到地址的变化的。
重定向行为是浏览器做了至少两次的访问请求的。 -
对比一下请求转发:
转发的过程:客户浏览器发送HTTP请求–>WEB服务器接受此请求–>调用内部的一个方法在容器内部完成请求处理和转发动作–>将目标资源发送给客户
转发的路径必须是同一个WEB容器下的URL,其不能转向到其他的WEB路径上去
中间传递的是自己的容器内的request。在客户浏览器路径栏显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器只做了一次访问请求。 -
一对多的关系要在多的一边设置外键,一对一和一对多都可以使用外键实现。外键多用在一对多上,多对多使用关联表。
-
Myeclipse/Eclipse将WEB-INF下的文件 复制到tomcat的webapps文件下;Intellij每次启动复制一份tomcat的配置文件,相当于将tomcat下的webapps搬运到本项目下的out目录,在项目中运行。out目录之外是工作区间,当有些改动未生效时,不妨查看out目录的文件是否已修改。
-
页面的触发事件绑定的函数名不能与页面某个元素的id名相同,一些浏览器可以通过在JS代码中指定ID访问节点元素,然后定义的函数就会被DOM中的元素覆盖了。
-
使用jQuery的AJAX时,一定要注意写好服务端返回到前端的数据类型。服务器仅返回一对键值对,可以使用text、JSON类型在前端取;返回两个以上或者返回的是集合、对象、数组,要使用JSON格式返回到前端。
-
JSON有两种类型,字符串和对象。JS解析的是JSON对象,字符串一般使用parse转为JSON对象,eval不常用;JSON对象使用stringfy转为字符串。
-
PrinterWriter中out方法和write方法的区别
1.out对象的类型是JspWriter。JspWriter继承了java.io.Writer类。
2.print方法是子类JspWriter,write是Writer类中定义的方法;
3.重载的print方法可将各种类型的数据转换成字符串的形式输出,而重载的write方法只能输出字符、字符数组和字符串等与字符相关的数据;
4.JspWriter类型的out对象使用print方法和write方法都可以输出字符串,但是,如果字符串对象的值为null时,print方法将输出内容为“null”的字符串,而write方法则是抛出NullPointerException异常。
5 分页功能
通常,数据的显示都伴随着分页。这里使用jQuery实现分页功能,其中需要定义一个分页查询的函数,每次加载页面完成时按页码、一页包含的条数、查询条件执行这个分页查询的函数,服务器将数据响应回来,数据中还包括总共有多少条数据、总页数、上一页、下一页页码,此时更新对应的JS全局变量,供下次使用。分页操作的四个按钮中分别包含不同起始页码作为参数的分页查询功能。详见我的Githubweb->index_tbList.jsp
同时,后端还需要定义一个Bean,来接收、计算、返回分页相关的参数。详见我的Githubsrc->common->Page->page.java。
6 模块介绍
6.1 关系表(见src下的table.sql)
- 管理员表
- 餐桌表
6.2 功能
- 管理员
- 新增管理员(基于jQuery的自制表单验证,基于SmartUpload同步上传文件、基于Servlet的验证码)
- 修改管理员信息(只针对手机号、邮箱修改,同上的表单验证、基于Servlet的验证码功能)
- 餐桌
- 新增餐桌(同上的表单验证)
- 分页显示餐桌(分页工具自制)
- 查询餐桌(输入桌号查询对应的餐桌)
- 过滤器(JavaWEB中自带的Filter的使用)
- 界面使用BootStrap搭建,点击选项卡切换页面使用iframe实现
6.3 源码
我的源码托管在Github,欢迎访问!
基于Java Servlet/DBUtils和BootStrap的餐厅(餐桌)管理系统部分模块