Response基础
HttpServletResponse对象
-
是一个给客户端做出相应的对象。客户端每请求一次,就会创建一个HttpServletResponse对象(响应)。
-
HttpServletResponse接口信息
- 接口名:HttpServletResponse(Response是简写)
- 包名:javax.servlet.http
- 父接口:ServletResponse
- 已知实现类:HttpServletResponseWrapper
- 功能(作用)
- 设置响应头信息
- 设置响应状态码
- 实现重定向
- 设置响应正文
- 功能详解
-
设置响应头信息
方法:
setHeader(String name, String value);
name:value,这个值是固定的。 发送个客户端浏览器,浏览器能够识别头信息。 -
设置响应状态码
方法:
setStatus();
2* OK:200
3* 重定向相关:304 重定向到本地的缓存 302 重定向到其他的资源
4* 客户端问题:404 找不到资源 405 没有相应的处理方法
5* 服务器端错误:500 服务器端异常
详情见《关于Http状态码》 -
实现重定向
发送一个302的状态码,并且发送一个浏览器重新请求的地址
重定向的方式:
1. 使用方法:
response.setStatus(302);
response.setHeader("Location","URL");
2. 重定向的便捷方法:
response.sendRedirect("URL");
重定向的特点:
1. 两次请求,两次响应。
2. 地址栏会发生变化。
3. 重定向能够访问到除了当前web以外的其他的资源。 -
设置响应正文
响应的内容:
- 可以是普通的字符。
- 设置的内容可以是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一共提供了两个响应流对象:
PrintWriter out = response.getWriter()
:获取字符流;ServletOutputStream out = response.getOutputStream()
:获取字节流
当然,如果响应正文内容为字符,那么使用
response.getWriter()
,如果响应内容是字节,例如下载时,那么可以使用response.getOutputStream()
注意,在一个请求中,不能同时使用这两个流!也就是说,要么你使用repsonse.getWriter()
,要么使用response.getOutputStream()
,但不能同时使用这两个流。不然会抛出IllegalStateException异常。 -
字符响应流
-
字符编码
在使用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头来解读响应数据。 -
缓冲区
response.getWriter()
是PrintWriter类型,所以它有缓冲区,缓冲区的默认大小为8KB。也就是说,在响应数据没有输出8KB之前,数据都是存放在缓冲区中,而不会立刻发送到客户端。当Servlet执行结束后,服务器才会去刷新流,使缓冲区中的数据发送到客户端。 如果希望响应数据马上发送给客户端:- 向流中写入大于8KB的数据
- 调用
response.flushBuffer()
方法来手动刷新缓冲区
- 向流中写入大于8KB的数据
-
设置响应头信息
可以使用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
重定向总结
- 重定向是两次请求
- 重定向的URL可以是其他应用,不局限于当前应用
- 重定向的响应头为302,并且必须要有Location响应头
- 重定向就不要再使用
response.getWriter()
或response.getOutputStream()
输出数据,不然可能会出现异常
request基础
request概述
request是Servlet.service()
方法的一个参数,类型为javax.servlet.http.HttpServletRequest
。在客户端发出每个请求时,服务器都会创建一个request对象,并把请求数据封装到request中,然后在调用Servlet.service()
方法时传递给service()方法,这说明在service()方法中可以通过request对象来获取请求数据。
HttpServletRequest接口信息
包:javax.servlet.http
父接口:ServletRequest
已知实现类:HttpServletRequestWrapper
request对象的功能
封装了请求头数据
封装了请求正文数据,如果是GET请求,那么就没有正文
request是一个域对象,可以把它当成Map来添加获取数据
request提供了请求转发和请求包含功能
获取请求头数据
request与请求头相关的方法有:
String getHeader(String name)
:获取指定名称的请求头Enumeration getHeaderNames()
:获取所有请求头名称int getIntHeader(String name)
:获取值为int类型的请求头
request获取请求相关的其它方法
返回值类型 | 方法名 | 参数类型 | 参数值 | 描述 |
---|---|---|---|---|
int | getContentLength | void | 获取请求体的字节数,GET请求没有请求体,没有请求体返回-1 | |
String | getContentType | void | 获取请求类型,如果请求是GET,那么这个方法返回null;如果是POST请求,那么默认为application/x-www-form-urlencoded,表示请求体内容使用了URL编码 | |
String | getMethod | void | 返回请求方法,例如:GET | |
java.util.Locale | getLocale | void | 返回当前客户端浏览器的Locale。java.util.Locale表示国家和言语,这个东西在国际化中很有用(此方法是父接口方法) | |
String | getCharacterEncoding | void | 获取请求编码,如果没有setCharacterEncoding(),那么返回null,表示使用ISO-8859-1编码 | |
void | setCharacterEncoding | String | code | 设置请求编码,只对请求体有效。注意,对于GET而言,没有请求体。所以此方法只能对POST请求中的参数有效。 |
String | getContextPath | void | 返回上下文路径,获取的是项目名称。例如:/hello | |
String | getQueryString | void | 返回请求URL中的参数,例如:name=zhangSan | |
String | getRequestURI | void | 返回请求URI路径,例如:/hello/oneServlet | |
java.lang.StringBuffer | getRequestURL | void | 返回请求URL路径,例如:http://localhost/hello/oneServlet,即返回除了参数以外的路径信息 | |
String | getServletPath | void | 返回Servlet路径,例如:/oneServlet | |
String | getRemoteAddr | void | 返回当前客户端的IP地址 | |
String | getRemoteHost | void | 返回当前客户端的主机名,但这个方法的实现还是获取IP地址 | |
String | getScheme | void | 返回请求协议,例如:http | |
String | getServerName | void | 返回主机名,例如:localhost | |
int | getServerPort | void | 返回服务器端口号,例如:8080 |
request获取从客户端传递过来的请求参数
最为常见的客户端传递参数方式有两种:
- 浏览器地址栏直接输入:一定是GET请求
- 超链接:一定是GET请求
- 表单:可以是GET,也可以是POST,这取决与
<form>
的method属性值
GET请求和POST请求的区别:
GET请求:
- 请求参数会在浏览器的地址栏中显示,所以不安全
- 请求参数长度限制长度在1K之内
- GET请求没有请求体,无法通过request.setCharacterEncoding()来设置参数的编码
POST请求:
- 请求参数不会显示浏览器的地址栏,相对安全
- 请求参数长度没有限制
- 获得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
在浏览器中可以查看到想服务端传递的参数
并在控制台可以看到打印信息
-
获得GET请求参数-form表单
编写html表单
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Test</title>
</head>
<body>
<form action="/Tech/Test" method="get">
user: <input type="text" name="name" /><br/>
password: <input type="password" name="pass"><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
访问http://localhost:8080/Tech/FormTest.html
并输入用户和密码
提交后查看浏览器地址栏和控制台
可以看到用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: <input type="text" name="name" /><br/>
password: <input type="password" name="pass"><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
可以看到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));
}
}
}
浏览器地址栏随便输入多个参数,且其中有同名但值不同的参数 服务端控制台可以看到打印信息
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共同完成。 (留头又留体)