JavaWeb 修炼之路(五)服务器中的请求和响应

请求响应流程

请求响应流程图

服务器处理请求的流程:

  • 服务器每次收到请求时,都会为这个请求开辟一个新的线程;
  • 服务器会把客户端的请求数据封装到request对象中,request就是请求数据的载体;
  • 服务器还会创建response对象,这个对象与客户端连接在一起,它可以用来向客户端发送响应

response:其类型为HttpServletResponse

ServletResponse –> 与协议无关的类型
HttpServletResponse –> 与http协议相关的类型

http协议中响应的内容有哪些东西呢?

200 ok//响应行(状态码及解释)
Content-Type: text/html;charset=utf-8(响应头:键值对)
//空行
<html>
//内容
</html>

  • 状态码:200表示成功、302表示重定向、404表示客户端错误(访问的资源不存在)、500表示服务器端错误
    • sendError(int sc):发送错误状态码,例如404、500;
    • sendError(int sc,String value):发送错误状态码,还可以带错误信息;
    • setStatus(int sc):发送成功的状态码,可以发送302;
    案例:发送404
  • 响应头(键值对):常用的是单值的方法;Content-Type、Refresh、Location等等
    • setHeader(String name,String value):适用于单值的响应头;此方法最常用
    • addHeader(String name,String value):适用于多值的响应头(即往同一个name里面添加值);
    • setIntHeader(String name,int value):适用于单值的int类型的响应头;
    • addIntHeader(String name,int value):适用于多值的int类型的响应头;
    • setDateHeader(String name,long value):适用于单值的毫秒类型的响应头;例如设置过期时间expires
    • addDateHeader(String name,long value):适用于多值的毫秒类型的响应头;
    • 案例
    • 发送302,设置Location头,完成重定向;
    • 定时刷新:设置Refresh头;可以理解为定时重定向
    • 禁用浏览器缓存:Cache-Control、pragma、expires;
    • <meta>标签可以代替响应头:<meta http-equiv="Content-Type" content="text/html;charset=utf-8">

  • 响应体:通常是html,也可以是图片
    • response的两个流:两个流不能同时使用
      • ServletOutputStream,用来想客户端发送字节数据;
      • PrintWriter,用来想客户端发送字符数据!需要设置编码!
    • 案例:
    • 使用PrintWrite发送字符数据;
    • 使用ServletOutputStream发送字节数据(图片);

  • 重定向:
    • sendRedirect(String location);方法

案例1:发送404
public class AServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        resp.sendError(404,"您访问的资源存在,就不给你看!");
    }
}

这里写图片描述

案例2:重定向302

创建两个Servlet,BServlet用来发送302和Location,这个Location指向CServlet
下面是BServlet:

public class BServlet extends HttpServlet {
    /*
    演示重定向:
    用户请求BServlet,然后BServlet响应302,给出Location头
     */

    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        /*
        重定向:
        1. 设置Location
        2. 发送302状态码
         */
        System.out.println("BServlet!");
        response.setHeader("Location","/day10/CServlet");
        response.setStatus(302);
    }
}

下面是CServlet:

public class CServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("CServlet!");
    }
}

可以Intellij下看到两个Servlet:

这里写图片描述

并打开浏览器的开发者工具:BServlet被访问时是302状态,并在响应头中给出了Location

这里写图片描述

CServlet的状态码是200,由BServlet给出的Location访问到

这里写图片描述

案例3:Refresh

创建两个Servlet,一个用来设置Refresh,一个用来设置跳转

下面是DServlet:

public class DServlet extends HttpServlet {
    /*
    演示定时刷新
    设置Refresh头,它表示定时刷新
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        /*
        下面是用来发送响应体!
         */
        PrintWriter write = response.getWriter();
        write.print("欢迎XXX登陆!5秒后会自动跳转到主页!您看到的一定是乱码!");
        /*
        设置名为Refresh的响应头
         */
        response.setHeader("Refresh","5;URL=/day10/EServlet");
    }
}

下面是EServlet:

public class EServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        response.getWriter().print("还是乱码!");
    }
}

用浏览器访问DServlet,页面上出现乱码

这里写图片描述

等待5秒后跳转到EServlet
这里写图片描述

案例4:禁用浏览器缓存

public class FServlet extends HttpServlet {
    /*
    禁用浏览器缓存
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        response.getWriter().print("hello world!");
        response.setHeader("Cache-Control","no-cache");
        response.setDateHeader("expires",-1);
        response.setHeader("pragma","no-cache");
    }
}

request

请求协议中的数据都可以通过request对象来获取!

  • 获取常用信息:
    • 获取客户端IP,案例:封IP;String request.getRemoteAddr()
    • 请求方式;request.getMethod();
  • 获取请求头:
    • String getHeader(String name):适用于单值头(这个方法很常用);
    • int getIntHeader(String name):适用于单值int类型的请求头;
    • long getDataHeader(String name):适用于多值请求头;
    • 案例
      • 通过User-Agent识别用户浏览器;包含系统版本,浏览器及其版本(这些都是浏览器告诉服务器的)
      • 防盗链:如果请求不是通过本站的超链接发出的,发送错误状态码404;Referer

  • 获取请求URL:
    • String getScheme():获取协议;http
    • String getServerName():获取服务器名;localhost
    • String getServerPort():获取服务器端口号;8080
    • String getContextPath():获取项目名;上下文路径就是项目路径 /my11
    • String getServletPath():获取Servlet路径;/AServlet
    • String getQueryString():获取参数部分,即问号后面的部分;
    • String getRequestURI():获取请求URI,等于项目名+Servlet路径;/my11/AServlet
    • String getRequestURL():获取请求URL,等于不包含参数的整个请求路径;不带参数的URL

  • 获取请求参数:请求参数是客户端发给服务器的,包含在请求体中的是post请求,在URL之后的的get请求
    • String getParameter(String name):获取指定名称的请求参数值,适用于单值请求参数;(很常用
    • String[] getParameterValues():获取指定名称的请求参数值,适用于多值请求参数;
    • Enumeration<String> getParameterNames():获取所有请求参数名称;
    • Map<String,String[]> getParameterMap():获取所有请求参数,其中key为参数名,value为参数值(很常用);
    • 案例:超链接参数;
      案例:表单数据;

  • 请求转发和请求包含
    RequestDispatcher rd = request.getResquestDispatcher("/MyServlet");:参数是被转发或包含的Servlet的路径。过程是首先获取被转发的Servlet的路径,这个路径写在了.xml文件中的url-pattern中;然后调用这个方法获取一个调度员对象;调度员对象有对应的方法来把request和response操作。request和response总是成对出现的
    请求转发:rd.forward(request,response)这个方法经常使用
    请求包含:rd.include(request,response)
    有时一个请求需要多个Servlet协作才能完成,所需要在一个Servlet跳转到另一个Servlet!

    • 一个请求跨多个Servlet,需要使用转发和包含
    • 请求转发:由下一个Servlet完成响应体!当前Servlet可以设置响应头(留头不留体)
    • 请求包含:由两个Servlet共同来完成响应体(都留)
    • 无论是请求转发还是请求包含,都在一个请求范围内!请使用同一个request和response!

  • request域:Servlet中三大域对象从小到大:request、session、application都有如下三个方法!域中是数据随着这个Servlet的消失而消失,所以在重定向中,存在前面中是数据就没有了
    void setAttribute(String name,Object value)
    Object getAttribute(String name)
    voidremoveAttribute(String name)
    • 同一请求范围内使用request.setAttribute()request.getAttribute()来传值!前一个Servlet调用setAttribute()保存值,之后一个Servlet调用getAttribute()取出值!

  • 请求转发和重定向的区别:
    • 请求转发是一个请求一次响应,而重定向是两次请求两次响应;
    • 请求转发地址栏不变化,而重定向会显示后一个请求的地址;
    • 请求转发只能转发到本项目其他Servlet,而重定向不只能定向到本项目,还能定向到其他项目;
    • 请求转发是服务器端行为,只需给出转发的Servlet路径,而重定向需要给出requestURL,既包含项目名;


请求转发或包含的图示:
这里写图片描述

下面创建两个Servlet来演示一下请求转发:在oneServlet中

public class oneServlet extends HttpServlet {
    /*
    演示请求转发
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("oneServlet...");
        //设置响应头,这个会出现!
        response.setHeader("aaa","AAA");
        //设置响应体,但是这句并不能出现!
        response.getWriter().print("Hello OneServlet!");
        //请求转发,等同于调用twoServlet的service方法
        request.getRequestDispatcher("/twoServlet").forward(request,response);
    }
}

在twoServlet中:

public class twoServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws {
        //这句会出现
        System.out.println("twoServlet...");
        //这句响应体才会出现在浏览器页面上
        response.getWriter().print("Hello TwoServlet!");
    }
}

结果展示:注意观察地址栏并没有变化,说明浏览器不知道请求了第二个Servlet,但是响应内容却是第二个Servlet!但是在打开工具以后可以看到响应头中添加了在oneServlet中设置的内容!
这里写图片描述

这里写图片描述

并且在Intellij中可以看到两个Servlet都被启动了
这里写图片描述

案例1:获取客户端的IP地址、获取请求方式、获取User-Agent

public class AServlet extends HttpServlet {
    /*
    演示:获取客户端的IP地址、获取请求方式、获取User-Agent,得到客户端的信息(OS,浏览器信息)
     */

    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        //获取IP地址
        System.out.println("IP: "+request.getRemoteAddr());

        //获取请求方式
        System.out.println("请求方式: "+request.getMethod());

        //获取名User-Agent的请求头
        String userAgent = request.getHeader("User-Agent");
        System.out.println("User-Agent: "+ userAgent);
    }
}

可以看到如下信息:
这里写图片描述

把其中的User-Agent拿出来分析一下:User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36;最前面代表是win10的64位操作系统,后面还包含了浏览器的信息。这时候可以加判断语句来输出客户端用的是什么浏览器:

if(request.getHeader("User-Agent").toLowerCase().contains("Chrome")){
    System.out.println("你用的是谷歌浏览器!");
}

案例2:通过request来获取url的相关方法

public class BServlet extends HttpServlet {

    /*
    通过request来获取url的相关方法
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        //获取请求协议
        response.getWriter().print(request.getScheme()+ "<br/>");

        //获取服务器名称
        response.getWriter().print(request.getServerName()+ "<br/>");

        //获取服务器端口号
        response.getWriter().print(request.getServerPort()+"<br/>");

        //获取项目名
        response.getWriter().print(request.getContextPath()+"<br/>");

        //获取Servlet路径
        response.getWriter().print(request.getServletPath()+"<br/>");

        //获取参数部分
        response.getWriter().print(request.getQueryString()+"<br/>");

        //获取URI
        response.getWriter().print(request.getRequestURI()+"<br/>");

        //获取请求URL
        response.getWriter().print(request.getRequestURL()+"<br/>");
    }
}

案例3:演示防盗链

Referer这个请求头表示请求的来源

public class CServlet extends HttpServlet {
    /*
    使用Referer请求头来演示防盗链
     */

    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        String referer = request.getHeader("Referer");
        System.out.println(referer);
        if (referer==null||!referer.contains("localhost")){
            response.sendRedirect("http://www.baidu.com");
        }else {
            System.out.println("Hello!");
        }
    }
}

当直接访问这个Servlet时,就会跳转到百度。只有从自己写的html访问时才会输出“hello”
下面是相应的html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>防盗链</title>
</head>
<body>
    <a href="/my11/CServlet">xxx</a>
</body>
</html>

从这个xxx的超链接点击时候:
这里写图片描述
当直接访问这个网页的时候就会跳转到百度,这就是防盗链的简单原理

案例4:获取请求参数

首先默认的参数可以用doGet获取,在页面上直接点击“点击这里”超链接,html里面设置的默认信息就会被获取。然后以post方式提交表单,可以用doPost获取相应的参数。

这里写图片描述
下面是html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试请求参数</title>
</head>
<body>
    <h1>测试请求参数</h1>
    <a href="/my11/DServlet?xxx=XXX&yyy=YYY">点击这里</a>
    <hr/>
    <form action="/my11/DServlet" method="post">
        用户名:<input type="text" name="user"/><br/>
        密码:<input type="password" name="password"/><br/>
        爱好:<input type="checkbox" name="hobby" value="cf"/>吃饭
        <input type="checkbox" name="hobby" value="sj"/>睡觉
        <input type="checkbox" name="hobby" value="ldm"/>撸代码
        <br/>
        <input type="submit" value="上传"/>
    </form>
</body>
</html>

下面是Servlet:

public class DServlet extends HttpServlet {
    /*
    演示request获取请求参数
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("GET:"+request.getParameter("xxx"));
        System.out.println("GET:"+request.getParameter("yyy"));
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("user");
        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中
         */
        Map<String,String[]> map = request.getParameterMap();
        for (String name:map.keySet()){
            String[] values = map.get(name);
            System.out.println(name+": "+Arrays.toString(values));
        }
    }
}

当直接访问这个html并没有提交信息的时候可以看到:
这里写图片描述

当填写信息并提交以后,post提交方式的信息就会显示出来:
这里写图片描述

可以看到由Map集合返回的所有请求参数:
这里写图片描述

本人是菜鸟一枚,当做学习笔记写博客。谢谢各路大咖驻足审阅

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值