JAVAWEB-NOTE08-request&response

发布时间 2023-03-23 23:07:24作者: 男人的浪漫

request与response对象简介

Request:获取请求的数据
Response:设置响应数据

@WebServlet(value = "/demo3")
public class servletdemo3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = req.getParameter("name");

        resp.setHeader("content-type","text/html;charset=utf-8");
        resp.getWriter().write("<h1>"+name+",欢迎你!</h1>");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

比如上面这段代码,获取到了用户请求中的name参数,然后再在响应的数据中输出欢迎name的操作

Request对象

继承体系

获取请求数据

请求的数据一般分为三部分:请求行、请求头、请求体
对于请求行,我们可以获取请求行消息的方法如下:

        String method = req.getMethod();//获取请求方式
        System.out.println(method);

        String contextPath = req.getContextPath();//获取虚拟目录(项目访问路径//)
        System.out.println(contextPath);

        StringBuffer requestURL = req.getRequestURL();//获取URL
        System.out.println(requestURL.toString());

        String requestURI = req.getRequestURI();//获取URI
        System.out.println(requestURI);

        String queryString = req.getQueryString();//获取请求参数
        System.out.println(queryString);

我们在地址栏输入

运行结果为:

对于请求头,我们有比如获取user-agent(浏览器版本)

String header = req.getHeader("user-agent");//获取浏览器版本
System.out.println(header);

对于请求体,也就是Post方法下会将传入的参数放入请求体中,对于请求体中的数据,tomcat提供了字节流与字符流的方法来获取参数:


request通用方式获取请求参数

我们发现如果客户端是get消息,那么我们需用getQueryString的方法来获取参数,而如果是post的消息,我们需要用getHeader来获取请求参数,而且还需需要进入到不同的doget和dopost两个方法中去处理获取请求参数,这样会稍显麻烦,所以我们需要用到一些通用的方式来获取请求参数。

我们首先获取到请求的方法,然后可以做一个判断,当时get请求时我们使用getQueryString,并且将值传入到params中,如果是post请求那么我们将使用getHeader方法并且将参数放到getHeader。
同时,对于获取到的参数,tomcat提供了上图的三个方法,来分别获取所有参数map集合、根据名称获取参数值(是一个数组)、根据名称获得参数值。

实验:
先写一个req.html,请求方式为get

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/demo1/req2" method="get">
    <input type="text" name="username"><br>
    <input type="text" name="pwd"><br>
    <input type="checkbox" name="hobby" value="1">篮球
    <input type="checkbox" name="hobby" value="2">足球
    <br>
    <input type="submit">
</form>
</body>
</html>

再写一个doGet方法来获取请求参数:

 protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        System.out.println("this is doGet");
        Map<String, String[]> parameterMap = req.getParameterMap();
        for(String key : parameterMap.keySet()){
            System.out.print(key + ":");
            String[] values = parameterMap.get(key);
            for (String value : values){
                System.out.print(value + " ");
            }
            System.out.println();
        }
    }

在浏览器页面输入表单信息后,浏览器显示get请求:

在服务器端可以看到获取到了用户输入为:

对于POST的请求方法,我们可以直接在doPost中书写如下代码:

protected void doPost(HttpServletRequest req, HttpServletResponse resp)  {
        this.doGet(req,resp);
    }

这样就简化了我们处理不同请求方法的代码书写

Request请求中文乱码问题--POST

我们request请求中如果有中文,那么会出现乱码的情况:

那么针对post的请求中有中文的情况,我们应该如果处理?因为post从底层读取数据的方法是getreader(),ISO-8859-1标准和页面的utf-8不匹配出现乱码,我们需要修改读取数据流的编码方式:

req.setCharacterEncoding("UTF-8");


但是以上的办法对于get请求是没有办法的,因为get是通过getQueryString来获取参数的

Request请求中文乱码问题--GET

GET请求乱码原因分析

浏览器的传输不支持中文,所以浏览器的网页首先通过utf-8的编码形式将中文转化为URL编码进行传输。随后TOMCAT在接受到数据之后又会对收到的URL进行解码,如果解码的方式也是utf-8的话,那么是不会发生乱码问题的,但是tomcat的getquerystring的底层代码是写死的,采用的是ISO-8859-1来进行解码,所以产生了Get请求中有中文乱码的现象

URL编码

虽然我们在参数输入的地方可以看到是中文,但是这时谷歌浏览器做的优化,看底层的消息头,我们发现张三这个字符被转化为了URL编码:

那么我们应该怎么做?
首先tomcat8之后的版本已经解决这个问题,get请求的中文参数不会乱码。
还有一种方法就是找寻两个编码之间的关系,无论是utf-8还是iso的编码,他们底层都是二进制数,只是由于编码解码的方式造成了乱码,我们可以这样做:

String name = req.getParameter("username");
byte[] bytes = name.getBytes(StandardCharsets.ISO_8859_1);
String s = new String(bytes, StandardCharsets.UTF_8);
System.out.println(s);

先将ISO_8859_1编码的数据转化为字节数组,然后将重新编码为utf-8来解决这一问题

请求转发


演示:
我们新建一个demo4代码如下:

System.out.println("this is demo4");
req.getRequestDispatcher("/req5").forward(req,resp);

新建一个demo5如下:

System.out.println("this is demo5");

然后我们运行我们的服务器,请求req4的数据后可以发现打印了两句话,说明req4的请求处理后又转到了req5这个资源当中

当然有时候我们两个资源之间需要资源共享,第一个资源处理了一部分的请求后,会将处理的数据交给第二个资源继续处理,所以request对象提供了以下三个方法:

演示:

输出:

特点

Response对象

Response对象来设置响应数据

功能介绍

Response对象完成重定向


演示:

@WebServlet(value = "/resp1")
public class respDemo1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setStatus(302);
        resp.setHeader("Location","/demo1/resp2");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req,resp);
    }
}

我们新建一个resp1的资源,然后什么都不做,重定向让浏览去去访问resp2的资源:

@WebServlet(value = "/resp2")
public class respDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("i am resp2");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req,resp);
    }
}

在浏览器中我们发现,当访问了resp1的资源后,地址栏发生了变化:

并且在控制台成功输出:

平时我们还可以将重定向的代码简化书写为:

resp.sendRedirect("/demo1/resp2");

同时我们在浏览器的网络中能看到产生了两次请求,浏览器在接受到302的状态码后知道了想要的资源不在resp1中,而location中有我将要访问的地址:

特点

路径问题

可以看到我们重定向转发和request请求转发的时候,我们对应的路径是不一样的,request转发我们没有加虚拟路径,而重定向的时候我们加载了虚拟路径:

那么原则应该是这样的

练习:

那么为了能够动态的获取虚拟目录,我们有这样一个方法利用getContextPath:

String contextPath = req.getContextPath();

响应字符数据


演示:

@WebServlet(value = "/resp3")
public class respDemo3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取字符输入流
        PrintWriter writer = resp.getWriter();
        writer.write("hello");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req,resp);
    }
}

输出:

并且我们在网络调试工具中也能看到响应的消息:

那么我们先显示中文应该怎么样做呢?如果我们直接在write中写入中文,肯定会乱码,因为默认的是iso的编码,这个时候我们就需要通过以下方法来设置编码格式了:

stContentType();

在这个方法中,我们还可以设置让浏览器知道我们写入的数据应该用什么文本解析,比如我们想写入HTML让浏览器能够解析,并且是中文,可以这样做:

resp.setContentType("text/html;charset=utf-8");
//获取字符输入流
PrintWriter writer = resp.getWriter();
writer.write("<h1>你好</h1>");

注意
我们无需释放write资源,因为write资源是跟随resp对象的产生而产生的,在响应结束后,服务器会自动回收,无需我们手动关闭。

响应字节数据


演示:
我们演示的目标是将一张D盘的图片a.jpg显示在我们的浏览器页面上,首先我们要将图片的字节消息读取出来:

FileInputStream fileInputStream = new FileInputStream("d://1.jpg");

随后我们需要通过Response对象获得字节输出流:

ServletOutputStream outputStream = resp.getOutputStream();

为了将输出流和输入流接起来,我们加载IO工具类,此工具类是apche提供的io工具类:

<dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.2</version>
</dependency>

随后我们做字节流的copy:

IOUtils.copy(fileInputStream,outputStream);
fileInputStream.close();

运行效果: