HTTP协议和Servlet编程

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域对象进行数据共享,只能用转发技术!!!

发布了68 篇原创文章 · 获赞 98 · 访问量 101万+

猜你喜欢

转载自blog.csdn.net/qq_32599479/article/details/90582198