简介:
->Request对象和Response对象起到了服务器与客户端之间的信息传递作用。
->Request对象用于接收客户端浏览器提交的数据
->Response对象则将服务器端的数据发送到客户端。
Response对象用来响应客户端请求,控制发送给用户的信息。
Response的两个接口
ServletResponse–>与http协议无关
HttpServletResponse–>与http协议相关
客户端和服务器通信主要是依赖于http协议,所以我们主要用HttpServletResponse接口。
-void sendError(int sc); //发送错误状态码
-void sendError(int sc,String msg); //发送错误状态码和说明信息
-void setStatus(int sc); //发送正确状态码
例子:
public class Test1 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.sendError(900, "错误,不能访问");
}
}
响应头:Context-Type,Refresh,Location等等
响应头就是一个键值对。可能一个键对应一个值,也有可能一个键对应多个值。
*setHeader(String name,String value);适用于单值的响应头
例如:response.setHeader("a","A");
*addHeader(String name,String value);适用于多值的响应头
例如:response.addHeader("a","A");
response.addHeader("a","B");
response.addHeader("a","C");
*setIntHeader(String name,int value);适用于单值的int类型的响应头
例如:response.setIntHeader("Content-length",500);
*addIntHeader(String name,int value);适用于多值的int类型的响应头
*setDateHeader(String name,long value);适用于单值的毫秒类型的响应头
例如:response.setDateHeader("expires",1000*60*60*24);
expires响应头代表页面缓存过期时间,例如访问百度,这里设置的是24小时,
这时候如果断网也能访问百度主页,24小时之后就不行了。
expires的值通常设置为-1,来防止缓存。
*addDateHeader(String name,long value);适用于多值的毫秒类型的响应头
流程
客户端向AServlet发送请求,AServlet响应返回 302 和Location响应头
然后客户端再向Location指定的地址发送请求,客户端发送两次请求。
案例:重定向
AServlet
public class AServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("AServlet");
response.setStatus(302);
response.setHeader("Location", "/Test/BServlet");//第一个参数为Location,第二个为/项目名/Servlet路径,因为是浏览器请求的,也叫URI
}
}
BServlet
public class BServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("BServlet");
}
}
输出为
AServlet
BServlet
设置Refresh响应头实现定时刷新
response.setHeader("Refresh", "5;URL=/Test/BServlet");//第一个参数为Refresh,第二个参数为 “秒数;URL=跳转的地址”
设置Cache-Control,pragma,expires这三个响应头可以禁用浏览器缓存
三个响应头加一起适用于所有http协议版本
response.setHeader("Cache-Control","no-cache");
response.setHeader("pragma","no-cache");
response.setDateHeader("expires",-1);
<meta>标签可以代替响应头
例如:<meta http-equiv="Content-Type" Content="text/html;charset=UTF-8">
响应体:通常是html代码,也可能是其他。
java提供了两个流可以发送响应体。
-ServletOutputStream 用来向客户端发送字节数据
获取方法:ServletOutputStream out=response.getOutputStream();
-PrintWriter 用来向客户端发送字符数据,需要设置编码
获取方法:PrintWriter pw=response.getWriter();
两个流不能同时使用,如果同时使用会抛出IllegalStateException(非法状态异常)
案例:发送字符
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter pw=response.getWriter();
pw.print("hello world");
}
案例:发送图片
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String path="e:\\http请求格式.png"; //要发送的图片
FileInputStream fis=new FileInputStream(path);//文件输入流
ServletOutputStream out=response.getOutputStream();//获取输出流
byte []b=new byte[5*1024]; //定义字节数组
while(fis.read(b)!=-1)
{
out.write(b); //将字节发送到客户端
}
}
当服务器进行重定义的时候:都是设置302状态码,设置Location值,变化的只有location
所以java提供了一个简单的方法完成重定向操作。
SendRedirect(String Location)方法
例如:response.sendRedirect("/Test/BServlet"); 里面的参数还是URI地址(即/项目名/Servlet路径)
封装了客户端发送的所有请求数据。
Http协议中发送的数据都可以通过request对象来获取。
-获取客户端IP request.getRemoteAddr()方法
-获取请求方式 request.getMethod() 可能是Get或者Post
-获取请求头
*String getHeader(String name):适用于单值请求头
*int getIntHeader(String name):适用于单值int类型的请求头
*long getDateHeader(String name):适用于单值毫秒类型的请求头
*Enumeration < String > getHeaders(String name),适用于多值的请求头
案例:获取客户端的ip,请求方式和客户端浏览器信息和系统信息
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String ip=request.getRemoteAddr(); //获取ip
String method=request.getMethod(); //获取请求方式
String user=request.getHeader("user-agent");//user-agent这个请求头包含了客户端的操作系统,浏览器等一些相关信息
System.out.println("ip地址为"+ip);
System.out.println("请求方式为"+method);
System.out.println(user);
}
-获取请求URL的相关方法
*String getScheme():获取协议
*String getServerName():获取服务器名
*String getServerPort():获取服务器端口
*String getContextPath():获取项目名
*String getServletPath():获取Servlet路径
*String getQueryString():获取参数部分,即问号后面的部分
*String getRequestURI():获取URI,即/项目名/Servlet路径
*String getRequestURL():获取请求URL,不包括参数部分
案例:根据给出的url地址,写出几个函数的结果
http://localhost:8080/Test/FServlet?username=aaa&password=123
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println(request.getScheme()); //http
System.out.println(request.getServerName()); //localhost
System.out.println(request.getContextPath()); // /Test
System.out.println(request.getServletPath()); // /FServlet
System.out.println(request.getQueryString()); // username=aaa&password=123
System.out.println(request.getRequestURI()); // /Test/FServlet
System.out.println(request.getRequestURL()); // http://localhost:8080/Test/FServlet
}
案例:防盗链
盗链:例如大网站的视频音乐,我们只需要把链接显示在自己网站的页面上,就会赚取大量流量
而这些音乐视频并没在自己的服务器上,就形成了盗链。
我们可以通过Referer请求头来实现防盗链。
Referer请求头:告诉服务器访客是从哪个页面过来的。(直接在地址栏输入地址的话,Referer则为空)
我们可以判断Referer是否来自于本网站,不是的话就不允许访问我的资源。这就是防盗链。
request.getHeader("Referer"),然后进行判断就可以。
-request获取请求参数
请求参数是由客户端发送给服务器的。
*String getParameter(String name):获取指定名称的请求参数值,适用于单值请求参数
*String[] getParameterValues(String name):获取指定名称的请求参数值,适用于多值请求参数
*Enumeration<String> getParameterNames():获取所有请求参数名称
*Map<String,String[]> getParameterMap():获取所有请求参数,key为参数名,value为参数值
例子:
这个HTML页面有两种请求方式,一种get,一个表单post提交
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>请求参数测试</h1>
<a href="/Test/HServlet?name=zhangsan&passwd=123456">点击跳转</a>
<hr>
<form action="/Test/HServlet" method="post">
用户名:<input type="text" name="username"></br>
密 码:<input typr="password" name="password"></br>
爱 好:<input type="checkbox" name="hobby" value="basketball">篮球
<input type="checkbox" name="hobby" value="soccor">足球
<input type="checkbox" name="hobby" value="pingpang">乒乓球
<input type="submit" value="提交">
</form>
</body>
</html>
Servlet中获取请求参数
public class HServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name=request.getParameter("name"); //获取name的值,zhangsan
String passwd=request.getParameter("passwd");//获取passwd的值,123456
System.out.println(name+":"+passwd);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username=request.getParameter("username");
String password=request.getParameter("password");
String []hobby=request.getParameterValues("hobby");
System.out.println(username+","+password+","+Arrays.toString(hobby));
Enumeration<String> names=request.getParameterNames();//获取所有请求参数名称
while(names.hasMoreElements())
{
System.out.println(names.nextElement());
}
Map<String,String[]> map=request.getParameterMap();//获取所有请求参数,key为参数名,value为参数值
for(String name:map.keySet())
{
String[]value=map.get(name);
System.out.println(name+","+Arrays.toString(value));
}
}
}
Web应用在响应客户端的请求时,有可能响应过程比较复杂,需要多个web组件相同协作,才能产生响应结果。
但一个Servlet对象无法直接调用另一个Servlet对象的service方法,但是Servlet规范为web组件之间的协作提供了两种方式。
-请求转发
-请求包含
***请求转发:希望将请求交给另外的web组件处理,所以只有最后真正要执行的web组件才能输出数据。
请求转发时,如果已经有数据写入到response的缓冲区,则请求转发时,这些数据会被清空,但是请求头不会被清空,只有最后执行请求的web组件可以输出请求体。(请求转发留头不留体)
***请求包含:把其他web组件生成的响应结果包含到自身的响应结果中。(留头又留体)
>请求转发和请求包含都只有一次请求。
>请求转发和请求包含都依赖于RequestDispatcher接口
RequestDispatcher rd=request.getRequestDispatcher("/被包含或者被转发的Servlet路径")
rd.forward(request,response):请求转发方法
(1)清空用于存放响应正文数据的缓冲区;
(2)如果目标组件为Servlet或JSP,就调用它们的service()方法,把该方法产生的响应结果发送到客户端,如果目标组件为文件系统中的静态html文档,就读去文档中的数据并把它发送到客户端。
(3)由于forward()方法先清空用于存放响应正文数据的缓冲区,因此servlet源组件生成的响应结果不会被发送到客户端,只有目标组件生成的结果才会被发送到客户端;
(4)如果源组件在进行请求转发之前,已经提交了响应结果(例如调用了flushBuffer方法,或者close()方法),那么forward()方法会抛出IllegalStateException。为了避免该异常,不应该在源组件中提交响应结果。
rd.include(request,response):请求包含方法
*包含与转发相比,源组件与被包含的目标组件的输出数据都会被添加到响应结果中
*在目标组件中对响应状态代码或者响应头所做的修改都会被忽略
请求转发案例
public class OneServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("OneServlet");
response.setHeader("name", "zhangsan"); //设置响应头
response.getWriter().print("Hello OneServlet"); //设置响应体
RequestDispatcher rd=request.getRequestDispatcher("/TwoServlet");
rd.forward(request, response); //请求转发
}
}
public class TwoServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("TwoServlet");
response.getWriter().print("Hello TwoServlet"); //设置响应体
}
}
结果为屏幕上显示Hello TwoServlet,说明OneServlet请求体被清空
请求包含案例
public class OneServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("OneServlet");
response.setHeader("name", "zhangsan"); //设置响应头
response.getWriter().print("Hello OneServlet"); //设置响应体
RequestDispatcher rd=request.getRequestDispatcher("/TwoServlet");
rd.include(request, response); //请求包含
}
}
public class TwoServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("TwoServlet");
response.getWriter().print("Hello TwoServlet"); //设置响应体
}
}
结果为屏幕上显示Hello OneServletHello TwoServlet(留头又留体)
生命周期:在service 方法调用前由服务器创建,传入service方法。整个请求结束,request生命结束。
范围:整个请求链(请求转发和请求包含也存在)
作用:在整个请求链中共享数据。
方法:void setAttribute(String name,Object value):设置参数
Object getAttribute(String name):获取值
void removeAttribute(String name):移除参数
案例:
public class OneServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setAttribute("name", "zhangsan"); //设置参数
RequestDispatcher rd=request.getRequestDispatcher("/TwoServlet");
rd.forward(request, response); //请求转发
}
}
public class TwoServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println(request.getAttribute("name"));//在TwoServlet中获取参数,输出zhangsan
}
}
常用字符编码:iso-8859-1(不支持中文),gbk(系统默认编码,支持中文),utf-8(万国码,支持全世界编码)
乱码原因:
Tomcat服务器默认编码为iso-8859-1,而浏览器一般的默认编码是gbk。
服务器发送数据用iso编码,浏览器接到数据,用gbk解码,就出现了乱码问题
响应编码
我们可以用response.setCharacterEncoding("utf-8")设置响应编码,即服务器
通过流发出去的字符都使用utf-8编码。
但是浏览器还是使用的gbk解码,所以还是出现乱码
这时我们可以设置Content-Type响应头告诉浏览器我们的服务器使用的什么编码
response.setHeader(“Content-Type”,“text/html;charset=utf-8”);
但是设置响应头也同时相当于设置了setCharacterEncoding()方法,即一句代码就可以。
response设置Content-Type头还有一个简便方法response.setContentType("text/html;charset=utf-8");
请求编码
浏览器发送请求时,点击链接或者表单,发送的数据都是utf-8编码。
但Servlet默认使用iso8859-1来解码,这时就会出现乱码。
两种请求的处理方式是不同的。
POST请求:
只需要在获取参数等方法前调用request.setCharacterEncoding("utf-8")设置编码即可。
GET请求
不允许设置编码。
我们需要将经过iso8859-1编码的字符给解码成字节。
然后将字节再按照utf-8进行编码。
例子:
public class EncodeTest extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name=request.getParameter("name"); //先获取参数
byte[] b=name.getBytes("iso-8859-1"); //将获取的数据反编为字节
name=new String(b,"utf-8"); //根据utf-8进行编码
System.out.println(name);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");//在获取参数前设置编码
String name=request.getParameter("username");
System.out.println(name);
}
}
url编码是一种浏览器用来打包表单输入的格式
URL编码格式 :需要先将字符解码,字符解码后,将对应的字节转换为16进制,前面加上%即可。
胡”的ascii码是-17670,十六进制是BAFA,url编码是“%BA%FA”。