JavaWeb开发与代码的编写(三)
HttpServletResponse对象
Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。
request和response对象即然代表请求和响应,那我们要获取客户机提交过来的数据,只需要找request对象就行了。要向客户机输出数据,只需要找response对象就行了。
HttpServletResponse对象代表服务器的响应。这个对象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。查看HttpServletResponse的API,可以看到这些相关的方法。
1.1、负责向客户端(浏览器)发送数据的相关方法
1.2、负责向客户端(浏览器)发送响应头的相关方法
1.3、负责向客户端(浏览器)发送响应状态码的相关方法
1.4、响应状态码的常量
HttpServletResponse定义了很多状态码的常量(具体可以查看Servlet的API),当需要向客户端发送响应状态码时,可以使用这些常量,避免了直接写数字,常见的状态码对应的常量:
状态码404对应的常量
状态码200对应的常量
状态码500对应的常量
HttpServletResponse对象常见应用
2.1、使用OutputStream流向客户端浏览器输出中文数据
使用OutputStream流输出中文注意问题:
在服务器端,数据是以哪个码表输出的,那么就要控制客户端浏览器以相应的码表打开,比如:outputStream.write("中国".getBytes("UTF-8"));使用OutputStream流向客户端浏览器输出中文,以UTF-8的编码进行输出,此时就要控制客户端浏览器以UTF-8的编码打开,否则显示的时候就会出现中文乱码,那么在服务器端如何控制客户端浏览器以以UTF-8的编码显示数据呢?可以通过设置响应头控制浏览器的行为,例如:response.setHeader("content-type", "text/html;charset=UTF-8");通过设置响应头控制浏览器以UTF-8的编码显示数据。
范例:使用OutputStream流向客户端浏览器输出"中国"这两个汉字
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ResponseDemo01 extends HttpServlet {
private static final long serialVersionUID = 4312868947607181532L;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
outputChineseByOutputStream(response);//使用OutputStream流输出中文
}
/**
* 使用OutputStream流输出中文
* @param request
* @param response
* @throws IOException
*/
public void outputChineseByOutputStream(HttpServletResponse response) throws IOException{
/**使用OutputStream输出中文注意问题:
* 在服务器端,数据是以哪个码表输出的,那么就要控制客户端浏览器以相应的码表打开,
* 比如:outputStream.write("中国".getBytes("UTF-8"));//使用OutputStream流向客户端浏览器输出中文,以UTF-8的编码进行输出
* 此时就要控制客户端浏览器以UTF-8的编码打开,否则显示的时候就会出现中文乱码,那么在服务器端如何控制客户端浏览器以以UTF-8的编码显示数据呢?
* 可以通过设置响应头控制浏览器的行为,例如:
* response.setHeader("content-type", "text/html;charset=UTF-8");//通过设置响应头控制浏览器以UTF-8的编码显示数据
*/
String data = "中国";
OutputStream outputStream = response.getOutputStream();//获取OutputStream输出流
response.setHeader("content-type", "text/html;charset=UTF-8");//通过设置响应头控制浏览器以UTF-8的编码显示数据,如果不加这句话,那么浏览器显示的将是乱码
/**
* data.getBytes()是一个将字符转换成字节数组的过程,这个过程中一定会去查码表,
* 如果是中文的操作系统环境,默认就是查找查GB2312的码表,
* 将字符转换成字节数组的过程就是将中文字符转换成GB2312的码表上对应的数字
* 比如: "中"在GB2312的码表上对应的数字是98
* "国"在GB2312的码表上对应的数字是99
*/
/**
* getBytes()方法如果不带参数,那么就会根据操作系统的语言环境来选择转换码表,如果是中文操作系统,那么就使用GB2312的码表
*/
byte[] dataByteArr = data.getBytes("UTF-8");//将字符转换成字节数组,指定以UTF-8编码进行转换
outputStream.write(dataByteArr);//使用OutputStream流向客户端输出字节数组
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
运行结果如下:
客户端浏览器接收到数据后,就按照响应头上设置的字符编码来解析数据,如下所示:
使用PrintWriter流向客户端浏览器输出中文数据
使用PrintWriter流输出中文注意问题:
在获取PrintWriter输出流之前首先使用"response.setCharacterEncoding(charset)"设置字符以什么样的编码输出到浏览器,如:response.setCharacterEncoding("UTF-8");设置将字符以"UTF-8"编码输出到客户端浏览器,然后再使用response.getWriter();获取PrintWriter输出流,这两个步骤不能颠倒,如下:
response.setCharacterEncoding("UTF-8");//设置将字符以"UTF-8"编码输出到客户端浏览器
/**
* PrintWriter out = response.getWriter();这句代码必须放在response.setCharacterEncoding("UTF-8");之后
* 否则response.setCharacterEncoding("UTF-8")这行代码的设置将无效,浏览器显示的时候还是乱码
*/
PrintWriter out = response.getWriter();//获取PrintWriter输出流
然后再使用response.setHeader("content-type", "text/html;charset=字符编码");设置响应头,控制浏览器以指定的字符编码编码进行显示,例如:
//通过设置响应头控制浏览器以UTF-8的编码显示数据,如果不加这句话,那么浏览器显示的将是乱码
response.setHeader("content-type", "text/html;charset=UTF-8");
除了可以使用response.setHeader("content-type", "text/html;charset=字符编码");设置响应头来控制浏览器以指定的字符编码编码进行显示这种方式之外,还可以用如下的方式来模拟响应头的作用
/**
* 多学一招:使用HTML语言里面的<meta>标签来控制浏览器行为,模拟通过设置响应头控制浏览器行为
*response.getWriter().write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'/>");
* 等同于response.setHeader("content-type", "text/html;charset=UTF-8");
*/
response.getWriter().write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'/>");
范例:使用PrintWriter流向客户端浏览器输出"中国"这两个汉字
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ResponseDemo01 extends HttpServlet {
private static final long serialVersionUID = 4312868947607181532L;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
outputChineseByPrintWriter(response);//使用PrintWriter流输出中文
}
/**
* 使用PrintWriter流输出中文
* @param request
* @param response
* @throws IOException
*/
public void outputChineseByPrintWriter(HttpServletResponse response) throws IOException{
String data = "中国";
//通过设置响应头控制浏览器以UTF-8的编码显示数据,如果不加这句话,那么浏览器显示的将是乱码
//response.setHeader("content-type", "text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8");//设置将字符以"UTF-8"编码输出到客户端浏览器
/**
* PrintWriter out = response.getWriter();这句代码必须放在response.setCharacterEncoding("UTF-8");之后
* 否则response.setCharacterEncoding("UTF-8")这行代码的设置将无效,浏览器显示的时候还是乱码
*/
PrintWriter out = response.getWriter();//获取PrintWriter输出流
/** * 多学一招:使用HTML语言里面的<meta>标签来控制浏览器行为,模拟通过设置响应头控制浏览器行为
* out.write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'/>");
* 等同于response.setHeader("content-type", "text/html;charset=UTF-8");
*/
out.write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'/>");
out.write(data);//使用PrintWriter流向客户端输出字符
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
当需要向浏览器输出字符数据时,使用PrintWriter比较方便,省去了将字符转换成字节数组那一步。
使用OutputStream或者PrintWriter向客户端浏览器输出数字
比如有如下的代码:
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ResponseDemo01 extends HttpServlet {
private static final long serialVersionUID = 4312868947607181532L;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
outputOneByOutputStream(response);//使用OutputStream输出1到客户端浏览器
}
/**
* 使用OutputStream流输出数字1
* @param request
* @param response
* @throws IOException
*/
public void outputOneByOutputStream(HttpServletResponse response) throws IOException{
response.setHeader("content-type", "text/html;charset=UTF-8");
OutputStream outputStream = response.getOutputStream();
outputStream.write("使用OutputStream流输出数字1:".getBytes("UTF-8"));
outputStream.write(1);
}
}
运行上面代码显示的结果如下:
运行的结果和我们想象中的不一样,数字1没有输出来,下面我们修改一下上面的outputOneByOutputStream方法的代码,修改后的代码如下:
/**
* 使用OutputStream流输出数字1
* @param request
* @param response
* @throws IOException
*/
public void outputOneByOutputStream(HttpServletResponse response) throws IOException{
response.setHeader("content-type", "text/html;charset=UTF-8");
OutputStream outputStream = response.getOutputStream();
outputStream.write("使用OutputStream流输出数字1:".getBytes("UTF-8"));
//outputStream.write(1);
outputStream.write((1+"").getBytes());
}
1+""这一步是将数字1和一个空字符串相加,这样处理之后,数字1就变成了字符串1了,然后再将字符串1转换成字节数组使用OutputStream进行输出,此时看到的结果如下:
这次可以看到输出来的1了,这说明了一个问题:在开发过程中,如果希望服务器输出什么浏览器就能看到什么,那么在服务器端都要以字符串的形式进行输出。
如果使用PrintWriter流输出数字,那么也要先将数字转换成字符串后再输出,如下:
/**
* 使用PrintWriter流输出数字1
* @param request
* @param response
* @throws IOException
*/
public void outputOneByPrintWriter(HttpServletResponse response) throws IOException{
response.setHeader("content-type", "text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();//获取PrintWriter输出流
out.write("使用PrintWriter流输出数字1:");
out.write(1+"");
}
文件下载
文件下载功能是web开发中经常使用到的功能,使用HttpServletResponse对象就可以实现文件的下载
文件下载功能的实现思路:
1.获取要下载的文件的绝对路径
2.获取要下载的文件名
3.设置content-disposition响应头控制浏览器以下载的形式打开文件
4.获取要下载的文件输入流
5.创建数据缓冲区
6.通过response对象获取OutputStream流
7.将FileInputStream流写入到buffer缓冲区
8.使用OutputStream将缓冲区的数据输出到客户端浏览器
范例:使用Response实现文件下载
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author gacl
* 文件下载
*/
public class ResponseDemo02 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
downloadFileByOutputStream(response);//下载文件,通过OutputStream流
}
/**
* 下载文件,通过OutputStream流
* @param response
* @throws FileNotFoundException
* @throws IOException
*/
private void downloadFileByOutputStream(HttpServletResponse response)
throws FileNotFoundException, IOException {
//1.获取要下载的文件的绝对路径
String realPath = this.getServletContext().getRealPath("/download/1.JPG");
//2.获取要下载的文件名
String fileName = realPath.substring(realPath.lastIndexOf("\\")+1);
//3.设置content-disposition响应头控制浏览器以下载的形式打开文件
response.setHeader("content-disposition", "attachment;filename="+fileName);
//4.获取要下载的文件输入流
InputStream in = new FileInputStream(realPath);
int len = 0;
//5.创建数据缓冲区
byte[] buffer = new byte[1024];
//6.通过response对象获取OutputStream流
OutputStream out = response.getOutputStream();
//7.将FileInputStream流写入到buffer缓冲区
while ((len = in.read(buffer)) > 0) {
//8.使用OutputStream将缓冲区的数据输出到客户端浏览器
out.write(buffer,0,len);
}
in.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
运行结果如下所示:
范例:使用Response实现中文文件下载
下载中文文件时,需要注意的地方就是中文文件名要使用URLEncoder.encode方法进行编码(URLEncoder.encode(fileName, "字符编码")),否则会出现文件名乱码。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author gacl
* 文件下载
*/
public class ResponseDemo02 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
downloadChineseFileByOutputStream(response);//下载中文文件
}
/**
* 下载中文文件,中文文件下载时,文件名要经过URL编码,否则会出现文件名乱码
* @param response
* @throws FileNotFoundException
* @throws IOException
*/
private void downloadChineseFileByOutputStream(HttpServletResponse response)
throws FileNotFoundException, IOException {
String realPath = this.getServletContext().getRealPath("/download/张家界国家森林公园.JPG");//获取要下载的文件的绝对路径
String fileName = realPath.substring(realPath.lastIndexOf("\\")+1);//获取要下载的文件名
//设置content-disposition响应头控制浏览器以下载的形式打开文件,中文文件名要使用URLEncoder.encode方法进行编码,否则会出现文件名乱码
response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(fileName, "UTF-8"));
InputStream in = new FileInputStream(realPath);//获取文件输入流
int len = 0;
byte[] buffer = new byte[1024];
OutputStream out = response.getOutputStream();
while ((len = in.read(buffer)) > 0) {
out.write(buffer,0,len);//将缓冲区的数据输出到客户端浏览器
}
in.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
运行结果如下所示:
文件下载注意事项:编写文件下载功能时推荐使用OutputStream流,避免使用PrintWriter流,因为OutputStream流是字节流,可以处理任意类型的数据,而PrintWriter流是字符流,只能处理字符数据,如果用字符流处理字节数据,会导致数据丢失。
范例:使用PrintWriter流下载文件
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author gacl
* 文件下载
*/
public class ResponseDemo02 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
downloadFileByPrintWriter(response);//下载文件,通过PrintWriter流
}
/**
* 下载文件,通过PrintWriter流,虽然也能够实现下载,但是会导致数据丢失,因此不推荐使用PrintWriter流下载文件
* @param response
* @throws FileNotFoundException
* @throws IOException
*/
private void downloadFileByPrintWriter(HttpServletResponse response)
throws FileNotFoundException, IOException {
String realPath = this.getServletContext().getRealPath("/download/张家界国家森林公园.JPG");//获取要下载的文件的绝对路径
String fileName = realPath.substring(realPath.lastIndexOf("\\")+1);//获取要下载的文件名
//设置content-disposition响应头控制浏览器以下载的形式打开文件,中文文件名要使用URLEncoder.encode方法进行编码
response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(fileName, "UTF-8"));
FileReader in = new FileReader(realPath);
int len = 0;
char[] buffer = new char[1024];
PrintWriter out = response.getWriter();
while ((len = in.read(buffer)) > 0) {
out.write(buffer,0,len);//将缓冲区的数据输出到客户端浏览器
}
in.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
运行结果如下:
正常弹出下载框,此时我们点击【保存】按钮将文件下载下来,如下所示:
可以看到,只下载了5.25MB,而这张图片的原始大小却是
这说明在下载的时候数据丢失了,所以下载不完全,所以这张图片虽然能够正常下载下来,但是却是无法打开的,因为丢失掉了部分数据,如下所示:
所以使用PrintWriter流处理字节数据,会导致数据丢失,这一点千万要注意,因此在编写下载文件功能时,要使用OutputStream流,避免使用PrintWriter流,因为OutputStream流是字节流,可以处理任意类型的数据,而PrintWriter流是字符流,只能处理字符数据,如果用字符流处理字节数据,会导致数据丢失。
生成随机图片用作验证码
生成图片主要用到了一个BufferedImage类,
生成随机图片范例:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ResponseDemo03 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setHeader("refresh", "5");//设置refresh响应头控制浏览器每隔5秒钟刷新一次
//1.在内存中创建一张图片
BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);
//2.得到图片
//Graphics g = image.getGraphics();
Graphics2D g = (Graphics2D)image.getGraphics();
g.setColor(Color.WHITE);//设置图片的背景色
g.fillRect(0, 0, 80, 20);//填充背景色
//3.向图片上写数据
g.setColor(Color.BLUE);//设置图片上字体的颜色
g.setFont(new Font(null, Font.BOLD, 20));
g.drawString(makeNum(), 0, 20);
//4.设置响应头控制浏览器浏览器以图片的方式打开
response.setContentType("image/jpeg");//等同于response.setHeader("Content-Type", "image/jpeg");
//5.设置响应头控制浏览器不缓存图片数据
response.setDateHeader("expries", -1);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
//6.将图片写给浏览器
ImageIO.write(image, "jpg", response.getOutputStream());
}
/**
* 生成随机数字
* @return
*/
private String makeNum(){
Random random = new Random();
String num = random.nextInt(9999999)+"";
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 7-num.length(); i++) {
sb.append("0");
}
num = sb.toString()+num;
return num;
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
运行结果如下:
设置响应头控制浏览器的行为
设置http响应头控制浏览器禁止缓存当前文档内容
response.setDateHeader("expries", -1);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
设置http响应头控制浏览器定时刷新网页(refresh)
response.setHeader("refresh", "5");//设置refresh响应头控制浏览器每隔5秒钟刷新一次
通过response实现请求重定向
请求重定向指:一个web资源收到客户端请求后,通知客户端去访问另外一个web资源,这称之为请求重定向。
应用场景:用户登陆,用户首先访问登录页面,登录成功后,就会跳转到某个页面,这个过程就是一个请求重定向的过程
实现方式:response.sendRedirect(String location),即调用response对象的sendRedirect方法实现请求重定向
sendRedirect内部的实现原理:使用response设置302状态码和设置location响应头实现重定向
例如:
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ResponseDemo04 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* 1.调用sendRedirect方法实现请求重定向,
* sendRedirect方法内部调用了
* response.setHeader("Location", "/JavaWeb_HttpServletResponse_Study_20140615/index.jsp");
* response.setStatus(HttpServletResponse.SC_FOUND);//设置302状态码,等同于response.setStatus(302);
*/
response.sendRedirect("/JavaWeb_HttpServletResponse_Study_20140615/index.jsp");
//2.使用response设置302状态码和设置location响应头实现重定向实现请求重定向
//response.setHeader("Location", "/JavaWeb_HttpServletResponse_Study_20140615/index.jsp");
//response.setStatus(HttpServletResponse.SC_FOUND);//设置302状态码,等同于response.setStatus(302);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
web工程中URL地址的推荐写法
在JavaWeb开发中,只要是写URL地址,那么建议最好以"/"开头,也就是使用绝对路径的方式,那么这个"/"到底代表什么呢?可以用如下的方式来记忆"/":如果"/"是给服务器用的,则代表当前的web工程,如果"/"是给浏览器用的,则代表webapps目录。
"/"代表当前web工程的常见应用场景
①.ServletContext.getRealPath(String path)获取资源的绝对路径
/**
* 1.ServletContext.getRealPath("/download/1.JPG")是用来获取服务器上的某个资源,
* 那么这个"/"就是给服务器用的,"/"此时代表的就是web工程
* ServletContext.getRealPath("/download/1.JPG")表示的就是读取web工程下的download文件夹中的1.JPG这个资源
* 只要明白了"/"代表的具体含义,就可以很快写出要访问的web资源的绝对路径
*/
this.getServletContext().getRealPath("/download/1.JPG");
②.在服务器端forward到其他页面
/**
* 2.forward
* 客户端请求某个web资源,服务器跳转到另外一个web资源,这个forward也是给服务器用的,
* 那么这个"/"就是给服务器用的,所以此时"/"代表的就是web工程
*/
this.getServletContext().getRequestDispatcher("/index.jsp").forward(request, response);
③.使用include指令或者<jsp:include>标签引入页面
<%@include file="/jspfragments/head.jspf" %>
<jsp:include page="/jspfragments/demo.jsp" />
此时"/"代表的都是web工程。
"/"代表webapps目录的常见应用场景
①.使用sendRedirect实现请求重定向
response.sendRedirect("/JavaWeb_HttpServletResponse_Study_20140615/index.jsp");
服务器发送一个URL地址给浏览器,浏览器拿到URL地址之后,再去请求服务器,所以这个"/"是给浏览器使用的,此时"/"代表的就是webapps目录,"/JavaWeb_HttpServletResponse_Study_20140615/index.jsp"这个地址指的就是"webapps\JavaWeb_HttpServletResponse_Study_20140615\index.jsp"
response.sendRedirect("/项目名称/文件夹目录/页面");这种写法是将项目名称写死在程序中的做法,不灵活,万一哪天项目名称变了,此时就得改程序,所以推荐使用下面的灵活写法:
将
response.sendRedirect("/JavaWeb_HttpServletResponse_Study_20140615/index.jsp");
这种写法改成
response.sendRedirect(request.getContextPath()+"/index.jsp");
request.getContextPath()获取到的内容就是"/JavaWeb_HttpServletResponse_Study_20140615",这样就比较灵活了,使用request.getContextPath()代替"/项目名称",推荐使用这种方式,灵活方便!
②.使用超链接跳转
<a href="/JavaWeb_HttpServletResponse_Study_20140615/index.jsp">跳转到首页</a>
这是客户端浏览器使用的超链接跳转,这个"/"是给浏览器使用的,此时"/"代表的就是webapps目录。
使用超链接访问web资源,绝对路径的写法推荐使用下面的写法改进:
<a href="${pageContext.request.contextPath}/index.jsp">跳转到首页</a>
这样就可以避免在路径中出现项目的名称,使用${pageContext.request.contextPath}取代"/JavaWeb_HttpServletResponse_Study_20140615"
③.Form表单提交
<form action="/JavaWeb_HttpServletResponse_Study_20140615/servlet/CheckServlet" method="post">
<input type="submit" value="提交">
</form>
这是客户端浏览器将form表单提交到服务器,所以这个"/"是给浏览器使用的,此时"/"代表的就是webapps目录。
对于form表单提交中action属性绝对路径的写法,也推荐使用如下的方式改进
<form action="${pageContext.request.contextPath}/servlet/CheckServlet" method="post">
<input type="submit" value="提交">
</form>
${pageContext.request.contextPath}得到的就是"/JavaWeb_HttpServletResponse_Study_20140615"
${pageContext.request.contextPath}的效果等同于request.getContextPath(),两者获取到的都是"/项目名称"
④.js脚本和css样式文件的引用
<%--使用绝对路径的方式引用js脚本--%>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/index.js"></script>
<%--${pageContext.request.contextPath}与request.getContextPath()写法是得到的效果是一样的--%>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/login.js"></script>
<%--使用绝对路径的方式引用css样式--%>
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/index.css" type="text/css"/>
综合范例:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>"/"代表webapps目录的常见应用场景</title>
<%--使用绝对路径的方式引用js脚本--%>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/index.js"></script>
<%--${pageContext.request.contextPath}与request.getContextPath()写法是得到的效果是一样的--%>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/login.js"></script>
<%--使用绝对路径的方式引用css样式--%>
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/index.css" type="text/css"/>
</head>
<body>
<%--form表单提交--%>
<form action="${pageContext.request.contextPath}/servlet/CheckServlet" method="post">
<input type="submit" value="提交">
</form>
<%--超链接跳转页面--%>
<a href="${pageContext.request.contextPath}/index.jsp">跳转到首页</a>
</body>
</html>
response细节问题
getOutputStream和getWriter方法分别用于得到输出二进制数据、输出文本数据的ServletOuputStream、Printwriter对象。
getOutputStream和getWriter这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法。
Servlet程序向ServletOutputStream或PrintWriter对象中写入的数据将被Servlet引擎从response里面获取,Servlet引擎将这些数据当作响应消息的正文,然后再与响应状态行和各响应头组合后输出到客户端。
Serlvet的service方法结束后,Servlet引擎将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用过close方法,如果没有,Servlet引擎将调用close方法关闭该输出流对象。
BufferedImage类
生成验证码图片主要用到了一个BufferedImage类,如下:
创建一个DrawImage Servlet,用来生成验证码图片
import java.awt.Color;
3 import java.awt.Font;
4 import java.awt.Graphics;
5 import java.awt.Graphics2D;
6 import java.awt.image.BufferedImage;
7 import java.io.IOException;
8 import java.util.Random;
9 import javax.imageio.ImageIO;
10 import javax.servlet.ServletException;
11 import javax.servlet.http.HttpServlet;
12 import javax.servlet.http.HttpServletRequest;
13 import javax.servlet.http.HttpServletResponse;
14 /**
15 * 生成随机图片,用来作为验证码
16 */
17 public class DrawImage extends HttpServlet {
18 private static final long serialVersionUID = 3038623696184546092L;
19
20 public static final int WIDTH = 120;//生成的图片的宽度
21 public static final int HEIGHT = 30;//生成的图片的高度
22
23 public void doGet(HttpServletRequest request, HttpServletResponse response)
24 throws ServletException, IOException {
25 this.doPost(request, response);
26 }
27
28 public void doPost(HttpServletRequest request, HttpServletResponse response)
29 throws ServletException, IOException {
30 String createTypeFlag = request.getParameter("createTypeFlag");//接收客户端传递的createTypeFlag标识
31 //1.在内存中创建一张图片
32 BufferedImage bi = new BufferedImage(WIDTH, HEIGHT,BufferedImage.TYPE_INT_RGB);
33 //2.得到图片
34 Graphics g = bi.getGraphics();
35 //3.设置图片的背影色
36 setBackGround(g);
37 //4.设置图片的边框
38 setBorder(g);
39 //5.在图片上画干扰线
40 drawRandomLine(g);
41 //6.写在图片上随机数
42 //String random = drawRandomNum((Graphics2D) g,"ch");//生成中文验证码图片
43 //String random = drawRandomNum((Graphics2D) g,"nl");//生成数字和字母组合的验证码图片
44 //String random = drawRandomNum((Graphics2D) g,"n");//生成纯数字的验证码图片
45 //String random = drawRandomNum((Graphics2D) g,"l");//生成纯字母的验证码图片
46 String random = drawRandomNum((Graphics2D) g,createTypeFlag);//根据客户端传递的createTypeFlag标识生成验证码图片
47 //7.将随机数存在session中
48 request.getSession().setAttribute("checkcode", random);
49 //8.设置响应头通知浏览器以图片的形式打开
50 response.setContentType("image/jpeg");//等同于response.setHeader("Content-Type", "image/jpeg");
51 //9.设置响应头控制浏览器不要缓存
52 response.setDateHeader("expries", -1);
53 response.setHeader("Cache-Control", "no-cache");
54 response.setHeader("Pragma", "no-cache");
55 //10.将图片写给浏览器
56 ImageIO.write(bi, "jpg", response.getOutputStream());
57 }
58
59 /**
60 * 设置图片的背景色
61 * @param g
62 */
63 private void setBackGround(Graphics g) {
64 // 设置颜色
65 g.setColor(Color.WHITE);
66 // 填充区域
67 g.fillRect(0, 0, WIDTH, HEIGHT);
68 }
69
70 /**
71 * 设置图片的边框
72 * @param g
73 */
74 private void setBorder(Graphics g) {
75 // 设置边框颜色
76 g.setColor(Color.BLUE);
77 // 边框区域
78 g.drawRect(1, 1, WIDTH - 2, HEIGHT - 2);
79 }
80
81 /**
82 * 在图片上画随机线条
83 * @param g
84 */
85 private void drawRandomLine(Graphics g) {
86 // 设置颜色
87 g.setColor(Color.GREEN);
88 // 设置线条个数并画线
89 for (int i = 0; i < 5; i++) {
90 int x1 = new Random().nextInt(WIDTH);
91 int y1 = new Random().nextInt(HEIGHT);
92 int x2 = new Random().nextInt(WIDTH);
93 int y2 = new Random().nextInt(HEIGHT);
94 g.drawLine(x1, y1, x2, y2);
95 }
96 }
97
98 /**
99 * 画随机字符
100 * @param g
101 * @param createTypeFlag
102 * @return
103 * String... createTypeFlag是可变参数,
104 * Java1.5增加了新特性:可变参数:适用于参数个数不确定,类型确定的情况,java把可变参数当做数组处理。注意:可变参数必须位于最后一项
105 */
106 private String drawRandomNum(Graphics2D g,String... createTypeFlag) {
107 // 设置颜色
108 g.setColor(Color.RED);
109 // 设置字体
110 g.setFont(new Font("宋体", Font.BOLD, 20));
111 //常用的中国汉字
112 String baseChineseChar = "\u7684\u4e00\u4e86\u662f\u6211\u4e0d\u5728\u4eba\u4eec\u6709\u6765\u4ed6\u8fd9\u4e0a\u7740\u4e2a\u5730\u5230\u5927\u91cc\u8bf4\u5c31\u53bb\u5b50\u5f97\u4e5f\u548c\u90a3\u8981\u4e0b\u770b\u5929\u65f6\u8fc7\u51fa\u5c0f\u4e48\u8d77\u4f60\u90fd\u628a\u597d\u8fd8\u591a\u6ca1\u4e3a\u53c8\u53ef\u5bb6\u5b66\u53ea\u4ee5\u4e3b\u4f1a\u6837\u5e74\u60f3\u751f\u540c\u8001\u4e2d\u5341\u4ece\u81ea\u9762\u524d\u5934\u9053\u5b83\u540e\u7136\u8d70\u5f88\u50cf\u89c1\u4e24\u7528\u5979\u56fd\u52a8\u8fdb\u6210\u56de\u4ec0\u8fb9\u4f5c\u5bf9\u5f00\u800c\u5df1\u4e9b\u73b0\u5c71\u6c11\u5019\u7ecf\u53d1\u5de5\u5411\u4e8b\u547d\u7ed9\u957f\u6c34\u51e0\u4e49\u4e09\u58f0\u4e8e\u9ad8\u624b\u77e5\u7406\u773c\u5fd7\u70b9\u5fc3\u6218\u4e8c\u95ee\u4f46\u8eab\u65b9\u5b9e\u5403\u505a\u53eb\u5f53\u4f4f\u542c\u9769\u6253\u5462\u771f\u5168\u624d\u56db\u5df2\u6240\u654c\u4e4b\u6700\u5149\u4ea7\u60c5\u8def\u5206\u603b\u6761\u767d\u8bdd\u4e1c\u5e2d\u6b21\u4eb2\u5982\u88ab\u82b1\u53e3\u653e\u513f\u5e38\u6c14\u4e94\u7b2c\u4f7f\u5199\u519b\u5427\u6587\u8fd0\u518d\u679c\u600e\u5b9a\u8bb8\u5feb\u660e\u884c\u56e0\u522b\u98de\u5916\u6811\u7269\u6d3b\u90e8\u95e8\u65e0\u5f80\u8239\u671b\u65b0\u5e26\u961f\u5148\u529b\u5b8c\u5374\u7ad9\u4ee3\u5458\u673a\u66f4\u4e5d\u60a8\u6bcf\u98ce\u7ea7\u8ddf\u7b11\u554a\u5b69\u4e07\u5c11\u76f4\u610f\u591c\u6bd4\u9636\u8fde\u8f66\u91cd\u4fbf\u6597\u9a6c\u54ea\u5316\u592a\u6307\u53d8\u793e\u4f3c\u58eb\u8005\u5e72\u77f3\u6ee1\u65e5\u51b3\u767e\u539f\u62ff\u7fa4\u7a76\u5404\u516d\u672c\u601d\u89e3\u7acb\u6cb3\u6751\u516b\u96be\u65e9\u8bba\u5417\u6839\u5171\u8ba9\u76f8\u7814\u4eca\u5176\u4e66\u5750\u63a5\u5e94\u5173\u4fe1\u89c9\u6b65\u53cd\u5904\u8bb0\u5c06\u5343\u627e\u4e89\u9886\u6216\u5e08\u7ed3\u5757\u8dd1\u8c01\u8349\u8d8a\u5b57\u52a0\u811a\u7d27\u7231\u7b49\u4e60\u9635\u6015\u6708\u9752\u534a\u706b\u6cd5\u9898\u5efa\u8d76\u4f4d\u5531\u6d77\u4e03\u5973\u4efb\u4ef6\u611f\u51c6\u5f20\u56e2\u5c4b\u79bb\u8272\u8138\u7247\u79d1\u5012\u775b\u5229\u4e16\u521a\u4e14\u7531\u9001\u5207\u661f\u5bfc\u665a\u8868\u591f\u6574\u8ba4\u54cd\u96ea\u6d41\u672a\u573a\u8be5\u5e76\u5e95\u6df1\u523b\u5e73\u4f1f\u5fd9\u63d0\u786e\u8fd1\u4eae\u8f7b\u8bb2\u519c\u53e4\u9ed1\u544a\u754c\u62c9\u540d\u5440\u571f\u6e05\u9633\u7167\u529e\u53f2\u6539\u5386\u8f6c\u753b\u9020\u5634\u6b64\u6cbb\u5317\u5fc5\u670d\u96e8\u7a7f\u5185\u8bc6\u9a8c\u4f20\u4e1a\u83dc\u722c\u7761\u5174\u5f62\u91cf\u54b1\u89c2\u82e6\u4f53\u4f17\u901a\u51b2\u5408\u7834\u53cb\u5ea6\u672f\u996d\u516c\u65c1\u623f\u6781\u5357\u67aa\u8bfb\u6c99\u5c81\u7ebf\u91ce\u575a\u7a7a\u6536\u7b97\u81f3\u653f\u57ce\u52b3\u843d\u94b1\u7279\u56f4\u5f1f\u80dc\u6559\u70ed\u5c55\u5305\u6b4c\u7c7b\u6e10\u5f3a\u6570\u4e61\u547c\u6027\u97f3\u7b54\u54e5\u9645\u65e7\u795e\u5ea7\u7ae0\u5e2e\u5566\u53d7\u7cfb\u4ee4\u8df3\u975e\u4f55\u725b\u53d6\u5165\u5cb8\u6562\u6389\u5ffd\u79cd\u88c5\u9876\u6025\u6797\u505c\u606f\u53e5\u533a\u8863\u822c\u62a5\u53f6\u538b\u6162\u53d4\u80cc\u7ec6";
113 //数字和字母的组合
114 String baseNumLetter = "0123456789ABCDEFGHJKLMNOPQRSTUVWXYZ";
115 //纯数字
116 String baseNum = "0123456789";
117 //纯字母
118 String baseLetter = "ABCDEFGHJKLMNOPQRSTUVWXYZ";
119 //createTypeFlag[0]==null表示没有传递参数
120 if (createTypeFlag.length > 0 && null != createTypeFlag[0]) {
121 if (createTypeFlag[0].equals("ch")) {
122 // 截取汉字
123 return createRandomChar(g, baseChineseChar);
124 }else if (createTypeFlag[0].equals("nl")) {
125 // 截取数字和字母的组合
126 return createRandomChar(g, baseNumLetter);
127 }else if (createTypeFlag[0].equals("n")) {
128 // 截取数字
129 return createRandomChar(g, baseNum);
130 }else if (createTypeFlag[0].equals("l")) {
131 // 截取字母
132 return createRandomChar(g, baseLetter);
133 }
134 }else {
135 // 默认截取数字和字母的组合
136 return createRandomChar(g, baseNumLetter);
137 }
138
139 return "";
140 }
141
142 /**
143 * 创建随机字符
144 * @param g
145 * @param baseChar
146 * @return 随机字符
147 */
148 private String createRandomChar(Graphics2D g,String baseChar) {
149 StringBuffer sb = new StringBuffer();
150 int x = 5;
151 String ch ="";
152 // 控制字数
153 for (int i = 0; i < 4; i++) {
154 // 设置字体旋转角度
155 int degree = new Random().nextInt() % 30;
156 ch = baseChar.charAt(new Random().nextInt(baseChar.length())) + "";
157 sb.append(ch);
158 // 正向角度
159 g.rotate(degree * Math.PI / 180, x, 20);
160 g.drawString(ch, x, 20);
161 // 反向角度
162 g.rotate(-degree * Math.PI / 180, x, 20);
163 x += 30;
164 }
165 return sb.toString();
166 }
167 }
运行结果如下:
在Form表单中使用验证码图片
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>在Form表单中使用验证码</title>
<script type="text/javascript">
//刷新验证码
function changeImg(){
document.getElementById("validateCodeImg").src="${pageContext.request.contextPath}/servlet/DrawImage?"+Math.random();
}
</script>
</head>
<body>
<form action="${pageContext.request.contextPath}/servlet/CheckServlet" method="post">
验证码:<input type="text" name="validateCode"/>
<img alt="验证码看不清,换一张" src="${pageContext.request.contextPath}/servlet/DrawImage" id="validateCodeImg" onclick="changeImg()">
<a href="javascript:void(0)" onclick="changeImg()">看不清,换一张</a>
<br/>
<input type="submit" value="提交">
</form>
</body>
</html>
运行结果:
DrawImage Servlet除了可以生成的字母和数字的组合的验证码图片之外,还可以生成汉字,纯数字,纯字母的验证码图片,只需要向DrawImage Servlet传递约定好的生成标识符参数即可,如下所示:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>在Form表单中使用验证码</title>
<script type="text/javascript">
//刷新验证码
function changeImg(obj,createTypeFlag){
document.getElementById(obj.id).src="${pageContext.request.contextPath}/servlet/DrawImage?createTypeFlag="+createTypeFlag+"&"+Math.random();
}
</script>
</head>
<body>
<form action="${pageContext.request.contextPath}/servlet/CheckServlet" method="post">
数字字母混合验证码:<input type="text" name="validateCode"/>
<img alt="验证码看不清,换一张" src="${pageContext.request.contextPath}/servlet/DrawImage" id="validateCodeImg1" onclick="changeImg(this,'nl')">
<br/>
中文验证码:<input type="text" name="validateCode"/>
<img alt="验证码看不清,换一张" src="${pageContext.request.contextPath}/servlet/DrawImage?createTypeFlag=ch" id="validateCodeImg2" onclick="changeImg(this,'ch')">
<br/>
英文验证码:<input type="text" name="validateCode"/>
<img alt="验证码看不清,换一张" src="${pageContext.request.contextPath}/servlet/DrawImage?createTypeFlag=l" id="validateCodeImg3" onclick="changeImg(this,'l')">
<br/>
数字验证码:<input type="text" name="validateCode"/>
<img alt="验证码看不清,换一张" src="${pageContext.request.contextPath}/servlet/DrawImage?createTypeFlag=n" id="validateCodeImg4" onclick="changeImg(this,'n')">
<br/>
<input type="submit" value="提交">
</form>
</body>
</html>
运行结果如下:
服务器端对form表单提交上来的验证码处理
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author gacl
* 服务器端接收到验证码后的处理
*/
public class CheckServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String clientCheckcode = request.getParameter("validateCode");//接收客户端浏览器提交上来的验证码
String serverCheckcode = (String) request.getSession().getAttribute("checkcode");//从服务器端的session中取出验证码
if (clientCheckcode.equals(serverCheckcode)) {//将客户端验证码和服务器端验证比较,如果相等,则表示验证通过
System.out.println("验证码验证通过!");
}else {
System.out.println("验证码验证失败!");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
HttpServletRequest对象
HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象提供的方法,可以获得客户端请求的所有信息。
Request常用方法
获得客户机信息
getRequestURL方法返回客户端发出请求时的完整URL。
getRequestURI方法返回请求行中的资源名部分。
getQueryString 方法返回请求行中的参数部分。
getPathInfo方法返回请求URL中的额外路径信息。额外路径信息是请求URL中的位于Servlet的路径之后和查询参数之前的内容,它以“/”开头。
getRemoteAddr方法返回发出请求的客户机的IP地址。
getRemoteHost方法返回发出请求的客户机的完整主机名。
getRemotePort方法返回客户机所使用的网络端口号。
getLocalAddr方法返回WEB服务器的IP地址。
getLocalName方法返回WEB服务器的主机名。
范例:通过request对象获取客户端请求信息
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author gacl
* 通过request对象获取客户端请求信息
*/
public class RequestDemo01 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* 1.获得客户机信息
*/
String requestUrl = request.getRequestURL().toString();//得到请求的URL地址
String requestUri = request.getRequestURI();//得到请求的资源
String queryString = request.getQueryString();//得到请求的URL地址中附带的参数
String remoteAddr = request.getRemoteAddr();//得到来访者的IP地址
String remoteHost = request.getRemoteHost();
int remotePort = request.getRemotePort();
String remoteUser = request.getRemoteUser();
String method = request.getMethod();//得到请求URL地址时使用的方法
String pathInfo = request.getPathInfo();
String localAddr = request.getLocalAddr();//获取WEB服务器的IP地址
String localName = request.getLocalName();//获取WEB服务器的主机名
response.setCharacterEncoding("UTF-8");//设置将字符以"UTF-8"编码输出到客户端浏览器
//通过设置响应头控制浏览器以UTF-8的编码显示数据,如果不加这句话,那么浏览器显示的将是乱码
response.setHeader("content-type", "text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write("获取到的客户机信息如下:");
out.write("<hr/>");
out.write("请求的URL地址:"+requestUrl);
out.write("<br/>");
out.write("请求的资源:"+requestUri);
out.write("<br/>");
out.write("请求的URL地址中附带的参数:"+queryString);
out.write("<br/>");
out.write("来访者的IP地址:"+remoteAddr);
out.write("<br/>");
out.write("来访者的主机名:"+remoteHost);
out.write("<br/>");
out.write("使用的端口号:"+remotePort);
out.write("<br/>");
out.write("remoteUser:"+remoteUser);
out.write("<br/>");
out.write("请求使用的方法:"+method);
out.write("<br/>");
out.write("pathInfo:"+pathInfo);
out.write("<br/>");
out.write("localAddr:"+localAddr);
out.write("<br/>");
out.write("localName:"+localName);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
运行结果:
获得客户机请求头
getHeader(string name)方法:String
getHeaders(String name)方法:Enumeration
getHeaderNames()方法
范例:通过request对象获取客户端请求头信息
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author gacl
* 获取客户端请求头信息
* 客户端请求头:
*
*/
public class RequestDemo02 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setCharacterEncoding("UTF-8");//设置将字符以"UTF-8"编码输出到客户端浏览器
//通过设置响应头控制浏览器以UTF-8的编码显示数据
response.setHeader("content-type", "text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
Enumeration<String> reqHeadInfos = request.getHeaderNames();//获取所有的请求头
out.write("获取到的客户端所有的请求头信息如下:");
out.write("<hr/>");
while (reqHeadInfos.hasMoreElements()) {
String headName = (String) reqHeadInfos.nextElement(); String headValue = request.getHeader(headName);//根据请求头的名字获取对应的请求头的值
out.write(headName+":"+headValue);
out.write("<br/>");
}
out.write("<br/>");
out.write("获取到的客户端Accept-Encoding请求头的值:");
out.write("<hr/>");
String value = request.getHeader("Accept-Encoding");//获取Accept-Encoding请求头对应的值
out.write(value);
Enumeration<String> e = request.getHeaders("Accept-Encoding");
while (e.hasMoreElements()) {
String string = (String) e.nextElement();
System.out.println(string);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { doGet(request, response);
}
}
运行结果如下:
获得客户机请求参数(客户端提交的数据)
- getParameter(String)方法(常用)
- getParameterValues(String name)方法(常用)
- getParameterNames()方法(不常用)
- getParameterMap()方法(编写框架时常用)
比如现在有如下的form表单
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Html的Form表单元素</title>
</head>
<fieldset style="width:500px;">
<legend>Html的Form表单元素</legend>
<!--form表单的action属性规定当提交表单时,向何处发送表单数据,method属性指明表单的提交方式,分为get和post,默认为get-->
<form action="${pageContext.request.contextPath}/servlet/RequestDemo03" method="post">
<!--输入文本框,SIZE表示显示长度,maxlength表示最多输入长度-->
编 号(文本框):
<input type="text" name="userid" value="NO." size="2" maxlength="2"><br>
<!--输入文本框,通过value指定其显示的默认值-->
用户名(文本框):<input type="text" name="username" value="请输入用户名"><br>
<!--密码框,其中所有输入的内容都以密文的形式显示-->
密 码(密码框):
<!-- 表示的是一个空格-->
<input type="password" name="userpass" value="请输入密码"><br>
<!--单选按钮,通过checked指定默认选中,名称必须一样,其中value为真正需要的内容-->
性 别(单选框):
<input type="radio" name="sex" value="男" checked>男
<input type="radio" name="sex" value="女">女<br>
<!--下拉列表框,通过<option>元素指定下拉的选项-->
部 门(下拉框):
<select name="dept">
<option value="技术部">技术部</option>
<option value="销售部" SELECTED>销售部</option>
<option value="财务部">财务部</option>
</select><br>
<!--复选框,可以同时选择多个选项,名称必须一样,其中value为真正需要的内容-->
兴 趣(复选框):
<input type="checkbox" name="inst" value="唱歌">唱歌
<input type="checkbox" name="inst" value="游泳">游泳
<input type="checkbox" name="inst" value="跳舞">跳舞
<input type="checkbox" name="inst" value="编程" checked>编程
<input type="checkbox" name="inst" value="上网">上网
<br>
<!--大文本输入框,宽度为34列,高度为5行-->
说 明(文本域):
<textarea name="note" cols="34" rows="5">
</textarea>
<br>
<!--隐藏域,在页面上无法看到,专门用来传递参数或者保存参数-->
<input type="hidden" name="hiddenField" value="hiddenvalue"/>
<!--提交表单按钮,当点击提交后,所有填写的表单内容都会被传输到服务器端-->
<input type="submit" value="提交(提交按钮)">
<!--重置表单按钮,当点击重置后,所有表单恢复原始显示内容-->
<input type="reset" value="重置(重置按钮)">
</form>
<!--表单结束-->
</fieldset>
</body>
<!--完结标记-->
</html>
<!--完结标记-->
在Form表单中填写数据,然后提交到RequestDemo03这个Servlet进行处理,填写的表单数据如下:
在服务器端使用getParameter方法和getParameterValues方法接收表单参数,代码如下:
package gacl.request.study;
import java.io.IOException;
import java.text.MessageFormat;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author gacl
* 获取客户端通过Form表单提交上来的参数
*/
public class RequestDemo03 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//客户端是以UTF-8编码提交表单数据的,所以需要设置服务器端以UTF-8的编码进行接收,否则对于中文数据就会产生乱码
request.setCharacterEncoding("UTF-8");
/**
* 编 号(文本框): <input type="text" name="userid" value="NO." size="2" maxlength="2">
*/
String userid = request.getParameter("userid");//获取填写的编号,userid是文本框的名字,<input type="text" name="userid">
/**
* 用户名(文本框):<input type="text" name="username" value="请输入用户名">
*/
String username = request.getParameter("username");//获取填写的用户名
/**
* 密 码(密码框):<input type="password" name="userpass" value="请输入密码">
*/
String userpass = request.getParameter("userpass");//获取填写的密码
String sex = request.getParameter("sex");//获取选中的性别
String dept = request.getParameter("dept");//获取选中的部门
//获取选中的兴趣,因为可以选中多个值,所以获取到的值是一个字符串数组,因此需要使用getParameterValues方法来获取
String[] insts = request.getParameterValues("inst");
String note = request.getParameter("note");//获取填写的说明信息
String hiddenField = request.getParameter("hiddenField");//获取隐藏域的内容
String instStr="";
/**
* 获取数组数据的技巧,可以避免insts数组为null时引发的空指针异常错误!
*/
for (int i = 0; insts!=null && i < insts.length; i++) {
if (i == insts.length-1) {
instStr+=insts[i];
}else {
instStr+=insts[i]+",";
}
}
String htmlStr = "<table>" +
"<tr><td>填写的编号:</td><td>{0}</td></tr>" + "<tr><td>填写的用户名:</td><td>{1}</td></tr>" +
"<tr><td>填写的密码:</td><td>{2}</td></tr>" +
"<tr><td>选中的性别:</td><td>{3}</td></tr>" +
"<tr><td>选中的部门:</td><td>{4}</td></tr>" +
"<tr><td>选中的兴趣:</td><td>{5}</td></tr>" +
"<tr><td>填写的说明:</td><td>{6}</td></tr>" +
"<tr><td>隐藏域的内容:</td><td>{7}</td></tr>" +
"</table>";
htmlStr = MessageFormat.format(htmlStr, userid,username,userpass,sex,dept,instStr,note,hiddenField);
response.setCharacterEncoding("UTF-8");//设置服务器端以UTF-8编码输出数据到客户端
response.setContentType("text/html;charset=UTF-8");//设置客户端浏览器以UTF-8编码解析数据
response.getWriter().write(htmlStr);//输出htmlStr里面的内容到客户端浏览器显示
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
运行结果如下:
在服务器端使用getParameterNames方法接收表单参数,代码如下:
Enumeration<String> paramNames = request.getParameterNames();//获取所有的参数名
while (paramNames.hasMoreElements()) {
String name = paramNames.nextElement();//得到参数名
String value = request.getParameter(name);//通过参数名获取对应的值
System.out.println(MessageFormat.format("{0}={1}", name,value));
}
运行结果如下:
//request对象封装的参数是以Map的形式存储的
Map<String, String[]> paramMap = request.getParameterMap();
for(Map.Entry<String, String[]> entry :paramMap.entrySet()){
String paramName = entry.getKey();
String paramValue = "";
String[] paramValueArr = entry.getValue();
for (int i = 0; paramValueArr!=null && i < paramValueArr.length; i++) {
if (i == paramValueArr.length-1) {
paramValue+=paramValueArr[i];
}else {
paramValue+=paramValueArr[i]+",";
}
}
System.out.println(MessageFormat.format("{0}={1}", paramName,paramValue));
}
在服务器端使用getParameterMap方法接收表单参数,代码如下:
//request对象封装的参数是以Map的形式存储的
Map<String, String[]> paramMap = request.getParameterMap();
for(Map.Entry<String, String[]> entry :paramMap.entrySet()){
String paramName = entry.getKey();
String paramValue = "";
String[] paramValueArr = entry.getValue();
for (int i = 0; paramValueArr!=null && i < paramValueArr.length; i++) {
if (i == paramValueArr.length-1) {
paramValue+=paramValueArr[i];
}else {
paramValue+=paramValueArr[i]+",";
}
}
System.out.println(MessageFormat.format("{0}={1}", paramName,paramValue));
}
运行结果如下:
request接收表单提交中文参数乱码问题
以POST方式提交表单中文参数的乱码问题
例如有如下的form表单页面:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>request接收中文参数乱码问题</title>
</head>
<body>
<form action="<%=request.getContextPath()%>/servlet/RequestDemo04" method="post">
用户名:<input type="text" name="userName"/>
<input type="submit" value="post方式提交表单">
</form>
</body>
</html>
此时在服务器端接收中文参数时就会出现中文乱码,如下所示:
post方式提交中文数据乱码产生的原因和解决办法
可以看到,之所以会产生乱码,就是因为服务器和客户端沟通的编码不一致造成的,因此解决的办法是:在客户端和服务器之间设置一个统一的编码,之后就按照此编码进行数据的传输和接收。
由于客户端是以UTF-8字符编码将表单数据传输到服务器端的,因此服务器也需要设置以UTF-8字符编码进行接收,要想完成此操作,服务器可以直接使用从ServletRequest接口继承而来的"setCharacterEncoding(charset)"方法进行统一的编码设置。修改后的代码如下:
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* 客户端是以UTF-8编码传输数据到服务器端的,所以需要设置服务器端以UTF-8的编码进行接收,否则对于中文数据就会产生乱码
*/
request.setCharacterEncoding("UTF-8");
String userName = request.getParameter("userName");
System.out.println("userName:"+userName);
}
使用request.setCharacterEncoding("UTF-8");设置服务器以UTF-8的编码接收数据后,此时就不会产生中文乱码问题了,如下所示:
以GET方式提交表单中文参数的乱码问题
例如有如下的form表单页面:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>request接收中文参数乱码问题</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/servlet/RequestDemo04" method="get">
姓名:<input type="text" name="name"/>
<input type="submit" value="get方式提交表单">
</form>
</body>
</html>
此时在服务器端接收中文参数时就会出现中文乱码,如下所示:
那么这个中文乱码问题又该如何解决呢,是否可以通过request.setCharacterEncoding("UTF-8");设置服务器以UTF-8的编码进行接收这种方式来解决中文乱码问题呢,注意,对于以get方式传输的中文数据,通过request.setCharacterEncoding("UTF-8");这种方式是解决不了中文乱码问题,如下所示:
get方式提交中文数据乱码产生的原因和解决办法
对于以get方式传输的数据,request即使设置了以指定的编码接收数据也是无效的(至于为什么无效我也没有弄明白),默认的还是使用ISO8859-1这个字符编码来接收数据,客户端以UTF-8的编码传输数据到服务器端,而服务器端的request对象使用的是ISO8859-1这个字符编码来接收数据,服务器和客户端沟通的编码不一致因此才会产生中文乱码的。解决办法:在接收到数据后,先获取request对象以ISO8859-1字符编码接收到的原始数据的字节数组,然后通过字节数组以指定的编码构建字符串,解决乱码问题。代码如下:
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
*
* 对于以get方式传输的数据,request即使设置了以指定的编码接收数据也是无效的,默认的还是使用ISO8859-1这个字符编码来接收数据
*/
String name = request.getParameter("name");//接收数据
name =new String(name.getBytes("ISO8859-1"), "UTF-8") ;//获取request对象以ISO8859-1字符编码接收到的原始数据的字节数组,然后通过字节数组以指定的编码构建字符串,解决乱码问题
System.out.println("name:"+name);
}
运行结果如下:
以超链接形式传递中文参数的乱码问题
客户端想传输数据到服务器,可以通过表单提交的形式,也可以通过超链接后面加参数的形式,例如:
<a href="${pageContext.request.contextPath}/servlet/RequestDemo05?userName=gacl&name=徐达沛">点击</a>
点击超链接,数据是以get的方式传输到服务器的,所以接收中文数据时也会产生中文乱码问题,而解决中文乱码问题的方式与上述的以get方式提交表单中文数据乱码处理问题的方式一致,如下所示:
String name = request.getParameter("name");
name =new String(name.getBytes("ISO8859-1"), "UTF-8");
另外,需要提的一点就是URL地址后面如果跟了中文数据,那么中文参数最好使用URL编码进行处理,如下所示:
<a href="${pageContext.request.contextPath}/servlet/RequestDemo05?userName=gacl&name=<%=URLEncoder.encode("徐达沛", "UTF-8")%>">点击</a>
提交中文数据乱码问题总结
1、如果提交方式为post,想不乱码,只需要在服务器端设置request对象的编码即可,客户端以哪种编码提交的,服务器端的request对象就以对应的编码接收,比如客户端是以UTF-8编码提交的,那么服务器端request对象就以UTF-8编码接收(request.setCharacterEncoding("UTF-8"))
2、如果提交方式为get,设置request对象的编码是无效的,request对象还是以默认的ISO8859-1编码接收数据,因此要想不乱码,只能在接收到数据后再手工转换,步骤如下:
1).获取获取客户端提交上来的数据,得到的是乱码字符串,data="???è?????"
String data = request.getParameter("paramName");
2).查找ISO8859-1码表,得到客户机提交的原始数据的字节数组
byte[] source = data.getBytes("ISO8859-1");
3).通过字节数组以指定的编码构建字符串,解决乱码
data = new String(source, "UTF-8");
通过字节数组以指定的编码构建字符串,这里指定的编码是根据客户端那边提交数据时使用的字符编码来定的,如果是GB2312,那么就设置成data = new String(source, "GB2312"),如果是UTF-8,那么就设置成data = new String(source, "UTF-8")
Request对象实现请求转发
请求转发的基本概念
请求转发:指一个web资源收到客户端请求后,通知服务器去调用另外一个web资源进行处理。
请求转发的应用场景:MVC设计模式
在Servlet中实现请求转发的两种方式:
1、通过ServletContext的getRequestDispatcher(String path)方法,该方法返回一个RequestDispatcher对象,调用这个对象的forward方法可以实现请求转发。
例如:将请求转发的test.jsp页面
RequestDispatcher reqDispatcher =this.getServletContext().getRequestDispatcher("/test.jsp");
reqDispatcher.forward(request, response);
2、通过request对象提供的getRequestDispatche(String path)方法,该方法返回一个RequestDispatcher对象,调用这个对象的forward方法可以实现请求转发。
例如:将请求转发的test.jsp页面
request.getRequestDispatcher("/test.jsp").forward(request, response);
request对象同时也是一个域对象(Map容器),开发人员通过request对象在实现转发时,把数据通过request对象带给其它web资源处理。
例如:请求RequestDemo06 Servlet,RequestDemo06将请求转发到test.jsp页面
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class RequestDemo06 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String data="大家好,我是孤傲苍狼,我正在总结JavaWeb";
/**
* 将数据存放到request对象中,此时把request对象当作一个Map容器来使用
*/
request.setAttribute("data", data);
//客户端访问RequestDemo06这个Servlet后,RequestDemo06通知服务器将请求转发(forward)到test.jsp页面进行处理
request.getRequestDispatcher("/test.jsp").forward(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
test.jsp页面代码如下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Request对象实现请求转发</title>
</head>
<body>
使用普通方式取出存储在request对象中的数据:
<h3 style="color:red;"><%=(String)request.getAttribute("data")%></h3>
使用EL表达式取出存储在request对象中的数据:
<h3 style="color:red;">${data}</h3>
</body>
</html>
运行结果如下:
request对象作为一个域对象(Map容器)使用时,主要是通过以下的四个方法来操作
- setAttribute(String name,Object o)方法,将数据作为request对象的一个属性存放到request对象中,例如:request.setAttribute("data", data);
- getAttribute(String name)方法,获取request对象的name属性的属性值,例如:request.getAttribute("data")
- removeAttribute(String name)方法,移除request对象的name属性,例如:request.removeAttribute("data")
- getAttributeNames方法,获取request对象的所有属性名,返回的是一个,例如:Enumeration<String> attrNames = request.getAttributeNames();
请求重定向和请求转发的区别
一个web资源收到客户端请求后,通知服务器去调用另外一个web资源进行处理,称之为请求转发/307。
一个web资源收到客户端请求后,通知浏览器去访问另外一个web资源进行处理,称之为请求重定向/302。
在Servlet使用getServletContext()获取ServletContext对象出现java.lang.NullPointerException(空指针)异常的解决办法
在servlet的service方法中获取ServletContext对象出现java.lang.NullPointerException(空指针)异常,代码如下:
//获取ServletContext对象
ServletContext servletContext = this.getServletContext();
这个问题很奇怪,也是第一次遇到,因为以前在servlet的doGet/doPost方法中要获取ServletContext对象时都是这样写的,也没有出现过java.lang.NullPointerException(空指针)异常,上网查了一下出现这个异常的原因:原来是我重写了init(ServletConfig)方法,但重写的init方法内部没有调用super.init(config);就是这导致了错误!父类的 init(ServletConfig)有处理获取ServletContext对象的引用,在doGet/doPost/service方法方法中才能够通过 getServletContext()方法获取到SeverletContext对象!重写了Servlet的init方法后一定要记得调用父类的init方法,否则在service/doGet/doPost方法中使用getServletContext()方法获取ServletContext对象时就会出现java.lang.NullPointerException异常
public void init(ServletConfig config) throws ServletException{
//重写了Servlet的init方法后一定要记得调用父类的init方法,否则在service/doGet/doPost方法中使用getServletContext()方法获取ServletContext对象时就会出现java.lang.NullPointerException异常
super.init(config);
}