Servlet第四篇【request对象经常使用方法、应用】

什么是HttpServletRequest

HttpServletRequest对象表明客户端的请求,当客户端经过HTTP协议访问服务器时,HTTP请求头中的全部信息都封装在这个对象中,开发人员经过这个对象的方法,能够得到客户这些信息。

简单来讲,要获得浏览器信息,就找HttpServletRequest对象html

HttpServletRequest经常使用方法

得到客户机【浏览器】信息

  • getRequestURL方法返回客户端发出请求时的完整URL。
  • getRequestURI方法返回请求行中的资源名部分。
  • getQueryString 方法返回请求行中的参数部分。
  • getPathInfo方法返回请求URL中的额外路径信息。额外路径信息是请求URL中的位于Servlet的路径以后和查询参数以前的内容,它以“/”开头。
  • getRemoteAddr方法返回发出请求的客户机的IP地址
  • getRemoteHost方法返回发出请求的客户机的完整主机名
  • getRemotePort方法返回客户机所使用的网络端口号
  • getLocalAddr方法返回WEB服务器的IP地址。
  • getLocalName方法返回WEB服务器的主机名

得到客户机请求头

  • getHeader方法
  • getHeaders方法
  • getHeaderNames方法

得到客户机请求参数(客户端提交的数据)

  • getParameter方法
  • getParameterValues(String name)方法
  • getParameterNames方法
  • getParameterMap方法

HttpServletRequest应用

防盗链

什么是防盗链呢?好比:我如今有海贼王最新的资源,想要看海贼王的要在个人网页上看。如今别的网站的人看到我有海贼王的资源,想要把个人资源粘贴在他本身的网站上。这样我独家的资源就被一个CTRL+C和CTRL+V抢走了?而反盗链就是不能被他们CRTL+C和CRTL+Vjava

  • 下面我模拟一下场景。如今我首页先有一个超连接,指向着海贼王最新资源

  • 当我点进去,就能看到海贼王最新资源了

  • 其余的人能够经过复制粘贴个人地址,放到它们的网页上

  • 这样我就划不来啦【个人广告你来没看呢!】。想要看个人资源,就必须通过个人首页点进去看
  • 想要实现这样的效果,就要获取Referer这个消息头判断Referer是否是从个人首页来的。若是不是从个人首页来的,跳转回个人首页
//获取到网页是从哪里来的
        String referer = request.getHeader("Referer");

        //若是不是从个人首页来或者从地址栏直接访问的,
        if ( referer == null || !referer.contains("localhost:8080/zhongfucheng/index.jsp") ) {

            //回到首页去
            response.sendRedirect("/zhongfucheng/index.jsp");
            return;
        }

        //能执行下面的语句,说明是从个人首页点击进来的,那没问题,照常显示
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().write("路飞作了XXXXxxxxxxxxxxxxxxxx");
  • 首先按正常预想的,别人从首页点击个人资源,访问我海贼王最新的资源

\web

  • 可以成功访问到资源

  • 若是我在浏览器直接输入地址【此时Referer是为null的】,咱们来看看

  • 跳回到首页上,不能访问到海贼王资源

  • 再试试,若是别人粘贴了个人资源url,在它的网页上挂了一个网址呢。

  • 在别人网页上点击的时候

  • 又跳回到了个人首页了。


表单提交数据【经过post方式提交数据】

<form action="/zhongfucheng/Servlet111" method="post">
    <table>
        <tr>
            <td>用户名</td>
            <td><input type="text" name="username"></td>
        </tr>
        <tr>
            <td>密码</td>
            <td><input type="password" name="password"></td>
        </tr>
        <tr>
            <td>性别</td>
            <td>
                <input type="radio" name="gender" value="男">男
                <input type="radio" name="gender" value="女">女
            </td>
        </tr>
        <tr>
            <td>爱好</td>
            <td>
                <input type="checkbox" name="hobbies" value="游泳">游泳
                <input type="checkbox" name="hobbies" value="跑步">跑步
                <input type="checkbox" name="hobbies" value="飞翔">飞翔
            </td>
        </tr>
        <input type="hidden" name="aaa" value="my name is zhongfucheng">
        <tr>
            <td>你的来自于哪里</td>
            <td>
                <select name="address">
                    <option value="广州">广州</option>
                    <option value="深圳">深圳</option>
                    <option value="北京">北京</option>
                </select>
            </td>
        </tr>
        <tr>
            <td>详细说明:</td>
            <td>
                <textarea cols="30" rows="2" name="textarea"></textarea>
            </td>
        </tr>
        <tr>
            <td><input type="submit" value="提交"></td>
            <td><input type="reset" value="重置"></td>
        </tr>
    </table>
  • 在Servlet111中获取到提交的数据,代码以下
//设置request字符编码的格式
        request.setCharacterEncoding("UTF-8");

        //经过html的name属性,获取到值
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String gender = request.getParameter("gender");

        //复选框和下拉框有多个值,获取到多个值
        String[] hobbies = request.getParameterValues("hobbies");
        String[] address = request.getParameterValues("address");

        //获取到文本域的值
        String description = request.getParameter("textarea");

        //获得隐藏域的值
        String hiddenValue = request.getParameter("aaa");

        ....各类System.out.println().......
  • 向表单输入数据

  • Servlet111获得表单带过来的数据,最后的一个数据是隐藏域带过来的。


超连接方式提交数据

常见的get方式提交数据有使用超连接,sendRedirect()浏览器

格式以下:服务器

sendRedirect("servlet的地址?参数名="+参数值 &"参数名="+参数值);
  • 咱们来使用一下,经过超连接将数据带给浏览器
<a href="/zhongfucheng/Servlet111?username=xxx">使用超连接将数据带给浏览器</a>
  • 在Servlet111接收数据
//接收以username为参数名带过来的值
        String username = request.getParameter("username");
        System.out.println(username);
  • 注意看浏览器左下角

  • 服务器成功接收到浏览器发送过来的数据

  • 而且,传输数据明文的出如今浏览器的地址栏上

  • sendRedirect()和超连接相似,在这里就不赘述了

解决中文乱码问题

细心的朋友会发现,我在获取表单数据的时候,有这句代码request.setCharacterEncoding("UTF-8");,若是没有这句代码,会发生什么事呢?咱们看看。微信

  • 再从新填写数据

  • 在服务器查看提交过来的数据,全部的中文数据都乱码了

  • 来这里咱们来分析一下乱码的缘由,在前面的博客中我已经介绍了,Tomcat服务器默认编码是ISO 8859-1,而浏览器使用的是UTF-8编码。浏览器的中文数据提交给服务器,Tomcat以ISO 8859-1编码对中文编码,当我在Servlet读取数据的时候,拿到的固然是乱码。而我设置request的编码为UTF-8,乱码就解决了。
  • 接下来使用get方式传递中文数据,把表单的方式改为get便可
  • 当咱们访问的时候,又出现乱码了!

  • 因而我按照上面的方式,把request对象设置编码为UTF-8试试
request.setCharacterEncoding("UTF-8");
        String name = request.getParameter("name");
  • 结果仍是乱码。这是为何呢?我明明已经把编码设置成UTF-8了,按照post方式,乱码问题已经解决了!。咱们来看看get和post方式的区别在哪?为何post方式设置了request编码就能够解决乱码问题,而get方式不能呢。
  • 首先咱们来看一下post方法是怎么进行参数传递的。当咱们点击提交按钮的时候,数据封装进了Form Data中http请求中把实体主体带过去了【传输的数据称之为实体主体】,既然request对象封装了http请求,因此request对象能够解析到发送过来的数据,因而只要把编码设置成UTF-8就能够解决乱码问题了

  • 而get方式不一样,它的数据是从消息行带过去的,没有封装到request对象里面,因此使用request设置编码是无效的。

  • 要解决get方式乱码问题也不难,咱们既然知道Tomcat默认的编码是ISO 8859-1,那么get方式由消息体带过去给浏览器的时候确定是用ISO 8859-1编码了
//此时获得的数据已是被ISO 8859-1编码后的字符串了,这个是乱码
        String name = request.getParameter("username");

        //乱码经过反向查ISO 8859-1获得原始的数据
        byte[] bytes = name.getBytes("ISO8859-1");

        //经过原始的数据,设置正确的码表,构建字符串
        String value = new String(bytes, "UTF-8");

上面的代码有些难理解,我画张图说明一下:网络

  • 通过咱们手工转换,再来访问一下

  • 好的,成功解决掉乱码问题了。
  • 除了手工转换,get方式还能够改Tomcat服务器的配置来解决乱码,可是不推荐使用,这样不灵活。
  • 咱们都知道Tomcat默认的编码是ISO 8859-1,若是在Tomcat服务器的配置下改为是UTF-8的编码,那么就解决服务器在解析数据的时候形成乱码问题了
  • 在8080端口的Connector上加入URIEncoding="utf-8",设置Tomcat的访问该端口时的编码为utf-8,从而解决乱码,这种改法是固定使用UTF-8编码的
<Connector port="8080" protocol="HTTP/1.1" 
               connectionTimeout="20000" 
               redirectPort="8443" URIEncoding="utf-8"/>
  • 设置了编码后,没有作任何手工转换,成功拿到数据

app

  • 固然也有另外一种改服务器编码的方式。设置Tomcat的访问该端口时的编码为页面的编码,这种改法是随着页面的编码而变
<Connector port="8080" protocol="HTTP/1.1" 
               connectionTimeout="20000" 
               redirectPort="8443" useBodyEncodingForURI="true" />
  • 设置编码为UTF-8
request.setCharacterEncoding("UTF-8");
        String name = request.getParameter("name");
  • 再次访问

  • 手写超连接若是附带中文参数问题,要URL重写,在JSP博客中会讲到
  • 总结:webapp

    • post方式直接改request对象的编码
    • get方式须要手工转换编码
    • get方式也能够修改Tomcat服务器的编码,不推荐,由于会太依赖服务器了!
    • 提交数据能用post就用post

实现转发

以前讲过使用response的sendRedirect()能够实现重定向,作到的功能是页面跳转,使用request的getRequestDispatcher.forward(request,response)实现转发,作到的功能也是页面跳转,他们有什么区别呢?如今我先来讲下转发jsp

  • 代码以下所示
//获取到requestDispatcher对象,跳转到index.jsp
        RequestDispatcher requestDispatcher = request.getRequestDispatcher("/index.jsp");

        //调用requestDispatcher对象的forward()实现转发,传入request和response方法
        requestDispatcher.forward(request, response);
  • 访问Servlet111

  • 上面已经说了,能够经过sendRedirect()重定向能够在资源尾部添加参数提交数据给服务器。那么转发能不能提交数据给服务器呢?
  • 答案明显是能够的,而且使用这种方法很是频繁
  • 在讲ServletContext的时候,曾经说过Servlet之间能够经过ServletContext实现通信,ServletContext也能称之为域对象。而request也能够称之为域对象,只不过ServletContext的域是整个web应用,而request的域仅仅表明一次http请求
  • 下面咱们来使用request实现Servlet之间的通信,Servlet111代码
//以username为关键字存zhongfucheng值
        request.setAttribute("username", "zhongfucheng");

        //获取到requestDispatcher对象
        RequestDispatcher requestDispatcher = request.getRequestDispatcher("/Servlet222");

        //调用requestDispatcher对象的forward()实现转发,传入request和response方法
        requestDispatcher.forward(request, response);
  • Servlet222代码
//获取到存进request对象的值
        String userName = (String) request.getAttribute("username");

        //在浏览器输出该值
        response.getWriter().write("i am :"+userName);
  • 访问Servlet111看下效果

  • 如上图所示,Servlet222成功拿到了request对象在Servlet111存进的数据
  • 如今问题又来了,咱们能够使用ServletContext和request实现Servlet之间的通信,那么咱们用哪种呢?通常的原则:可使用request就尽量使用request。由于ServletContext表明着整个web应用,使用ServletContext会消耗大量的资源,而request对象会随着请求的结束而结束,资源会被回收使用request域进行Servlet之间的通信在开发中是很是频繁的

转发的时序图

请求转发的细节

  • 若是在调用forward方法以前,在Servlet程序中写入的部份内容已经被真正地传送到了客户端,forward方法将抛出IllegalStateException异常。 也就是说:不要在转发以前写数据给浏览器
  • 咱们来试试是否是真的会出现异常。
OutputStream outputStream = response.getOutputStream();
        outputStream.write("--------------------------------------------".getBytes());

        //关闭流,确保让数据到浏览器中
        outputStream.close();
        
        //跳转
        request.getRequestDispatcher("/Foot").forward(request, response);
  • 访问的时候,看到浏览器能够输出数据,Tomcat后台抛出了异常

  • 若是在调用forward方法以前向Servlet引擎的缓冲区中写入了内容,只要写入到缓冲区中的内容尚未被真正输出到客户端,forward方法就能够被正常执行,原来写入到输出缓冲区中的内容将被清空,可是,已写入到HttpServletResponse对象中的响应头字段信息保持有效

转发和重定向的区别

实际发生位置不一样,地址栏不一样

  • 转发是发生在服务器的

    • 转发是由服务器进行跳转的,细心的朋友会发现,在转发的时候,浏览器的地址栏是没有发生变化的,在我访问Servlet111的时候,即便跳转到了Servlet222的页面,浏览器的地址仍是Servlet111的。也就是说浏览器是不知道该跳转的动做,转发是对浏览器透明的。经过上面的转发时序图咱们也能够发现,实现转发只是一次的http请求一次转发中request和response对象都是同一个。这也解释了,为何可使用request做为域对象进行Servlet之间的通信。
  • 重定向是发生在浏览器的

    • 重定向是由浏览器进行跳转的,进行重定向跳转的时候,浏览器的地址会发生变化的。曾经介绍过:实现重定向的原理是由response的状态码和Location头组合而实现的。这是由浏览器进行的页面跳转实现重定向会发出两个http请求request域对象是无效的,由于它不是同一个request对象

用法不一样

不少人都搞不清楚转发和重定向的时候,资源地址究竟怎么写。有的时候要把应用名写上,有的时候不用把应用名写上。很容易把人搞晕。记住一个原则:给服务器用的直接从资源名开始写,给浏览器用的要把应用名写上

  • request.getRequestDispatcher("/资源名 URI").forward(request,response)

    • 转发时"/"表明的是本应用程序的根目录【zhongfucheng】
  • response.send("/web应用/资源名 URI");

    • 重定向时"/"表明的是webapps目录

可以去往的URL的范围不同

  • 转发是服务器跳转只能去往当前web应用的资源
  • 重定向是浏览器跳转,能够去往任何的资源

传递数据的类型不一样

  • 转发的request对象能够传递各类类型的数据,包括对象
  • 重定向只能传递字符串

跳转的时间不一样

  • 转发时:执行到跳转语句时就会马上跳转
  • 重定向:整个页面执行完以后才执行跳转

转发和重定向使用哪个?

根据上面说明了转发和重定向的区别也能够很容易归纳出来。转发是带着转发前的请求的参数的。重定向是新的请求

典型的应用场景:

  1. 转发: 访问 Servlet 处理业务逻辑,而后 forward 到 jsp 显示处理结果,浏览器里 URL 不变
  2. 重定向: 提交表单,处理成功后 redirect 到另外一个 jsp,防止表单重复提交,浏览器里 URL 变了

RequestDispatcher再说明

RequestDispatcher对象调用forward()能够实现转发上面已经说过了。RequestDispatcher还有另一个方法include(),该方法能够实现包含,有什么用呢?

咱们在写网页的时候,通常网页的头部和尾部是不须要改变的。若是咱们多个地方使用Servlet输出网头和网尾的话,须要把代码从新写一遍。而使用RequestDispatcher的include()方法就能够实现包含网头和网尾的效果了

  • 咱们来操做吧!如今我有网头和网尾的Servlet

  • 使用Servlet111将网头和网尾包含
request.getRequestDispatcher("/Head").include(request, response);

        response.getWriter().write("--------------------------------------------");


        request.getRequestDispatcher("/Foot").include(request, response);
  • 访问一下Servlet111,成功把网头和网尾包含了


若是文章有错的地方欢迎指正,你们互相交流。习惯在微信看技术文章的同窗,能够关注微信公众号:Java3y
相关文章
相关标签/搜索