首页 > WEB开发 > 后台开发 > response & request
2014
08-13

response & request

07. response & request

Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。

HttpServletResponse

HttpServletResponse对象代表服务器的响应,它封装了向客户端发送数据、发送响应头、发送响应状态码的方法。HttpServletResponse接口继承ServletResponse接口,常用方法如下:

07. response & request217

1、response常见应用

1)向客户端输出中文数据(☆ 乱码问题)

向客户端输出中文乱码的原因在于服务器端使用的中文编码和客户端显示时使用的中文编码不一致导致的。

OutputStream向客户端输出

☆ String类的两个方法(补充):
getBytes():使用平台的默认字符集将字符串编码为byte序列。
getBytes( charsetName ):使用指定的字符集将字符串编码为byte序列。

乱码问题代码:

//☆ 当服务器端未指定客户端显示编码时,客户端默认用GBK编码显示

String data = "我是中文,乱码不?";
//使用平台默认字符集(GBK)将字符串编码为byte序列,未乱码
response.getOutputStream().write(data.getBytes());
//使用UTF-8将字符串编码为byte序列,客户端浏览器查看时会乱码
response.getOutputStream().write(data.getBytes("UTF-8"));

这种乱码问题的解决方式有如下几种:

方式一:更改浏览器的显示编码 (不可取)

方式二:输出<meta>标签指定浏览器显示编码(不可取,太长)

方式三:设置响应消息头:setHeader(“Content-Type”,”……”) (推荐)

方式四:设置响应头:setContentType(……) (推荐)

//方式二
out.write("<meta http-equiv='Content-Type' content='text/html;charset=UTF-8'>".getBytes());
//方式三
response.setHeader("Content-Type", "text/html;charset=utf-8");
//方式四
response.setContentType("text/html;charset=utf-8");

② Writer向客户端输出

乱码问题代码:

String data = "看我乱码了吗?";
PrintWriter out = response.getWriter();
out.write(data);

HttpServletResponse的实例由Tomcat服务器提供,对输出字符流的编码默认会去查ISO-8859-1的码表,由于该码表中没有中文,因此客户端浏览器会显示“????”,修改后的代码如下:(Tomcat8.x的默认编码变成了UTF-8)

String data = "看我乱码了吗?";
//改变输出字符流查的码表
response.setCharacterEncoding("UTF-8");
//指定浏览器显示使用的编码
response.setHeader("Content-Type","text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(data);

实际上,上述代码还可以简写为如下形式(也是实际中最常用):

String data = "看我乱码了吗?";
//除了改变输出字符流查的码表(编码),还能告知客户端用UTF-8编码显示(解码)
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(data);

setContentType()的作用相当于以上两条代码,这是response提供的一种更简单的方式。

2)生成验证码

原理:在Servlet中生成验证码图片,并将<img>标签的src属性指定为该验证码图片访问路径。

在Servlet中生成验证码图片时主要用到JDK中的以下类:BufferedImage(内存图像)、Graphics(画笔)、ImageIO(输出图像)。

HTML代码如下:

 请输入验证码:<input type='text' size=3 /><img id="img" src="/response_demo/servlet/ResponseDemo3"/>
  <a href="javascript:change()" >看不清</a><br/>
  <input type="submit" value="登陆" />
<script type="text/javascript">
function change(){
	var imgObj = document.getElementById("img");
	//带上一个变化的参数,因为如果浏览器发现地址没有变化,根本不发请求
	imgObj.src = "/response_demo/servlet/ResponseDemo3?time=" + new Date().getTime();
}
</script>

Servlet代码如下:

int width = 120;
int height = 30;

//创建一副内存图像(画布)
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//创建画笔
Graphics g = image.getGraphics();
//开始"作画"
	//边框
	g.setColor(Color.DARK_GRAY);
	g.drawRect(0, 0, width, height);
	//填充背景色
	g.setColor(Color.CYAN);
	g.fillRect(1, 1, width - 2, height - 2);
	//加入干扰线
	g.setColor(Color.GRAY);
	Random r = new Random();
	for(int i = 0;i < 9;i ++){
		g.drawLine(r.nextInt(width), r.nextInt(height), r.nextInt(width), r.nextInt(height));
	}
	
	//验证码
	g.setColor(Color.BLACK);
	g.setFont(new Font("宋体",Font.ITALIC|Font.BOLD , 20));
	int x = 15;
	for(int i = 0;i < 4;i ++){
		g.drawString(r.nextInt(10) + "", x, 20);
		x+=22;
	}
	
	
//输出
response.setHeader("Expires", "-1");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
ImageIO.write(image, "jpg", response.getOutputStream());

3)定时刷新(2种刷新)

刷新本页面:response.setHeader(“Refresh”, 1);

“刷新”到其他页面:如登录网站后,会提示“登录成功,两秒后跳转到主页”:response.setHeader(“Refresh”, “2;URL=index.html”);

 4)控制浏览器缓存当前内容

response.setDateHeader(“Expires”, System.currentTimeMillis()+1000*60*60); //缓存1小时

可用IE浏览器查看缓存文件,作用:有些不怎么变化的数据,利用缓存能减轻服务器的负担。

5)通过response实现请求重定向

一个web资源收到客户端请求后,通知客户端去访问另外一个web资源,这称之为请求重定向(在下个章节会和请求跳转作比较)。

特点:共发送两次请求,且地址栏会变;可以重定向到任意地址(包括本应用以外)

用法:response.sendRedirect(“/response_demo/login.html”);

实现原理:利用302/307状态码和Location头即可实现重定向。

 2、response细节(了解)

1)getOutputStream和getWriter这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法。否则会抛异常。

2)Servlet程序向ServletOutputStream或PrintWriter对象中写入的数据将被Servlet引擎从response里面获取,Servlet引擎将这些数据当作响应消息的正文,然后与响应状态行和各响应头组合后输出到客户端。

3)Serlvet的service方法结束后,Servlet引擎将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用过close方法,如果没有,Servlet引擎将调用close方法关闭该输出流对象。

HttpServletRequest

HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,开发人员通过这个对象的方法,可以获得客户端的相关信息。HttpServletRequest接口继承ServletRequest接口,常用方法如下:

07. response & request4218

1、request常用方法(差不多都是getXxx方法)

1)获得用户访问有关的信息(了解)

getRequestURL方法返回客户端发出请求时的完整URL。

getRequestURI方法返回请求行中的资源名部分。

getQueryString 方法返回请求行中的参数部分。

getRemoteAddr方法返回发出请求的客户机的IP地址

getRemotePort方法返回客户机所使用的网络端口号

getMethod得到客户机请求方式

 2)获得用户请求头信息(掌握)

getHead(name)

getHeaders(name):Http协议允许头字段有重复的情况,即多个重名的头

getHeaderNames()

3)获得客户端请求参数(客户端提交的数据)(☆ 经常用)

getParameter(name)

getParameterValues(name):获取重名的请求参数值,返回String数组,如hobby。

getParameterNames():获取所有请求参数名,返回Enumeration<String>

getParameterMap():获取请求参数名-值的映射,返回Map<String,String[]> //做框架用,非常实用

技巧 ☆:

① 通常会将客户端参数的值封装到JavaBean中。在编写JavaBean时,一个大的原则就是约定优于编码:即各种表单组件的name取值和JavaBean中的属性名(由getXxx和setXxx决定)要一致。

② 利用BeanUtils工具配合getParameterMap()可一次性将所有请求参数塞到JavaBean中,如:BeanUtils.populate(user, request.getParameterMap());

例1:利用JavaBean封装所有表单数据(request.getParameterNames()方法)

Enumeration<String> names = request.getParameterNames();
User user = new User();
System.out.println("封装前:" + user);
while (names.hasMoreElements()) {
	String name = names.nextElement();
	String[] values = request.getParameterValues(name);

	try {
		PropertyDescriptor pd = new PropertyDescriptor(name, User.class);
		Method method = pd.getWriteMethod();// 得到setter方法
		if(values.length>1){
			method.invoke(user, (Object)values);
		}else{
			method.invoke(user, values[0]);
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
}
System.out.println("封装后" + user);

例2:利用JavaBean封装所有表单数据(request.getParameterMap()方法)

Map<String,String[]> map = request.getParameterMap();
User user = new User();
System.out.println("1、封装前:" + user);
for(Map.Entry<String, String[]> entry: map.entrySet()){
	String name = entry.getKey();
	String[] values = entry.getValue();
	
	try {
		PropertyDescriptor pd = new PropertyDescriptor(name,User.class);
		Method method = pd.getWriteMethod();
		if(values.length > 1){
			method.invoke(user, (Object)values);
		}else{
			method.invoke(user, values[0]);
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
}
System.out.println("2、封装后"+user);

例3:利用JavaBean封装所有表单数据(BeanUtils工具)

User user = new User();
System.out.println("封装前:"+user);
try {
	BeanUtils.populate(user, request.getParameterMap());
} catch (Exception e) {
	e.printStackTrace();
}
System.out.println("封装后:"+user);

4)getInputStream():以输入流的形式接受请求正文,由于Get请求方式没有请求正文,因此getInputStream()只对Post请求方式有效。(提示:该方法一般情况下用不着,只在文件上传时使用)

2、request常见应用

1)解决请求乱码问题(☆)

① 解决Get方式请求参数乱码问题(不安全):

String name = request.getParameter("name");
byte b[] = name.getBytes("ISO-8859-1");
name = new String(b,"UTF-8");

② 解决请求正文乱码问题(Post方式):针对请求正文中的中文,直接使用request.setCharacterEncoding(“UTF-8”);即可。

注:请求编码和响应编码没有关系,如果请求正文和响应正文都有中文,则需要同时使用request.setCharacterEncoding(“utf-8”);和response.setContentType(“text/html;charset=utf-8”);

2)实现请求转发(☆)

请求转发(服务器端跳转)指一个web资源收到客户端请求后,通知服务器去调用另外一个web资源进行处理。

request对象提供了一个getRequestDispatcher方法,该方法返回一个RequestDispatcher对象(请求分发器),调用这个对象的forward方法可以实现请求转发:request.getRequestDispatcher(targetPath).forward(request,response),服务器的处理流程如下:

清空用于存放响应正文数据的缓冲区

如果目标组件为Servlet或JSP,就调用它们,把它们产生的响应结果发送到客户端;如果目标组件为文件系统中的静态HTML文档,就读取文档中的数据并把它发送给客户端。

注:由于forward()方法先清空用于存放响应正文数据的缓冲区,因此源组件生成的响应结果不会被发送到客户端,只有目标组件生成的响应结果才会被送到客户端。

3)页面包含

页面包含就是Servelt(源组件)把其他web组件(目标组件)生成的响应结果包含到自身的响应结果中。页面包含使用RequestDispatcher中的include方法,用法同forward()。服务器处理页面包含的流程如下:

如果目标组件为Servlet或JSP,就执行它们,并把它们产生的响应正文添加到源组件的响应结果中;如果目标组件为HTML文档,就直接把文档的内容添加到源组件的响应结果中。

返回到源组件的服务方法中,继续执行后续代码块。

注:源组件与被包含的目标组件的输出数据都会被添加到响应结果中;在目标组件中对响应状态代码或者响应头所做的修改都会被忽略。

3、其他知识

1)转发和包含

源组件和目标组件处理的都是同一个客户请求(☆),源组件和目标组件共享同一个ServeltRequest和ServletResponse对象。

② 目标组件都可以为Servlet、JSP或HTML文档。

③ 都依赖 javax.servlet.RequestDispatcher接口。

2)请求重定向和请求转发

① 客户端跳转和服务器端跳转

② 两次请求和一次请求

3)开发中的资源路径表示问题(☆ 很重要)

开发中尽量多使用绝对路径!如果地址是给服务器用的,则用“/”代表当前应用,如果地址是给客户端用的,则用“/appName”代表当前应用!

注:①为了使程序更好的适应各种变化,“/appName”部分通常使用request.getContextPath()代替。

② ServletContext中的各种方法必须用绝对路径。

4)域对象(范围)

① web应用范围内的共享数据作为ServeltContext对象的属性而存在(setAttribute),只要共享ServletContext对象也就共享了其数据。

② 请求范围内的共享数据作为ServletRequest对象的属性而存在(setAttribute),只要共享ServletRequest对象也就共享了其数据。


留下一个回复

你的email不会被公开。