Response基础
HttpServletResponse对象

  • 是一个给客户端做出相应的对象。客户端每请求一次,就会创建一个HttpServletResponse对象(响应)。

  • HttpServletResponse接口信息

  1. 接口名:HttpServletResponse(Response是简写)
  2. 包名:javax.servlet.http
  3. 父接口:ServletResponse
  4. 已知实现类:HttpServletResponseWrapper

  • 功能(作用)
  1. 设置响应头信息
  2. 设置响应状态码
  3. 实现重定向
  4. 设置响应正文

  • 功能详解
  1. 设置响应头信息

    方法:setHeader(String name, String value); name:value,这个值是固定的。 发送个客户端浏览器,浏览器能够识别头信息。

  2. 设置响应状态码

    方法:setStatus();
    2*  OK:200
    3*  重定向相关:304 重定向到本地的缓存 302 重定向到其他的资源
    4*  客户端问题:404 找不到资源 405 没有相应的处理方法
    5*  服务器端错误:500 服务器端异常
    详情见《关于Http状态码》

  3. 实现重定向

    发送一个302的状态码,并且发送一个浏览器重新请求的地址

    重定向的方式:
    1. 使用方法:
    response.setStatus(302);
    response.setHeader("Location","URL");
    2. 重定向的便捷方法:
    response.sendRedirect("URL");

    重定向的特点:
    1. 两次请求,两次响应。
    2. 地址栏会发生变化。
    3. 重定向能够访问到除了当前web以外的其他的资源。

  4. 设置响应正文

    响应的内容:

    1. 可以是普通的字符。
    2. 设置的内容可以是HTML标签,浏览器能够直接解析。

    • 使用字节流:response.getOutputStream();
    • 使用字符流:response.getWriter();
    • 注意:字节流和字符流在使用的时候只能选择其中一个。 否则将抛出异常:IllegalStateException

  • response乱码问题

    造成乱码的原因,在Servlet当中,服务器使用ISO-8859-1对汉字进行了编码。 (不支持中文)
    将编号后的结果发送给了浏览器,浏览器默认情况下,按照gbk进行解码。 所以出现了乱码。
    解决:告知服务器使用UTF-8进行编码:response.setCharacterEcoding("utf-8");
    发送给浏览器一个响应头,告知浏览器使用utf-8解码:response.setHeader("Content-Type","text/html;charset=utf-8"); response.setContentType("text/html;charset=utf-8");



response响应正文

  • response是响应对象,向客户端输出响应正文(响应体)可以使用response的响应流,repsonse一共提供了两个响应流对象:

    1. PrintWriter out = response.getWriter():获取字符流;
    2. ServletOutputStream out = response.getOutputStream():获取字节流

    当然,如果响应正文内容为字符,那么使用response.getWriter(),如果响应内容是字节,例如下载时,那么可以使用response.getOutputStream() 注意,在一个请求中,不能同时使用这两个流!也就是说,要么你使用repsonse.getWriter(),要么使用response.getOutputStream(),但不能同时使用这两个流。不然会抛出IllegalStateException异常。


  • 字符响应流

    1. 字符编码
      在使用response.getWriter()时需要注意默认字符编码为ISO-8859-1,如果希望设置字符流的字符编码为utf-8,可以使用response.setCharaceterEncoding(“utf-8”)来设置。这样可以保证输出给客户端的字符都是使用UTF-8编码的。 但客户端浏览器并不知道响应数据是什么编码的。如果希望通知客户端使用UTF-8来解读响应数据,那么还是使用response.setContentType("text/html;charset=utf-8")方法比较好,因为这个方法不只会调用response.setCharaceterEncoding(“utf-8”),还会设置content-type响应头,客户端浏览器会使用content-type头来解读响应数据。

    2. 缓冲区
      response.getWriter()是PrintWriter类型,所以它有缓冲区,缓冲区的默认大小为8KB。也就是说,在响应数据没有输出8KB之前,数据都是存放在缓冲区中,而不会立刻发送到客户端。当Servlet执行结束后,服务器才会去刷新流,使缓冲区中的数据发送到客户端。 如果希望响应数据马上发送给客户端:

      • 向流中写入大于8KB的数据
      • 调用response.flushBuffer()方法来手动刷新缓冲区

设置响应头信息

可以使用response对象的setHeader()方法来设置响应头。使用该方法设置的响应头最终会发送给客户端浏览器!

  • response.setHeader(“content-type”, “text/html;charset=utf-8”);:设置content-type响应头,该头的作用是告诉浏览器响应内容为html类型,编码为utf-8。而且同时会设置response的字符流编码为utf-8,即response.setCharaceterEncoding(“utf-8”);
  • response.setHeader("Refresh","5;http://blog.fireflyhut.cc");:5秒后自动跳转到萤虫屋主页。



设置状态码及其他方法

  • response.setStatus(200);:设置状态码
  • response.sendError(404, “您要查找的资源不存在”);:当发送错误状态码时,服务器会跳转到固定的错误页面去,但可以显示错误信息

重定向
什么是重定向

当你访问http://www.sun.com/时,你会发现浏览器地址栏中的URL会变成http://www.oracle.com/sun/(不信你试试),这就是重定向了。重定向是服务器通知浏览器去访问另一个地址,即再发出另一个请求。

完成重定向

响应码为200表示响应成功,而响应码为302表示重定向。所以完成重定向的第一步就是设置响应码为302。
因为重定向是通知浏览器再第二个请求,所以浏览器需要知道第二个请求的URL,所以完成重定向的第二步是设置Location头,指定第二个请求的URL地址。

public class TestServlet extends HttpServlet {
	public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
		response.setStatus(302);
		response.setHeader("Location", "http://blog.fireflyhut.cc");
	}
}

上面代码的作用是:当访问TestServlet后,会通知浏览器重定向到萤虫屋主页。客户端浏览器解析到响应码为302后,就知道服务器让它重定向,所以它会马上获取响应头Location,然发出第二个请求。

便捷的重定向方式

public class TestServlet extends HttpServlet {
	public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
		response.sendRedirect("http://blog.fireflyhut.cc");
	}
}

response.sendRedirect()方法会设置响应头为302,以设置Location响应头。 如果要重定向的URL是在同一个服务器内,那么可以使用相对路径,例如:

public class TestServlet extends HttpServlet {
	public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
		response.sendRedirect("/gh/AnotherServlet");
	}
}

重定向的URL地址为:http://localhost:8080/gh/AnotherServlet

重定向总结

  1. 重定向是两次请求
  2. 重定向的URL可以是其他应用,不局限于当前应用
  3. 重定向的响应头为302,并且必须要有Location响应头
  4. 重定向就不要再使用response.getWriter()response.getOutputStream()输出数据,不然可能会出现异常

request基础
request概述

request是Servlet.service()方法的一个参数,类型为javax.servlet.http.HttpServletRequest。在客户端发出每个请求时,服务器都会创建一个request对象,并把请求数据封装到request中,然后在调用Servlet.service()方法时传递给service()方法,这说明在service()方法中可以通过request对象来获取请求数据。

RequestStructure-img

HttpServletRequest接口信息

包:javax.servlet.http
父接口:ServletRequest
已知实现类:HttpServletRequestWrapper

request对象的功能

封装了请求头数据
封装了请求正文数据,如果是GET请求,那么就没有正文
request是一个域对象,可以把它当成Map来添加获取数据
request提供了请求转发和请求包含功能

获取请求头数据

request与请求头相关的方法有:

  • String getHeader(String name):获取指定名称的请求头
  • Enumeration getHeaderNames():获取所有请求头名称
  • int getIntHeader(String name):获取值为int类型的请求头
request获取请求相关的其它方法

返回值类型方法名参数类型参数值描述
intgetContentLengthvoid获取请求体的字节数,GET请求没有请求体,没有请求体返回-1
StringgetContentTypevoid获取请求类型,如果请求是GET,那么这个方法返回null;如果是POST请求,那么默认为application/x-www-form-urlencoded,表示请求体内容使用了URL编码
StringgetMethodvoid返回请求方法,例如:GET
java.util.LocalegetLocalevoid返回当前客户端浏览器的Locale。java.util.Locale表示国家和言语,这个东西在国际化中很有用(此方法是父接口方法)
StringgetCharacterEncodingvoid获取请求编码,如果没有setCharacterEncoding(),那么返回null,表示使用ISO-8859-1编码
voidsetCharacterEncodingStringcode设置请求编码,只对请求体有效。注意,对于GET而言,没有请求体。所以此方法只能对POST请求中的参数有效。
StringgetContextPathvoid返回上下文路径,获取的是项目名称。例如:/hello
StringgetQueryStringvoid返回请求URL中的参数,例如:name=zhangSan
StringgetRequestURIvoid返回请求URI路径,例如:/hello/oneServlet
java.lang.StringBuffergetRequestURLvoid返回请求URL路径,例如:http://localhost/hello/oneServlet,即返回除了参数以外的路径信息
StringgetServletPathvoid返回Servlet路径,例如:/oneServlet
StringgetRemoteAddrvoid返回当前客户端的IP地址
StringgetRemoteHostvoid返回当前客户端的主机名,但这个方法的实现还是获取IP地址
StringgetSchemevoid返回请求协议,例如:http
StringgetServerNamevoid返回主机名,例如:localhost
intgetServerPortvoid返回服务器端口号,例如:8080

RequestURLStructureMethod-img



request获取从客户端传递过来的请求参数

最为常见的客户端传递参数方式有两种:

  1. 浏览器地址栏直接输入:一定是GET请求
  2. 超链接:一定是GET请求
  3. 表单:可以是GET,也可以是POST,这取决与<form>的method属性值

GET请求和POST请求的区别:
GET请求:

  1. 请求参数会在浏览器的地址栏中显示,所以不安全
  2. 请求参数长度限制长度在1K之内
  3. GET请求没有请求体,无法通过request.setCharacterEncoding()来设置参数的编码

POST请求:

  1. 请求参数不会显示浏览器的地址栏,相对安全
  2. 请求参数长度没有限制


  • 获得GET请求参数-浏览器地址栏传参
    编写代码:
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TestHttpServletRequest extends HttpServlet{

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doPost(req,resp);
	}
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html;charset=utf-8");
		
		String name = request.getParameter("name");
		String pass = request.getParameter("pass");	
		System.out.println("get请求的请求参数"+name+":"+pass);
	}
}

启动服务器后访问http://localhost:8080/Test?name=lalala&pass=123456在浏览器中可以查看到想服务端传递的参数 GetTransferParameters-img

并在控制台可以看到打印信息

GetTransferParametersConsole-img



  • 获得GET请求参数-form表单

    编写html表单

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>Test</title>
	</head>
	<body>
		<form action="/Tech/Test" method="get">
		
			user:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type="text" name="name" /><br/>
			password:&nbsp;<input type="password" name="pass"><br/>
			<input type="submit" value="提交">
		
		</form>
	</body>
</html>

访问http://localhost:8080/Tech/FormTest.html并输入用户和密码

GetFormTransferParametersTypingValueInHTMLPage-img

提交后查看浏览器地址栏和控制台

GetFormTransferParametersTypingValueInHTMLPage-res-img

GetFormTransferParametersTypingValueInHTMLPage-res2-img

可以看到用form表单提交并且提交后执行访问TestServlet也可以将参数通过get方法传到服务端

  • 获得post请求参数-form表单

    更改html代码

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>Test</title>
	</head>
	<body>
		<form action="/Tech/Test" method="post"><!--这里把method的值改成post-->
		
			user:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type="text" name="name" /><br/>
			password:&nbsp;<input type="password" name="pass"><br/>
			<input type="submit" value="提交">
		
		</form>
	</body>
</html>

PostFormTransferParametersTypingValueInHTMLPage-res-img

PostFormTransferParametersTypingValueInHTMLPage-res2-img

可以看到post提交并不会在浏览器地址栏显示提交内容,且信息依然也可以传到服务端,由于没有更改服务端打印信息所以打印的还是“get请求的请求参数”。另外,超链接传参是get方法,通过url连接一个或者多个参数,这里不再展示

ps:如果获取不到该名称的参数值,会返回服务端会打印null,且不会报错。如果想要获取多个同名参数的值需要用String values[] getParameterValues(String name)方法来返回一个字符串数组(下面我们将介绍)

将获得的参数封装到Map集合中

  • ServletRequest接口中的方法

java.util.Map<java.lang.String,java.lang.String[]> getParameterMap()

  • 测试代码
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TestHttpServletRequest extends HttpServlet{

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doPost(req,resp);	
	}

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {	
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html;charset=utf-8");
		
		Map<String,String[]> map = request.getParameterMap();
		
		for(String key: map.keySet()) {
			String[] value = map.get(key);
			System.out.println(key+":"+Arrays.toString(value));
		}
	}
}

浏览器地址栏随便输入多个参数,且其中有同名但值不同的参数 getParameterMap-Method-Exp-res-img 服务端控制台可以看到打印信息 getParameterMap-Method-Exp-console-res-img

java.util.Map<java.lang.String,java.lang.String[]> getParameterMap()方法将所有客户端传递的参数全部用Map以键值对的形式接收到了集合中(传参方法可以是get也可以是post,本例为get)

域对象
域方法

  • void setAttribute(String name, Object value):用来存储一个对象,也可以称之为存储一个域属性,例如:servletContext.setAttribute(“xxx”, “XXX”),在request中保存了一个域属性,域属性名称为xxx,域属性的值为XXX。请注意,如果多次调用该方法,并且使用相同的name,那么会覆盖上一次的值,这一特性与Map相同;
  • Object getAttribute(String name):用来获取request中的数据,当前在获取之前需要先去存储才行,例如:String value = (String)request.getAttribute(“xxx”);,获取名为xxx的域属性;
  • void removeAttribute(String name):用来移除request中的域属性,如果参数name指定的域属性不存在,那么本方法什么都不做;
  • Enumeration getAttributeNames():获取所有域属性的名称;



请求转发和请求包含
请求转发

无论是请求转发还是请求包含,都表示由多个Servlet共同来处理一个请求。例如Servlet1来处理请求,然后Servlet1又转发给Servlet2来继续处理这个请求。

  • 请求转发:在AServlet中,把请求转发到BServlet
//AServlet
public class AServlet extends HttpServlet {
	public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
		System.out.println("AServlet");
		RequestDispatcher rd = request.getRequestDispatcher("/BServlet");
		rd.forward(request, response);
	}
}
//BServlet
public class BServlet extends HttpServlet {
	public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
		System.out.println("BServlet");
	}
}



请求包含

在AServlet中,把请求包含到BServlet:

//AServlet
public class AServlet extends HttpServlet {
	public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
		System.out.println("AServlet");
		RequestDispatcher rd = request.getRequestDispatcher("/BServlet");
		rd.include(request, response);
	}
}
//BServlet
public class BServlet extends HttpServlet {
	public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
		System.out.println("BServlet");
	}
}



请求转发与重定向的区别

  • 请求转发是一个请求,而重定向是两个请求
  • 请求转发后浏览器地址栏不会有变化,而重定向会有变化,因为重定向是两个请求
  • 请求转发的目标只能是本应用中的资源,重定向的目标可以是其他应用
  • 请求转发对AServlet和BServlet的请求方法是相同的,即要么都是GET,要么都是POST,因为请求转发是一个请求
  • 重定向的第二个请求一定是GET


请求转发和请求包含的区别

  • 请求转发:

方法: response.getRequestDispather("ServletURL").forword(request,response);负责转发的Servlet设置的请求头生效,设置的请求体不生效(留头不留体)。

  • 请求包含:

方法: response.getRequestDispather("ServletURL").include(request,response);负责转发的Servlet和被包含的Servlet共同完成。 (留头又留体)