JavaEE实战——HttpServletResponseHttpServletRequest中文乱码

Posted 李春春_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaEE实战——HttpServletResponseHttpServletRequest中文乱码相关的知识,希望对你有一定的参考价值。

学习Servlet技术 两条主线

1、HTTP协议 

2、Servlet生命周期 

前言

上一篇博客:

1、阐述了Servlet生命周期
2、init(config) 方法中参数 ServletConfig 对象使用
3、通过ServletConfig 获得 ServletContext对象 使用
4、客户端/ 和 服务器端/ 的区别、绝对路径和相对路径的区别

本篇博客:
service方法 含有两个参数对象 ServletRequest ServletResponse 
在实际开发Servlet过程中 不需要覆盖service ---- HttpServlet已经覆写了service方法,并会根据请求方式自动调用doGet 或者 doPost 
doGet和doPost参数:HttpServletRequest 和 HttpServletResponse 

本篇博客的重点就是:

HttpServletRequestHttpServletResponse 

综述

客户端每次请求,都会创建request对象和response对象 ------  被传递到 service、doGet、doPost 中


HttpServletRequest 封装 客户端相关信息,服务器Servlet程序可以通过request对象 操作客户端信息

HttpServletResponse 封装服务器 向客户端发送响应数据信息,Servlet程序通过response对象 向客户端发送响应

HttpServletResponse

response常用 API

    setStatus 设置响应行当中的状态码
    setHeader 设置响应头信息
    getOutputStream 获得字节流 ---- 输出响应体内容
    getWriter 获得字符流 ---- 输出响应体内容


注意两点:

* HttpServletResponse 继承 ServletResponse 接口 ,ServletResponse 并没有提供与HTTP协议相关API ,HttpServletResponse 添加了与协议相关 API

* JavaEE API 中并没有提供 HttpServletResponse 实现类 ---- 实现类由tomcat服务器提供的

Response设置头信息和状态码

状态码(Status Code)

* setStatus(int)
* 常用状态码 : 200 302 304 404 500 
200 请求处理成功
302 客户端重定向
304 客户端访问资源没有被修改,客户端访问本地缓存
404 访问资源不存在
500 服务器内部出错

头信息(Header Info)

* addHeader(String, String) 在原有值添加
* setHeader(String, String) 替换原有值

区分上面两种设置头信息的函数 ---- 头信息存在多个值的情况:

Accept-Encoding: gzip, deflate --- key:value1,value2 
response.addHeader 用于设置响应头有多个值 ------ 不常用 

重点:setHeader 

接下来介绍几个response状态码和头信息的应用:

Response完成请求重定向

请求重定向指:一个web资源收到客户端请求后,通知客户端去访问另外一个web资源,这称之为请求重定向。

案例一:

通过302 + Location 头信息实现页面重定向效果

response.setStatus(302);

response.setHeader("Location", "/day06/welcome.html"); // 相对路径 和 绝对路径 

* / 由客户端定向服务器,代表客户端 / , 必须添加工程虚拟目录

package ustc.lichunchun.servlet.response.demo1;

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 ResponseServlet1 extends HttpServlet 

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		System.out.println("response1 ...");
		//response.setStatus(500);//通过setStatus 向客户端设置不同响应 状态码
		
		// 302 客户端重定向 --- 结合 Location头信息一起使用
		response.setStatus(302);
		// 通知浏览器 定向到哪个页面
		response.setHeader("Location", "/day06/welcome.html");// 相对路径 和 绝对路径
	

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		doGet(request, response);
	


Response登陆重定向功能

在Response API 中提供 response.sendRedirect---- 完成302+Location重定向效果 

例如: response.sendRedirect("/day06/welcome.html");

案例二:

登陆重定向:用户登录时,用户名密码错误,重定向回登陆页面

分析:


代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="/day06/response2" method="post">
	用户名 <input type="text" name="username" /><br/>
	密码 <input type="password" name="password"/><br/>
	<input type="submit" value="登陆"/>
</form>
</body>
</html>
package ustc.lichunchun.servlet.response.demo2;

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 ResponseServlet2 extends HttpServlet 

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		// 1. 获得客户端提交用户名和密码
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		
		// 2、判断是否正确 ,假设 admin / admin
		if("admin".equals(username) && "admin".equals(password))
			// 用户名 和 密码正确
			// response.setStatus(302);
			// response.setHeader("Location", "/day06/welcome.html");

			// 使用response.sendRedirect完成重定向
			response.sendRedirect("/day06/welcome.html");
		else
			// 不正确
			// response.setStatus(302);
			// response.setHeader("Location",
			// "/day06/response/demo2/login.html");
			//第一个/day06代表绝对路径工程名,第二个/代表WebRoot
			response.sendRedirect("/day06/response/demo2/login.html");
		
	

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		doGet(request, response);
	


结果:



Response自动刷新与禁止浏览器缓存

案例三:

自动刷新网页 

* 登陆成功,5秒后自动跳转XX页面 
原理:通过refresh 头信息 
格式:refresh:时间;url=跳转路径(绝对路径)
例如: refresh:3;url=http://www.itcast.cn -------- 3秒后自动跳转http://www.itcast.cn 网站

代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>自动刷新 跳转后页面!!!</h1>
</body>
</html>
package ustc.lichunchun.servlet.response.demo3;

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 ResponseServlet3 extends HttpServlet 

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		// 通过refresh 头信息完成跳转
		response.setHeader("refresh", "3;url=/day06/response/demo3/result.html");
		response.setContentType("text/html;charset=utf-8");// 设置响应编码
		response.getWriter().println("3秒后 将跳转 result.html页面!");
	

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		doGet(request, response);
	

结果:


* HTML 页面中存在一类非常特殊标签 <meta> ,<meta> 起到设置头信息作用,和http头信息相同效果

http-equiv相当于头信息 name、content相当于头信息 value

<meta content="3;url=/day06/response/demo3/result.html" http-equiv="refresh"> ---- 完成自动跳转

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<!-- meta标签非常特殊, 实现http头信息相同效果 -->
<!-- http-equiv 相当于 头信息 name -->
<!-- content 相当于头信息value -->
<meta http-equiv="refresh" content="3;url=/day06/response/demo3/result.html">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>通过 meta实现 3秒页面自动跳转</h1>
</body>
</html>

案例四:

通过response头信息设置 浏览器 禁止缓存

原理:和禁用缓存相关头信息 三个 
    Cache-Control:no-cache
    Expires:Thu, 01 Dec 1994 16:00:00 GMT  ----- setDateHeader("expires",-1);
    Pragma : no-cache
package ustc.lichunchun.servlet.response.demo4;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 通过Servlet禁止 缓存
 */
public class ResponseServlet4 extends HttpServlet 

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		// 默认 浏览器产生 该程序缓存

		// 禁用浏览器缓存
		response.setHeader("Cache-Control", "no-cache");
		response.setHeader("Pragma", "no-cache");
		// 设置expires 因为日期格式很复杂
		// response.setHeader("Expires", "Thu, 01 Dec 1994 16:00:00 GMT");

		// 通过 setDateHeader 为 expires设置 毫秒值 --- 从1970年1月1日 00:00:00毫秒
		response.setDateHeader("expires", -1);// 已经过期
		
		response.getWriter().println("No Cache! We can change~");
	

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		doGet(request, response);
	

查看缓存:IE 工具---Internet选项 ---常规 --- 设置 --- 查看文件

实际开发中,对于Servlet生成HTML页面,会经常需要改变,所以需要禁止Servlet动态程序缓存 

* 设置Expires时,通常使用 setDateHeader 为过期时间设置一个毫秒值,生成HTTP响应时,会自动转换日期字符串表示 

Response生成响应

获得向客户端进行数据输出的流对象

    |--字节流数据输出

        |--OUtputStream out = response.getOutputStream();

    |--字符流数据输出

        |--PrintWriter pw = response.getWriter();

指定body内容的类型

    |--setContentType("text/html")

指定输出数据的编码格式

    |--setCharacterEncoding("gb2312");

    |--默认情况下,编码格式是ISO-8859-1

Response输出流字符编码问题

通过response 生成 客户端响应体,通过字节流和字符流两种输出方式 

* 哪些情况用字节流? 哪些情况用字符流 ? 
文件拷贝 ---- 字节流 

分析文件内容 --- 字符流 (中文操作 字符流)

案例五:

输出中文信息

对中文信息进行编码 
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8"); 

setCharacterEncodig 和 setContentType 区别 ?

    |--setCharacterEncodig 设置响应内容编码,无法设置浏览器查看编码
    |--setContentType 设置响应内容编码,同时通知浏览器查看编码
        |--原理:setContentType可以在响应报文中添加 Content-Type:text/html;charset=utf-8 的头信息


代码:

package ustc.lichunchun.servlet.response.demo5;

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;
/**
 * 向浏览器输出 中文信息
 */
public class ResponseServlet5 extends HttpServlet 

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		response.setCharacterEncoding("utf-8");
		System.out.println("c1: "+ response.getCharacterEncoding());// 设置响应内容编码集 ,无法设置浏览器查看编码
		
		// 需要通知浏览器 查看编码
		response.setContentType("text/html;charset=gbk");// 设置响应编码 与 浏览器查看编码
		
		System.out.println("c2: "+ response.getCharacterEncoding());//c2: gbk
		
		// 输出中文信息--- 字符流
		PrintWriter out = response.getWriter();
		out.println("response向浏览器输出中文信息--setContentType中文编码!");
		
		// 默认在方法结束后,自动关闭 response输出流,flush缓冲区内容
	

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		doGet(request, response);
	

结果:


结论:开发中只需要使用setContentType就可以了

response生成响应注意事项

1、getOutputStream和getWriter 不能同时使用

2、必须在getOutputStream和getWriter 之前 设置响应 编码 

3、getOutputStream和getWriter 输出内容 是 HTTP响应体 

4、getOutputStream和getWriter 存在缓冲区的 ,在service方法结束时,自动关闭流,flush缓冲区内容


Response文件下载案例

案例六:

文件下载

第一种:通过超链接 完成文件下载

* 如果浏览器可以识别该文件格式,直接打开;只有链接文件浏览器不识别文件格式,才会实现下载 

第二种:通过Servlet程序实现下载

原理:通过Servlet读取目标程序,将资源返回客户端

通过程序下载文件 需要设置两个头信息:
    |--Content-Type 设置文件类型头信息
    |--Content-Disposition  设置此头信息,使浏览器以附件形式打开

response.setContentType(getServletContext().getMimeType(filename));  ---- 设置文件类型 

response.setHeader("Content-Disposition", "attachment;filename="+ filename); ---- 设置文件以附件形式下载(特别是对于浏览器识别格式文件的下载)

* 注意:URLEncoder --- 对get方式提交 中文文件名 手动编码(后续博客会详述)

代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 文件下载 功能 -->
<h1>通过超链接实现文件 下载</h1>
<a href="/day06/response/demo6/xpp3.zip">zip压缩包下载</a><!--浏览器不识别文件格式,才会实现下载-->
<a href="/day06/response/demo6/mm.jpg">jpg图片下载</a><!--浏览器可以识别该文件格式,直接打开-->
<h1>通过程序实现下载 </h1>
<!-- 通过 ? 在url地址里 拼接参数 get方式提交 -->
<a href="/day06/response6?filename=xpp3.zip">zip压缩包下载</a>
<a href="/day06/response6?filename=mm.jpg">jpg图片下载</a>
<!-- URLEncoder 可以对get方式提交 中文文件名 手动编码 参见后续博客-->
</body>
</html>
package ustc.lichunchun.servlet.response.demo6;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
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 ResponseServlet6 extends HttpServlet 

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		// 获得文件名
		String filename = request.getParameter("filename");
		System.out.println(filename);
		
		// 设置MIME类型 tomcat/conf/web.xml
		response.setContentType(getServletContext().getMimeType(filename));
		// 设置文件以附件形式下载
		response.setHeader("Content-Disposition", "attachment;filename="+filename);
		
		// 下载 读取目标文件,通过response 将目标文件内容写到客户端
		// 绝对磁盘路径 读取文件
		String fullFilename = getServletContext().getRealPath("/response/demo6/"+filename);
		InputStream in = new FileInputStream(fullFilename);
		PrintWriter out = response.getWriter();
		int b;
		while((b = in.read()) != -1)
			out.write(b);
		
		in.close();
		out.close();
	

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		doGet(request, response);
	

结果:


Response生成验证码图片

案例七:

验证码输出案例 

Java图形API 生成验证码图片 ----- 实际开发中,我们很少自己写,都是从网上找现成的实现拿来用。我只是演示一下玩玩。 
为什么需要验证码 ?防止有人通过程序恶意攻击网站 
为什么验证码是图片 ?防止有人通过网页的程序代码获得验证码,图片就不会出现这种问题
为什么雪花或者干扰线 ?防止对方通过算法分析出图片中的内容

常用验证码:字母和数字

Response设置验证码旋转和图片刷新

验证码旋转效果 

rotate(double theta, double x, double y)  ----- 参数 theta 旋转弧度 
2PI 弧度 = 360 角度 
-30 - 30 角度范围

验证码看不清楚,点击切换验证码 ---- 编写javascript程序 
方法一:设置验证码图片不缓存
方法二:每次访问使url 不同 ----- url?new Date().getTime() 当前时间 

代码:

package ustc.lichunchun.servlet.response.demo7;

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;
/**
 * 通过 Servlet生成验证码图片
 */
public class ResponseServlet7 extends HttpServlet 

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		// 方法一:设置验证码图片不缓存
		// response.setHeader("Cache-Control", "no-cache");
		// response.setHeader("Pragma", "no-cache");
		// response.setDateHeader("expires", -1);
		
		int width = 120;
		int height = 30;
		// 1、创建一张 内存中的缓冲图片
		BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		
		// 2、背景色
		Graphics graphics = bufferedImage.getGraphics();// 通过 graphics对象 绘制图片
		// 设置颜色
		graphics.setColor(Color.WHITE);
		graphics.fillRect(0, 0, width, height);
		
		// 3、边框
		graphics.setColor(Color.BLUE);
		graphics.drawRect(0, 0, width-1, height-1);
		
		// 4、写验证码内容
		Graphics2D graphics2d = (Graphics2D)bufferedImage.getGraphics();
		graphics2d.setColor(Color.RED);
		// 设置字体
		graphics2d.setFont(new Font("Times New Roman", Font.BOLD, 18));
		String content = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
		//String content = "\\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";
		
		// 内容四个字 --- 随机从content中抽取四个字
		Random random = new Random();
		int x = 10;
		int y = 20;
		for(int i = 0; i < 5; i++)// 循环四次/五次
			// 为字 生成旋转角度 -30 - 30
			double angle = random.nextInt(60) - 30;
			// 将旋转角度 换算弧度
			double theta = angle/180 * Math.PI;
			
			int index = random.nextInt(content.length());
			char letter = content.charAt(index);// letter 验证码内容
			graphics2d.rotate(theta, x, y);
			graphics2d.drawString(letter+"", x, y);
			// 将角度还原
			graphics2d.rotate(-theta, x, y);
			x+=20;
		
		
		// 5 绘制随机干扰线
		int x1;
		int x2;
		int y1;
		int y2;
		graphics.setColor(Color.GREEN);
		for(int i = 0; i < 10; i++)
			x1 = random.nextInt(width);
			x2 = random.nextInt(width);
			y1 = random.nextInt(height);
			y2 = random.nextInt(height);
			// 根据两点 绘制直线
			graphics.drawLine(x1, y1, x2, y2);
		
		
		// 内存中资源 释放
		graphics.dispose();
		
		// 将图片输出到 浏览器 ImageIO
		// 将内存的图片 通过 浏览器输出流 写成 jpg格式图片
		ImageIO.write(bufferedImage, "jpg", response.getOutputStream());
	

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		doGet(request, response);
	

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
	function change()
		// 重新加载验证码图片 
		// 方法二:如果图片缓存
		document.getElementById("myimg").src="/day06/response7?"+new Date().getTime();
	
</script>
</head>
<body>
<!-- 模拟用户注册 -->
<form>
	用户名<input type="text" /><br/>
	密码<input type="password" /><br/>
	<!-- 图片 由Servlet生成的 -->
	请输入验证码 <input type="text" name="checkcode"/> <img src="/day06/response7" οnclick="change()" id="myimg" style="cursor: pointer;" /><br/>
	<input type="submit" value="注册">
</form>
</body>
</html>

至于完整的用户登录校验,我会在下一篇博客Session中详述!

HttpServletRequest

HttpServletRequest 分为四个部分

HttpServletRequest 相比 ServletRequest 添加与协议相关 API



1、Request获得请求行相关数据

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

getRequestURI 方法返回请求行中的资源名部分
getQueryString 方法返回请求行中的参数部分
getRemoteAddr 方法返回发出请求的客户机的IP地址
getMethod 得到客户机请求方式
getContextPath 获得工程虚拟目录名称

URI 和 URL 区别

url: http://localhost/day06/request1  --- 完整
uri: /day06/request1  ---- 部分

URI 包含 URL的 ,URL 一定是完整路径,URI可以相对路径也可以完整路径 

http://localhost/day06/request1 是一个URL 同时也是 URI
./hello  /day06/request1 ---- 都是URI 不是URL 

获得ip : request.getRemoteAddr();

获得当前访问资源路径request.getRequestURI().substring(request.getContextPath().length());

package ustc.lichunchun.servlet.request.demo1;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 通过request api 获得客户机信息
 */
public class RequestServlet1 extends HttpServlet 

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		// 问题一 区分 getRequestURL 和 getRequestURI
		System.out.println("url:" + request.getRequestURL()); // 返回 http://192.168.1.114/day06/request1
		System.out.println("uri:" + request.getRequestURI()); // 返回 /day06/request1

		// 问题二:了解 通过 getQueryString 可以获得 get方式提交查询串 url中?后面部分
		System.out.println("querystring:" + request.getQueryString());

		// 获得客户端ip
		System.out.println("ip: " + request.getRemoteAddr());// 192.168.1.103

		// 通过 getContextPath 获得工程虚拟目录名称
		System.out.println(request.getContextPath()); // 返回 /day06

		// 通过getMethod 获得请求方式
		System.out.println("请求方式 : " + request.getMethod());// GET

		// 问题: 获得当前访问资源路径 --- /request1
		System.out.println("当前访问资源路径 :"
				+ request.getRequestURI().substring(
						request.getContextPath().length()));//--/request1
	

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		doGet(request, response);
	

2、Request获得请求头信息与防盗链案例

getHeader 获得头信息的值,转换一个字符串
getHeaders 获得头信息值 ,获得Enumeration
getHeaderNames 获得所有头信息名称  返回 Enumeration

* 掌握getHeader使用,遍历Enumeration 获得所有头信息

package ustc.lichunchun.servlet.request.demo2;

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 获得头信息内容
 */
public class RequestServlet2 extends HttpServlet 

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		System.out.println("test...");
		// 获得指定头信息字段 Accept-Encoding: gzip, deflate
		String value = request.getHeader("Accept-Encoding"); // 如果头信息存在多个值,将多个值拼接到一起,形成String
		System.out.println(value); // gzip, deflate

		System.out.println("--------------------------------");
		// 获得所有头信息内容 ---- 获得所有头信息名称
		Enumeration<String> enumeration = request.getHeaderNames();
		while (enumeration.hasMoreElements()) 
			String name = enumeration.nextElement(); // name 就是一个头信息名字
			System.out.println(name + ":" + request.getHeader(name));
		
	

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		doGet(request, response);
	

案例:

通过referer信息防盗链

编写防盗链程序,存在合法referer不是盗链,否则控制目标资源无法访问 !

* 通过URL 绕过盗链判断 ---> 见之前我的博客

代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>通过链接 访问 RequestServlet3 不是盗链</h1>
<a href="/day06/request3">link</a>
</body>
</html>
package ustc.lichunchun.servlet.request.demo3;

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 RequestServlet3 extends HttpServlet 

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		// 通过getHeader 获得referer头信息
		String referer = request.getHeader("referer");
		// 判断头信息 可以知道是不是盗链
		response.setContentType("text/html;charset=utf-8");
		if (referer == null || !referer.startsWith("http://192.168.1.114")) 
			// 是盗链
			response.getWriter().println("是盗链");
		 else 
			// 不是盗链
			response.getWriter().println("这个真不是盗链!");
		
	

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		doGet(request, response);
	

3、Request获得请求参数

什么是请求参数? 用户通过请求提交服务器一些数据 

<a href="url?xxx=xxx" >、<form method="get"> 、 <form method="post">

例如:

第一种 通过超链接 提交数据 以?方式:

/day06/request4?name=zhangsan&city=beijing  ---- 包括两个参数的 name 和 city 

第二种 通过form的post方式提交数据

等等。。

获得request请求参数,常用API四个:
    getParameter --- String 通过name获取值,如果是多值只取第一个
    getParameterValues --- String[] 通过name获得多值 checkbox
    getParameterNames --- Enumeration<String> 获得所有name
    getParameterMap --- Map<String,String[]> key:name value:多值

代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
   <h1>分别以get 和 post方式 提交数据</h1>
   <h1>第一种 通过超链接 提交数据 以?方式</h1>
   <a href="/day06/request4?name=zhangsan&city=beijing">超链接提交数据</a>
   <h1>第二种 通过form的post方式提交数据</h1>
   <form action="/day06/request4" method="post">
   	  请输入姓名 <input type="text" name="name" /><br/>
   	  请输入城市 <input type="text" name="city" /> <br/>
   	 <input type="submit" value="提交" />
   </form>	
</body>
</html>
package ustc.lichunchun.servlet.request.demo4;

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 RequestServlet4 extends HttpServlet 

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		// 通过getParameter 获得请求数据
		String name = request.getParameter("name");
		System.out.println(name);
		String city = request.getParameter("city");
		System.out.println(city);
	

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		doGet(request, response);
	

现在我来编写一个最复杂表单,及其数据的获取:

代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 最复杂form表单 -->
<!-- 用户注册 -->
<form action="/day06/request5" method="get">
	<table>
		<!-- 文本输入框 -->
		<tr>
			<td>用户名</td>
			<td><input type="text" name="username"/></td>
		</tr>
		<!-- 密码框 -->
		<tr>
			<td>密码</td>
			<td><input type="password" name="password"/></td>
		</tr>
		<!-- 单选按钮 -->
		<tr>
			<td>性别</td>
			<td>
				<input type="radio" name="gender" value="male"/>男
				<input type="radio" name="gender" value="female"/>女
			</td>
		</tr>
		<!-- 复选框  -->
		<tr>
			<td>爱好</td>
			<td>
				<input type="checkbox" name="hobby" value="sport"/>运动
				<input type="checkbox" name="hobby" value="drink"/>颤酒
				<input type="checkbox" name="hobby" value="music"/>音乐
			</td>
		</tr>
		<!-- 下拉框 -->
		<tr>
			<td>城市</td>
			<td>
				<select name="city">
					<option value="hefei">合肥</option>
					<option value="nanjing">南京</option>
					<option value="shanghai">上海</option>
					<option value="shouxian">寿县</option>
				</select>
			</td>
		</tr>
		<!-- 多行文本框 -->
		<tr>
			<td>个人简介</td>
			<td><textarea rows="5" cols="60" name="introduce"></textarea></td>
		</tr>
		<tr>
			<td colspan="2"><input type="submit" value="注册"/></td>
		</tr>
	</table>
</form>
</body>
</html>
package ustc.lichunchun.servlet.request.demo5;

import java.io.IOException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 获得最复杂form 的所有数据
 */
public class RequestServlet5 extends HttpServlet 

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		// 解决post乱码
		//request.setCharacterEncoding("utf-8");//只能对请求体重新编码
		
		// 通过 getParameter 获得指定数据
		String username = request.getParameter("username");
		System.out.println(username);// 获得一个值
		
		// 解决get乱码 --- 使用手动编码
		// username = URLEncoder.encode(username, "ISO-8859-1");// 用ISO编码
		// username = URLDecoder.decode(username, "utf-8"); // 用utf-8解码
		
		username = new String(username.getBytes("ISO-8859-1"), "utf-8");
		System.out.println(username);
		
		// 非空校验
		if(username !=null && username.trim().length()>0)
			System.out.println("username 合法");
		
		
		// 使用 getParameter 获得 checkbox 提交数据。默认只能获得第一个数据
		String hobby = request.getParameter("hobby"); // 多选框
		System.out.println(hobby);
		
		// 获得checkbox所有提交数据--- getParameterValues
		String[] hobbies = request.getParameterValues("hobby");
		System.out.println(Arrays.toString(hobbies));
		
		System.out.println("--------------------------");
		
		// 打印所有请求提交参数
		// 方式一 : 先获得所有参数 name ,然后通过name 获得value
		Enumeration<String> names = request.getParameterNames();
		while(names.hasMoreElements())
			String name = names.nextElement();
			System.out.println(name +":"+ Arrays.toString(request.getParameterValues(username)));
		
		
		System.out.println("--------------------------");
		// 方式二 :通过request.getParameterMap
		Map<String,String[]> parameterMap = request.getParameterMap();
		Set<String> keys = parameterMap.keySet();
		for (String key : keys) // key是参数 name
			System.out.println(key +":"+ Arrays.toString(parameterMap.get(key)));
		
	

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		doGet(request, response);
	

结果:

*数据非空校验

if (username != null && username.trim().length() > 0)  ----- 短路 

Request请求参数乱码问题分析与解决


post 乱码解决 ---- request.setCharacterEncoding("客户端编码集");

get 乱码手动解决

username = URLEncoder.encode(username, "ISO-8859-1");// 用ISO编码
username = URLDecoder.decode(username, "utf-8"); // 用utf-8解码
简化上面写法 : username = new String(username.getBytes("ISO-8859-1"), "utf-8");

get乱码 配置tomcat默认解码字符集

在tomcat/conf/server.xml 
Connector中 添加一个属性 URIEncoding="utf-8"

结论:开发时,尽量不要修改tomcat默认解码集 ,提交请求请尽量使用post,如果非要使用get ,手动编码

URL特殊字符转义规则


代码示例:

package ustc.lichunchun.test;

import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;

public class URLEncoderTest 

	public static void main(String[] args)  throws IOException 
		String s = " +/?%&#";
		String s1 = URLEncoder.encode(s, "utf-8");
		System.out.println(s1);//+%2B%2F%3F%25%26%23
		String s2 = URLDecoder.decode(s1, "utf-8");
		System.out.println(s2);// +/?%&#
	

问题:http://localhost/day06/servlet?username=zhangsan+lisi

在服务器端 通过 request.getParameter("username") 结果是什么?----- zhangsan lisi

* 因为 + 是 空格 的转义,在地址栏中的username=zhangsan+lisi,实际上已经是文本框zhangsan lisi的encode转义之后结果,在服务器端getParameter时,直接decode这个zhangsan+lisi后得到zhangsan lisi

图片分析:




4、通过请求转发传输request中数据

request对象同时是一个域对象,开发人员通过request对象在实现转发时,把数据通过request对象带给其他web资源处理

    |--setAttribute方法
    |--getAttribute方法
    |--removeAttribute方法
    |--getAttributeNames方法
request方法提供了一个getRequestDispatcher方法,该方法返回一个RequestDispatcher对象,调用这个对象的forward方法可以实现请求转发,从而共享请求中的数据。

HttpServletRequest 和 ServletContext 类似 都是数据域对象 , 以Map方式保持数据
区别:存活时间不同 
ServletContext对象 服务器启动对象创建,服务器停止对象销毁

ServletRequest对象 当产生一次请求时 创建,当响应结束后,对象销毁


Request数据域对象传递数据原理:


代码示例:

package ustc.lichunchun.servlet.request.demo6;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 通过AServlet传递一个数据 给 BServlet
 */
public class AServlet extends HttpServlet 

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		//在使用forward之前 不能将响应内容传输到客户端
		
		// 情况一 如果flush ,不能forward
		// response.getWriter().println("Hello");// Hello默认输出浏览器,服务器内部缓存
		// response.getWriter().flush();

		// 情况二 在同一个Servlet不能使用多次 forward 或者 sendRedirect
		// response.sendRedirect("/day06/welcome.html");

		response.getWriter().println("Hello"); // Hello输出到内存缓冲区。在forward时,缓冲区会被清除

		// 通过request对象传递
		// 向request域对象 保存一个属性
		request.setAttribute("name", "itcast");

		// 传递数据给 BServlet 必须使用请求转发
		RequestDispatcher dispatcher = request.getRequestDispatcher("/b");
		dispatcher.forward(request, response);
	

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		doGet(request, response);
	

package ustc.lichunchun.servlet.request.demo6;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 接收A转发 request对象
 */
public class BServlet extends HttpServlet 

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		// 因为是同一个Request对象
		System.out.println(request.getAttribute("name"));
	

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 
		doGet(request, response);
	

通过request转发请求,request保存数据进行Servlet之间传递 ---- 应用?什么时候会用到request请求域对象?

* Servlet进行数据处理 --- 生成结果 --- 转发结果给JSP显示 

注意事项:

1、在使用forward之前 不能将响应内容传输到客户端
      情况一 response输出流执行flush 
      情况二 同一个Servlet不能连续使用forward 和 redirect
2、调用forward之前向Servlet引擎缓冲区(response)中写入内容,只要该内容还没有被真正输出到客户端(还没有调用flush方法),那么forwad方法就可以被正常执行,且在执行forward 和 redirect时,会清除之前写入到缓冲区的响应流数据,但是已写入到Response对象中的响应头字段信息保持有效 ( 在执行forward 和 redirect时,会清除之前写入到缓冲区的响应流数据 )
3、ServletContext进行转发 路径必须/开始,request进行转发路径可以使用相对路径 

转发和重定向区别:
1、转发一次请求、一次响应 重定向 两次请求 两次响应
2、转发只能跳转站内程序,重定向定向任何站点 
3、转发 URL地址不变 ,重定向URL地址改变 
4、转发 对客户端不可见,重定向对客户端可见 
5、转发共享同一个Request中数据,重定向两次请求,不同Request对象,不能共享Request数据 



* 结论:request.setAttribute 必须和 request.getRequestDispatcher().forward 一起使用 

RequestDispatcher的include进行网页布局

很多语言都有include方法,比如php、.net、java,但是forward是java特有的。

include方法:

RequestDispatcher.include方法用于将ResquestDipatcher对象封装的资源内容作为当前响应内容的一部分包含进来,从而实现可编程的服务器端包含功能
被包含的Servlet程序不能改变响应消息的状态码和响应头,如果它里面存在这样的语句,这些语句的执行结果将被忽略

include在程序执行上效果类似forward,但是使用forward只有一个程序可以生成响应(上一节说过调用forward时会清除响应流数据),include可以由多个程序一同生成响应 ---- 常用来页面布局


RequestDispatcher 的 include 方法 用来做页面布局 不常用 ------ 常用的是 <%@include%> <jsp:include> 后续博客讲述

将页面公共部分抽取出来,通过include 引用到页面中 ---- 更加方便维护

代码示例:

<h1>LOGO USTC</h1>
<h1>@COPY</h1>
package ustc.lichunchun.servlet.request.demo7;

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 MainServlet1 extends HttpServlet 

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletExce

以上是关于JavaEE实战——HttpServletResponseHttpServletRequest中文乱码的主要内容,如果未能解决你的问题,请参考以下文章

JavaEE在线实战班要开班啦

JavaEE企业应用实战学习记录Filter

JavaEE企业应用实战学习记录authorityFilter

JavaEE企业应用实战学习记录requestListener

JavaEE精讲之SpringMVC框架实战(文末福利)

JavaEE企业应用实战学习记录sessionListener