Http和Servlet(request和response)

1.http

客户端和服务器端之间通信的规则。

 

版本

1.0 一次通信结束以后,就会断开连接

1.1 请求数据,一次通信结束后还会保持连接,除非关闭服务器端或者客户端。当然连接也有时限,如果一直空着,也会关闭。

 

http请求数据内容

请求行,请求头,请求体

* 请求行

        POST /examples/servlets/servlet/RequestParamExample HTTP/1.1 

        POST : 请求方式 ,以post去提交数据
            
        /examples/servlets/servlet/RequestParamExample
        请求的地址路径 , 就是要访问哪个地方。
    
        HTTP/1.1 协议版本

* 请求头

        Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, */*
        Referer: http://localhost:8080/examples/servlets/servlet/RequestParamExample
        Accept-Language: zh-CN
        User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)
        Content-Type: application/x-www-form-urlencoded
        Accept-Encoding: gzip, deflate
        Host: localhost:8080
        Content-Length: 31
        Connection: Keep-Alive
        Cache-Control: no-cache

        Accept: 客户端向服务器端表示,我能支持什么类型的数据。 
        Referer : 真正请求的地址路径,全路径
        Accept-Language: 支持语言格式
        User-Agent: 用户代理 向服务器表明,当前来访的客户端信息。 
        Content-Type: 提交的数据类型。经过urlencoding编码的form表单的数据
        Accept-Encoding: gzip, deflate : 压缩算法 。 
        Host : 主机地址
        Content-Length: 数据长度
        Connection : Keep-Alive 保持连接
        Cache-Control : 对缓存的操作

* 请求体

>浏览器真正发送给服务器的数据 
    
        发送的数据呈现的是key=value ,如果存在多个数据,那么使用 &

        firstname=zhang&lastname=sansan
 

http响应数据内容

请求的数据里面包含三个部分内容 : 响应行 、 响应头 、响应体

    HTTP/1.1 200 OK
    Server: Apache-Coyote/1.1
    Content-Type: text/html;charset=ISO-8859-1
    Content-Length: 673
    Date: Fri, 17 Feb 2017 02:53:02 GMT

    ...这里还有很多数据...

* 响应行
    
        HTTP/1.1 200 OK

        协议版本  

        状态码 
        
            200 : 成功,正常处理,得到数据。
    
            403  : for bidden  拒绝
            404 : Not Found
            500 : 服务器异常

        OK

            对应前面的状态码  

* 响应头

        Server:  服务器是哪一种类型。  Tomcat
    
        Content-Type : 服务器返回给客户端你的内容类型

        Content-Length : 返回的数据长度

        Date : 通讯的日期,响应的时间        

 

2.两种请求方式的区别

get

     1.地址栏后面拼接数据,安全隐患。一般从服务器获取数据使用get方式。  

     2.能歇等待的数据太小,只有1kb。

post

     1.以流的方式发送数据,一般向服务器发送数据使用该方式。

     2.携带的数据大小没有上限。还有Content-Length来表明数据的长度。

 

3.Web资源的分类

静态资源

html,js,css

动态资源

Servlet,jsp

 

4.Servlet

Servlet就是一个运行在java上面的小程序,用于接受和响应客户端的http请求。

更多的时候是配合动态资源来做这件事。静态资源也会用到,用的是Tomcat里面已经定义好的的defaultServlet。

执行过程:

 

5.Servlet通用写法

         Servlet (接口)
            |
            |
        GenericServlet
            |
            |
        HttpServlet (用于处理http的请求)

继承httpServlet或者实现Servlet接口。

主要实现的方法时doPost和doGet两种方法,一个处理get方式的请求,一个处理post方式的请求。

Service方法也能实现功能的原因是,他会判断发送过来的是什么方式,然后调用对应的doGet和doPost方法,完成对数据的处理。

 

6.Servlet的生命周期

* init方法

        在创建该servlet的实例时,就执行该方法。
        一个servlet只会初始化一次, init方法只会执行一次
        默认情况下是 : 初次访问该servlet,才会创建实例。 

* service方法

        只要客户端来了一个请求,那么就执行这个方法了。
          该方法可以被执行很多次。 一次请求,对应一次service方法的调用

* destroy方法

        
          servlet销毁的时候,就会执行该方法
        
              1. 该项目从tomcat的里面移除。
              2. 正常关闭tomcat就会执行 shutdown.bat
         

> doGet 和 doPost不算生命周期方法,所谓的生命周期方法是指,从对象的创建到销毁一定会执行的方法, 但是这两个方法,不一定会执行。

让Servlet创建实例的时机 提前。

1. 默认情况下,只有在初次访问servlet的时候,才会执行init方法。 有的时候,我们可能需要在这个方法里面执行一些初始化工作,甚至是做一些比较耗时的逻辑。 

2. 那么这个时候,初次访问,可能会在init方法中逗留太久的时间。 那么有没有方法可以让这个初始化的时机提前一点。 

3. 在配置的时候, 使用load-on-startup元素来指定, 给定的数字越小,启动的时机就越早。 一般不写负数, 从2开始即可。 

  <servlet>
              <servlet-name>HelloServlet04</servlet-name>
              <servlet-class>com.itheima.servlet.HelloServlet04</servlet-class>
              <load-on-startup>2</load-on-startup>
          </servlet>

 

7.ServletConfig

	//1. 得到servlet配置对象 专门用于在配置servlet的信息
		ServletConfig config = getServletConfig();
		
		//获取到的是配置servlet里面servlet-name 的文本内容
		String servletName = config.getServletName();
		System.out.println("servletName="+servletName);
		
		
		//2、。 可以获取具体的某一个参数。 
		String address = config.getInitParameter("address");
		System.out.println("address="+address);


		//3.获取所有的参数名称
		Enumeration<String> names = config.getInitParameterNames();
		//遍历取出所有的参数名称
		while (names.hasMoreElements()) {
			String key = (String) names.nextElement();
			String value = config.getInitParameter(key);
			System.out.println("key==="+key + "   value="+value);
			
		}

 

作用

 刚好这个servlet 里面需要一个数字或者叫做变量值。 但是这个值不能是固定了。 所以要求使用到这个servlet的公司,在注册servlet的时候,必须要在web.xml里面,声明init-params

 

8.Servlet的配置方式

Servlet配置方式

       * 1. 全路径匹配

              > 以 / 开始   /a /aa/bb

              > localhost:8080/项目名称/aa/bb 

       * 2. 路径匹配 , 前半段匹配

              > 以  / 开始 , 但是以 * 结束  /a/* /*  

              > * 其实是一个通配符,匹配任意文字

              > localhost:8080/项目名称/aa/bb 

       * 3. 以扩展名匹配

              > 写法: 没有/  以 * 开始   *.扩展名    *.aa *.bb 

前半段匹配防止输入小错误,拓展名匹配可以权限管理

 

9.ServletContext

每个Web工程都只有一个ServletContext,不管在项目的哪个Servlet下面,都可以获取到。

有什么作用?

1. 获取全局配置参数

web.xml中
 <context-param>
    <param-name>address</param-name>
    <param-value>三和</param-value>
  </context-param>
servlet中:
ServletContext context = getServletContext();
		String value = context.getInitParameter("address");


2. 获取web工程中的资源

共有如下三种方式:

private void demo3() throws IOException {
		ServletContext context = getServletContext();
		InputStream is = this.getClass().getClassLoader().getResourceAsStream("../../file/config.properties");
		Properties p = new Properties();
		p.load(is);
		String value = p.getProperty("address");
		System.out.println(value);
	}

	private void method2() throws IOException {
		ServletContext context = getServletContext();
		InputStream is = context.getResourceAsStream("/file/config.properties");
		Properties p = new Properties();
		p.load(is);
		String value = p.getProperty("address");
		System.out.println(value);
	}

	private void method1() throws FileNotFoundException, IOException {
		ServletContext context = getServletContext();
		String realPath = context.getRealPath("/file/config.properties");
//		System.out.println(realPath);
		Properties p = new Properties();
		FileInputStream is = new FileInputStream(realPath);
		p.load(is);
		String value = p.getProperty("address");
		System.out.println(value);
	}

第一种是直接调用getRealpath方法获取全路径,第二种和第三种的文件路径分析如下图:


3. 存取数据,servlet间共享数据  域对象

 

案例代码:用ServletContext记录登记的次数

Object attribute = getServletContext().getAttribute("count");
			int totalcount = 0;
			if(attribute!=null){
				totalcount = (Integer) attribute;
			}
			System.out.println("已经登陆的次数为"+totalcount);
			getServletContext().setAttribute("count", totalcount+1);
在另一个Servlet中
int count = (Integer) getServletContext().getAttribute("count");
		response.getWriter().write("登陆了"+count+"次");

ServletContext 何时创建, 何时销毁?

服务器启动的时候,会为托管的每一个web应用程序,创建一个ServletContext对象

从服务器移除托管,或者是关闭服务器。 

* ServletContext 的作用范围

> 只要在这个项目里面,都可以取。 只要同一个项目。 A项目 存, 在B项目取,是取不到的? ServletContext对象不同。

 

10.HttpServletRequest

这个对象封装了一切从客户端提交过来的数据。

//		获取请求头的信息
//		Enumeration<String> headerNames = request.getHeaderNames();
//		while (headerNames.hasMoreElements()) {
//			String name = (String) headerNames.nextElement();
//			String value = request.getHeader(name);
//			System.out.println(name+":"+value);
//		}
		
//		获取传入的信息
//		String name = request.getParameter("name");
//		String address = request.getParameter("address");
//		System.out.println("name="+name+",address="+address);
		
		
		Enumeration<String> parameterNames = request.getParameterNames();
		while (parameterNames.hasMoreElements()) {
			String name = (String) parameterNames.nextElement();
			System.out.println(request.getParameter(name));
		}
		
		Map<String, String[]> parameterMap = request.getParameterMap();
		Set<String> keySet = parameterMap.keySet();
		Iterator<String> iterator = keySet.iterator();
		while (iterator.hasNext()) {
			String key = (String) iterator.next();
			String value = parameterMap.get(key)[0];
            String value2 = parameterMap.get(key)[1];
            String value3= parameterMap.get(key)[2];
			System.out.println(key+"=="+value+value2+value3);
		}

map的value不确定有几个,所以是数组形式。

 

在获取中文数据的时候,可能出现乱码 ,针对两种不同的方式,解决方案也不一样

get方式

1.转码显示

String username = request.getParameter("username");
		String password = request.getParameter("password");
		System.out.println("username="+username+"password="+password);
		String username1 = new String(username.getBytes("ISO-8859-1"),"utf-8");
		System.out.println("username="+username+"password="+password);

get因为数据跟在url后面,已经完成了对应的转码工作,所以只能在显示的时候将其转化为指定编码的String才可以正确显示。

2.在Tomcat里面修改配置文件

在server.xml 加上URIEncoding="utf-8"
 
      <Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8"/>

Post方式

         在接收数据最开始设置解码的格式: request.setCharacterEncoding("UTF-8");

        这个说的是设置请求体里面的文字编码。  get方式,用这行,有用吗? ---> 没用

         这行设置一定要写在getParameter之前。

 

11.HttpServletResponse

和request的作用相反,负责返回数据给客户端。

	response.setHeader("Location", "login_success.html");
		response.setStatus(202);

可以设置响应头等信息。

当返回数据的时候,可能也会出现中文乱码的情况,解决方法:

response.setContentType("text/html;charset=UTF-8");

按照不同的方式,具体的细节不同,了解

以字符流输出: response.getWriter()

		//1. 指定输出到客户端的时候,这些文字使用UTF-8编码
		response.setCharacterEncoding("UTF-8");
		
		//2. 直接规定浏览器看这份数据的时候,使用什么编码来看。
		response.setHeader("Content-Type", "text/html; charset=UTF-8");
		
		response.getWriter().write("我爱黑马训练营...");


* 以字节流输出 : response.getOutputStream()

	//1. 指定浏览器看这份数据使用的码表
		response.setHeader("Content-Type", "text/html;charset=UTF-8");
		
		//2. 指定输出的中文用的码表
		response.getOutputStream().write("我爱深圳黑马训练营..".getBytes("UTF-8"));

默认是utf-8,所以一般不用写。

 

12.点击下载资源手动编码的Web实现

不使用任何代码,超链接也可以完成下载

<h3>文件查看</h3>
	<a href="file/family.jpg">啥家庭啊.jpg</a>
	<br>
	<a href="file/csdn.png">csdn.png</a>
	<br>

原因是Tomcat里面有默认的DefaultServlet专门用来处理这些静态资源

手动编码实现下载:

<h3>文件下载</h3>
	<a href="DownloadServlet?filename=family.jpg">啥家庭啊.jpg</a>
	<br>
	<a href="DownloadServlet?filename=csdn.png">csdn.png</a>
	<br>

在静态资源中,超链接到Servlet的同时,携带参数:filename

Servlet中的代码实现:

protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 通过request获取到携带的参数
		String filename = request.getParameter("filename");
		// 资源存放位置+参数拼接路径名,并通过context获取到资源文件存放的真实路径
		String path = getServletContext().getRealPath("/file/" + filename);
		// 设置弹框提示下载请求
		response.setHeader("Content-Disposition", "attachment; filename=" + filename);
		// 标准的输入输出流拷贝文件。从原文件输入,使用response作为输出
		FileInputStream fis = new FileInputStream(path);
		OutputStream os = response.getOutputStream();
		int len = 0;
		byte[] arr = new byte[1024 * 8];
		while ((len = fis.read(arr)) != -1) {
			os.write(arr, 0, len);
		}
		fis.close();
		os.close();
	}

如果要对中文名称的文件进行下载

// 因为是使用get方式,所以如果是中文,转码
		filename = new String(filename.getBytes("ISO-8859-1"), "UTF-8");
		// 资源存放位置+参数拼接路径名,并通过context获取到资源文件存放的真实路径
		String path = getServletContext().getRealPath("/file/" + filename);
		// 判断浏览器的类型,给出对应的解决方式
		String clientType = request.getHeader("User-Agent");
		if (clientType.contains("Firefox")) {
			filename = FireFoxEncoding.base64EncodeFileName(filename);
		} else {
			filename = URLEncoder.encode(filename, "UTF-8");
		}
		// 设置弹框提示下载请求
		response.setHeader("Content-Disposition", "attachment; filename=" + filename);

工具类对火狐浏览器请求处理如下

public static String base64EncodeFileName(String fileName) {
		BASE64Encoder base64Encoder = new BASE64Encoder();
		try {
			return "=?UTF-8?B?"
					+ new String(base64Encoder.encode(fileName
							.getBytes("UTF-8"))) + "?=";
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

上半部分针对请求进行转码,确保可以找到资源。下半部分确保下载栏显示的文件名称正确

13.请求转发和重定向

   重定向

            /*
            之前的写法
            response.setStatus(302);
            response.setHeader("Location", "login_success.html");*/
            
            //重定向写法: 重新定位方向 参数即跳转的位置
        response.sendRedirect("login_success.html");

        1. 地址上显示的是最后的那个资源的路径地址

        2. 请求次数最少有两次, 服务器在第一次请求后,会返回302 以及一个地址, 浏览器在根据这个地址,执行第二次访问。

        3. 可以跳转到任意路径。 不是自己的工程也可以跳。

        4. 效率稍微低一点, 执行两次请求。 

        5. 后续的请求,没法使用上一次的request存储的数据,或者 没法使用上一次的request对象,因为这是两次不同的请求。

    请求转发

        //请求转发的写法: 参数即跳转的位置
        request.getRequestDispatcher("login_success.html").forward(request, response);

        1. 地址上显示的是请求servlet的地址。  返回200 ok

        2. 请求次数只有一次, 因为是服务器内部帮客户端执行了后续的工作。 

        3. 只能跳转自己项目的资源路径 。  

        4. 效率上稍微高一点,因为只执行一次请求。 

        5. 可以使用上一次的request对象。 

最根本的区别,一个request调用,一个response调用。一个处理前转页面,一个处理后转页面。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值