一、前言

SpringMVC是JAVA中经常用的一款Web MVC框架,本文将详解介绍HttpServletRequest,HttpServletResponse等类,同时提供获取IP地址,URL地址以及URL参数,文件上传,返回JSON数据等各种常用应用。

二、HttpServletRequest

2.1 HttpServletRequest包详解

HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象提供的方法,可以获得客户端请求的所有信息,包括请求的地址,请求的参数,提交的数据,上传的文件客户端的ip甚至客户端操作系统都包含在其内。java中HttpServletRequest专门处理GET/POST请求。

2.1.1 HTTP请求协议

a.请求地址(URL)
b.请求头(Request headers)
c.实体数据(Entity body)

//HTTP 1.1协议信息
POST /examples/default.jsp HTTP/1.1
Accept: text/plain; text/html
Accept-Language: en-gb
Connection: Keep-Alive
Host: localhost
User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows 98)
Content-Length: 33
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
lastName=Franks&firstName=Michael

2.1.2 常用方法

HttpServletRequest封装了所有的http头部信息,java中就可以通过这个类很容易获取。

获得客户端信息
getMethod  //返回HTTP请求消息中的请求方式。
getRequestURI  //返回请求行中的资源名部分。
getQueryString   //返回请求行中的参数部分。
getProtocol  //返回请求行中的协议名和版本。
getContextPath  //返回请求资源所属于的WEB应用程序的路径。
getPathInfo  //返回请求URL中的额外路径信息。额外路径信息是请求URL中的位于Servlet的路径之后和查询参数之前的内容,它以“/”开头。
getPathTranslated  //返回URL中的额外路径信息所对应的资源的真实路径。
getServletPath  //返回Servlet的名称或Servlet所映射的路径。获取网络连接信息
getRemoteAddr  //返回发出请求的客户机的IP地址,其格式为“192.168.0.3”这种形式的字符文本。 (*)
getRemoteHost  //返回发出请求的客户机的完整主机名,即“pc1.it315.org”这种格式。
getRemotePort  //返回发出请求的客户机所使用的网络接口的端口号。
getLocalAddr  //返回WEB服务器上接收当前请求的网络接口的IP地址。
getLocalName  //返回WEB服务器上接收当前请求的网络接口的IP地址所对应的主机名。
getLocalPort  //返回WEB服务器上接收当前请求的网络接口的端口号。
getServerName  //返回当前请求所指向的主机名。
getServerPort  //返回当前请求所连接的服务器端口号。
getScheme  //返回请求的协议名,例如http、https或ftp。
getRequestURL  //返回客户端发出请求时的完整URL。
获得客户端机器请求头部信息
getHeader(string name)  //返回String
getHeaders(String name)  //返回Enumeration
getHeaderNames方法
getIntHeader方法
getDateHeader方法
getContentType方法
getContentLength方法
getCharacterEncoding方法
常用的方法
String getMethod()  返回这个请求使用的HTTP方法(例如:GET、POST、PUT)
String getQueryString() 返回这个请求URL所包含的查询字符串。一个查询字串符在一个URL中由一个“?”代表。
String getRequestURI() 返回请求的路径
HttpSession getSession() 返回一个当前有效的session
String getParameter(String key) 是从Get或者Post提交的数据中获取数据 
setAttribute(String name,Object o)  将数据作为request对象的一个属性存放到request对象中,例如:request.setAttribute("data", data);
getAttribute(String name)  返回获取request对象的name属性的属性值或是session内存在的对象尤服务器端设置,例如:request.getAttribute("data")
removeAttribute(String name)  移除request对象的name属性,例如:request.removeAttribute("data")
getAttributeNames  获取request对象的所有属性名,返回的是一个,例如:EnumerationattrNames = request.getAttributeNames();
String getHeader("referer") 则是获取上一次请求的url
获得客户请求参数(客户端提交的数据)
getParameter(String)(常用)
getParameterValues(String name)(常用)
getParameterNames()(不常用)
getParameterMap()(编写框架时常用)

2.1.3 常用方法

  1. 他们都可以向服务器发送数据,Get是从服务器上获取数据,Post则是传输数据给服务器进行增删改等操作。

  2. GET提交,请求的数据会附在URL之后(就是把数据放置在HTTP协议头中),以?分割URL和传输数据,多个参数用&连接;例如:login.action?name=hyddd&password=idontknow&verify=%E4%BD%A0 %E5%A5%BD。如果数据是英文字母/数字,原样发送,如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64加密,得出如: %E4%BD%A0%E5%A5%BD,其中%XX中的XX为该符号以16进制表示的ASCII。

  3. POST提交:把提交的数据放置在是HTTP包的包体中。上文示例中红色字体标明的就是实际的传输数据 。因此,GET提交的数据会在地址栏中显示出来,而POST提交,地址栏不会改变

  4. Get限制form表单数据必须为ASCII字符,而Post支持整个ISO10646字符集。

2.1.4 注意事项

  1. 如果提交FORM表单的页面与处理表单请求的Servlet程序都是由同一个人或同一个项目组开发的,那么只要在处理表单提交的Servlet程序中将传递给setCharacterEncoding方法的字符集编码设置为该表单所在页面的字符集编码即可。

  2. 如果Servlet程序可以接收来自多个其他站点的FORM表单提交的数据,而每个其他站点的页面所采用的字符集编码可能各不一样,要想让Servlet程序知道FORM表单内容的字符集编码,可以在FORM表单中增加一个隐藏字段来传递当前FORM内容的字符集编码名称。

  3. 只要超链接、FORM表单的action属性设置、请求转发和重定向的URL等任何一种请求路径中要包含参数,就必须对参数部分进行URL编码。对参数进行URL编码时所选择的字符集编码应尽量与当前页面的字符集编码保持一致,也可以用一个参数来指定URL编码内容的字符集编码名称。

  4. 所有标准的浏览器和客户机终端都支持UTF-8编码,如果WEB服务器要能兼容处理各个国家和地区版本的浏览器所传递的FORM表单信息,网页文档应当使用UTF-8编码格式。

  5. 在编写应用程序时应该注意一些隐性的字符编码错误。有些程序在输出时有编码转换错误,在读取输入时返回的字符串也有转换错误,当该程序输出它所读取到的有问题的字符串时,显示的结果可能是正常的中文字符

  6. HttpServletRequest对象处理所有客户端请求的数据是最重要的java类之一,一定要重点学好。

2.2 HttpServletRequest获取所有参数

HttpServletRequest获取所有参数有以下2种方法:

2.2.1 request.getParameterNames()

改方法获取所有参数名称列表,得到枚举类型的参数名称,参数名称若有重复的只能得到第一个,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private Map showParams(HttpServletRequest request) {
Map map = new HashMap();
Enumeration paramNames = request.getParameterNames();
while (paramNames.hasMoreElements()) {
String paramName = (String) paramNames.nextElement();
String[] paramValues = request.getParameterValues(paramName);
if (paramValues.length == 1) {
String paramValue = paramValues[0];
if (paramValue.length() != 0) {
map.put(paramName, paramValue);
}
}
}
return map;
}

2.2.2 request.getParameterMap()

该方法返回一个Map类型的值,该返回值记录着前端(如jsp页面)所提交请求中的请求参数和请求参数值的映射关系。这个返回值有个特别之处——只能读。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//获取request对象
HttpServletRequest request = ServletActionContext.getRequest();
Map<String,String[]> map=request.getParameterMap();
//遍历
for(Iterator iter=map.entrySet().iterator();iter.hasNext();){
Map.Entry element=(Map.Entry)iter.next(); //key值
Object strKey = element.getKey(); //value,数组形式
String[] value=(String[])element.getValue();
System.out.print(strKey.toString() +"=");
for(int i=0;i<value.length;i++){
System.out.print(value[i]+",");
}
System.out.println();
}

2.3 HttpServletRequest获取URL(参数,路径,端口号,协议等)详解

HttpServletRequest获取URL常用方法如下:

假设客户端请求的地址url:http://localhost:8082/TestReq/MyServlet/username=51gjie&age=20

//客户请求求的URL,不包括参数数据
request.getRequestURL //返回http://localhost:8082/TestReq/MyServlet

//将URL的域名和尾随的参数截取掉,剩下的那部分就是URI
request.getRequestURI //返回/TestReq/MyServlet

//返回URL上的参数部分的字符串,必须是GET的请求才有效,不然报错
request.getQueryString //返回username=51gjie&age=20

//返回请求的方案名,如http,ftp,https等
request.getScheme //返回http

//HTTP请求的的方法名,默认是GET,也可以指定PUT或POST
request.getMethod //返回GET

//即斜杆加工程名
request.getContextPath //返回/TestReq

//服务器主机名
request.getServerName //返回localhost

//服务器上web应用的访问端口
request.getServerPort request.getLocalPort //返回8082

//返回请求的协议名和版本,如HTTP/1.1等
request.getProtocol //返回HTTP/1.1

//工程部署的完整路径字符串接上参数中的字符串
request.getRealPath("/WEB-INF") //返回d:\omc_jboss\server\default.\deploy\TestReq.war\WEB-INF

//工程之后到参数之前的这部分字符串
request.getServletPath //返回/MyServlet

//字符串包含与客户端发送请求的URL相关的额外信息
request.getPathInfo //返回null

//请求URL体内容的长度,只对POST和PUT类型的请求有效
request.getContentLength //返回-1

//返回一个表示在服务器文件系统上的PathInfol转换成路径的字符串
request.getPathTranslated //返回null

2.4 HttpServletRequest获取真实IP地址(无视代理)详解

2.4.1 HttpServletRequest获取IP地址(无代理)

1
2
//获取发送请求的客户端主机的IP
request.getRemoteAddr //返回127.0.0.1

2.4.2 HttpServletRequest获取真实IP地址(代理)

如果通过Apache,Squid等反向代理软件就不能获取到客户端的真实IP地址了。如果使用了反向代理软件,用 request.getRemoteAddr()方法获取的IP地址是代理的IP,而并不是客户端的真实IP。获取真实IP有如下2种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//获取真实IP一
public String getRemortIP(HttpServletRequest request) {
if (request.getHeader("x-forwarded-for") == null) {
return request.getRemoteAddr();
}
return request.getHeader("x-forwarded-for");
}
//获取真实IP二
public String getRemoteHost(javax.servlet.http.HttpServletRequest request){
String ip = request.getHeader("x-forwarded-for");
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
ip = request.getHeader("Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
ip = request.getHeader("WL-Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
ip = request.getRemoteAddr();
}
return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip;
}

2.5 HttpServletRequest获取GET请求参数5种方法

HttpServletRequest获取HTTP GET请求过来的字符串有如下5种方式:

2.5.1 request.getQueryString()

获取到完整的GET请求字符串(userName=51gjie&password=123456),然后解析字符串

2.5.2 request.getReader()

1
2
BufferedReader reader = request.getReader();
String par = reader.readLine() ; //userName=51gjie&password=123456

和上面一样,获取到完整的GET请求字符串,然后解析字符串

2.5.3 request.getParameter(key)

1
2
3
4
5
6
Enumeration names = request.getParameterNames();
while(names.hasMoreElements()){
String name = (String) names.nextElement();
String value = request.getParameter(name);
System.out.println(name+"---"+value);
}

Enumeration getParameterNames()获取所有的请求参数的名称
String getParameter(String name)根据名称获取对应的请求参数的值

2.5.4 request.getParameterMap()

1
2
3
4
5
6
7
Map map = request.getParameterMap();
Set> entrySet = map.entrySet();
for (Entry entry : entrySet) {
String key = entry.getKey();
String[] value = entry.getValue();
System.out.println(key + "----" + Arrays.toString(value));
}

Map<String , String[]> getParameterMap()获取所有的请求参数对应的Map集合, 键是请求参数的名称 ,值是请求参数的值

2.5.5 @RequestParam

1
2
3
4
5
@RequestMapping(value = "/testurl", method = RequestMethod.GET)@ResponseBody
public ServiceResult TestUrl(HttpServletRequest request,@RequestParam("username")String username,
@RequestParam("pwd")String pwd) {
String txt = username + pwd;
}

controller中直接获取到GET字符串里面的参数。
Spring MVC中可以直接通过方法中的变量获取到GET请求的参数信息。

2.6 HttpServletRequest获取POST请求参数3种方法

客户端通过HTTP POST多参数字符串,比如(username=51gjie&pwd=123456789),HttpServletRequest 获取到POST的参数有如下3种方法:

2.6.1 request.getInputStream()

1
2
3
4
5
6
7
8
9
10
11
12
13
private static String getPostData(HttpServletRequest request) {
StringBuffer data = new StringBuffer();
String line = null;
BufferedReader reader = null;
try {
reader = request.getReader();
while (null != (line = reader.readLine()))
data.append(line);
} catch (IOException e) {
} finally {
}
return data.toString();
}

获取到POST过来的信息(username=51gjie&pwd=123456789),后面直接解析字符串就好了。
request.getInputStream()执行一次后(可正常读取body数据),之后再执行就无效了。

2.6.2 @RequestBody

1
2
3
4
5
6
7
@RequestMapping(value = "/testurl", method = RequestMethod.POST)
@ResponseBody
public ServiceResult TestUrl(HttpServletRequest request,
@RequestBody JSONObject jsonObject){
String username = jsonObject.get("username").toString();
String pwd = jsonObject.get("pwd").toString();
}

@RequestBody 可以使用JSONObject, Map ,或者ObjectDTO绑定body。

2.6.3 @RequestParam

1
2
3
4
5
6
@RequestMapping(value = "/testurl", method = RequestMethod.POST)
@ResponseBody
public ServiceResult TestUrl(HttpServletRequest request,@RequestParam("username")String username,
@RequestParam("pwd")String pwd) {
String txt = username + pwd;
}

2.7 HttpServletRequest获取body内容(字符串/二进制)详解

2.7.1 获取HTTP字符串body

1
2
3
4
5
6
7
8
String getBodytxt(HttpServletRequest request) {
BufferedReader br = request.getReader();
String str, wholeStr = "";
while((str = br.readLine()) != null){
wholeStr += str;
}
return wholeStr;
}

2.7.2 获取HTTP二进制body

1
2
3
4
5
6
7
8
9
10
11
12
13
private String getBodyData(HttpServletRequest request) {
StringBuffer data = new StringBuffer();
String line = null;
BufferedReader reader = null;
try {
reader = request.getReader();
while (null != (line = reader.readLine()))
data.append(line);
} catch (IOException e) {
} finally {
}
return data.toString();
}

2.7.3 @RequestBody获取body

1
2
3
4
5
6
7
@RequestMapping(value = "/testurl", method = RequestMethod.POST)
@ResponseBody
public ServiceResult TestUrl(HttpServletRequest request,
@RequestBody JSONObject jsonObject){
String username = jsonObject.get("username").toString();
String pwd = jsonObject.get("pwd").toString();
}

@RequestBody 可以使用JSONObject, Map ,或者ObjectDTO绑定body。

2.7.4 @RequestParam获取body

1
2
3
4
5
6
@RequestMapping(value = "/testurl", method = RequestMethod.POST)
@ResponseBody
public ServiceResult TestUrl(HttpServletRequest request,@RequestParam("username")String username,
@RequestParam("pwd")String pwd) {
String txt = username + pwd;
}

三、HttpServletResponse

3.1 HttpServletResponse详解

Web服务器收到一个http请求,会针对每个请求创建一个HttpServletRequest和HttpServletResponse对象,从客户端取数据找HttpServletRequest,向客户端发送数据就是HttpServletResponse,HttpServletResponse对象可以向客户端发送三种类型的数据:a.响应头b.状态码c.数据.

3.1.1 数据

HttpServletResponse对象向客户端发送数据格式如下:

HTTP/1.1 200 OK
Server: Microsoft-IIS/4.0
Date: Mon, 5 Jan 2004 13:13:33 GMT
Content-Type: text/html
Last-Modified: Mon, 5 Jan 2004 13:13:12 GMT
Content-Length: 112
<html><head><title>HTTP Response Example</title></head>....</html>

3.1.2 常用方法

addHeader(String name,String value)  //将指定的名字和值加入到响应的头信息中
encodeURL(String url)  //编码指定的URL
sendError(int sc)  //使用指定状态码发送一个错误到客户端
setDateHeader(String name,long date  //将给出的名字和日期设置响应的头部
setHeader(String name,String value)  //将给出的名字和值设置响应的头部 eg:response.setHeader(“Refresh”,”2;url=”http://www.baidu.com”); 页面的刷新
setStatus(int sc)  //给当前响应设置状态码
getOutputStream()  //字节输出流对象
getWriter()   //字符的输出流对象
sendRedirect ()   //对浏览器的请求直接作出响应,响应的结果就是告诉浏览器去重新发出对另外一个URL的访问请求;方法调用者与被调用者使用各自的request对象和response对象,它们属于两个独立的访问请求和响应过程。
setContentType(String ContentType)  //设置响应的MIME类型 ,页面的设置文本类型,获取或设置输出流的 HTTP MIME 类型。
setCharacterEncoding(String charset) //告知服务器用什么方式编码解析

3.1.3 响应状态码

HttpServletResponse定义了很多状态码的常量(具体可以查看Servlet的API),当需要向客户端发送响应状态码时,可以使用这些常量,避免了直接写数字,常见的状态码对应的常量:

状态码404对应的常量   SC_NOT_FOUND
状态码200对应的常量   SC_OK
状态码500对应的常量   SC_INTERNAL_SERVER_ERROR

3.1.4 用途

  1. 向客户端写入Cookie

  2. 重写URL

  3. 获取输出流对象,向客户端写入文本或者二进制数据

  4. 设置响应客户端浏览器的字符编码类型

  5. 设置客户端浏览器的MIME类型。

3.1.5 细节总结

  1. getOutputStream和getWriter方法分别用于得到输出二进制数据、输出文本数据的ServletOuputStream、Printwriter对象。

  2. getOutputStream和getWriter这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法。

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

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

  5. HttpServletResponse输出中文的时候,注意服务端,客户端2边的编码一定要相同,不如就会出现乱码。

3.2 HttpServletResponse OutputStream中文乱码解决方法

HttpServletResponse使用OutputStream输出中文的时候,如果编码不设置就会产生乱码,产生乱码的原因有以下几种:

  1. Tomcat服务器默认的编码为ISO-8859-1,不支持中文,应当告诉浏览器和服务器,使用UTF-8编码。

  2. 服务端,浏览器端2边的编码不一致,比如服务器用utf-8,浏览器用gb2312,也会产生乱码。

解决方法

1
2
3
4
5
6
7
8
public void outputChineseByOutputStream(HttpServletResponse response) throws IOException{
String data = "javaschool免费在线java教程";
OutputStream outputStream = response.getOutputStream();//获取OutputStream输出流
response.setHeader("content-type", "text/html;charset=UTF-8");//通过设置响应头控制浏览器以UTF-8的编码显示数据,如果不加这句话,那么浏览器显示的将是乱码
//getBytes()方法如果不带参数,那么就会根据操作系统的语言环境来选择转换码表,如果是中文操作系统,那么就使用GB2312的码表
byte[] dataByteArr = data.getBytes("UTF-8");//将字符转换成字节数组,指定以UTF-8编码进行转换
outputStream.write(dataByteArr);//使用OutputStream流向客户端输出字节数组
}

response.setHeader("content-type", "text/html;charset=UTF-8") 通过设置响应头控制浏览器以UTF-8的编码显示数据
使用OutputStream流向客户端浏览器输出中文,以UTF-8的编码进行输出,此时就要控制客户端浏览器以UTF-8的编码打开,否则显示的时候就会出现中文乱码。

总结

  1. 如果中文返回出现??字符,这表明没有加HttpServletResponse.setCharacterEncoding("UTF-8");这句话。

  2. 如果返回的中文是“烇湫”这种乱码,说明浏览器的解析问题,应该检查下是否忘加response.setHeader("Content-type", "text/html;charset=UTF-8");这句话。

  3. 如果还是有乱码,就指定html文件里内容的编码方式

3.3 HttpServletResponse PrintWriter中文乱码解决方法

HttpServletResponse使用PrintWriter输出中文的时候,如果不设置流的编码就会产生乱码,PrintWriter直接输出的字符流首先使用"response.setCharacterEncoding(charset)"设置字符以什么样的编码输出到浏览器,如果不设置则默认是ISO-8859-1,这个是不支持中文的。

**解决方法 **

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void outputChineseByPrintWriter(HttpServletResponse response) throws IOException{
String data = "javaschool免费在线java教程";

//通过设置响应头控制浏览器以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语言里面的标签来控制浏览器行为,模拟通过设置响应头控制浏览器行为
* out.write("");
* 等同于response.setHeader("content-type", "text/html;charset=UTF-8");
*/
out.write(data);//使用PrintWriter流向客户端输出字符
}

HttpServletResponse.setCharacterEncoding("UTF-8");设置将字符以"UTF-8"编码输出到客户端浏览器,然后再使用HttpServletResponse.getWriter();获取PrintWriter输出流,这两个步骤不能颠倒。

3.4 HttpServletResponse图片下载实例(OutputStream,PrintWriter)

HttpServletResponse进行图片下载时推荐使用OutputStream流,避免使用PrintWriter流,因为OutputStream流是字节流,可以处理任意类型的数据,而PrintWriter流是字符流,只能处理字符数据,如果用字符流处理字节数据,会导致数据丢失。

3.4.1 OutputStream下载图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//HttpServletResponse OutputStream下载图片例子
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、得到文件的绝对路径,并且通过该路径得到一个字节输入流
String path = this.getServletContext().getRealPath("/WEB-INF/classes/51gjie.png");//得到下载文件的绝对路径
FileInputStream fis = new FileInputStream(path);
//2、创建字节输出流
ServletOutputStream sos = resp.getOutputStream();
//3、得到下载的文件名
String filename = path.substring(path.lastIndexOf("\\")+1);//得到的文件名为TomCat.png
//4、设置文件编码
filename = URLEncoder.encode(filename, "UTF-8");//编码为UTF-8
//5、告知客户端(浏览器)要下载文件
resp.setHeader("content-disposition", "attachment;filename="+filename);
resp.setHeader("content-type", "image/png");//文件类型
//6、输出
byte[] b = new byte[1024];
int len = 0;
while((len=fis.read(b)) != -1){
sos.write(b, 0, len);
}
sos.close();
fis.close();
}

注意下载图片的时候也需要注意编码的问题,第四步将不安全的文件名改为UTF-8编码,即将文件名中的字母用他们本身替换,将"."、","、"_"、"-"、"*"用他们本身替换,将空格替换为"+",除了这些以外的字符,都是不安全字符,将它们替换为%xy,即16进制格式,其中xy是两个16进制数值。

3.4.2 PrintWriter下载图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//HttpServletResponse PrintWriter下载图片实例
private void downloadFileByPrintWriter(HttpServletResponse response) throws FileNotFoundException, IOException {
String realPath = this.getServletContext().getRealPath("/download/51gjie.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();
}

3.5 HttpServletResponse文件下载实例(OutputStream)

HttpServletResponse文件下载流程如下:

  1. 获取要下载的文件的绝对路径

  2. 获取要下载的文件名

  3. 设置content-disposition响应头控制浏览器以下载的形式打开文件

  4. 获取要下载的文件输入流

  5. 创建数据缓冲区//缓冲区解释见下文

  6. 通过response对象获取OutputStream流

  7. 将FileInputStream流写入到buffer缓冲区

  8. 使用OutputStream将缓冲区的数据输出到客户端浏览器

OutputStream下载xls文件例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//HttpServletResponse OutputStream下载xls文件
@RequestMapping("/download")
public void download(HttpServletRequest req,HttpServletResponse res){
String fileName = "51gjie java教程.xls";//要下载的文件名
String realPath = req.getSession().getServletContext().getRealPath("/wbms/download");
File file=new File(realPath+"/"+fileName); //设置content-disposition响应头控制浏览器以下载的形式打开文件
res.setCharacterEncoding("utf-8");
res.setContentType("application/octet-stream");
res.setHeader("Content-Disposition", "attachment;fileName="+ URLEncoder.encode("javaschool.xls", "UTF-8"));
InputStream inputStream=new FileInputStream(file);根据路径获取要下载的文件输入流
OutputStream out = res.getOutputStream();
byte[] b=new byte[1024]; //创建数据缓冲区
int length;
while((length=inputStream.read(b))>0){ 把文件流写到缓冲区里
out.write(b,0,length);
}
out.flush();
out.close();
inputStream.close();
}

推荐使用OutputStream流,避免使用PrintWriter流,因为OutputStream流是字节流,可以处理任意类型的数据,而PrintWriter流是字符流,只能处理字符数据,如果用字符流处理字节数据,会导致数据丢失。

3.6 HttpServletResponse sendRedirect重定向

HttpServletResponse中一般用sendRedirect进行重定向,sendRedirect进行重定向做了下面2件事:

  1. 设置HTTP响应报头中的Status为302

  2. 设置HTTP响应报头中的Location值为指定的URL

用法

1
HttpServletResponse.sendRedirect("url?参数名1=参数值&参数名2=参数值");

实例

1
2
3
4
5
6
7
8
9
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
/*方法一:使用response.sendRedirect*/
// response.sendRedirect("index.html");

/*方法二:设置响应头和重定向地址*/
response.setHeader("Location", "index.html");
response.setStatus(HttpServletResponse.SC_FOUND);//设置302状态码,等同于response.setStatus(302);
}

sendRedirect重定向不可以在页面中共享HttpServletRequest对象中的数据。但是可以通过重定向的url中携带需要的参数,但这里的参数只能携带字符串的参数。但是其优点是重定向时浏览器默认是使用get请求方式,这样的请求方式速度更快一点。但是安全性却不高。

3.7 HttpServletResponse设置http控制浏览器禁止缓存当前文档内容

HttpServletResponse设置http响应头控制浏览器禁止缓存当前文档内容,设置代码如下:

1
2
3
4
5
protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException {
response.setDateHeader("expries", -1);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
}

3.8 HttpServletResponse设置http定时刷新网页(refresh)

HttpServletResponse设置http响应头控制浏览器定时刷新网页(refresh),设置代码如下:

1
2
3
protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException {
response.setHeader("refresh", "5");//设置refresh响应头控制浏览器每隔5秒钟刷新一次
}

四、HttpSession

4.1 HttpSession对象详解

HttpSession是由JavaWeb提供用来会话跟踪的类,Session是服务器端对象,保存在服务器,它的底层是依赖Cookie,或者是URL重写。

4.1.1 会话范围

某个用户首次访问服务器开始,到该用户完全退出这个项目(也就是同一个浏览器访问服务器到关闭浏览器) ;如果一个用户对服务的多次连贯性请求, 就是用户的多次请求中间没有关闭浏览器,那么session一直有效,只到用户关闭浏览器。

4.1.2 创建session

  1. 若当前的JSP是客户端访问的当前WEB应用的第一个资源,且JSP的page指定的session属性值为 false, 则服务器就不会为JSP创建一个 HttpSession对象。

  2. 若当前JSP不是客户端访问的当前WEB应用的第一个资源,且其他页面已经创建一个HttpSession 对象,则服务器也不会为当前 JSP 页面创建一个HttpSession对象,而回会把和当前会话关联的那个HttpSession对象返回给当前的JSP页面.

  3. 若Serlvet是客户端访问的第一个WEB应用的资源, 则只有调用了request.getSession()或request.getSession(true)才会创建 HttpSession对象。

4.1.3 获取session

  1. 获取cookie中的request.getSession()方法,获取Cookie中的SessionId。

  2. 如果SessionId不存在,创建Session,把Session 保存起来,把新创建的SessionID保存到cookie中。

  3. 如果SessionId存在,通过SessionId查找Session对象,如果没有查找到,创建Session,把Session保存到新创建的SessionId的cookie中。

  4. 如果SessionId存在,通过SessionId查找到了Session对象,那么就不会再创建Session对象了

  5. 返回Session。

如果创建了新的Session,浏览器会得到一个包含SessionId的cookie,这个cookie的生命值是-1,即只在内存中保存,关闭浏览器就丢弃。 下一次如果再次访问,因为从内存的cookie中可以找到Session,那么就是同一个Session。

4.1.4 销毁session

  1. 直接调用 HttpSession 的 invalidate() 方法, 该方法使 HttpSession 失效

  2. 服务器卸载了当前 WEB 应用.

  3. 超出 HttpSession 的过期时间.

设置 HttpSession 的过期时间: session.setMaxInactiveInterval(5); 单位为秒

在 web.xml 文件中设置 HttpSession 的过期时间: 单位为分钟。

1
2
3
<session-config>
<session-timeout>30</session-timeout>
</session-config>
  1. 关闭浏览器只会使存储在客户端浏览器内存中的session cookie失效,不会使服务器端的session对象失效。

4.1.5 URL重写session操作

Session依赖cookie,目的是让客户端发出请求时归还SessionId,这样才能找到对应的Session,如果客户端禁用了cookie,那么就无法得到SessionId。

URL重写的方式替代cookie ,让网站的所有超链接,表单中都添加一特殊的请求参数即SessionId,这样服务器可以通过获取请求参数得到SessionId,从而获得Session对象。

response.encode(String url)这个方法会对URL进行智能的重写,当请求中没有归还SessionId这个cookie,那么这个方法会重写URL,否则不重写,url必须是指向本站的url。

4.1.6 总结

session机制采用的是在服务器端保持 HTTP 状态信息的方案 。当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否包含了一个session标识(即sessionId),如果已经包含一个sessionId则说明以前已经为此客户创建过session,服务器就按照session id把这个session检索出来使用(如果检索不到,可能会新建一个,这种情况可能出现在服务端已经删除了该用户对应的session对象,但用户人为地在请求的URL后面附加上一个JSESSION的参数)。如果客户请求不包含sessionId,则为此客户创建一个session并且生成一个与此session相关联的sessionId,这个session id将在本次响应中返回给客户端保存。

4.2 Java中httpsession生命周期

HttpSession会话范围是某个用户从首次访问服务器开始,到该用户关闭浏览器结束,那么从用户访问到退出浏览器它的生命周期过程如下:

  1. 当浏览器A向服务器发送会话请求,并且访问服务器端某个能和浏览器开启会话的servlet程序时,服务器为这次会话创建一个HttpSession对象,并会这次会话分配一个唯一标识号id。并且这个HttpSession对象和这个id唯一对应。

  2. HttpSession对象存储在服务器端,通过响应消息把会话标识id传给浏览器A端。

  3. 浏览器A只要记住这个会话标识id,并且在后续的访问请求中把这个id传递给服务器,服务器根据这个id就可以判断是哪个浏览器发出的请求,就可以选择它对应的HttpSession对象。

  4. 如果在特定时间内,浏览器都没有向服务器发出请求,则认为该浏览器已经关闭,服务器负责销毁这个浏览器对应的HttpSession对象。

  5. 当浏览再次向服务器发出请求,服务器重复第一步的操作。

4.2.1 JSP创建session

  1. 若当前的JSP是客户端访问的当前WEB应用的第一个资源,且JSP的page指定的session属性值为 false, 则服务器就不会为JSP创建一个 HttpSession对象。

  2. 若当前JSP不是客户端访问的当前WEB应用的第一个资源,且其他页面已经创建一个HttpSession 对象,则服务器也不会为当前 JSP 页面创建一个HttpSession对象,而回会把和当前会话关联的那个HttpSession对象返回给当前的JSP页面.

4.2.2 Serlvet创建session

若Serlvet是客户端访问的第一个WEB应用的资源, 则只有调用了request.getSession()或request.getSession(true)才会创建 HttpSession对象。

4.2.3 销毁session

  1. 程序调用HttpSession.invalidate()

  2. 距离上一次收到客户端发送的session id时间间隔超过了session的最大有效时间

  3. 服务器进程被停止

4.3 Java如何注销httpsession,httpsession销毁的3种方法

注销或者销毁httpsession不能简单的关闭浏览器,关闭浏览器只会使存储在客户端浏览器内存中的session cookie失效,不会使服务器端的session对象失效。因此销毁httpsession有以下3中方法:

  1. 直接调用 HttpSession 的 invalidate() 方法, 该方法使 HttpSession 失效

  2. 服务器进程被停止或者服务器卸载了当前 WEB 应用

  3. 距离上一次收到客户端发送的session id时间间隔超过了session的最大有效时间

设置 HttpSession 的过期时间: session.setMaxInactiveInterval(5); 单位为秒

或者在web.xml 文件中设置 HttpSession 的过期时间: 单位为分钟。

1
2
3
<session-config>
<session-timeout>30</session-timeout>
</session-config>

4.4 Java httpsession默认超时时间设置

Java中HttpSession对象默认超时为30分钟,如果要更改默认超时时间,则有以下2种方法:

  1. java代码中设置HttpSession的过期时间: session.setMaxInactiveInterval(5); 单位为秒

  2. 在web.xml文件中设置HttpSession的过期时间: 单位为分钟,设置如下:

1
2
3
<session-config>
<session-timeout>30</session-timeout>
</session-config>

4.5 Java中如何创建httpsession

4.5.1 JSP创建session

  1. 若当前的JSP是客户端访问的当前WEB应用的第一个资源,且JSP的page指定的session属性值为 false, 则服务器就不会为JSP创建一个 HttpSession对象。

  2. 若当前JSP不是客户端访问的当前WEB应用的第一个资源,且其他页面已经创建一个HttpSession 对象,则服务器也不会为当前 JSP 页面创建一个HttpSession对象,而回会把和当前会话关联的那个HttpSession对象返回给当前的JSP页面.

4.5.2 Serlvet创建session

若Serlvet是客户端访问的第一个WEB应用的资源, 则只有调用了request.getSession()或request.getSession(true)才会创建 HttpSession对象。

1
2
3
String name = "51gjie"
HttpSession session = request.getSession(true);
sesison.setAttribute("Myname",name);

参数为true时,如果不存在,则创建一个新的会话,如果存在就返回当前HTTP会话对象。

参数为false时,如果不存在,则返回null,如果存在就返回该sesion。

4.6 Java中如何清除httpsession(删除httpsession各种应用)

  1. 移除一个特定session属性

调用public void removeAttribute(String name) 方法来移除指定的属性。

  1. 删除整个会话

调用public void invalidate() 方法来使整个session无效。

  1. 设置会话有效期

调用 public void setMaxInactiveInterval(int interval) 方法来设置session超时。

  1. 退出登录

支持servlet2.4版本的服务器,可以调用 logout()方法来登出用户,并且使所有相关的session无效。

  1. 设置服务器的超时时间,自动删除session

如果使用的是Tomcat, 超时以分钟为单位,Tomcat中的默认的超时时间是30分钟。配置web.xml文件如下:

1
2
3
<session-config>
<session-timeout>15</session-timeout>
</session-config>