码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • JavaWeb核心篇(2)——Request和Response


    JavaWeb核心篇(2)——Request和Response

    上篇文章中提及到了Servlet,在Servlet中我们主要继承了HTTPServlet类,在HTTPServlet类中我们会接触到两个对象

    这篇文章主要介绍Servlet中使用的Request和Respnse对象

    简单介绍

    首先我们先了解一下客户端与服务端之间信息如何传递:

    从上图,我们可以看到:

    • 客户端发送请求,而请求是以HTTP请求数据格式进行发送,因而Servlet就创建了一个Request类来封装这些接收数据
    • 服务端给出响应,而响应是以HTTP响应数据格式进行发送,因而Servlet就创建了一个Response类来封装这些返回数据

    简单来说:

    • Request对象:获得请求数据
    • Response对象:设置响应数据

    Request介绍

    我们在介绍Request之前,应当先了解一下Request的继承体系:

    我们可以看到HttpServletRequest是基于ServletRequest接口创建的针对Http协议的请求对象接口

    我们在使用Request对象时,也常常使用HttpServletRequest接口

    Request获得请求数据

    Request对象被创建的主要目的就是获得请求数据

    我们将根据HTTP请求数据对象的三种格式分开介绍获得请求数据方法

    1. 请求行:

    请求行格式:

    GET/request-demo/req1?username=zhangsan HTTP/1.1
    

    请求行获得代码:

    函数 解释
    String getMethod() 获得请求方式(GET/POST)
    String getContextPath() 获得虚拟目录(项目访问路径):/request-demo
    StringBuffer getRequestURL() 获得URL(统一资源定位符):http://localhost:8080/request-demo/req1
    String getRequestURI() 获得URI(统一资源标识符):/request-demo/req1
    String getQueryString 获得请求参数(GET方法):username=zhangsan HTTP/1.1
    1. 请求头:

    请求头格式:

    User-Agent: Mozilla/5.0 Chrome/91.0.4472.106
    

    请求头获得代码:

    函数 解释
    String getHeader(String name) 根据请求头名称,获得值
    1. 请求体:

    请求体格式:

    username=superbaby&password=123456
    

    请求体获得代码:

    函数 解释
    ServletInputStream getInputStream() 获得字节输入流
    BufferedReader getReader() 获得字符输入流

    最后给出代码展示:

    package com.itheima.web.request;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.BufferedReader;
    import java.io.IOException;
    
    /**
     * request 获取请求数据
     */
    @WebServlet("/req1")
    public class RequestDemo1 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // String getMethod():获取请求方式: GET
            String method = req.getMethod();
            System.out.println(method);//GET
            // String getContextPath():获取虚拟目录(项目访问路径):/request-demo
            String contextPath = req.getContextPath();
            System.out.println(contextPath);
            // StringBuffer getRequestURL(): 获取URL(统一资源定位符):http://localhost:8080/request-demo/req1
            StringBuffer url = req.getRequestURL();
            System.out.println(url.toString());
            // String getRequestURI():获取URI(统一资源标识符): /request-demo/req1
            String uri = req.getRequestURI();
            System.out.println(uri);
            // String getQueryString():获取请求参数(GET方式): username=zhangsan
            String queryString = req.getQueryString();
            System.out.println(queryString);
    
    
            //------------
            // 获取请求头:user-agent: 浏览器的版本信息
            String agent = req.getHeader("user-agent");
            System.out.println(agent);
    
    
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //获取post 请求体:请求参数
    
            //1. 获取字符输入流
            BufferedReader br = req.getReader();
            //2. 读取数据
            String line = br.readLine();
            System.out.println(line);
    
    
        }
    }
    

    Request通用方式获得请求参数

    在请求参数的获取方法上GET与POST有所不同:

    • GET:String getQueryString()
    • POST: BufferedReader getReader()

    那么如果我们能够采用一种方法同时使GET和POST获得参数,就可以实现两者的通用

    Request对此提供了一下方法:

    函数 解释
    Map getParameterMap() 获得所有参数Map的集合
    String[] getParametervalues(String name) 根据名称获得参数值(数组)
    String getParameter(String name) 根据名称获得参数值

    我们给出通用方法,并做出解释:

    package com.itheima.web.request;
    
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.*;
    import java.io.IOException;
    import java.util.Map;
    
    /**
     * request 通用方式获取请求参数
     */
    @WebServlet("/req2")
    public class RequestDemo2 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //GET请求逻辑
            //System.out.println("get....");
    
            //1. 获取所有参数的Map集合
            Map map = req.getParameterMap();
            for (String key : map.keySet()) {
                // username:zhangsan lisi
                System.out.print(key+":");
    
                //获取值
                String[] values = map.get(key);
                for (String value : values) {
                    System.out.print(value + " ");
                }
    
                System.out.println();
            }
    
            System.out.println("------------");
    
            //2. 根据key获取参数值,数组(我们希望查询参数为hobby的值,在查询中hobby的值为1,2)
            String[] hobbies = req.getParameterValues("hobby");
            for (String hobby : hobbies) {
    
                System.out.println(hobby);
            }
    
            //3. 根据key 获取单个参数值
            String username = req.getParameter("username");
            String password = req.getParameter("password");
    
            System.out.println(username);
            System.out.println(password);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //POST请求逻辑
    
            // 因为两者可以共用一个方法,所以doPost直接调用doGet即可
            this.doGet(req,resp);
    
            /*System.out.println("post....");
    
            //1. 获取所有参数的Map集合
            Map map = req.getParameterMap();
            for (String key : map.keySet()) {
                // username:zhangsan lisi
                System.out.print(key+":");
    
                //获取值
                String[] values = map.get(key);
                for (String value : values) {
                    System.out.print(value + " ");
                }
    
                System.out.println();
            }
    
            System.out.println("------------");
    
            //2. 根据key获取参数值,数组
            String[] hobbies = req.getParameterValues("hobby");
            for (String hobby : hobbies) {
    
                System.out.println(hobby);
            }
    
            //3. 根据key 获取单个参数值
            String username = req.getParameter("username");
            String password = req.getParameter("password");
    
            System.out.println(username);
            System.out.println(password);*/
    
    
        }
    }
    

    请求参数中文化导致乱码问题

    当我们的请求参数中如果存在中文数据,可能会出现乱码(Tomcat8以下版本)

    我们分别介绍POST和GET的中文乱码解决方案

    1. POST:

    首先我们从根本上解释一下为什么会出现乱码:

    • POST底层以getReader()的方式以ISO-8859-1的形式获得输入流
    //1. 解决乱码:POST,getReader()
    
    // 默认情况下POST以ISO-8859-1的形式获取流
    // POST中可以直接设置字符输入流的编码
    
    // request.setCharacterEncoding() 改变字符输入流的获得格式
    request.setCharacterEncoding("UTF-8");
    
    1. GET:

    首先我们从根本上解释一下为什么会出现乱码:

    • 当HTML识别到内容后,会以UTF-8的形式进行编码,并发送给服务端
    • 但服务端在接收到数据后,会以ISO-8859-1的形式进行解码
    • 编码解码方式不同,对中文的处理方式不同,导致中文数据出现乱码
    // 我们以一个例子来模拟GET乱码的解决过程
    
    package com.itheima.web.request;
    
    import java.io.UnsupportedEncodingException;
    import java.net.URLDecoder;
    import java.net.URLEncoder;
    
    public class URLDemo {
    
        public static void main(String[] args) throws UnsupportedEncodingException {
            String username = "张三";
    
            // 1.2步模拟了乱码过程;3,4步解决乱码过程
            
            
            //1. URL编码(HTML传递)
            //URLEncoder.encode(username, "utf-8");表示对username进行utf-8形式的编码,形成code
            String encode = URLEncoder.encode(username, "utf-8");
            System.out.println(encode);
    
            //2. URL解码
            //URLDecoder.decode(encode, "ISO-8859-1");表示对encode进行ISO-8859-1形式的解码,形成decode
            String decode = URLDecoder.decode(encode, "ISO-8859-1");
    
            System.out.println(decode);
    
            //3. 转换为字节数据,编码
            byte[] bytes = decode.getBytes("ISO-8859-1");
    
            //4. 将字节数组转为字符串,解码
            String s = new String(bytes, "utf-8");
    
            System.out.println(s);
    
        }
    }
    

    因为GET的方法属于通用方法,所以在整个项目中可以直接通过第二种方法来进行GET和POST的中文乱码修改问题

    package com.itheima.web.request;
    
    
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.*;
    import java.io.IOException;
    import java.nio.charset.StandardCharsets;
    
    /**
     * 中文乱码问题解决方案
     */
    @WebServlet("/req4")
    public class RequestDemo4 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //1. 解决乱码:POST,getReader()
            //request.setCharacterEncoding("UTF-8");//设置字符输入流的编码
    
            //2. 获取username
            String username = request.getParameter("username");
            System.out.println("解决乱码前:"+username);
    
            //3. GET,获取参数的方式:getQueryString
            // 乱码原因:tomcat进行URL解码,默认的字符集ISO-8859-1
           /* //3.1 先对乱码数据进行编码:转为字节数组
            byte[] bytes = username.getBytes(StandardCharsets.ISO_8859_1);
            //3.2 字节数组解码
            username = new String(bytes, StandardCharsets.UTF_8);*/
    
            username  = new String(username.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);
    
            System.out.println("解决乱码后:"+username);
    
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request, response);
        }
    }
    

    Request请求转发

    首先讲解一下请求转发的概念:

    • 请求转发:一种在服务器内部的资源跳转方法
    • 当客户端发送请求后,服务端可以选择把这个请求转发出去或者说是共享出去

    实现方式:

    req.getRequestDispatcher("资源B地址").forward(req,resp)
    

    实现所需函数:

    函数 解释
    void setAttribute(String name,Object o) 存储数据到request域(一般在资源A地址存储)
    Object getAttribute(String name) 根据key,获得value(一般在资源B地址存储)
    void removeAttribute(String name) 根据key,删除该键值对

    请求转发特点:

    • 浏览器地址栏路径不发生变化
    • 只能转发到当前服务器的内部资源
    • 一次请求,可以在转发的资源间使用request共享数据

    我们给出两个/demo服务器端进行模拟:

    // 客户端资源A
    
    package com.itheima.web.request;
    
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.nio.charset.StandardCharsets;
    
    /**
     * 请求转发
     */
    @WebServlet("/req5")
    public class RequestDemo5 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("demo5...");
            System.out.println(request);
            //存储数据
            request.setAttribute("msg","hello");
    
            //请求转发
            request.getRequestDispatcher("/req6").forward(request,response);
    
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request, response);
        }
    }
    
    // 客户端资源B
    
    package com.itheima.web.request;
    
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * 请求转发
     */
    @WebServlet("/req6")
    public class RequestDemo6 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("demo6...");
            System.out.println(request);
            //获取数据
            Object msg = request.getAttribute("msg");
            System.out.println(msg);
    
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request, response);
        }
    }
    

    Response介绍

    同样,我们也来介绍一下Response的体系结构:

    我们在使用Response时,也以HttpServletResponse为主

    设置响应数据

    和获取数据相同,我们把函数根据响应数据的三部分分别展示:

    1. 响应行:

    响应行格式:

    HTTP/1.1 200 OK
    

    响应行设置函数:

    函数 解释
    void setStatus(int sc) 设置响应状态码
    1. 响应头:

    响应头格式:

    Content-Type:text/html
    

    响应头设置函数:

    函数 解释
    void setHeader(String name,String value) 设置响应头键值对
    1. 响应体:

    响应体格式:

    
    

    响应体设置函数:

    函数 解释
    PrintWriter getWriter() 获得字符输出流
    ServletOutputStream getOutputStream() 获得字节输出流

    下面给出代码示例:

    package com.itheima.web.response;
    
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.*;
    import java.io.IOException;
    
    @WebServlet("/resp1")
    public class ResponseDemo1 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("resp1....");
    
            //1.设置响应状态码 302
            response.setStatus(302);
            //2. 设置响应头 Location
            response.setHeader("Location","/request-demo/resp2");
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request, response);
        }
    }
    

    重定位

    首先我们介绍一下重定位:

    • 重定位:一种资源跳转方式
    • 客户端向服务器A发出请求,当该资源A无法满足客户端发出的请求,资源A返回响应(响应码302表示无法处理,并给出响应头location:xxx告诉客户端应该去哪个资源B解决问题),然后客户端再向资源B发送请求,由资源B来设置响应数据

    实现方式:

    resp.setStatus(302);
    resp.setHeader("location","资源B的路径")
    
    resp.sendRedirect("资源B的路径")
    

    重定位特点:

    • 浏览器地址栏路径发生变化
    • 可以重定位到任意位置的资源(服务器内部,外部均可以)
    • 两次请求,不能在多个资源使用request共享数据

    下面给出代码示例:

    package com.itheima.web.response;
    
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.*;
    import java.io.IOException;
    
    @WebServlet("/resp1")
    public class ResponseDemo1 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("resp1....");
    
            //重定向
            /*//1.设置响应状态码 302
            response.setStatus(302);
            //2. 设置响应头 Location
            response.setHeader("Location","/request-demo/resp2");*/
    
            //简化方式完成重定向
            //动态获取虚拟目录
            String contextPath = request.getContextPath();
    
            // 这里资源B的路径是虚拟目录+设置名
            response.sendRedirect(contextPath+"/resp2");
            //response.sendRedirect("https://www.itcast.cn");
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request, response);
        }
    }
    

    资源路径问题:

    浏览器使用:需要加虚拟目录(项目访问路径)

    服务端使用:不需要加虚拟目录

    目前已学习内容:

    resp.sendRedirect("路径") : 加虚拟目录

    req.getRequestDispatcher("路径") : 不加虚拟目录

    Response响应字符数据

    我们可以使用Response对象向客户端发送数据

    具体步骤:

    1. 通过Response对象获得字符输出流:
    PrintWriter writer = resp.getWriter();
    
    1. 写数据
    writer.write("aaa");
    

    注意:

    • 该流不需要关闭,随着响应结束,response对象销毁,由服务器关闭
    • 中文数据乱码:原因通过Response获取的字符输出流默认编码:ISO-8859-1
    resp.setContentType("text/html;charset=utf-8");
    

    代码展示:

    package com.itheima.web.response;
    
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    /**
     * 响应字符数据:设置字符数据的响应体
     */
    @WebServlet("/resp3")
    public class ResponseDemo3 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            response.setContentType("text/html;charset=utf-8");
            //1. 获取字符输出流
            PrintWriter writer = response.getWriter();
            //content-type
            //response.setHeader("content-type","text/html");
    
            writer.write("你好");
            writer.write("

    aaa

    "
    ); //细节:流不需要关闭 } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }

    Response响应字节数据

    我们可以使用Response对象向客户端发送数据

    传统具体步骤:

    1. 通过Response对象获得字符输出流
    ServletOutputStream outputStream = resp.getOutputStream();
    
    1. 写数据
    outputStream.write(字节数据);
    

    IOUtils工具类使用:

    1. 导入坐标:
            <dependency>
                <groupId>commons-iogroupId>
                <artifactId>commons-ioartifactId>
                <version>2.6version>
            dependency>
    
    1. 使用即可:
    IOUtils.copy(输入流,输出流);
    

    代码展示:

    package com.itheima.web.response;
    
    
    import org.apache.commons.io.IOUtils;
    
    import javax.servlet.ServletException;
    import javax.servlet.ServletOutputStream;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    /**
     * 响应字节数据:设置字节数据的响应体
     */
    @WebServlet("/resp4")
    public class ResponseDemo4 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            //1. 读取文件
            FileInputStream fis = new FileInputStream("d://a.jpg");
    
            //2. 获取response字节输出流
            ServletOutputStream os = response.getOutputStream();
    
            //3. 完成流的copy
           /* byte[] buff = new byte[1024];
            int len = 0;
            while ((len = fis.read(buff))!= -1){
                os.write(buff,0,len);
            }*/
    
            IOUtils.copy(fis,os);
    
            fis.close();
    
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request, response);
        }
    }
    

    结束语

    好的,关于Request和Respone的内容就到这了

    附录

    该文章属于学习内容,具体参考B站黑马程序员陈老师的JavaWeb课程

    这里附上链接:01-Request和Response介绍&Request继承体系_哔哩哔哩_bilibili

  • 相关阅读:
    Java23种设计模式-结构型模式之组合模式
    关于 MyBatis-Plus 分页查询的探讨 → count 都为 0 了,为什么还要查询记录?
    Day12 尚硅谷JUC——集合的线程安全
    DeepLabV3(Rethinking Atrous Convolution for Semantic Image Segmentation)
    深入探讨医保购药APP的技术架构与设计思路
    ESP32是真火
    【图像识别-车牌识别】基于BP神经网络求解车牌识别问题含GUI界面和报告
    数据密集型应用第六章数据分区
    ROS机器人RIKIBOT硬件介绍
    【初始RabbitMQ】工作队列的实现
  • 原文地址:https://www.cnblogs.com/qiuluoyuweiliang/p/16671453.html
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号