JavaWeb 学习笔记(二)—— Request 与 Response

Response

响应 的作用是往浏览器写东西。包括响应行、响应头、响应体。

操作 Response 对象

操作响应行

响应行的格式:协议/版本 状态码 状态码说明

状态码 说明
1xx 已发送请求
2xx 已完成响应
200:正常响应
3xx 还需浏览器进一步操作
302:重定向,配合响应头location
304:读缓存
4xx 用户操作错误
404:用户操作错误
访问的方法不存在
5xx 服务器错误
500:内部异常

设置状态码:

1
2
3
4
5
//仅针对于上表的 1xx、2xx、3xx
void setStatus(int 状态码):Sets the status code for this response.

//针对于 4xx 和 5xx【了解】
void sendError(int 状态码):Sends an error response to the client using the specified status code and clearing the buffer.

操作响应头

格式:key/value(value 可以是多个值)

常用方法

1
2
3
4
5
6
7
setHeader(String key,String value):设置字符串形式的响应头
【了解】setIntHeader(String key,int value):设值整型的响应头
【了解】setDateHeader(String key,long value):设值时间的响应头

addHeader(String key,String value):添加字符串形式的响应头,之前设置过则追加,若没有设置过则设置
【了解】addIntHeader(String key,int value):添加整型的响应头
【了解】addDateHeader(String key,long value):添加时间的响应头

常用的响应头

  • location:重定向

  • refresh:定时刷新

  • content-type:设置文件的 mime 类型,设置响应流的编码及告诉浏览器用什么编码打开

  • content-disposition:文件下载

重定向与定时刷新

重定向
  • 方式1:★★
1
response.sendRedirect("/javaweb/test1");
  • 方式2:
1
2
response.setStatus(302);
respooen.setHeader("location","/day10/loc2");
定时刷新
  • 方式1:
1
2
//设置头 refresh
response.setHeader("refresh","秒数;url=跳转的路径");
  • 方式2:
1
2
<--! 设置html的meta标签 -->
<meta http-equiv="refresh" content="3;url=/javaweb/test1.html">

操作响应体

响应体即页面上要展示的内容。

常用方法

1
2
Writer getWriter():字符流
ServletOutputStream getOutputStream():字节流

注:自己写的东西用字符流,其他一概用字节流.

处理响应中文乱码

  • 方式1:
1
response.setContentType("text/html;charset=utf-8");
  • 方式2:理解
1
response.setHeader("content-type", "text/html;charset=utf-8");

详细的介绍如下:

处理字节流
1
2
3
4
5
//设置浏览器默认打开编码
response.setHeader("Content-Type", "text/html;charset=UTF-8");

//中文转成字节数组编码
response.getOutputStream().write("上山打老虎".getBytes("UTF-8"));
处理字符流
1
2
3
4
5
6
//设置response的缓冲区的编码
response.setCharacterEncoding("UTF-8");
//设置浏览器默认打开的编码.
response.setHeader("Content-Type", "text/html;charset=UTF-8");

response.setContentType("text/html;charset=UTF-8");//相当于上面两句

注意:

  • 两个流互斥
  • 当响应完成之后,服务器会判断一下流是否已经关闭,若没有关闭,服务器会帮我们关闭。(底层使用的缓冲流)

扩展-文件下载

超链接方式

1
<a href="/javaweb/download/Notes.txt">下载 Notes.txt</a>

若浏览器能解析该资源的mime类型,则打开;若不能解析,则下载。

手动编码方式

通过 servlet 完成。

a.设置文件的 mime 类型

1
2
String mimeType=context.getMimeType(文件名);
response.setContentType(mimeType);

b.设置下载头信息

1
response.setHeader("content-disposition", "attachment;filename="+文件名称);

c.提供流

1
response.getOutputStream();
  • 扩展-使用commons-io工具类,对拷流:
1
IOUtils.copy(is,os);

案例-文件下载

1.在页面中提供一组下载的链接
1
2
3
response.getWriter().println("<h2>手动编码方式下载</h2>");
response.getWriter().println("<a href='/WEB10/downloadServlet?filename=a.bmp'>a.bmp</a><br/>");
response.getWriter().println("<a href='/WEB10/downloadServlet?filename=WEB01.zip'>WEB01.zip</a>");
2.编写 DownloadServlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//获取下载文件的名称
String filename = request.getParameter("name");

//注意中文乱码:
filename = new String(filename.getBytes("iso8859-1"), "utf-8");

ServletContext context = this.getServletContext();
//文件下载
//1.设置文件的mimeType
String mimeType = context.getMimeType(filename);
response.setContentType(mimeType);

//2.设置下载的头信息
//原始的
//response.setHeader("content-disposition", "attachment;filename="+filename);

//常见的浏览器将文件名称使用utf-8 不推荐 不兼容火狐
//response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(filename, "utf-8"));

//方式1:通过 DownLoadUtils 工具类编码
//String _filename=DownLoadUtils.getName(request.getHeader("user-agent"), filename);
//response.setHeader("content-disposition", "attachment;filename="+_filename);

//方式2:网络上的方式 (8成好使)
response.setHeader("content-disposition", "attachment;filename=" + new String(filename.getBytes("gbk"), "iso8859-1"));

//3.对拷流
//获取输入流
InputStream is = context.getResourceAsStream("/download/" + filename);

//获取输出流
ServletOutputStream os = response.getOutputStream();

/*int len = -1;
byte[] b = new byte[1024];
while((len = is.read(b))!=-1){
os.write(b, 0, len);
}
is.close();*/

//使用commons-io工具类对拷流
IOUtils.copy(is, os);

os.close();
is.close();

其中,DownLoadUtils 如下进行封装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class DownLoadUtils {
public static String getName(String agent, String filename) throws UnsupportedEncodingException {
if (agent.contains("MSIE")) {
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
// 火狐浏览器
BASE64Encoder base64Encoder = new BASE64Encoder();
filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
}

扩展-生成验证码

  • 编写 CodeServlet:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// 使用java图形界面技术绘制一张图片

int charNum = 4;
int width = 30 * 4;
int height = 30;

// 1. 创建一张内存图片
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

// 2.获得绘图对象
Graphics graphics = bufferedImage.getGraphics();

// 3、绘制背景颜色
graphics.setColor(Color.YELLOW);
graphics.fillRect(0, 0, width, height);

// 4、绘制图片边框
graphics.setColor(Color.BLUE);
graphics.drawRect(0, 0, width - 1, height - 1);

// 5、输出验证码内容
graphics.setColor(Color.RED);
graphics.setFont(new Font("宋体", Font.BOLD, 20));

// 随机输出4个字符
Graphics2D graphics2d = (Graphics2D) graphics;
String s = "ABCDEFGHGKLMNPQRSTUVWXYZ23456789";
Random random = new Random();
// session中要用到
String msg = "";
int x = 5;
for (int i = 0; i < 4; i++) {
int index = random.nextInt(32);
String content = String.valueOf(s.charAt(index));
msg += content;
double theta = random.nextInt(45) * Math.PI / 180;
// 让字体扭曲
graphics2d.rotate(theta, x, 18);
graphics2d.drawString(content, x, 18);
graphics2d.rotate(-theta, x, 18);
x += 30;
}

// 6、绘制干扰线
graphics.setColor(Color.GRAY);
for (int i = 0; i < 5; i++) {
int x1 = random.nextInt(width);
int x2 = random.nextInt(width);

int y1 = random.nextInt(height);
int y2 = random.nextInt(height);
graphics.drawLine(x1, y1, x2, y2);
}

// 释放资源
graphics.dispose();

// 图片输出 ImageIO
ImageIO.write(bufferedImage, "jpg", response.getOutputStream());
  • 点击换一张验证码的 js 代码:
1
2
3
4
function changeImg(obj){
//操作src属性
obj.src="/day10/code?i="+Math.random();
}

结果如下图:

Request

请求 的作用是获取浏览器发送过来的数据。

操作 Request 对象

操作请求行

请求行的格式:请求方式 请求资源 协议/版本

常用方法:HttpServletRequest

  • 【掌握】
1
2
3
String getMethod() //获取请求方式
String getRemoteAddr() //获取ip地址
String getContextPath() //在 java 中获取项目名称 (/javaweb)
  • 【了解】
1
2
3
4
getRequestURI() //获取的是 从项目名到参数之前的内容  /javaweb/regist
getRequestURL() //获取的带协议的完整路径 http://localhost/javaweb/regist
String getQueryString() //get请求的所有参数 username=tom&password=123
String getProtocol() //获取协议和版本
  • 例如:请求行
1
GET  /day10/row?username=tom&password=123  HTTP/1.1

操作请求头

格式:key/value(value 可以是多个值)

常用方法

  • 【掌握】
1
String getHeader(String key) //通过key获取指定的value(一个)
  • 【了解】
1
2
3
4
Enumeration getHeaders(String name) //通过key获取指定的value(多个)
Enumeration getHeaderNames() //获取所有的请求头的名称
int getIntHeader(String key) //获取整型的请求头
long getDateHeader(String key) //获取时间的请求头
  • 重要的请求头:

user-agent:浏览器内核 msie firefox chrome
referer:页面从那里来 防盗链

操作请求参数

请求参数:username=tom&password=123&hobby=drink&hobby=sleep

常用方法

1
2
3
String getParameter(String key) //获取一个值
String[] getParameterValues(String key) //通过一个key获取多个值
Map<String,String[]> getParameterMap() //获取所有的参数名称和值

例如:

1
2
3
4
5
6
7
8
9
10
//value
username:tom

//values
hobby:[drink, sleep]

//map
username::[tom]
password::[123]
hobby::[drink, sleep]

请求的中文乱码

  • 对于 get 请求:参数追加到地址栏,会使用 utf-8 编码,服务器(tomcat7)接受到请求之后,使用 iso-8859-1 解码,所以会出现乱码。

  • 对于 post 请求:参数是放在请求体中,服务器获取请求体的时候使用 iso-8859-1 解码,也会出现乱码。

通用的方法:缺点是需要一个一个设置

1
new String(参数.getBytes("iso-8859-1"),"utf-8");

针对于 post 请求的方法:只需要将请求流的编码设置成 utf-8 即可。

1
request.setCharacterEncoding("utf-8");

域对象 request

request 域 生命周期
创建 一次请求来的时候
销毁 响应生成的时候
作用 存放一次请求里面的数据
  • 请求转发(也叫请求链、请求串)
1
request.getRequestDispatcher("内部路径").forward(request,response);
  • 作为域对象存取值
1
2
3
void setAttribute(String name, Object o)
void removeAttribute(String name)
Object getAttribute(String name)

例如:

1
2
3
request.setAttribute("username","tom");

request.getAttribute("username");

请求转发与重定向

  • 重定向
1
response.sendRedirect(String path);
  • 请求转发
1
request.getRequestDispatcher(String path).forward(request,response);

重定向发送两次请求,请求转发一次请求

重定向地址栏发生改变,请求转发不变

重定向是从浏览器发送,请求转发是服务器内部

重定向不存在 request 域对象,请求转发可以使用 request 域对象

重定向是 response 的方法,请求转发是 request 的方法

重定向可以请求站外资源,请求转发不可以

扩展-编码解码

1
2
URLEncoder.encode(String s, "utf-8"); //指定编码
URLDecoder.decode(String s8, "iso8859-1"); //指定解码

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) throws Exception {
String s="天下";

//用 utf-8 进行编码
String s8=URLEncoder.encode(s, "utf-8");

//System.out.println(s8);
//对utf-8的字符串通过iso8859-1进行解码,则会出现乱码
String so = URLDecoder.decode(s8, "iso8859-1");
System.out.println(so); //乱码

byte[] b = so.getBytes("iso-8859-1");
String _s = new String(b, "utf-8");
//上面的两句简写为:String _s=new String(so.getBytes("iso8859-1"),"utf-8");
System.out.println(_s);
}

整理的也许不全面,就这样吧!