Java学习 day52_request

ServletRequest

概述

Defines an object to provide client request information to a servlet. The servlet container creates a ServletRequest object and passes it as an argument to the servlet’s service method.

ServletRequest对象其实是tomcat创建的一个关于用户请求信息的封装,其实也就是请求报文的封装。

我们之前提到过,如果使用不同的服务器产品,那么可能解析之后的请求报文的封装对象各不相同,今后我使用某一个公司的服务器产品获取请求资源,接下来更换服务器产品之后,大概率我需要重新去实现一套新的代码逻辑,因为这两个对象不是一样的,获取请求资源的方法也是不同的,非常不利于行业的发展。

sun公司制定了这么一个标准,ServletRequest就是针对请求报文的封装,里面定义了非常多的方法,各个服务器需要对其进行实现,虽然实现的逻辑各不相同,但是需要保障最终调用同一个方法,返回的结果时一致的。

Animal animal = new Pig();

animal.run();

ServletRequest request = new XXXRequest();/new YYYRequest();

request.getProtocol();

HttpServletRequest  extends ServletRequest

父子接口的关系。

ServletRequest其实是对于请求报文的封装,但是请求报文有很多种协议,HTTP、AJP,

HttpServletRequest是对于HTTP请求报文的封装。当发送HTTP请求报文时才可以解析成HTTPServletRequest

但是一般情况下,99%的情况下,我们发送的都是HTTP请求,所以这两个接口、对象你可以认为是一致的


常见方法

其实就是对于请求报文的封装,,那么肯定可以将原先的请求报文加以还原。

HTTP请求报文

package com.cskaoyan.request;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
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.util.Enumeration;

@WebServlet("/request1")
public class RequestServlet1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    /**
     * requestURL = 访问协议:主机、端口号 + requestURI
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //利用request提供的方法,将HTTP请求报文给它还原出来
        String method = request.getMethod();
        String requestURI = request.getRequestURI();
        String requestURL = request.getRequestURL().toString();
        String protocol = request.getProtocol();
        System.out.println(method + " " + requestURI + " " + protocol);
        System.out.println(method + " " + requestURL + " " + protocol);

        //获取请求头 Enumeration当做iterator来使用即可
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()){
            String headerName = headerNames.nextElement();
            String headerValue = request.getHeader(headerName);
            System.out.println(headerName + ":" + headerValue);
        }

        //请求体的获取方式
//        ServletInputStream inputStream = request.getInputStream();
    }
}

除此之外还有一些方法,比如可以获取客户机和服务器主机的相关信息。

package com.cskaoyan.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.util.Enumeration;

@WebServlet("/request2")
public class RequestServlet2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }


    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       //获取客户机和服务器主机的相关信息
        //获取服务器信息
        String localAddr = request.getLocalAddr();
        //服务器监听的端口号
        int localPort = request.getLocalPort();
        //获取客户机信息
        String remoteAddr = request.getRemoteAddr();
        //客户端使用的端口号
        int remotePort = request.getRemotePort();
        
        //打印,大家可以相互之间访问来测试
        //黑名单:将指定ip来源加入黑名单
        System.out.println(localAddr + "===" + localPort);
        System.out.println(remoteAddr + "----" + remotePort);
    }
}

获取请求参数

客户端和服务器在通讯的时候,主要是数据的传输。比如用户登录、注册、提交一个订单,用户的数据提交到服务器上面去。

这些数据肯定会随着HTTP请求报文传输到服务器,解析封装到request对象中;服务器需要做的事情就是利用一些方法将这些请求参数给获取到

最常见的提交请求参数的形式就是通过form表单或者直接在浏览器地址输入一些参数的方式来进行提交。

比如get请求方法:

http://localhost/app/submit?username=xxxx&categoryId=xxxx

请求报文:

GET http://192.168.1.26/app/request2?username=xxx&category=xxxxy HTTP/1.1
Host: 192.168.1.26
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.73
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6

还可以使用post方法:

<form action="xxxx" method="post">
	<input type="text" name="username"></input>
    <input type="submit" ></input>
</form>

无论是get方式进行提交还是post方式进行提交,提交过来的请求参数格式是什么样的?

POST http://192.168.1.26/app/request2 HTTP/1.1
Host: 192.168.1.26
Connection: keep-alive
Content-Length: 29
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://192.168.1.26
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.73
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.1.26/app/form.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6

username=admin&password=aaaaa

总结:

无论是在地址栏后面附着请求参数,还是使用form表单发送post请求提交请求参数,都是key=value&key=value类型的数据

只要请求参数的格式是key=value&key=value型,那么都可以使用接下来介绍的获取请求参数的API来获取,无论请求参数在请求行还是在请求体。但是如果请求参数的格式是json类型({“key”:“value”,"key:“value”}),不可以使用该方法来获取。

package com.cskaoyan.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("/param")
public class ParameterServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //接收post请求参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        System.out.println(username);
        System.out.println(password);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //接收get方法请求参数
        //username=zzzz&category=ssss
        String username = request.getParameter("username");
        String category = request.getParameter("category");
        System.out.println(username);
        System.out.println(category);
    }
}

获取多个请求参数(一个key有多个value)

如果页面内提交过来的请求参数非常多,服务器需要再次去扩充,然后去适配前端页面的参数的修改。也就是说每当前端页面增加了一个表单,那么服务器就需要再次去增加一行代码来获取对应的请求参数。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="http://192.168.1.26/app/param" method="post">
        <input type="text" name="username"><br>
        <input type="password" name="password"><br>
        <input type="radio" name="gender" value="male"><input type="radio" name="gender" value="female"><br>
        <input type="checkbox" name="hobby" value="java">java
        <input type="checkbox" name="hobby" value="c++">c++
        <input type="checkbox" name="hobby" value="python">python<br>
        <input type="submit">
    </form>
</body>
</html>

如何做到当前前端页面的表单增加时,服务器的逻辑不需要修改、增加

package com.cskaoyan.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.util.Arrays;
import java.util.Enumeration;

@WebServlet("/param2")
public class ParameterServlet2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Enumeration<String> names = request.getParameterNames();
        while (names.hasMoreElements()){
            String key = names.nextElement();
            //这个方法无法获取到提交多个请求参数的情形
            //也就是无法获取到checkbox类型的参数
//            String value = request.getParameter(key);
            String[] values = request.getParameterValues(key);
            System.out.println(key + ":" + Arrays.toString(values));
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

获取请求参数,并且封装到JavaBean中

JavaBean:java对象。

bean:

1.成员变量应当尽可能是private,与此同时,提供public的get和set方法

2.一定要有无参构造函数

使用BeanUtils封装。

如果状态码出现500,那么表示的是服务器发生异常。去找错误日志。

查看tomcat的log

在这里插入图片描述

这两处都需要查看。

在这里插入图片描述

出现这个问题,是什么原因?

类没有找到。

编译问题还是运行问题?运行问题。(如果是编译问题,那么无法编译通过)

EE阶段所特有的一个表现。

EE项目是无法独立运行的,必须由tomcat服务器来调用才可以运行,相应的这些jar包,也是需要通过tomcat提供的类加载器去加载到内存的。但是tomcat的类加载器只会到你的应用WEB-INF/lib目录下寻找jar包,如果找不到,则不会加载。不加载,则运行时无法找到该jar包。
你的ee项目只提供了一些代码片段,但是你没有提供main方法,也就是程序的入口,tomcat会给你提供程序入口。所以在提供jar包的时候,只能放在tomcat执行bin目录的lib下才能被调用

如何把jar包放到WEB-INF/lib目录下?

方式一:

在这里插入图片描述

web目录其实是一个功能性目录,表示的是对部署根目录做了一个映射,凡是web目录里面出现的文件,都会复制到部署根目录中,WEB-INF目录下存放了一个lib目录,那么也会同步过去

方式二:

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SPGvUJMW-1629626287469)(ServletRequest.assets/image-20210817163649377.png)]

package com.cskaoyan.request;

import com.cskaoyan.request.bean.User;
import org.apache.commons.beanutils.BeanUtils;

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.lang.reflect.InvocationTargetException;

@WebServlet("/param5")
public class ParameterServlet5 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //可以使用一个工具类,来帮助我们完成赋值操作
        //找jar包
        User user = new User();
        try {
            //实际上也是利用反射来实现的
            BeanUtils.populate(user, request.getParameterMap());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        System.out.println(user);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

请求参数中文乱码

post方式进行提交

如果用户提交过来的请求参数是中文

为什么会出现乱码问题?

在发送HTTP请求报文时,中文是何种编码格式?UTF-8

HTTP请求报文到达服务器,被服务器解析,封装到Request对象中,此时取出中文,发现已经乱码了。说明了再解析的过程中出现了问题。

如何解决呢?

void setCharacterEncoding(java.lang.String env)
                   throws java.io.UnsupportedEncodingException

Overrides the name of the character encoding used in the body of this request. This method must be called prior to reading request parameters or reading input using getReader().

注意事项:

1该方法可以重写请求体里面的编码格式

2该方法必须要在读取请求参数之前调用

package com.cskaoyan.request;

import com.cskaoyan.request.bean.User;
import org.apache.commons.beanutils.BeanUtils;

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.lang.reflect.InvocationTargetException;

@WebServlet("/param5")
public class ParameterServlet5 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        //可以使用一个工具类,来帮助我们完成赋值操作
        //找jar包
        User user = new User();
        try {
            //实际上也是利用反射来实现的
            BeanUtils.populate(user, request.getParameterMap());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        System.out.println(user);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

get方法进行提交

没有乱码问题。

EE debug

很有用,是sout替代不了的。

1.学会打断点。首先要清楚程序的执行流程。在这条程序执行通路上面打断点。一般情况下建议在程序入口的地方,也就是service方法第一行代码

2.打完断点之后还需要这行代码被执行触发。得tomcat调用,访问,发起请求访问对应servlet断点才能够被触发。

在这里插入图片描述

service方法执行完毕之后,发现前端页面此时还是无响应,说明客户端此时还是没有接收到响应报文,此时为什么没有接收到呢?

service方法只是其中的一小部分片段,后面还有connector读取response生成响应报文这些代码还没有执行,所以此时页面依然在阻塞。

点击resume program按钮,全部放行后续的代码

在这里插入图片描述

网络路径

最熟悉的写法应当是http://localhost/app/1.html这种写法,全路径写法

全路径

完整的网络路径

可以写,但是有些部分不是特别的合适。

localhost这个地方不是特别的合适

开发环境

个人开发环境:个人电脑—localhost可以的,但是代码开发完毕之后,需要到测试服务器上面去测试

公司测试环境:本机的环境和生产环境相差比较大,尽可能去模拟生产环境下可能出现的问题,所以会有一个测试环境,该环境应当尽可能和生产环境保持一致。 ip、域名

正式的生产环境:jd.com taobao.com

每当更换一个环境,你的代码如果写成了全路径,那么都需要更改一下。

针对localhost主机这部分,不可以直接写死,应当写在一些配置文件里面,从配置文件里面去读取这个数值,然后填充到路径里面

相对路径

相对当前页面的一个相对应的路径

比如当前表单页面是

http://localhost/app/1.html

里面youyigeform表单提交的地址是
http://localhost/app/submit

相对路径应该如何写呢?

submit即可。

推荐吗?不是特别推荐。过分依赖于当前页面所在的路径。

如果把页面所在的位置更换一下,那么最终提交的路径地址也需要随着更换。

/开头的路径

写法: /应用名/资源路径

比如提交的servlet地址:/app/submit

无论页面所在哪个目录下, 始终是不需要更改的。 推荐

疑惑:注意上面指的全部都是网络路径。和我们之前介绍的file等路径不同,file路径是硬盘路径。

浏览器里面输入的是网络路径 http://localhost/app/1.html

发送一个HTTP请求报文,发往localhost的80端口号 请求资源是/app/1.html

tomcat解析处理-------》/app的应用(有效的路径是/1.html---------》 docBase + /1.html ------ realPath------file)


转发包含

场景:

登录,提交请求参数到servlet,servlet处理完毕之后,需要跳转到一个页面,如何跳转到一个页面呢?

假设一个servlet做了一些逻辑,接下来想调用另外一个servlet,如何调用另外一个servlet呢?

可以使用转发来实现。主要用在页面跳转中,但是今后项目开发过程中,java主要位于服务端,主要负责提供数据即可,页面的渲染、跳转由前端来实现(前后端分离)。但是在学习EE知识的过程中需要给大家去举一些页面跳转的例子,所以需要用到这部分,所以会稍微讲解一下。

包含:不是一个重点。包含主要用在一个页面包含另外一个页面中。java语言很少去涉及到页面相关,vue来实现,所以这部分内容基本没什么用。


作业

3.通过form表单提交参数到servlet,要求在servlet中使用获取请求参数的API将参数封装到一个对象中,要求使用反射来实现,不要使用BeanUtils(尽量尝试去完成)
遇到的难点是如何处理value是一个多个的情况

package com.cskaoyan;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.Map;

@WebServlet("/request3")
public class GetUserServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) {
        User user = new User();


        Map<String, String[]> parameterMap = request.getParameterMap();


        try {
            ReflectionUtils.toBean(user, parameterMap);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
          
        System.out.println(user);

    }
}
package com.cskaoyan;

import com.sun.xml.internal.ws.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;

public class ReflectionUtils {
  private static final String PREFIX = "set";

  public static void toBean(Object o, Map<String, String[]> parameterMap) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    Class<?> aClass = o.getClass();


    for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
      String[] values = parameterMap.get(entry.getKey());
      Field field = aClass.getDeclaredField(entry.getKey());

      Method method = aClass.getDeclaredMethod(PREFIX + StringUtils.capitalize(entry.getKey()), field.getType());
      String simpleName = field.getType().getSimpleName();
      if("String".equals(simpleName)){
        method.invoke(o, values[0]);
      }else if("String[]".equals(simpleName)){
        method.invoke(o, (Object) values);
      }else if("Integer".equals(simpleName)){
        method.invoke(o, Integer.parseInt(values[0]));
      }else {
        throw new IllegalArgumentException("暂不支持除了简单数据类型以外的数据封装");
      }

    }

  }
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值