JAVA WEB从入门到精通day11 Request&Response,编码问题

Request&Response

简介:
->Request对象和Response对象起到了服务器与客户端之间的信息传递作用。
->Request对象用于接收客户端浏览器提交的数据
->Response对象则将服务器端的数据发送到客户端。

Response对象

Response对象用来响应客户端请求,控制发送给用户的信息。

Response的两个接口
ServletResponse–>与http协议无关
HttpServletResponse–>与http协议相关

客户端和服务器通信主要是依赖于http协议,所以我们主要用HttpServletResponse接口。

Response与http协议相关方法

使用Response发送http状态码

    -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, "错误,不能访问");
        }
    }

Response发送http协议中响应头的相关方法

响应头: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);适用于多值的毫秒类型的响应头 

Response完成重定向

流程
客户端向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">

Response发送http协议中响应体的相关方法

响应体:通常是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);  //将字节发送到客户端
        }

    }

Response的快捷重定义方法

当服务器进行重定义的时候:都是设置302状态码,设置Location值,变化的只有location
所以java提供了一个简单的方法完成重定向操作。
SendRedirect(String Location)方法

例如:response.sendRedirect("/Test/BServlet"); 里面的参数还是URI地址(即/项目名/Servlet路径)

Request对象

封装了客户端发送的所有请求数据。

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(留头又留体)

Request域

    生命周期:在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编码是一种浏览器用来打包表单输入的格式
    URL编码格式 :需要先将字符解码,字符解码后,将对应的字节转换为16进制,前面加上%即可。
    胡”的ascii码是-17670,十六进制是BAFA,url编码是“%BA%FA”。

你可能感兴趣的:(java,java,Web)