HTTP协议
本文是基于Windows 10系统环境,学习和使用HTTP协议:
- Windows 10
- MyEclipse 10
一、 HTTP协议的基本概念
(1) HTTP协议的定义
- 对浏览器客户端和服务器端之间数据传输的格式规范
(2) request的内容
- request请求(浏览器->服务器)
GET /day09/hello HTTP/1.1 # 请求行(请求方式:URI:版本号)
# 请求头(多个key-value键值对)
Accept: text/html,image/* # 浏览器接受的数据类型
Accept-Charset: ISO-8859-1 # 浏览器接受的编码格式
Accept-Encoding: gzip,compress # 浏览器接受的数据压缩格式
Accept-Language: en-us,zh- # 浏览器接受的语言
Host: www.it315.org:80 # (必须的)当前请求访问的目标地址(主机:端口)
If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT # 浏览器最后的缓存时间
Referer: http://www.it315.org/index.jsp # 当前请求来自于哪里
User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0) # 浏览器类型
Cookie:name=eric # 浏览器保存的cookie信息
Connection: close/Keep-Alive # 浏览器跟服务器连接状态。close: 连接关闭 keep-alive:保存连接。
Date: Tue, 11 Jul 2000 18:23:51 GMT # 请求发出的时间
name=eric&password=123456 # 请求实体内容
- request请求相关API代码
// HttpServletRequest对象作用是用于获取请求数据。
// 核心的API:
// 请求行:
request.getMethod(); //请求方式
request.getRequetURI()
request.getRequetURL() //请求资源
request.getProtocol() //请求http协议版本
// 请求头:
request.getHeader("名称") //根据请求头获取请求值
request.getHeaderNames() //获取所有的请求头名称
// 打印所有的请求头内容
Enumeration<String> enums = request.getHeaderNames();
while(enums.hasMoreElements()){
String headerName = enums.nextElement();
String headerValue = request.getHeader(headerName);
}
// 实体内容:
request.getInputStream() //获取实体内容数据
// 打印实体内容
InputStream in = request.getInputStream();
byte[] buf = new byte[1024];
int len = 0;
while((len=in.read(buf))!=-1){
String str = new String(buf, 0, len);
System.out.println(str);
}
- request获取GET与POST提交的参数
request.getParameter("参数名"); //根据参数名获取参数值(单值)
request.getParameterValues("参数名"); //根据参数名获取参数值(多值)
// 打印所有的参数
Enumeration<String> enums = request.getParameterNames();
while(enums.hasMoreElements()){
String paraName = enums.nextElement();
String paraValue = request.getParameter(paraName);
System.out.println(paraName+"="+paraValue);
}
- 请求参数编码问题
// 修改post方式参数编码
request.setCharacterEncoding("utf-8");
// 修改get方式参数编码
String name = new String(name.getBytes("ISO-8859-1"),"utf-8");
(2) response的内容
- response响应(服务器->浏览器)
HTTP/1.1 200 OK # 响应行(版本号:状态码:状态描述)
# 响应头(多个key-value键值对)
Location: http://www.it315.org/index.jsp # 表示重定向的地址,该头和302的状态码一起使用。
Server:apache tomcat # 表示服务器的类型
Content-Encoding: gzip # 表示服务器发送给浏览器的数据压缩类型
Content-Length: 80 # 表示服务器发送给浏览器的数据长度
Content-Language: zh-cn # 表示服务器支持的语言
Content-Type: text/html; charset=GB2312 # 表示服务器发送给浏览器的数据类型及内容编码
Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT # 表示服务器资源的最后修改时间
Refresh: 1;url=http://www.it315.org # 表示定时刷新
Content-Disposition: attachment; filename=aaa.zip # 表示告诉浏览器以下载方式打开资源(下载文件时用到)
Transfer-Encoding: chunked
Set-Cookie:SS=Q0=5Lb_nQ; path=/search # 表示服务器发送给浏览器的cookie信息(会话管理用到)
Expires: -1 # 表示通知浏览器不进行缓存
Cache-Control: no-cache
Pragma: no-cache
Connection: close/Keep-Alive # 表示服务器和浏览器的连接状态。close:关闭连接 keep-alive:保存连接
this is hello servlet!!! # 响应实体内容
- response请求相关API代码
// HttpServletResponse对象
// 响应行:
response.setStatus(404) //设置状态码
// 响应头:
response.setHeader("name","value") //设置响应头
response.setHeader("Server","JBoss") //设置响应头
// 实体内容:
response.getWriter().writer("hello"); //发送字符实体内容
response.getOutputStream().writer("hello".getBytes()); //发送字节实体内容
- 响应参数编码问题
// 修改post方式参数编码
response.setCharacterEncoding("utf-8");
(3) 案例
- 重定向
response.setStatus(302) //设置状态码
response.setHeader("location","/day09/adv.html") //设置URI
//简化版
response.sendRedirect("/day09/adv.html") //重定向至adv.html
- 隔3秒后,跳转至新页面
response.setHeader("refresh","3;/day09/adv.html") //隔3秒后,跳转至新页面
- 发送图片
response.setContentType("image/jpg"); /tomcat/conf/web.xml
FileInputStream in = new FileInputStream(new File("e:/mm.jpg"));
byte[] buf = new byte[1024];
int len = 0;
while(len(in.read(buf))!=-1){
response.getOutputStream().write(buf,0,len);
}
二、 Servlet编程
(1) Servlet映射问题
- web.xml配置文件
<servlet-mapping>
<!-- servlet的内部名称,一定要和上面的内部名称保持一致!! -->
<servlet-name>FirstServlet</servlet-name>
<!-- servlet的映射路径(访问servlet的名称) -->
<url-pattern>/first</url-pattern>
</servlet-mapping>
- 精确匹配
url-pattern 浏览器输入
/first http://localhost:8080/day10/first
/itcast/demo1 http://localhost:8080/day10/itcast/demo1
- 模糊匹配
url-pattern 浏览器输入
/* http://localhost:8080/day10/任意路径
/itcast/* http://localhost:8080/day10/itcast/任意路径
*.后缀名 http://localhost:8080/day10/任意路径.后缀名
*.do http://localhost:8080/day10/任意路径.do
*.action http://localhost:8080/day10/任意路径.action
*.html(伪静态) http://localhost:8080/day10/任意路径.html
- 注意事项
1. url-pattern要么以 / 开头,要么以*开头。 例如, itcast是非法路径。
2. 不能同时使用两种模糊匹配,例如 /itcast/*.do是非法路径
3. 当有输入的URL有多个servlet同时被匹配的情况下:
3.1 精确匹配优先。(长的最像优先被匹配)
3.2 以后缀名结尾的模糊url-pattern优先级最低!!!
(2) Servlet的自动加载
默认情况下,第一次访问servlet的时候创建servlet对象。如果servlet的构造方法或init方法中执行了比较多的逻辑代码,那么导致用户第一次访问sevrlet的时候比较慢。
改变servlet创建对象的时机: 提前到加载web应用的时候!!!
在servlet的配置信息中,加上一个即可!!
- web.xml配置文件
<servlet>
<servlet-name>LifeDemo</servlet-name>
<servlet-class>gz.itcast.c_life.LifeDemo</servlet-class>
<!-- 让servlet对象自动加载 -->
<load-on-startup>1</load-on-startup>
<!--注意: 整数值越大,创建优先级越低!!-->
</servlet>
(3) Servlet的多线程并发问题
- servlet对象在tomcat服务器是单实例多线程的
因为servlet是多线程的,所以当多个servlet的线程同时访问了servlet的共享数据,如成员变量,可能会引发线程安全问题。 - 解决方案
1)把使用到共享数据的代码块进行同步(使用synchronized关键字进行同步)
2)建议在servlet类中尽量不要使用成员变量。如果确实要使用成员,必须同步。而且尽量缩小同步代码块的范围。(哪里使用到了成员变量,就同步哪里!!),以避免因为同步而导致并发效率降低。
(4) ServletConfig对象
- 作用
ServletConfig对象: 主要是用于加载servlet的初始化参数。在一个web应用可以存在多个ServletConfig对象(一个Servlet对应一个ServletConfig对象) - 对象创建和得到
创建时机: 在创建完servlet对象之后,在调用init方法之前创建。
得到对象: 直接从有参数的init方法中得到!!! - 核心API
String getInitParameter(String name) // 根据参数名获取参数值
Enumeration getInitParameterNames() // 获取所有参数
ServletContext getServletContext() // 得到servlet上下文对象
String getServletName() // 得到servlet的名称
- servlet的初始化参数配置
- web.xml
<servlet>
<servlet-name>ConfigDemo</servlet-name>
<servlet-class>gz.itcast.f_config.ConfigDemo</servlet-class>
<!-- 初始参数:这些参数会在加载web应用的时候,封装到ServletConfig对象中 -->
<init-param>
<param-name>path</param-name>
<param-value>e:/b.txt</param-value>
</init-param>
<init-param>
<param-name>path2</param-name>
<param-value>e:/a.txt</param-value>
</init-param>
</servlet>
public class ConfigDemo extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* 读取servlet的初始参数
*/
String path = this.getServletConfig().getInitParameter("path");
File file = new File(path);
//读取内容
BufferedReader br = new BufferedReader(new FileReader(file));
String str = null;
while( (str=br.readLine())!=null ){
System.out.println(str);
}
//查询当前servlet的所有初始化参数
Enumeration<String> enums = this.getServletConfig().getInitParameterNames();
while(enums.hasMoreElements()){
String paramName = enums.nextElement();
String paramValue = this.getServletConfig().getInitParameter(paramName);
System.out.println(paramName+"="+paramValue);
}
//得到servlet的名称
String servletName = this.getServletConfig().getServletName();
System.out.println(servletName);
}
}
(5) ServletContext对象
- 基本概念
ServletContext对象 ,叫做Servlet的上下文对象。表示一个当前的web应用环境。一个web应用中只有一个ServletContext对象。 - 核心API
// 得到当前web应用的路径
String getContextPath()
// 得到web应用的初始化参数
String getInitParameter(String name)
Enumeration getInitParameterNames()
// 域对象有关的方法
void setAttribute(String name, Object object)
Object getAttribute(String name)
void removeAttribute(String name)
// 转发(类似于重定向)
RequestDispatcher getRequestDispatcher(String path)
// 得到web应用的资源文件
String getRealPath(String path)
InputStream getResourceAsStream(String path)
- 获取ServletContext对象
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ServletContext context = this.getServletContext();
}
- 得到当前web应用的路径—重定向
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.得到ServletContext对象
ServletContext context = this.getServletContext();
//2.得到web应用路径 /day10
/**
* web应用路径:部署到tomcat服务器上运行的web应用名称
*/
String contextPath = context.getContextPath();
System.out.println(contextPath);
/**
* 案例:应用到请求重定向
*/
response.sendRedirect(contextPath+"/index.html");
}
- 得到web应用的初始化参数(全局)
public class ContextDemo2 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//得到SErvletContext对象
ServletContext context = this.getServletContext();
System.out.println("参数"+context.getInitParameter("AAA"));
Enumeration<String> enums = context.getInitParameterNames();
while(enums.hasMoreElements()){
String paramName = enums.nextElement();
String paramValue =context.getInitParameter(paramName);
System.out.println(paramName+"="+paramValue);
}
//尝试得到ConfigDemo中的servlet参数
String path = this.getServletConfig().getInitParameter("path");
//会打印path=null,因为ServletConfig属于servlet,而不是全局的
System.out.println("path="+path);
}
}
- 域对象有关的方法
域对象:作用是用于保存数据,获取数据。可以在不同的动态资源之间共享数据。 - 所有域对象
HttpServletRequet 域对象
ServletContext域对象
HttpSession 域对象
PageContext域对象
// 保存数据
void setAttribute(String name, Object object)
// 获取数据
Object getAttribute(String name)
// 删除数据
void removeAttribute(String name)
- 转发(类似于重定向)
public class ForwardDemo1 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//转发
/**
* 注意:不能转发当前web应用以外的资源。
*/
RequestDispatcher rd = this.getServletContext().getRequestDispatcher("/GetDataServlet");
rd.forward(request, response);
}
}
- 携带数据的转发
public class ForwardDemo1 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/**
* 保存数据到request域对象
*/
request.setAttribute("name", "rose");
RequestDispatcher rd = this.getServletContext().getRequestDispatcher("/GetDataServlet");
rd.forward(request, response);
}
}
- 接收数据
public class GetDataServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/**
* 从request域对象中获取数据
*/
String name = (String)request.getAttribute("name");
System.out.println("name=" + name);
}
}
(6) 转发和重定向的区别
- 转发
a)地址栏不会改变
b)转发只能转发到当前web应用内的资源
c)可以在转发过程中,可以把数据保存到request域对象中 - 重定向
a)地址栏会改变,变成重定向到地址。
b)重定向可以跳转到当前web应用,或其他web应用,甚至是外部域名网站。
c)不能再重定向的过程,把数据保存到request中。 - 结论
如果要使用request域对象进行数据共享,只能用转发技术!!!