JavaWeb笔记-HTTP协议/request/response
0.HTTP协议-先修知识
注:实例说明
1. 请求消息
1.1. 请求消息行
- POST /day08_02/1.html HTTP/1.1
-
请求方式: Get(默认) POST DELETE HEAD等
- GET: 明文传输 不安全,数据量有限,不超过1kb
例: GET /day08_02/1.html?uName=tom&pwd=123 HTTP/1.1 - POST: 暗文传输,安全。数据量没有限制。
- GET: 明文传输 不安全,数据量有限,不超过1kb
-
URI: 统一资源标识符。去协议和IP地址。
协议/版本 : HTTP/1.1
1.2. 请求消息头
从第2行到空行处,都叫消息头
Accept: 浏览器可接受的MIME类型 ,告诉服务器客户端能接收什么样类型的文件。
- Accept-Charset: 浏览器通过这个头告诉服务器,它支持哪种字符集
- Accept-Encoding: 浏览器能够进行解码的数据编码方式,比如gzip
- Accept-Language: 浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到。 可以在浏览器中进行设置。
- Host: 初始URL中的主机和端口
- Referrer: 包含一个URL,用户从该URL代表的页面出发访问当前请求的页面
- Content-Type: 内容类型,告诉服务器浏览器传输数据的MIME类型,文件传输的类型,application/x-www-form-urlencoded,If-Modified-Since: Wed, 02 Feb 2011 12:04:56 GMT利用这个头与服务器的文件进行比对,如果一致,则从缓存中直接读取文件。
- User-Agent: 浏览器类型.
- Content-Length: 表示请求消息正文的长度
- Connection:表示是否需要持久连接。如果服务器看到这里的值为“Keep -Alive”,或者看到请求使用的是HTTP 1.1(HTTP 1.1默认进行持久连接
- Cookie: 这是最重要的请求头信息之一 (在讲会话时解析)
- Date:Date: Mon, 22 Aug 2011 01:55:39 GMT请求时间GMT
1.3.消息正文: 当请求方式是get方式时,才能看见消息正文:uName=tom&pwd=123
2.响应消息
2.1. 响应消息行
-第一行 POST /day08_02/1.html HTTP/1.1
- HTTP/1.1 200 OK
- 协议/版本 响应状态码 对响应码的描述(一切正常)
- 响应状态码:常用的就40多个。
- 200(正常) 一切正常
- 302/307(临时重定向)
- 304(未修改):表示客户机缓存的版本是最新的,客户机可以继续使用它,无需到服务器请求。
- 404(找不到) 服务器上不存在客户机所请求的资源。
- 500(服务器内部错误)
2.2. 响应消息头
- Location: http://www.it315.org/index.jsp指示新的资源的位置,通常和302/307一起使用,完成请求重定向
- Server :apache tomcat指示服务器的类型
- Content-Encoding: gzip服务器发送的数据采用的编码类型
- Content-Length: 80 告诉浏览器正文的长度
- Content-Language: zh-cn服务发送的文本的语言
- Content-Type: text/html: charset=GB2312服务器发送的内容的MIME类型
- Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT文件的最后修改时间
- Refresh: 1;url=http://www.it315.org指示客户端刷新频率。单位是秒
- Content-Disposition: attachment; filename=aaa.zip指示客户端下载文件
- Set-Cookie:SS=Q0=5Lb_nQ; path=/search服务器端发送的Cookie
- Expires: -1
- Cache-Control: no-cache (1.1)
- Pragma: no-cache (1.0) 表示告诉客户端不要使用缓存
- Connection: close/Keep-Alive
- Date: Tue, 11 Jul 2000 18:23:51 GMT
2.2. 响应正文
- 和网页右键“查看源码”看到的内容一样。
1、HttpServletRequest
1.1 请求状态行
Get http://localhost:8080/day09/servlet/req1?username=zs http/1.1
- getMethod(); 获得请求方式:get
- getRequestURL();返回客户端发出请求时的完整URL: http://localhost:8080/day09/servlet/req1
- getRequestURI(); 返回请求行中的资源名部分:day09/servlet/req1
- getContextPath(); 当前应用的虚拟目录: /day09_01_request
- getQueryString() ; 返回请求行中的参数部分,即?之后:username=zs
PrintWriter out = response.getWriter(); //
out.write(request.getMethod()+"\n"); // 获取浏览器请求方式
out.write(request.getRequestURL().toString()+"\n");//获取请求地址
out.write(request.getRequestURI()+"\n"); // 获取请求的资源路径
out.write(request.getContextPath()+"\n"); //获取虚拟目录
out.write(request.getQueryString()+"\n"); // 获取请求参数
1.2 请求消息头
- String getHeader(String name) 根据头名称得到头信息值
- Enumeration getHeaderNames() 得到所有头信息name
- Enumeration getHeaders(String name) 根据头名称得到相同名称头信息值
PrintWriter out = response.getWriter();
out.write(request.getHeader("User-Agent")); // 获取浏览器信息
Enumeration<String> headerNames = request.getHeaderNames(); // 获得所有消息信息头
while (headerNames.hasMoreElements()) {
String headerName = (String) headerNames.nextElement(); // 得到每个消息头名称
out.write(headerName+":"+request.getHeader(headerName)+"\n"); //打印消息头名称和值
}
1.3 请求正文(重要)
1.3.1 与获取表单数据相关的方法
<input type="text" name="username" />
- getParameter(name) 根据表单中name属性的名,获取value属性的值方法
- getParameterValues(String name) 专业为复选框取取提供的方法,即获取同名的value值存在数组中
- getParameterNames() 得到表单提交的所有name的方法
- getParameterMap()得到表单提交的所有值的方法 //做框架用,非常实用
- getInputStream() 以字节流的方式得到所有表单数据,常用在上传文件、图片等
例:1
register.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>register.html<title>
</head>
<body>
<form action="/day09_01_HttpServletRequest/ServerRegisterDemo" method="post">
用户名:<input type="text" name="usreName"><br>
密码:<input type="password" name="password"><br>
性别:<input type="radio" name="sex" value="男" checked="checked">男
<input type="radio" name="sex" value="女">女<br>
爱好:
<input type="checkbox" name="hobby" value="篮球" checked="checked">篮球
<input type="checkbox" name="hobby" value="唱歌">唱歌
<input type="checkbox" name="hobby" value="编码">编码
<br>
所在城市:
<select name="city">
<option>------请选择------</option>
<option value="bj" selected="selected">北京</option>
<option value="sh">上海</option>
<option value="gz">广州</option>
</select>
<br>
<input type="submit" value="注册">
</form>
</body>
</html>
ServerRegisterDemo.java
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//告诉浏览器该用什么编码方式解析前端数据
request.setCharacterEncoding("UTF-8");
// 获取表单数据
// 根据表单中的name属性名,过去value属性的值
String usreName = request.getParameter("usreName");
String password = request.getParameter("password");
String sex = request.getParameter("sex");
String[] hobby = request.getParameterValues("hobby");//多值的情况,几乎是专给复选框用的,即获取同名的value值存在数组中
String city = request.getParameter("city");
System.out.println("usreName:"+usreName);
System.out.println("password:"+password);
System.out.println("sex:"+sex);
System.out.println("hobby:"+Arrays.toString(hobby));
System.out.println("city:"+city);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
console
usreName:tao
password:1234
sex:女
hobby:[篮球, 唱歌, 编码]
city:bj
例:2
register2.html
<!DOCTYPE html>
<html>
<head>
<title>register2.html</title>
<meta name="keywords" content="keyword1,keyword2,keyword3">
<meta name="description" content="this is my page">
<meta name="content-type" content="text/html; charset=UTF-8">
<meta charset = "utf-8">
<!--<link rel="stylesheet" type="text/css" href="./styles.css">-->
</head>
<body>
<form action="/day09_01_HttpServletRequest/ServletRegisterDemo2" method="post">
用户名:<input type="text" name="usreName"><br>
密码:<input type="password" name="password"><br>
性别:<input type="radio" name="sex" value="男" checked="checked">男
<input type="radio" name="sex" value="女">女<br>
爱好:
<input type="checkbox" name="hobby" value="篮球" checked="checked">篮球
<input type="checkbox" name="hobby" value="唱歌">唱歌
<input type="checkbox" name="hobby" value="编码">编码
<br>
所在城市:
<select name="city">
<option>------请选择------</option>
<option value="bj" selected="selected">北京</option>
<option value="sh">上海</option>
<option value="gz">广州</option>
</select>
<br>
<input type="submit" value="注册">
</form>
</body>
</html>
ServletRegisterDemo1.java
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
Enumeration<String> pNames = request.getParameterNames();
while (pNames.hasMoreElements()) {
String pname = (String) pNames.nextElement();
String[] pValues = request.getParameterValues(pname);
System.out.print(pname+":");
for (String pValue : pValues) {
if (pValue.isEmpty()) {
System.out.println("null");
}else {
System.out.println(pValue);
}
}
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
console
usreName:123
password:11233
sex:男
hobby:篮球
hobby:唱歌
city:sh
- getParameterMap()得到表单提交的所有值的方法 //做框架用,非常实用
request.setCharacterEncoding("UTF-8");
Map<String, String[]> parameterMap = request.getParameterMap();
for (Entry<String, String[]> entry : parameterMap.entrySet()) {
System.out.println(entry.getKey()+":"+Arrays.toString(entry.getValue()));
}
console
usreName:[3]
password:[]
sex:[男]
hobby:[篮球, 唱歌, 编码]
city:[bj]
- 实际应用,将表单的数据封装到JavaBean中
User.java
// 在实体类中的字段要与表单中的name名一直,约定优于编码
private String usreName;
private String password;
private String sex;
private String[] hobby;
private String city;
public String getUsreName() {
return usreName;
}
public void setUsreName(String usreName) {
this.usreName = usreName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String[] getHobby() {
return hobby;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "User [usreName=" + usreName + ", password=" + password + ", sex=" + sex + ", hobby="
+ Arrays.toString(hobby) + ", city=" + city + "]";
}
原生实现对数据的封装:Servlet.java
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
User user = new User();
System.out.println("封装数据前的user:"+user);
try {
Map<String, String[]> parameterMap = request.getParameterMap();
for (Entry<String, String[]> entry : parameterMap.entrySet()) {
String key = entry.getKey();
String[] value = entry.getValue();
// 创建一个属性描述器
PropertyDescriptor pd = new PropertyDescriptor(key, User.class);
// 得到User类的setter方法
Method setter = pd.getWriteMethod();
// 设置user对象的字段
if (value.length<2) {
setter.invoke(user, value[0]);
}else {
setter.invoke(user, (Object)value);
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("封装数据后的user:"+user);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
工具实现对数据的封装:Servlet.java
- 工具包:commons-beanutils-1.8.3.jar
- 工具包 >依赖包:commons-logging-1.1.1.jar
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
try {
User user = new User();
System.out.println("封装前的user:"+user);
// 通过工具类BeanUtils.populate(Object bean, Map properties)封装数据;
BeanUtils.populate(user, request.getParameterMap());
System.out.println("封装后的user:"+user);
} catch (Exception e) {
e.printStackTrace();
}
}
1.3.2 与操作非表单数据相关的方法(request也是一个域对象)
- void setAttribute(String name, Object value);
- Object getAttribute(String name);
- Void removeAttribute(String name);
1.3.3 与请求转发相关的方法
- RequestDispatcher getRequestDispatcher(String path) //得到请求转发或请求包含的协助对象
- forward(ServletRequest request, ServletResponse response) //转发的方法
- include(ServletRequest request, ServletResponse response) //请求包含,将被包含的Servlet里面的代码拿到包含的Servlet里执行,等于是2合一-
- 例:. include(ServletRequest request, ServletResponse response)
String path = "/demo"
request.getRequestDispatcher(path).include(request, response);
- 例:
-ServletDemo5_RequestDispatcher.java
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
System.out.println("A:我想办事");
System.out.println("B:我办不了,但是我可以找人帮你办");
// 将非表单数据添加到request域中
request.setAttribute("file", "办事材料");
// request请求转发,服务器将直接进行重定向,所以浏览器的地址栏的地址不会发生变化
// request请求转发与response的重定向不同,response的重定向是服务器将需要重定向的操作告诉浏览器,由浏览器来决定要不要重定向
RequestDispatcher rd = request.getRequestDispatcher("/ServletDemo6_RequestDispatcher");
rd.forward(request, response);
System.out.println("B:找人帮你办理了");
}
ServletDemo6_RequestDispatcher.java
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
System.out.println("B:帮A办点事");
// 获取request域中传递的数据
String file = (String)request.getAttribute("file")
System.out.println("C:材料齐全--" + file );
System.out.println("C:A的事儿办好了");
}
console
A:我想办事
B:我办不了,但是我可以找人帮你办
B:帮A办点事
C:材料齐全--办事材料
C:A的事儿办好了
B:找人帮你办理了
1.3.4 与请求编码相关的方法:
-
request.setCharacterEncoding(“UTF-8”); //解决post方式编码,告诉服务器客户端什么编码,只能处理post请求方式
-
String name = new String(name.getBytes(“iso-8859-1”),”UTF-8”); //解决get方式编码
2、HttpServletResponse
2.1 响应状态行
/* 告诉浏览器重定向:
重定向的理解:
1.浏览器先访问servlet1
2.servlet1收到浏览器的请求后,告诉浏览器你的请求我没法帮你完成,请你重定向到servlet2,她可以给你完成,同时把需要重定向的信息"302",和重定向地址一起反馈给浏览器
3.浏览器收到servlet1的反馈信息"302"后,知道需要重定向,根据反馈的地址重新访问servlet2,(相当于重新在地址栏输入servlet2的地址,进行访问)
*/
response.setStatus(302); // 302 代表需要重定向
response.setHeader("location", "/day09_00_HttpServletResponse/ServletDemo8"); //重定向地址
// 重定向需要以上两句话同时使用
/*关于重定向的地址:在这里是返回去浏览器在重定向,所以这里的地址是从服务器的根目录"/"下面的网站的根目录"/day09_00_HttpServletResponse"下去找到servlet;
如果是在服务器去访问,那么"/"就是代表的是当前网站的根目录也就是:"day09_00_HttpServletResponse/"*/
// 重定向方法二,等于上面的两行合在一起
response.sendRedirect("/day09_00_HttpServletResponse/ServletDemo8");// 直接给出浏览器需要重新访问的地址
2.2 响应消息头
// 设置头 告诉浏览器不使用缓存
response.setHeader("pragma", "no-cache");
response.setHeader("cache-control", "no-cache");
response.setHeader("expires", "0");
response.setHeader("refresh","3;url=...") // 告诉3秒钟后刷新,跳转到...页面
// 告诉浏览器下载文件
fileName = URLEncoder.encode(fileName, "utf-8"); // 将不安全的文件名改为utf-8格式
response.setHeader("content-disposition","attachment;filename=" + fileName); // 告诉浏览器下载文件
2.3 响应正文
response.setCharacterEncoding("UTF-8"); // 告诉服务用什么编码方式
response.setHeader("content-type","text/html;charset=UTF-8"); // 告诉浏览器使用什么编码方式
response.setContentType("text/html;charset=UTF-8"); // 同时告诉服务器和浏览器用什么编码方式
response.getWrite().write("你好"); // 字符输出流
response.getOutputStream().write("你好".getBytes()); // 字节输出流
2.4 response注意细节
- getOutputStream()和getWrite()方法分别用于得到输出二进制数据和输出文本数据的ServletOutputStreamh和Printwriter对象。
- getOutputStream()和getWrite()两个方法互相排斥,在一个Servlet中只能调用一个方法。
- Servlet程序向getOutputStream或者getWrite对象中写入的数据,将被Servlet引擎(如:tomcat服务器)从response里面获取,Servlet引擎将这些数据当做响应消息的正文,然后在于响应状态行和各种响应头组合后输出到客户端。
- Servlet的service方法结束后,Servlet引擎将检查getOutputStream()/getWrite()方法返回的输出流对象是否调用close()方法,如果没有,Servlet引擎将会自动调用close()方法关闭该输出流对象。