请求响应流程图
===================
服务器处理请求的流程:
服务器每次收到请求时,都会为这个请求开辟一个新的线程。
服务器会把客户端的请求数据封装到request对象中,request就是请求数据的载体!
服务器还会创建response对象,这个对象与客户端连接在一起,它可以用来向客户端发送响应。
===================
response:其类型为HttpServletResponse
*状态码:200表示成功、302表示重定向、404表示客户端错误(访问的资源不存在)、500表示服务器错误
>sendError(int sc):发送错误的状态码,例如404、500
>sendError(int sc, String msg):发送错误的状态码+错误信息
>sendStatus(int sc):发送成功的状态码,例如302
404案例:
1 //404案例 2 @Override 3 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 4 resp.sendError(404,"您访问的资源不存在!!!"); 5 }
*响应头:Content-Type、Refresh、Location等等
头就是一个键值对!可能会存在一个头(一个名称,一个值),也可能会存在一个头(一个名称,多个值!)
>(重要)setHeader(String name, String value):适用于单值的响应头,例如:response.setHeader("aa","AAA");
>addHeader(String name, String value):适用于多值的响应头
response.addHeader("aa","A");
response.addHeader("aa","AA");
response.addHeader("aa","AAA");
>setIntHeader(String name, Int value):适用于单值的int类型的响应头
response.setIntHeader("Content-Length",888); 响应的长度
>addIntHeader(String name, int value):适用于多值的int类型的响应头
>setDateHeader(String name, long value):适用于单值的毫秒类型的响应头
response.setDateHeader("expires", 1000*60*60*24); 设置页面过期时间为24小时
>addDateHeader(String name, long value):适用于多值的毫秒类型的响应头
案例:
1)发送302,设置Location头,完成重定向!
BServlet
1 //重定向1.设置Location头(重定向的地址)2.响应302状态码 2 3 @WebServlet("/BServlet") 4 public class BServlet extends HttpServlet { 5 //重定向1.设置Location 2.发送302状态码 6 7 @Override 8 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 9 System.out.println("BServlet"); 10 // 请求URI s1的值:/项目名/Servlet名 11 resp.setHeader("Location","/CServlet"); 12 resp.setStatus(302); 13 } 14 }
CServlet
1 @WebServlet("/CServlet") 2 public class CServlet extends HttpServlet { 3 @Override 4 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 5 System.out.println("CServlet"); 6 } 7 }
2)定时刷新:设置Refresh头(你可以把它理解成,定时重定向!)
DServlet
1 @WebServlet("/DServlet") 2 public class DServlet extends HttpServlet { 3 @Override 4 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 5 System.out.println("DServlet"); 6 resp.setHeader("Refresh","5;URL=/CServlet");//5秒后重定向到Cservlet 7 } 8 }
3)禁用浏览器缓存:Cache-Control、pragma、expires
FServlet
1 @WebServlet("/FServlet") 2 public class FServlet extends HttpServlet{ 3 @Override 4 protected void doGet(HttpServletRequest request, HttpServletResponse response){ 5 response.setHeader("Cache-Control","no-cache"); 6 response.setHeader("pragma","no-cache"); 7 response.setDateHeader("expires",-1);//过期时间为-1,也就是不缓存 8 } 9 }
在index.jsp中有
4)<meta>标签可以替代响应头:<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
*响应体: 通常是html、也可以是图片!
>response的两个流:
<>ServletOutputStream,用来向客户端发送字节数据。ServletOutputStream out=response.getOutputStream();
<>PrintWriter,用来向客户端发送字符数据!需要设置编码。PrintWriter pw=response.getWriter();
<>两个流不能同时使用!
在Interface ServletResponse接口中有方法getOutputStream()和getWriter(),如果同时使用,会抛出IllegalStateException异常
案例:(不同时)使用两个流
1)使用ServletOutputStream字节流向客户端写字符串和图片
1 @WebServlet("/GServlet") 2 public class GServlet extends HttpServlet { 3 @Override 4 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 5 /*String s="Hello outputStream 谢"; 6 byte[] bytes=s.getBytes();//将字符串转换成字节,存储到byte数组中 7 resp.getOutputStream().write(bytes);//字节流*/ 8 9 //响应字节数据,1.把一张图片读取到字节数组中2.使用字节流进行输出 10 FileInputStream in=new FileInputStream("G://金软软.jpg"); 11 //读取输入流内容的字节到字节数组中 12 byte[] bytes1= IOUtils.toByteArray(in); 13 resp.getOutputStream().write(bytes1); 14 } 15 }
2)使用PrintWriter字符流向客户端写内容,需要设置字符编码
1 @WebServlet("/HServlet") 2 public class HServlet extends HttpServlet { 3 @Override 4 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 5 resp.setContentType("text/html;charset=utf-8"); 6 resp.getWriter().write("谢军帅"); 7 resp.getWriter().print("<br>金泰妍"); 8 } 9 }
*重定向:设置302,设置Location!其中变化的只有Location,所以Java提供了一个快捷方法,完成重定向1
>sendRedirect(String location)方法
1 //快捷 2 resp.sendRedirect("http://www.baidu.com");
request:封装了客户端所有的请求数据!
请求行
请求头
空行
请求体(GET没体)
*获取常用信息
>获取客户端IP,request.getRemoteAddr()
>请求方式,request.getMethod(),可能时POST或GET
*获取请求头
>(重要)String getHeader(String name),适用于单值头
>int getInHeader(String name),适用于单指int类型的请求头
>long getDateHeader(String name),适用于单值毫秒类型的请求头
>Enumeration<String> getHeaders(String name),适用于多值请求头
通过User-Agent识别用户浏览器类型
1 //演示:获取客户端的IP地址、获取请求方式、获取User-Agent,得到客户端的信息(操作系统,浏览器) 2 @WebServlet("/Servlet2") 3 public class Servlet2 extends HttpServlet { 4 @Override 5 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 6 String remoteAddr = req.getRemoteAddr(); 7 System.out.println("(IPV6)IP:"+remoteAddr); 8 String method = req.getMethod(); 9 System.out.println("请求方式:"+method); 10 String userAgent = req.getHeader("User-Agent"); 11 System.out.println("userAgent:"+userAgent); 12 //先把字符串转换成小写,,然后再看是否包含 13 if(userAgent.toLowerCase().contains("chrome")){ 14 System.out.println("您好"+remoteAddr+"您使用的是谷歌浏览器"); 15 }else if (userAgent.toLowerCase().contains("firefox")){ 16 System.out.println("您好"+remoteAddr+"您使用的是火狐浏览器"); 17 }else if (userAgent.toLowerCase().contains("msie")){ 18 System.out.println("您好"+remoteAddr+"您使用的时IE浏览器"); 19 }else{ 20 System.out.println("不晓得你用的啥"); 21 } 22 } 23 }
防盗链:如果请求不是通过本站的超链接发出的,发送错误状态码404。Refresh这个请求头,表示请求的来源!(比如:从一个页面跳转到另一个页面,都会有Refresh值)
1 @WebServlet("/Servlet3") 2 public class Servlet3 extends HttpServlet { 3 @Override 4 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { 5 /*使用Refresh请求头,来防盗链*/ 6 String referer = req.getHeader("Referer"); 7 System.out.println(referer); 8 if(referer == null || !referer.contains("localhost")){//如果前面的满足的话,后边的就不用判断了 9 resp.sendRedirect("http://www.baidu.com"); 10 }else{ 11 System.out.println("hello"); 12 } 13 } 14 } 15 /* 16 Referer这个头可以得到请求的来源; 17 1.在地址栏中请求的话其值为null 18 2.在其它页面以链接的形式请求的话其值为URL 19 */
*获取请求URL
http://localhost:8080/day10_2/AServlet?username=xxx&password=yyy
>String getScheme():获取协议,http
>String getServerName():获取服务器名,localhost
>String getServerPort():获取服务器端口,8080
>String getContextPath():获取项目名(会经常使用,重要),/day10_2
>String getServletPath():获取Servlet路径,/AServlet
>String getQueryString():获取参数部分,即问号后面的部分,username=xxx&password=yyy
>String getRequestURI():获取请求URI,等于项目名+Servlet路径,/day10_2/AServlet
>String getRequestURL():获取请求URL,等于不包括参数的整个请求路径,http://localhost:8080/day10_2/AServlet
*获取请求参数:请求参数是由客户端发送给服务器的!有可能是在请求体中(POST),也可能在URL之后(GET)
>***String getParameter(String name):获取指定名称的请求参数值,适用于单值请求参数
>String[] getParameterValues(String name):获取指定名称的请求参数值,适用于多值请求参数
>Enumeration<String> getParameterNames():获取所有请求参数的名称
>***Map<String,String[] > getParameterMap():获取所有的请求参数,其中key为参数名,value为参数值。将请求的参数封装到Map中
案例:超链接参数
案例:表单数据
*请求转发和请求包含---dispatcher的英文意思是调度员,来调用别的Servlet
RequestDispatcher rd=request.getRequestDispatcher("/MyServlet");---->使用request获取RequestDispatcher对象,括号里的参数是被转发或包含的Servlet的servlet路径
请求转发:***rd.forward(request,response);
请求包含:rd.include(request,response);
有时一个请求需要多个Servlet协作才能完成,所以需要在一个Servlet跳到另一个Servlet!
>一个请求跨多个Servlet,需要使用转发和包含。
>请求转发:由下一个Servlet完成响应体!当前Servlet可以设置响应头!(留头不留体)
>请求包含:由两个Servlet共同来完成响应体!(都留)
>无论是请求转发还是请求包含,都在一个请求范围内!使用同一个request和response!
请求转发示例:
OneServlet:
1 public class OneServlet extends HttpServlet { 2 3 public void doGet(HttpServletRequest request, HttpServletResponse response) 4 throws ServletException, IOException { 5 System.out.println("OneServlet..."); 6 response.setHeader("aa", "AA");//头 7 response.getWriter().print("hello OneServlet!");//体 8 9 //转发 10 RequestDispatcher rd=request.getRequestDispatcher("/TwoServlet"); 11 rd.forward(request, response); 12 } 13 }
TwoServlet:
1 public class TwoServlet extends HttpServlet { 2 public void doGet(HttpServletRequest request, HttpServletResponse response) 3 throws ServletException, IOException { 4 System.out.println("TwoServlet..."); 5 response.getWriter().print("hello TwoServlet"); 6 } 7 }
在浏览器中访问http://localhost:8080/XJS_Servlet3/OneServlet
控制台结果:
OneServlet... TwoServlet...
浏览器页面结果:
还有OneServlet设置的头也发送到浏览器了
hello TwoServlet
证明了OneServlet留头不留体
请求包含示例:
Servlet1:
1 public class Servlet1 extends HttpServlet { 2 3 public void doGet(HttpServletRequest request, HttpServletResponse response) 4 throws ServletException, IOException { 5 System.out.println("Servlet1..."); 6 response.setHeader("aa", "AA");//头 7 response.getWriter().print("hello OneServlet!");//体 8 9 //转发 10 RequestDispatcher rd=request.getRequestDispatcher("/include/Servlet2"); 11 rd.include(request, response); 12 } 13 }
Servlet2:
1 public class Servlet2 extends HttpServlet { 2 3 public void doGet(HttpServletRequest request, HttpServletResponse response) 4 throws ServletException, IOException { 5 System.out.println("TwoServlet..."); 6 response.getWriter().print("hello TwoServlet"); 7 } 8 9 }
请求http://localhost:8080/XJS_Servlet3/include/Servlet1的结果:
控制台:
Servlet1... TwoServlet...
浏览器页面:
hello OneServlet!hello TwoServlet
结果证明:include留头又留体
*request域
Servlet中三大域对象:request、session、application,都有如下三个方法:
>void setAttribute(String name, Object value)
>Object getAttribute(String name)
>void removeAttribute(String name)
>同一请求范围内使用request.setAttribute()、request.getAttribute()来传值!前一个Servlet调用setAttribute()来保存值,后一个Servlet调用getAttribute()获取值。
*请求转发和重定向的区别
>请求转发是一个请求一次响应,而重定向是两次请求两次响应
>请求转发地址栏不变化,而重定向会显示后一个请求的地址
>请求转发只能转发到本项目的其他Servlet,而重定向不只能重定向到本项目的其他Servlet,还能定向到其他项目
>请求转发是服务器端行为,只需给出转发的Servlet路径,而重定向需要给出requestURL,即包含项目名!!!
>请求转发和重定向效率是转发高!因为是一个请求!
<>需要地址栏发生变化,那么必须使用重定向!
<>需要在下一个Servlet中获取request域中的数据,必须使用转发!
编码
常见字符编码:iso-8859-1(不支持中文)、gbk(系统默认编码,中国的国标码)、utf-8(万国码,支持全世界的编码,所以我们使用这个)
1.响应编码
response.setCharacterEncoding("utf-8");//设置服务器的编码为utf-8
response.setHeader("Content-Type","text/html;charset=utf-8"); //设置响应头,服务器编码格式为utf-8
response设置Content-Type的快捷方法:
response.setContentType("text/html;charset=utf-8");
想不乱码:在使用getWriter()方法之前,先调用下面方法:
***response.setContentType("text/html;charset=utf-8");
2.请求编码---所有的乱码都有解决的方法(先在Servlet中获取参数,然后在对参数进行反编码)
一般请求参数,在Servlet中获取时不会出现乱码,如果出现乱码就按照下面方法解决乱码:
*客户端发送给服务器的请求参数是什么编码:
客户端首先要打开一个页面,然后在页面中提交表单或点击超链接!在请求这个页面时,服务器响应的编码是什么,那么客户端发送请求时的编码就是什么
*服务器端默认使用什么编码来解码参数:
(tomcat8之前的)服务器端默认使用ISO-8859-1来解码!所以这一定会出现乱码的!因为iso不支持中文!
在Servlet中设置request.setCharacterEncoding("utf-8");---然后在获取请求参数,可以防止乱码
Tomcat8默认编码为UTF-8
3.URL编码
表单的类型:Content-Type:application/x-www-form-urlencoded,就是把中文转换成%后面跟随两位的16进制。
为什么要用它:在客户端和服务器之间传递中文时需要把它转换成网络适合的方式。
*它不是字符编码!
*它是用来在客户端和服务器端之间传递参数用的一种方式!
*URL编码需要先指定一种字符编码,把字符串解码后,得到byte[] ,然后把小于0的字节+256,再转换成16进制。前面加一个%。
*POST请求默认就使用URL编码!tomcat会自动使用URL解码!
*URL编码:String username=URLEncoder.encode(username,"utf-8");
*URL解码:String username=URLDecoder.decode(username,"utf-8");
1)GET请求中的中文没有URL编码,可能会出现丢失字节
2)使用的是表单,表单自动使用URL编码
3)服务器会自动识别URL编码,然后自动做URL解码
4.路径
*web.xml中<url-pattern>路径,(叫它Servlet路径!)
>要么以 “*” 开头,要么以 “/” 开头。
*转发和包含路径
>以 “/”开头:相对当前项目的路径,例如:http://localhost:8080/项目名/ request.getRequestdispacher("/BServlet").forward(request,response);
>不以 “/” 开头:相对当前Servlet路径
*重定向路径(客户端路径)
>以“ /”开头:相对于当前主机,例如:http://localhost:8080/,所以需要自己手动添加项目名
*页面中超链接和表单路径
>与重定向相同,都是客户端路径!需要添加项目名
><form action="/XJS_Servlet3/AServlet">
><a href="/XJS_Servlet3/AServlet">
>****建议使用以 “/” 开头的路径,即绝对路径!
*ServletContext获取资源路径
>相对当前项目目录,即index.jsp所在目录。
*ClassLoader获取资源路径
>相对classes目录
>ClassLoader获取资源时,不能以“/”开头!
*Class获取资源路径
>以“/”开头相对classes目录
>不以"/"开头相对当前.class文件所在目录。