• 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文件所在目录。

  • 相关阅读:
    SQL入门学习4-复杂查询
    SQL入门学习3-数据更新
    SQL入门学习2-聚合与排序
    SQL入门学习1-查询基础
    SQL入门学习0-数据库与SQL
    Exp9 20155218 Web安全基础实践
    20155218《网络对抗》Exp8 Web基础
    # 20155218 徐志瀚 EXP7 网络欺诈
    Exp6 20155218 信息搜集与漏洞扫描
    20155218《网络对抗》MSF基础应用
  • 原文地址:https://www.cnblogs.com/xjs1874704478/p/10792878.html
Copyright © 2020-2023  润新知