JavaWeb_Response和Request对象

本文详细解析了Web服务器如何处理HTTP请求,包括创建request和response对象的过程,以及如何通过response对象向客户端输出中文数据。文章还介绍了使用OutputStream和PrintWriter输出中文时的注意事项,并提供了实现中文文件下载的方法。此外,文章通过实例展示了如何生成随机图片验证码,以及如何控制浏览器定时刷新网页。最后,文章讨论了请求重定向与请求转发的区别,以及如何利用referer头防止盗链。

简介

Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。
request和response对象即然代表请求和响应,那我们要获取客户机提交过来的数据,只需要找request对象就行了。要向客户机输出数据,只需要找response对象就行了。

HttpServletResponse

HttpServletResponse对象对应服务器的响应。这个对象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。

response常见应用

以Response的OutputStream向客户端输出中文数据 

浏览器默认解码的码表是gb2312,程序以什么码表写给客户机,就一定要控制浏览器以什么码表打开:
response.setHeader("Content-type", "text/html;charset=UTF-8");
String data = "中国";
OutputStream out = response.getOutputStream();
out.write(data.getBytes(“UTF-8”));//将中国按指定的码表编码
多学一招:使用HTML语言里的<meta>标签模拟一个http响应头来控制浏览器的行为,替代上面设置HTTP头的代码
out.write(“<meta http-equiv =‘content-type’ content = ‘text/html;charset=UTF-8’>”.getBytes());//equiv表示模拟什么头
注意下面写错了(分号写成了逗号),浏览器会提示下载(使用ie打开)
response.setHeader(“Content-type”, “text/html, charset=UTF-8");
问题:用OutputStream输出1,客户端看到的为什么不是1?
因为浏览器会默认按数字1查gb2312码表,然后按照1对应的字符显示。
要显示1就应该输出字符1:1+ “”.getBytes()

以Response的PrintWriter流向客户端输出中文数据

Response对象默认码表是iso8859,会将中文数据以其编码,但是该码表没有中文,所以会返回两个问号(未知字符)显示在浏览器中 ,所以就需要指定response的码表
response.setCharacterEncoding(“UTF-8”);//指定response对象使用的码表,以控制response以什么码表向浏览器写出数据         
response.setHeader(“content-type”, “text/html;charset=UTF-8”);//指定浏览器以什么码表打开服务器发送的数据
String data = "中国";
PrintWriter out = response.getWriter();
out.write(data);
简单写法,既指定response对象使用的码表又指定浏览器以什么码表打开数据,但推荐使用上面的方法
response.setContentType(“text/html;charset=UTF-8”);

文件下载和中文文件的下载

首先在WebRoot下新建一个download目录来保存供下载的web资源,比如:1.jpg
		//使用servletContext来获取资源的绝对地址(需要获取资源的名称显示在下载框中)
		String path  = this.getServletContext().getRealPath(“/download/1.jpg”);
		//获取资源名称,并让浏览器以下载方式打开。
		String filename = path.substring(path.lastIndexOf("\\")+1);
		response.setHeader("content-disposition","attachment;filename="+filename);
		//如果下载文件是中文文件(比如:我.jpg),则文件名需要经过url编码
		//response.setHeader("content-disposition","attachment;filename="+URLEncoder.encode(filename,"UTF-8"));
		//将资源写入到文件读取流中,用response获取输出流写出(读取模板代码在eclipse中配置了,输入“rw”+alt+/就会自动写出)
		InputStream in = null;
		OutputStream out = null;
		try{
			in = new FileInputStream(path);
			out = response.getOutputStream();
			int len = 0;
			byte[] buf = new byte[1024];
			while((len = in.read(buf))!=-1){
				out.write(buf,0,len);
			}
		} finally{
			if(in !=null){
				try{
					in.close();
				}catch(Exception e){
					e.printStackTrace();
				}
			}
			if(out !=null){
				try{
					out.close();
				}catch(Exception e){
					e.printStackTrace();
				}
			}
		}

输出随机图片

应用:网站注册,防止机器人注册
package cn.itcast;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
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 RandomPicDemo extends HttpServlet {
	
	public static final int WIDTH = 120;
	public static final int HEIGHT = 35;	
	
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//首先通过Java的BufferedImage类构建一副在内存中的图形
		BufferedImage image = new BufferedImage(WIDTH,HEIGHT,BufferedImage.TYPE_INT_RGB);
		//然后调用它的getGraphics方法得到这幅图形,以在图片中写数据
		Graphics g = image.getGraphics();
		//1.设置背景色
		setBackGround(g);
		//2.设置边框
		setBorder(g);
		//3.画干扰线
		drawRandomLine(g);
		//4.写随机数
		drawRandomNum((Graphics2D)g);
		//5.图形写给浏览器
		//注意:浏览器默认会缓存该图片,导致再次按回车访问页面时图片不会改变。但是用户每次访问需要一个全新的图片,所以需要加头控制浏览器不要缓存该图片
		//刷新页面图片会改变,因为刷新有两个作用:一是不论有没有缓存,都会向服务器发送请求,二是把上次的事情再干一次
		response.setDateHeader("expires", -1);//此头控制缓存时间
		//加入其它头,确保对于其它浏览器也不会缓存图片
		response.setHeader("Cache-Control","no-cache");
		response.setHeader("Pragma","no-cache");
		//最后控制浏览器以图片方式打开数据,并调用ImageIO类的write方法将该图形写给与浏览器相关的输出流
		response.setContentType("image/jpeg");
		ImageIO.write(image, "jpg", response.getOutputStream());
	}
	
	//分别创建方法设置图形的背景色、边框、干扰线、随机数
	private void setBackGround(Graphics g) {
		g.setColor(Color.WHITE);//将图形的颜色设为白色
		g.fillRect(0, 0, WIDTH, HEIGHT);//将颜色填充整个矩形区域,0,0代表从页面最左上角开始,常量:宽120,高35
	}
	private void setBorder(Graphics g) {
		g.setColor(Color.BLUE);
		g.drawRect(1, 1, WIDTH-2, HEIGHT-2);//画一个矩形,调整参数使边框在图形内部
	}
	private void drawRandomLine(Graphics g) {
		g.setColor(Color.GREEN);//设置干扰线的颜色为绿色
		for (int i=0; i<5; i++) {
		     int x1 = new Random().nextInt(WIDTH);//设置起始点坐标
		     int y1 = new Random().nextInt(HEIGHT);
		     int x2 = new Random().nextInt(WIDTH);
		     int y2 = new Random().nextInt(HEIGHT);
		     g.drawLine(x1, y1, x2, y2);;
		}
	}
	
	//生成随机字符方法
	//小知识:Unicode表中所有中文的编码范围:[\u4e00-\u9fa5]。java默认Unicode码表,char c =‘一’与char c = ‘\u4e00’等价,因为会将字符翻译成对应编码
	//为了使用汉字旋转方法,需要将Graphics强转为Graphics2D(其实本来就是Graphics2D,但Java中为了兼容性考虑,使用了前者)
	private void drawRandomNum(Graphics2D g) {
		g.setColor(Color.RED);
		g.setFont(new Font("宋体",Font.BOLD,20));
		//取四个常用的汉字,常用汉字见下面全部汉字字符集
		String base = "\u7684\u4e00\u662f...";
		int x = 5;
		for(int i=0;i<4;i++){
			int degree = new Random().nextInt()%30;//随机旋转度数,生成一个-30至30的随机数
		   	String ch = base.charAt(new Random().nextInt(base.length()))+"";	
			g.rotate(degree*Math.PI/180, x, 20); //绕一个点旋转某一弧度  
		   	g.drawString(ch, x,20);//写入汉字,定义汉字的坐标
			g.rotate(-degree*Math.PI/180, x, 20);//还原位置
		   	 x+=30;//因为字体大小是20
		}
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request,response);
	}
}
在供注册的html页面的表单中加入此servlet程序,并编写javascript函数控制图片点击刷新(html页面更改了服务器无需重新部署和重启!)
<!DOCTYPE html>
<html>
  <head>
    <title>Register.html</title>
   	<script type="text/javascript">
		function changeImage(img){
			//后面加上随机数是为了避免使用缓存的资源,而是重新向服务器发送请求
			img.src = img.src+"?"+new Date().getTime();
		}
	</script>
  </head>
  <body>
    <form>
    	用户名:<input type="text" name="username"><br/>
    	密码:<input type="password" name="password"><br/>
    	认证码:<input type="text" name="checkcode">
    	<img src="/Day05/servlet/ServletDemo4" onclick="changeImage(this)" alt="change image" style="cursor:hand"><br/>
    	<input type = "submit" value="注册">
    </form>
  </body>
</html>

全部简单汉字字符集:

String base = "\u7684\u4e00\u662f\u4e86\u6211\u4e0d\u4eba\u5728\u4ed6\u6709\u8fd9\u4e2a\u4e0a\u4eec\u6765\u5230\u65f6\u5927\u5730\u4e3a\u5b50\u4e2d\u4f60\u8bf4\u751f\u56fd\u5e74\u7740\u5c31\u90a3\u548c\u8981\u5979\u51fa\u4e5f\u5f97\u91cc\u540e\u81ea\u4ee5\u4f1a\u5bb6\u53ef\u4e0b\u800c\u8fc7\u5929\u53bb\u80fd\u5bf9\u5c0f\u591a\u7136\u4e8e\u5fc3\u5b66\u4e48\u4e4b\u90fd\u597d\u770b\u8d77\u53d1\u5f53\u6ca1\u6210\u53ea\u5982\u4e8b\u628a\u8fd8\u7528\u7b2c\u6837\u9053\u60f3\u4f5c\u79cd\u5f00\u7f8e\u603b\u4ece\u65e0\u60c5\u5df1\u9762\u6700\u5973\u4f46\u73b0\u524d\u4e9b\u6240\u540c\u65e5\u624b\u53c8\u884c\u610f\u52a8\u65b9\u671f\u5b83\u5934\u7ecf\u957f\u513f\u56de\u4f4d\u5206\u7231\u8001\u56e0\u5f88\u7ed9\u540d\u6cd5\u95f4\u65af\u77e5\u4e16\u4ec0\u4e24\u6b21\u4f7f\u8eab\u8005\u88ab\u9ad8\u5df2\u4eb2\u5176\u8fdb\u6b64\u8bdd\u5e38\u4e0e\u6d3b\u6b63\u611f\u89c1\u660e\u95ee\u529b\u7406\u5c14\u70b9\u6587\u51e0\u5b9a\u672c\u516c\u7279\u505a\u5916\u5b69\u76f8\u897f\u679c\u8d70\u5c06\u6708\u5341\u5b9e\u5411\u58f0\u8f66\u5168\u4fe1\u91cd\u4e09\u673a\u5de5\u7269\u6c14\u6bcf\u5e76\u522b\u771f\u6253\u592a\u65b0\u6bd4\u624d\u4fbf\u592b\u518d\u4e66\u90e8\u6c34\u50cf\u773c\u7b49\u4f53\u5374\u52a0\u7535\u4e3b\u754c\u95e8\u5229\u6d77\u53d7\u542c\u8868\u5fb7\u5c11\u514b\u4ee3\u5458\u8bb8\u7a1c\u5148\u53e3\u7531\u6b7b\u5b89\u5199\u6027\u9a6c\u5149\u767d\u6216\u4f4f\u96be\u671b\u6559\u547d\u82b1\u7ed3\u4e50\u8272\u66f4\u62c9\u4e1c\u795e\u8bb0\u5904\u8ba9\u6bcd\u7236\u5e94\u76f4\u5b57\u573a\u5e73\u62a5\u53cb\u5173\u653e\u81f3\u5f20\u8ba4\u63a5\u544a\u5165\u7b11\u5185\u82f1\u519b\u5019\u6c11\u5c81\u5f80\u4f55\u5ea6\u5c71\u89c9\u8def\u5e26\u4e07\u7537\u8fb9\u98ce\u89e3\u53eb\u4efb\u91d1\u5feb\u539f\u5403\u5988\u53d8\u901a\u5e08\u7acb\u8c61\u6570\u56db\u5931\u6ee1\u6218\u8fdc\u683c\u58eb\u97f3\u8f7b\u76ee\u6761\u5462\u75c5\u59cb\u8fbe\u6df1\u5b8c\u4eca\u63d0\u6c42\u6e05\u738b\u5316\u7a7a\u4e1a\u601d\u5207\u600e\u975e\u627e\u7247\u7f57\u94b1\u7d36\u5417\u8bed\u5143\u559c\u66fe\u79bb\u98de\u79d1\u8a00\u5e72\u6d41\u6b22\u7ea6\u5404\u5373\u6307\u5408\u53cd\u9898\u5fc5\u8be5\u8bba\u4ea4\u7ec8\u6797\u8bf7\u533b\u665a\u5236\u7403\u51b3\u7aa2\u4f20\u753b\u4fdd\u8bfb\u8fd0\u53ca\u5219\u623f\u65e9\u9662\u91cf\u82e6\u706b\u5e03\u54c1\u8fd1\u5750\u4ea7\u7b54\u661f\u7cbe\u89c6\u4e94\u8fde\u53f8\u5df4\u5947\u7ba1\u7c7b\u672a\u670b\u4e14\u5a5a\u53f0\u591c\u9752\u5317\u961f\u4e45\u4e4e\u8d8a\u89c2\u843d\u5c3d\u5f62\u5f71\u7ea2\u7238\u767e\u4ee4\u5468\u5427\u8bc6\u6b65\u5e0c\u4e9a\u672f\u7559\u5e02\u534a\u70ed\u9001\u5174\u9020\u8c08\u5bb9\u6781\u968f\u6f14\u6536\u9996\u6839\u8bb2\u6574\u5f0f\u53d6\u7167\u529e\u5f3a\u77f3\u53e4\u534e\u8ae3\u62ff\u8ba1\u60a8\u88c5\u4f3c\u8db3\u53cc\u59bb\u5c3c\u8f6c\u8bc9\u7c73\u79f0\u4e3d\u5ba2\u5357\u9886\u8282\u8863\u7ad9\u9ed1\u523b\u7edf\u65ad\u798f\u57ce\u6545\u5386\u60ca\u8138\u9009\u5305\u7d27\u4e89\u53e6\u5efa\u7ef4\u7edd\u6811\u7cfb\u4f24\u793a\u613f\u6301\u5343\u53f2\u8c01\u51c6\u8054\u5987\u7eaa\u57fa\u4e70\u5fd7\u9759\u963f\u8bd7\u72ec\u590d\u75db\u6d88\u793e\u7b97\u4e49\u7adf\u786e\u9152\u9700\u5355\u6cbb\u5361\u5e78\u5170\u5ff5\u4e3e\u4ec5\u949f\u6015\u5171\u6bdb\u53e5\u606f\u529f\u5b98\u5f85\u7a76\u8ddf\u7a7f\u5ba4\u6613\u6e38\u7a0b\u53f7\u5c45\u8003\u7a81\u76ae\u54ea\u8d39\u5012\u4ef7\u56fe\u5177\u521a\u8111\u6c38\u6b4c\u54cd\u5546\u793c\u7ec6\u4e13\u9ec4\u5757\u811a\u5473\u7075\u6539\u636e\u822c\u7834\u5f15\u98df\u4ecd\u5b58\u4f17\u6ce8\u7b14\u751a\u67d0\u6c89\u8840\u5907\u4e60\u6821\u9ed8\u52a1\u571f\u5fae\u5a18\u987b\u8bd5\u6000\u6599\u8c03\u5e7f\u8716\u82cf\u663e\u8d5b\u67e5\u5bc6\u8bae\u5e95\u5217\u5bcc\u68a6\u9519\u5ea7\u53c2\u516b\u9664\u8dd1\u4eae\u5047\u5370\u8bbe\u7ebf\u6e29\u867d\u6389\u4eac\u521d\u517b\u9999\u505c\u9645\u81f4\u9633\u7eb8\u674e\u7eb3\u9a8c\u52a9\u6fc0\u591f\u4e25\u8bc1\u5e1d\u996d\u5fd8\u8da3\u652f\u6625\u96c6\u4e08\u6728\u7814\u73ed\u666e\u5bfc\u987f\u7761\u5c55\u8df3\u83b7\u827a\u516d\u6ce2\u5bdf\u7fa4\u7687\u6bb5\u6025\u5ead\u521b\u533a\u5965\u5668\u8c22\u5f1f\u5e97\u5426\u5bb3\u8349\u6392\u80cc\u6b62\u7ec4\u5dde\u671d\u5c01\u775b\u677f\u89d2\u51b5\u66f2\u9986\u80b2\u5fd9\u8d28\u6cb3\u7eed\u54e5\u547c\u82e5\u63a8\u5883\u9047\u96e8\u6807\u59d0\u5145\u56f4\u6848\u4f26\u62a4\u51b7\u8b66\u8d1d\u8457\u96ea\u7d22\u5267\u554a\u8239\u9669\u70df\u4f9d\u6597\u503c\u5e2e\u6c49\u6162\u4f5b\u80af\u95fb\u5531\u6c99\u5c40\u4f2f\u65cf\u4f4e\u73a9\u8d44\u5c4b\u51fb\u901f\u987e\u6cea\u6d32\u56e2\u5723\u65c1\u5802\u5175\u4e03\u9732\u56ed\u725b\u54ed\u65c5\u8857\u52b3\u578b\u70c8\u59d1\u9648\u83ab\u9c7c\u5f02\u62b1\u5b9d\u6743\u9c81\u7b80\u6001\u7ea7\u7968\u602a\u5bfb\u6740\u5f8b\u80dc\u4efd\u6c7d\u53f3\u6d0b\u8303\u5e8a\u821e\u79d8\u5348\u767b\u697c\u8d35\u5438\u8d23\u4f8b\u8ffd\u8f83\u804c\u5c5e\u6e10\u5de6\u5f55\u4e1d\u7259\u515a\u7ee7\u6258\u8d76\u7ae0\u667a\u51b2\u53f6\u80e1\u5409\u5356\u575a\u559d\u8089\u9057\u6551\u4fee\u677e\u4e34\u85cf\u62c5\u620f\u5584\u536b\u836f\u60b2\u6562\u9760\u4f0a\u6751\u6234\u8bcd\u68ee\u8033\u5dee\u77ed\u7956\u4e91\u89c4\u7a97\u6563\u8ff7\u6cb9\u65e7\u9002\u4e61\u67b6\u6069\u6295\u5f39\u94c1\u535a\u96f7\u5e9c\u538b\u8d85\u8d1f\u52d2\u6742\u9192\u6d17\u91c7\u6beb\u5634\u6bd5\u4e5d\u51b0\u65e2\u72b6\u4e71\u666f\u5e2d\u73cd\u7ae5\u9876\u6d3e\u7d20\u8131\u519c\u7591\u7ec3\u91ce\u6309\u72af\u62cd\u5f81\u574f\u9aa8\u4f59\u627f\u7f6e\u81d3\u5f69\u706f\u5de8\u7434\u514d\u73af\u59c6\u6697\u6362\u6280\u7ffb\u675f\u589e\u5fcd\u9910\u6d1b\u585e\u7f3a\u5fc6\u5224\u6b27\u5c42\u4ed8\u9635\u739b\u6279\u5c9b\u9879\u72d7\u4f11\u61c2\u6b66\u9769\u826f\u6076\u604b\u59d4\u62e5\u5a1c\u5999\u63a2\u5440\u8425\u9000\u6447\u5f04\u684c\u719f\u8bfa\u5ba3\u94f6\u52bf\u5956\u5bab\u5ffd\u5957\u5eb7\u4f9b\u4f18\u8bfe\u9e1f\u558a\u964d\u590f\u56f0\u5218\u7f6a\u4ea1\u978b\u5065\u6a21\u8d25\u4f34\u5b88\u6325\u9c9c\u8d22\u5b64\u67aa\u7981\u6050\u4f19\u6770\u8ff9\u59b9\u85f8\u904d\u76d6\u526f\u5766\u724c\u6c5f\u987a\u79cb\u8428\u83dc\u5212\u6388\u5f52\u6d6a\u542c\u51e1\u9884\u5976\u96c4\u5347\u7883\u7f16\u5178\u888b\u83b1\u542b\u76db\u6d4e\u8499\u68cb\u7aef\u817f\u62db\u91ca\u4ecb\u70e7\u8bef";

控制浏览器定时刷新网页

response.setHeader("refresh", "3");//设置头控制浏览器每隔3秒刷新一次
String data = new Random().nextInt(1000)+"";
response.getWriter().write(data);
假设这是一个处理登陆的Servlet
//假设程序运行到此,用户登陆成功了
//向浏览器发送了中文,需要设置response码表以及控制浏览器以该码表打开
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
response.setHeader("refresh", "3;url='/day05/index.jsp'");//加上跳转的页面的url地址
response.getWriter().write("恭喜您登陆成功,本浏览器将在3秒后跳到首页,如果没有跳转,请点<a href=''>超链接</a>");

实用的自动跳转技术

在实际开发中应由Servlet生成数据,再由JSP负责显示数据。
Servlet中的代码:
		//创建需要显示的消息,用meta标签模拟一个http头来定时刷新
		String message="<meta http-equiv='refresh' content='3;url=/day05/index.jsp'>恭喜您登陆成功,本浏览器将在3秒后跳到首页,如果没有跳转,请点<a href=''>超链接</a>";
		//将消息带过去
		this.getServletContext().setAttribute("message", message);
		//跳转到消息显示页面
		this.getServletContext().getRequestDispatcher("/message.jsp").forward(request,response);
message.jsp中的代码:
<body>
    <%
    	String message = (String)application.getAttribute("message");
   		out.write(message); 
     %>
</body>

用Expires头控制浏览器缓存

如果一个servlet提供用户访问的数据始终不改变,就需要对该数据进行缓存
		//将数据缓存一小时,注意一定要加上当前时间值
		response.setDateHeader("expires",System.currentTimeMillis()+1000*3600);
		String data = "aaaaa";
		response.getWriter().write(data);
之后一个小时之内向服务器发请求都不会有请求头了
网站的图片,css,js,就需要控制浏览器缓存,减轻浏览器负荷

请求重定向

请求重定向指:一个web资源收到客户端请求后,通知客户端去访问另外一个web资源,这称之为请求重定向。区别于请求转发
实现方式:
                response.sendRedirect("/day05/index.jsp");
		//以下代码是原理
		/*response.setStatus(302);
		response.setHeader("location", "/day05/index.jsp");*/
特点:
  1. 浏览器地址栏会发生变化
  2. 向服务器发送两次请求,意味着会有两对request/response对象所以请求重定向尽量不使用,会加重服务器负担
应用场景:
  1. 用户登陆时必须使用重定向到首页,地址栏的变化会提示用户已到首页。所以这里不使用自动跳转技术
  2. 用户点击购物后会跳转到购物车显示页面,如果使用转发,刷新一下页面就会执行上次的操作,则再次购买商品,而使用重定向不会。
请求重定向运行流程图详见PPT

response细节

1. getOutputStream和getWriter方法分别用于得到输出二进制数据、输出文本数据的ServletOuputStream、Printwriter对象。
2. getOutputStream和getWriter这两个方法互相排斥,同一个response对象调用了其中的任何一个方法后,就不能再调用另一方法。
注意:比如在一个Servlet中使用了一个方法getOutputStream,然后使用转发代码:
this.getServletContext().getRequestDispatcher("/ServletDemo8").forward(request,response);
然后在ServletDemo8中却调用了另一个方法,浏览器访问原servlet就会出现getOutputStream() has already been called for this response提示
不管什么数据,都可以用字节流输出
重定向不会出现该问题,因为重定向会产生两个response对象
3. Servlet程序向ServletOutputStream或PrintWriter对象中写入的数据将被Servlet引擎从response里面获取,Servlet引擎将这些数据当作响应消息的正文,然后再与响应状态行和各响应头组合后输出到客户端。 
4. Serlvet的service方法结束后,Servlet引擎将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用过close方法,如果没有,Servlet引擎将调用close方法关闭该输出流对象。所以不需要自己关闭servlet的流对象,但是自己新建的流对象需要自己关闭

HttpServletRequest

HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,开发人员通过这个对象的方法,可以获得客户这些信息。

request获取请求行信息

String getMethod():获取请求方式GET\POST
URI:全球统一资源定位符,用于标识任意一个资源,例:/news/1.html,开发中使用URI多
URL:统一资源定位符,用于标识一个互联网上资源(是URI的子集),例:http://www.sina.com/news/1.html
getRequestURL方法返回客户端发出请求时的完整URL。
getRequestURI方法返回请求行中的资源名部分。
应用:
  • 权限管理:开发中使用一个过滤器拦截用户的所有请求,然后通过getRequestURI方法获取用户请求的资源,再判断该资源访问需要什么权限,判断用户有没有访问权限
  • 统计页面访问次数,将该URI访问次数加1
getPathInfo方法返回请求URL中的额外路径信息。额外路径信息是请求URL中的位于Servlet的路径之后和查询参数之前的内容,它以“/”开头。

request获取请求头信息

getHeader方法 ,返回一个字符串,它是头的值。如果是一个日期头就直接调用getDateHeader方法,直接返回一个long型值,因此不需要进行格式转换,简单。
getHeaders方法 ,返回一个枚举,包含了同名头的所有值。
getHeaderNames方法,返回一个枚举,获取所有头的名称 

request获取请求参数

getParameter方法
getParameterValues(String name)方法,获取同一参数名的所有值
getParameterNames方法,返回一个枚举,获取所有的参数名 
getParameterMap方法,将所有的请求参数封装到一个Map集合中
getInputStream方法,将客户机的请求数据作为InputStream流返回,然后通过流去读,一般用于文件上传
getQueryString 方法返回请求行中的参数部分。

客户机带数据给服务器的两种方式

1. 超链接
<a href="/day05/servlet/ServletDemo2?username=xxx">点</a>
2. 表单
<form action="/day05/servlet/ServletDemo2?name=xxx" method="post">
    	用户名:<input type="text" name="username"></br>
    	密码:<input type="password" name="password"></br>
    	<input type="submit" value="提交">
</form>

servlet中获取数据示例:

		//1.获取单个参数
		String data = request.getParameter("username");
		//获取请求数据的时候一般都要先检查后使用,这里if中先判断数据不为空,再判断数据不全为空格
		if(data!=null&&!data.trim().equals("")){
			System.out.println(data);			
		}
		
		//2.获取所有参数及值
		Enumeration e = request.getParameterNames();
		while(e.hasMoreElements()){
			String name = (String) e.nextElement();
			String value = request.getParameter(name);
			System.out.println(name+"="+value);
		}
		
		//3.获取同名参数的所有值
		String[] values = request.getParameterValues("username");
		//实际开发中,当用户输入的值为null,为了程序健壮性,就不能处理该数据,所以这里不能用增强for循环。
		for(int i=0;values!=null&&i<values.length;i++){
			System.out.println(values[i]);
		}
		
		//4.把所有的请求参数和值放在map中,针对多个同名参数值的情况可以简化编程。做框架常用
		Map<String,String[]> map = request.getParameterMap();//泛型的参数名称类型是String,参数值类型是String数组(因为request中会封装同名的数据)
		//实际开发中,客户机提交的数据服务器会用一个对象(比如User类)即JavaBean类来封装。
		//首先会设计一个表单Bean,会将数据封装进该Bean,然后对数据进行校验,校验完成之后再拷贝到实体Bean中:BeanUtils.copyProperties(user,formbean)。
		//这里需要在项目中单独导入BeanUtils和logging的jar包
		User user = new User();//创建Bean对象
		try {
			BeanUtils.populate(user, map);//用Map集合中的数据填充Bean
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		
		//5.通过InputStream流获取数据,文件上传
		InputStream in = request.getInputStream();
		int len=0;
		byte[] buf = new byte[1024];
		while((len=in.read(buf))!=-1){
			System.out.println(new String(buf,0,len));
		}

request获取客户机其他信息

getRemoteAddr方法返回发出请求的客户机的IP地址
getRemoteHost方法返回发出请求的客户机的完整主机名。客户机需要在DNS上注册之后才会有主机名,如果没有注册还是显示IP
getRemotePort方法返回客户机的浏览器所使用的网络端口号,是随机的
getLocalAddr方法返回WEB服务器的IP地址。
getLocalName方法返回WEB服务器的主机名

request对象常见应用

各种表单输入项数据的获取

一个表单中可以有很多种不同类型的输入项:text,password,radio,select,checkbox,file,textarea
一般可能还有隐藏输入项hidden,它是不可见的,用于在用户无意识的情况下提交一些数据
html表单页面代码:
  <body>
  	<!-- 使用超链接提交数据时如果URL后的数据是中文的一定要对该数据进行URL编码再提交,或者在服务器端手工处理 -->
    <a href="/day05/servlet/ServletDemo2?username=我们">点</a>
    <form action="/day05/servlet/ServletDemo2?name=xxx" method="post">
    	用户名:<input type="text" name="username"></br>
    	密码:<input type="password" name="password"></br>
    	性别:
    		<input type="radio" name="gender" value="male">男
    		<input type="radio" name="gender" value="female">女<br/>
    	所在地:
    		<select name="city">
    			<option value="beijing">北京
    			<option value="shanghai">上海
    			<option value="wuhan">武汉
    		</select><br/>
    	爱好:
    		<input type="checkbox" name="likes" value="sing">唱歌	
    		<input type="checkbox" name="likes" value="dance">跳舞
    		<input type="checkbox" name="likes" value="basketball">篮球<br/>
    	备注:
    		<textarea rows="6" cols="60" name="description"></textarea><br/>
    	大头照:
    		<input type="file" name="image"><br/>
    		<input type="hidden" name="id" value="12345">
    	<input type="submit" value="提交">
    </form>
  </body>
Servlet中获取数据代码:
                //对于只有单个值的参数的获取
		System.out.println(request.getParameter("username"));
		System.out.println(request.getParameter("password"));
		//对于有多个值的参数的获取,注意程序健壮性,防止抛出空指针异常
		String[] likes = request.getParameterValues("likes");
		for (int i =0;likes!=null&&i<likes.length;i++){
			System.out.println(likes[i]);
		}
		//对于上传的文件的获取见单独的案例讲解(image、button给js编程用)

请求参数的中文乱码问题 

浏览器以哪个码表提交中文要看当前网页的码表是哪个
当表单中提交了中文数据之后服务器端获取会显示乱码。产生乱码的原理图如下:

因为request对象默认使用iso8859-1码表,所以要控制request对象用正确的码表打开:
		request.setCharacterEncoding("utf-8");//但是只对post提交有效
		System.out.println(request.getParameter("username"));
对于get方式提交的中文数据,只能手工处理,先将数据用默认码表编码,再使用正确码表解码:
		String username=request.getParameter("username");
		username = new String(username.getBytes("iso8859-1"),"utf-8");
		System.out.println(username+request.getMethod());
对于超链接提交的中文数据,默认的是get请求,所以控制request对象的码表方式无效。所以也只能手工处理
拓展了解:更改服务器的配置也可以解决中文乱码问题,但是开发时不要使用,因为实际中服务器在远端,而且服务器类型不一致
进入tomcat服务器的conf文件夹下的server.xml文件,在8080的连接器Connector标签里加上URIEncoding="utf-8"或者加上useBodyEncodingForURI="true"(这样设置request的码表对于get方式也有效了)

request实现请求转发及MVC模式

request对象也可以实现请求转发(另一个是ServletContext):请求转发指一个web资源收到客户端请求后,通知服务器去调用另外一个web资源进行处理。请求转发的应用场景:MVC设计模式
MVC:Model(JavaBean)、View(Jsp)、Controller(Servlet)。Servlet将数据存在JavaBean中,然后将Javabean存在request域中带给Jsp页面显示
request对象提供了一个getRequestDispatcher方法,该方法返回一个RequestDispatcher对象,调用这个对象的forward方法可以实现请求转发。
request对象同时也是一个域对象,开发人员通过request对象在实现转发时,把数据通过request对象带给其它web资源处理。
实际开发中不能通过ServletContext域对象带数据,会出现多线程问题,第二个用户产生的数据将第一个用户产生的数据覆盖了。实际中使用request对象,因为每一次请求都有一个request对象
Servlet中代码:
		String data="aaaaaaa";
		//不能使用ServletContext对象带数据
		//this.getServletContext().setAttribute("data",data);
		//使用request带数据
		request.setAttribute("data", data);
		//request实现转发
		request.getRequestDispatcher("/message.jsp").forward(request, response);;
jsp中代码(两种取出方式):
  <body>
  	${data}<!-- 一个EI表达式,会自动从域对象中找该数据并输出 -->
  	<%
  		String data=(String)request.getAttribute("data");//取出域中存的数据而getParameter是获取客户机提交的数据
  	 	out.write(data);
  	 %>
  </body>

请求转发细节

forward方法用于将请求转发到RequestDispatcher对象封装的资源。
细节1:
如果在调用forward方法之前,在Servlet程序中写入的部分内容已经被真正地传送到了客户端(比如response的流对象被手动关闭或者已经跳转了),forward方法将抛出IllegalStateException异常。 
                String data="aaaaaaa";
		PrintWriter out = response.getWriter();
		out.write(data);
		out.close();
		//以下跳转会导致:java.lang.IllegalStateException: Cannot forward after response has been committed
		request.getRequestDispatcher("/message.jsp").forward(request, response);
实际开发中容易出现的问题(解决方式是加上return语句):
                String data="aaaaaaa";
		if(true){
			//因为这里已经向浏览器写数据了,在下面再次跳转就会出现相同问题。
			request.getRequestDispatcher("/index.jsp").forward(request, response);
			//养成良好习惯,在跳转之后一定要写return语句,因为return后的代码不会执行
			return;
		}
		request.getRequestDispatcher("/message.jsp").forward(request, response);
细节2:
forward前会清空response中的数据
如果在调用forward方法之前向Servlet引擎的缓冲区中写入了内容,只要写入到缓冲区中的内容还没有被真正输出到客户端,forward方法就可以被正常执行(因为此时流未关闭),原来写入到输出缓冲区中的内容将被清空,但是,已写入到HttpServletResponse对象中的响应头字段信息保持有效。
		//页面中指挥显示index.jsp中的内容而不会显示data的数据。因为在跳转之前data已经被清空了
		String data="aaaaaaa";
		response.getWriter().write(data);
		request.getRequestDispatcher("/index.jsp").forward(request, response);
forward请求转发的特点(区别于请求重定向):
  1. 客户端只发一次请求,而服务器端有多个资源调用
  2. 客户端浏览器地址栏不会发生变化

请求重定向和请求转发的区别 

一个web资源收到客户端请求后,由request通知服务器去调用另外一个web资源进行处理,称之为请求转发。
一个web资源收到客户端请求后,由response通知浏览器去访问另外一个web资源,称之为请求重定向。
RequestDispatcher.forward方法只能将请求转发给同一个WEB应用中的组件;而HttpServletResponse.sendRedirect 方法还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。 
如果传递给HttpServletResponse.sendRedirect 方法的相对URL以“/”开头,它是相对于整个WEB站点的根目录;如果创建RequestDispatcher对象时指定的相对URL以“/”开头,它是相对于当前WEB应用程序的根目录。 
调用HttpServletResponse.sendRedirect方法重定向的访问过程结束后,浏览器地址栏中显示的URL会发生改变,由初始的URL地址变成重定向的目标URL;调用RequestDispatcher.forward 方法的请求转发过程结束后,浏览器地址栏保持初始的URL地址不变。
HttpServletResponse.sendRedirect方法对浏览器的请求直接作出响应,响应的结果就是告诉浏览器去重新发出对另外一个URL的访问请求;RequestDispatcher.forward方法在服务器端内部将请求转发给另外一个资源,浏览器只知道发出了请求并得到了响应结果,并不知道在服务器程序内部发生了转发行为。 
RequestDispatcher.forward方法的调用者与被调用者之间共享相同的request对象和response对象,它们属于同一个访问请求和响应过程;而HttpServletResponse.sendRedirect方法调用者与被调用者使用各自的request对象和response对象,它们属于两个独立的访问请求和响应过程。  

include方法实现页面包含

每一个网页都有共同的网头和网脚,它们是重复代码,需要被提取成公共的页面以便重复利用。这些公用页面需要在服务器输出网页内容时被包含进去。
RequestDispatcher.include方法用于将RequestDispatcher对象封装的资源内容作为当前响应内容的一部分包含进来,从而实现可编程的服务器端包含功能。
被包含的Servlet程序不能改变响应消息的状态码和响应头,如果它里面存在这样的语句,这些语句的执行结果将被忽略。
步骤:
在WebRoot下新建一个public文件夹用于存放公共网头和网脚JSP页面。然后在public文件夹中新建head.jsp和foot.jsp页面
然后在servlet中输出网页时包含进网头和网脚页面
		//这里省略了输出全局架构标签
		//包含网头
		request.getRequestDispatcher("/public/head.jsp").include(request, response);
		//输出网页内容
		response.getWriter().write("hahaha<br/>");
		//包含网脚
		request.getRequestDispatcher("/public/foot.jsp").include(request, response);
		//这里省略了输出全局架构标签
细节:注意实际开发中被包含页面不能包含全局架构标签:html,body,head,全局架构标签应在包含的页面中输出,这样才能保证最终页面的完整标签架构
实际开发中用jsp输出页面内容,jsp中的页面包含技术会在Jsp中讲到

利用referer头防盗链

别人的网站利用网络资源的URL地址将资源盗走,用户没有通过首页或者看广告就直接访问到了资源。

		//先拿referer头,拿到资源的地址
		String referer = request.getHeader("referer");
		//referer为null表示用户直接访问了资源的URL,而没有从任何页面链接过来
		//用户不是从指定的地址链接过来也被认定为盗链者
		if(referer==null||!referer.startsWith("http://localhost")){
			//重定向用户到网站首页,在首页提供该资源的链接
			response.sendRedirect("/day05/index.jsp");
			return;//重定向之后下面的代码不需要执行,return比else好
		}
		String data = "凤姐日记";
		response.getWriter().write(data);
首页代码:
  <body>
    	看广告 <br/>
  	<a href="/day05/servlet/ServletDemo2">看凤姐</a>
  </body>

web工程中各类地址的写法

原则:写地址时最好就以斜杠开头。
如果该地址是给服务器使用的,那么斜杠就代表当前web应用
如果该地址是给浏览器使用的,那么斜杠就代表网站,一个网站下可能有多个web应用
针对名称为day06的web应用下的form.html文件为例:
		//1.给服务器用
		request.getRequestDispatcher("/form.html").forward(request, response);
		//2.给浏览器用,因为是让浏览器跳转
		response.sendRedirect("/day05/form.html");
		//3.给服务器用的,让服务器获取该资源的绝对路径
		this.getServletContext().getRealPath("/form.html");
		//4.给服务器用的
		this.getServletContext().getResourceAsStream("/form.html");
		//5.
		/*
		 给浏览器用的,用户点击超链接浏览器使用该地址发请求
		 <a href="/day05/form.html">点</a>
		 给浏览器用的
		 <form action="/day05/form.html"></form>
		 */

斜杠的使用规则

如果要获取服务器的某一个资源(URL资源),统统用左斜杠:/,比如:/day05/form.html
如果要获取硬盘上的某一个资源,统统用反斜杠:\\,比如c:\\
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值