一、Request的另一重功能
1.请求/资源的转发
1.1 作用:资源跳转的一种方式 request得到转发器,然后调用方法进行请求转发
request.getRequestDispatcher("/success.html").forward(request,response);
1.2 业务场景:客户端发送请求,请求服务器某一个资源的时候,这个资源处理不了请求,然后再把请求交给其它资源处理
1.3 特点:
浏览器只会发生了一次请求
浏览器地址栏不会发生变化
请求转发的时候只能跳转到服务器内容资源
2.作为域对象共享数据
2.1 什么是域对象??
在一定的范围内可以共享数据的对象
2.2 每个域对象的作用范围??
request域对象的作用范围--- 一次请求范围内(请求转发的时候)
2.3 域对象是如何共享数据的??
request对象,两个servlet所使用的request对象是一个对象
将数据放入到request对象中,在两个Servlet中如何传递数据,使用request对象
request.setAttribute("money",15);/设置数据
(int)request.getAttribute("money");/获取数据
request.removeAttribute("money");/移除数据
二、response的学习–设置响应数据
1.http协议响应消息数据
1.1 响应行--HTTP/1.1 200 ok(协议版本,响应状态码,状态码的描述)
状态码是用来标识服务器此次响应的状态:
状态码分类
1** 信息 服务器收到请求,需要请求者继续执行操作
2** 成功 操作被成功接收并处理
3** 重定向 需要进一步的操作以完成请求
4** 客户端错误 请求包含语法错误或无法完成请求
5** 服务器错误 服务器在处理请求的过程中发生了错误
常用状态码
200 OK 请求成功。一般用于GET与POST请求
302 Found 临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI
304 Not Modified 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
404 Not Found 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
405 Method Not Allowed 客户端请求中的方法被禁止
500 Internal Server Error 服务器内部错误,无法完成请求
1.2 响应头--服务器传给客户端
Content-Type:text/html;charset="utf-8"/服务器告诉浏览器本次响应的数据格式和编码
Content-length: 50/本次响应体的长度
2.response对象的基础功能
2.1 描述:response是代表本次请求的响应消息对象,可以用来设置响应数据
2.2 设置响应行的数据
res.setStatus(200);/设置状态码
2.3 设置响应头
res.setHeader("key1","value1");
2.4 设置响应体数据
字节流
os-->res.getOutputStream();
os.write("hello tomcat!!".getBytes());
字符流
os-->res.getWriter();
os.write("hello tomcat!!");
3.response对象的其它功能
3.1 重定向:资源跳转的一种方式--302状态码+location响应头
res.setStatus(302);
res.setHeader("location","/responseServlet04")
res.sendRedirect("/responseServlet04");/一行代码替代上面两行代码实现重定向
3.2 面试题:重定向和请求转发的区别
请求转发时客户端只会发送一次请求,重定向会发送两次请求
请求转发时浏览器地址栏不会发生变化,而重定向时浏览器地址栏会发生变化
请求转发时只能跳转到服务器内部资源,而重定向可以跳转到其它服务器资源
3.3 什么时候使用重定向,什么时候使用请求转发
如果跳转的两个资源之间要通过request对象去共享数据,那就只能使用请求转发
如果地址栏要发生变化,或者要跳转到其它服务器的资源的时候,必须使用重定向
3.4 如果为项目配置一个虚拟路径
重定向的路径前面一定要加虚拟路径,如果是请求转发前面不需要添加虚拟路径
资源跳转的路径问题
请求转发的时候--服务器内部使用路径
重定向的时候--不在服务器内部资源的使用路径
虚拟路径--区分不在同一个项目里面的资源所要用到的
4.响应字符数据到浏览器(不需要在控制台打印日志了)
4.1 响应的如果是英文字符,就不会出现乱码问题
res.getWriter().println("hello tomcat!!");
4.2 响应的如果是中文
res.getWriter().println("中文");/会出现乱码
1.字符流
会有缓冲区,缓冲区会有乱码,设置响应数据,数据的编码是utf-8 缓冲区的编码是gbk
设置缓冲区的编码:
res.setCharacterEncoding("utf-8");
服务器告诉浏览器本次响应的数据格式和编码
res.setHeader("Content-Type","text/html;charset=utf-8");
resp.setContentType("text/html;charset=UTF-8")
2.字节流
那么不会有缓冲区,在调用"中文".getBytes()--->GB2312
如果浏览器的默认编码和服务器的默认编码不一致就可能会导致中文乱码问题
resp.setContentType("text/html;charset=UTF-8")
res.getOutputStream().write("中文".getBytes("utf-8"))
三、动态表格展示
1.前言
1.1 首先要写出这样一个动态表格展示的程序必须对Servlet、tomcat要有基础,如果不懂的请先参考博主有关Javaweb的笔记进行相应的学习
1.2 学习完成后先去找到博主的上一篇博客,独立完成上一篇关于登录案例的练习
1.3 对tomcat和Servlet有了全面的了解之后再跟着博主的步伐一起来完成动态表格的展示
2.过程分析
在servlet里面给html里面响应一个表格(里面的信息是数据库中的信息)
请求/userServlet,在UserServlet这个类中接收到请求,然后查询到数据库
中所有的用户记录--UserDao/findAll(),遍历集合生成表格响应到浏览器中
3.创建一个domian包,在这个包下面创建一个Student类
package com.bianyiit.domain;
public class Student {
private int id;
private String username;
private String password;
public Student() {
}
public Student(int id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
4.新建一个dao包,在这个包创建一个UserDao类,使用这个类来完成整个数据库的操作,当然druid连接池和spring对jdbc的封装这些都要有,具体的参考博主上一篇的登录案例
package com.bianyiit.dao;
import com.bianyiit.domain.Student;
import com.bianyiit.util.DruidUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
public class UserDao {
public List<Student> findAll(){
JdbcTemplate jdbcTemplate = new JdbcTemplate(DruidUtils.getDataSource());
String sql="select * from message";
List<Student> query = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Student>(Student.class));
return query;
}
}
5.新建一个Servlet包,在这个包下面创建一个UserServlet类
package com.bianyiit.servlet;
import com.bianyiit.dao.UserDao;
import com.bianyiit.domain.Student;
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("/userServlet")
public class UserServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*1.查询所有的用户信息
* 2.遍历用户集合,生成表格,响应到浏览器中*/
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=UTF-8");
UserDao userDao = new UserDao();
List<Student> userList = userDao.findAll();
response.getWriter().println("<!DOCTYPE html>\n" +
"<html lang=\"en\">\n" +
"<head>\n" +
" <meta charset=\"UTF-8\">\n" +
" <title>数据展示</title>\n" +
"</head>\n" +
"<body>\n" +
" <table border='1px' width='200px'>\n" +
" <tr>\n" +
" <td>id</td>\n" +
" <td>username</td>\n" +
" <td>password</td>\n" +
" </tr>\n");
for (Student student : userList) {
response.getWriter().println("<tr>");
response.getWriter().println("<td>"+student.getId()+"</td>");
response.getWriter().println("<td>"+student.getUsername()+"</td>");
response.getWriter().println("<td>"+student.getPassword()+"</td>");
response.getWriter().println("</tr>");
}
response.getWriter().println(" </table>\n" +
"</body>\n" +
"</html>");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
6.结果展示
7.当我们在数据库中插入一条数据,然后刷新一下浏览器
四、文件下载
1.文件下载的两种方式
方式一:使用超链接的方式指向要下载的资源
注意:超链接下载的资源必须是浏览器不能直接打开的资源,如果浏览器能打开就直接打开了,不会启动下载程序
方式二:在servlet里面读取一个文件,然后通过response将这个文件响应到浏览器上
必须添加一个响应头,并设置为attachment为附件的形式
response.setHeader("Content-Disposition","attachment;filename="+filename);
2.使用方式二如何完成文件下载的案例
3.首先创建一个download.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="img/e.rar">下载</a>
<a href="/day01/downloadServlet?filename=1.jpg">下载图片</a>
<a href="/day01/downloadServlet?filename=2.jpg">下载图片</a>
</body>
</html>
4.新建一个Servlet包,在这个包下面创建一个DownloadServlet类
package com.bianyiit.servlet;
import com.bianyiit.util.FilenameUtils;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
@WebServlet("/downloadServlet")
public class DownloadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*1.接收请求参数,返回文件名*/
String filename = request.getParameter("filename");
/*2.通过输入流读取文件内容*/
String basepath="D:\\学习专用代码库\\day01\\web\\img\\";
FileInputStream fis = new FileInputStream(basepath+filename);
/*处理中文文件名*/
String agent = request.getHeader("User-Agent");
filename= FilenameUtils.getFileName(agent,filename);
/*3.通过responnse把文件写回浏览器*/
response.setHeader("Content-Disposition","attachment;filename="+filename);
ServletOutputStream os = response.getOutputStream();
byte[] buf=new byte[1024];
int len=0;
while((len=fis.read(buf))!=-1){
os.write(buf,0,len);
}
fis.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
5.新建一个util包,在这个包下面新建一个FilenameUtils类,用来解决图片中可能存在中文的情况
package com.bianyiit.util;
import sun.misc.BASE64Encoder;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
public class FilenameUtils {
public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {
if (agent.contains("MSIE")) {
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
// 火狐浏览器
BASE64Encoder base64Encoder = new BASE64Encoder();
filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
}
6.结果演示--当我们点击图片下载的时候,会自动下载图片
四、验证码的实现案例
1.新建一个index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<title>Bootstrap 101 Template</title>
<!-- Bootstrap -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- HTML5 shim 和 Respond.js 是为了让 IE8 支持 HTML5 元素和媒体查询(media queries)功能 -->
<!-- 警告:通过 file:// 协议(就是直接将 html 页面拖拽到浏览器中)访问页面时 Respond.js 不起作用 -->
<!--[if lt IE 9]>
<script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
<![endif]-->
<script>
window.onload=function () {
document.getElementById("checkcode").onclick=function () {
document.getElementById("checkcode").src="/day01/checkCodeServlet?time="+new Date().getTime()
}
}
</script>
</head>
<body>
<!--留白的-->
<div class="container">
<br>
<hr width="50%">
<form class="form-horizontal" action="/day01/web" method="post">
<div class="form-group">
<label for="username" class="col-sm-4 control-label">请输入用户名</label>
<div class="col-sm-3">
<input type="text" class="form-control" id="username" name="username">
</div>
</div>
<div class="form-group">
<label for="password" class="col-sm-4 control-label">请输入密码</label>
<div class="col-sm-3">
<input type="password" class="form-control" id="password" name="password">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-4 col-sm-10">
<img id="checkcode" src="/day01/checkCodeServlet">
<button type="submit" class="btn btn-default">登录</button>
</div>
</div>
</form>
<hr width="50%">
</div>
<!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
<script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
<!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
</body>
</html>
从上面提取的与验证码相关的代码如下所示:
<script>
window.onload=function () {
document.getElementById("checkcode").onclick=function () {
document.getElementById("checkcode").src="/day01/checkCodeServlet?time="+new Date().getTime()
}
}
</script>
<img id="checkcode" src="/day01/checkCodeServlet">
新建一个Servlet包,在这个包下新建一个ValidateCodeServlet类
package com.bianyiit.servlet;
import javax.imageio.ImageIO;
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.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
@WebServlet("/checkCodeServlet")
public class ValidateCodeServlet extends HttpServlet {
public static final int WIDTH = 120;//定义图片的长
public static final int HEIGHT = 30;//定义图片的高
@Override
public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//首先先生成一张图片
BufferedImage bi = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
//获取画笔
Graphics graphics = bi.getGraphics();
//给图片设置背景 写一个设置背景的方法
setBackground(graphics);
//设置边框 在下面定义一个设置边框的方法
setBorder(graphics);
//像图片中设置干扰线 在下面定义一个干扰线
setLine(graphics);
//设置干扰点
setPoint(graphics);
//创建一个随机数
String baseString = "1234567890abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ";
String code = createRandomCode(baseString, (Graphics2D) graphics);//调用下面写入随机数的方法 将随机生成的字符写入到图片中
req.getSession().setAttribute("code", code);
//向页面中输出
resp.setContentType("image/jpeg");//设置类型
ImageIO.write(bi, "jpeg", resp.getOutputStream());
}
/**
* 设置背景
*
* @param g
*/
public void setBackground(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, WIDTH, HEIGHT);
}
/**
* 设置边框
*
* @param g
*/
public void setBorder(Graphics g) {
g.setColor(Color.GRAY);
g.drawRect(1, 1, WIDTH - 2, HEIGHT - 2);
}
/**
* 设置干扰线
*
* @param g
*/
public void setLine(Graphics g) {
//设置彩色的干扰线
g.setColor(new Color(new Random().nextInt(200), new Random().nextInt(90), new
Random().nextInt(255)));
for (int i = 0; i < 5; i++) {
int x1 = new Random().nextInt(WIDTH);
int x2 = new Random().nextInt(WIDTH);
int y1 = new Random().nextInt(HEIGHT);
int y2 = new Random().nextInt(HEIGHT);
g.drawLine(x1, y1, x2, y2);
}
}
/**
* 设置干扰点
*
* @param g
*/
public void setPoint(Graphics g) {
//设置彩色的干扰线
g.setColor(new Color(new Random().nextInt(200), new Random().nextInt(90), new Random().nextInt(255)));
for (int i = 0; i < 100; i++) {
int x = new Random().nextInt(WIDTH);
int y = new Random().nextInt(HEIGHT);
g.drawOval(x, y, 1, 1);
}
}
/**
* 产生随机数
*
* @param baseString
* @param g
* @return
*/
public String createRandomCode(String baseString, Graphics2D g) {
StringBuffer sb = new StringBuffer();
//定义X
int x = 5;
String ch = "";
//产生随机数
for (int i = 0; i < 5; i++) {
//产生一个随机索引
int index = new Random().nextInt(baseString.length());
//产生一个随机数
ch = baseString.charAt(index) + "";
//把产生的随机数添加到stringbuffer中
sb.append(ch);
//为每个字体设置颜色
g.setColor(new Color(new Random().nextInt(255), new Random().nextInt(255), new Random().nextInt(255)));
//设置字体
g.setFont(new Font("微软雅黑", Font.BOLD, 20));
//把随机数写入图片
g.drawString(ch, x, 20);
//设置一个旋转的角度
int degree = new Random().nextInt(5) % 30;
//实现旋转
g.rotate(degree * Math.PI / 180, x, 20);
x += 20;
}
return sb.toString();
}
}
3.结果演示--当我们点击验证码时,会随机的生成不同的验证码