Java Web请求和响应机制

请求响应流程图

=================== 

服务器处理请求的流程:

  服务器每次收到请求时,都会为这个请求开辟一个新的线程。

  服务器会把客户端的请求数据封装到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文件所在目录。

原文地址:https://www.cnblogs.com/xjs1874704478/p/10792878.html