HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象提供的方法,可以获得客户端请求的所有信息。
获得客户机信息
getRequestURL方法返回客户端发出请求时的完整URL。
getRequestURI方法返回请求行中的资源名部分。
getQueryString 方法返回请求行中的参数部分。
getPathInfo方法返回请求URL中的额外路径信息。额外路径信息是请求URL中的位于Servlet的路径之后和查询参数之前的内容,它以“/”开头。
getRemoteAddr方法返回发出请求的客户机的IP地址。
getRemoteHost方法返回发出请求的客户机的完整主机名。
getRemotePort方法返回客户机所使用的网络端口号。
getLocalAddr方法返回WEB服务器的IP地址。
getLocalName方法返回WEB服务器的主机名。
获得客户机请求头
getHeader(string name)方法:String
getHeaders(String name)方法:Enumeration
getHeaderNames()方法
获得客户机请求参数(客户端提交的数据)
getParameter(String)方法
getParameterValues(String name)方法
getParameterNames()方法
getParameterMap()方法
request是一个域对象
request对象也是一个存储数据的区域对象,所以也具有如下方法:
setAttribute(String name, Object o)
getAttribute(String name)
removeAttribute(String name)
request完成请求转发
RequestDispatcher getRequestDispatcher(String path) // 获得请求转发
requestDispathcer.forward(ServletRequest request, ServletResponse response) // 通过转发器对象转发
Http 请求传输时将 url 以 ISO-8859-1 编码,服务器收到字节流后默认会以 ISO-8859-1 编码来解码成字符流(造成中文乱码)
// POST请求,处理中文乱码
request.setCharacterEncoding("UTF-8");
//get请求,需要把 request.getParameter(“参数名”) 获取到的字符串先用 ISO-8859-1 编码成字节流,然后再将其用 utf-8 解码成字符流
String str = new String(request.getParameter("参数名").getBytes("iso-8859-1"), "utf-8");
一、获取请求消息行的方法
这部分内容很简单直接上代码:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8"); //告诉服务器使用什么编码,要与浏览器传过来的编码一致
System.out.println("请求方法是:"+req.getMethod()); //获取请求方式
System.out.println("请求的URL是:"+req.getRequestURL()); //获取请求完整url
System.out.println("请求的URI是:"+req.getRequestURI()); //获取请求资源名部分
System.out.println("请求的当前目录(项目目录)是:"+req.getContextPath()); //获取当前目录(项目目录)<重要>
String queryString = req.getQueryString(); //获取url中参数部分
System.out.println("请求的参数是:"+URLDecoder.decode(queryString,"UTF-8"));
//这边对参数进行了解码,因为在url中直接输入中文不解码的话,拿到的会是带有百分号的那种字符串,无法正常展示
//这里还要注意一点,如果请求url没有带参数,这句话会报空指针,所以保险起见,用解码的时候,最好加上非空判断。
}
输出的结果如下图:
二、获取请求消息头的方法
代码演示两种方法:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Enumeration<String> names = req.getHeaderNames(); //获得所有请求消息头的name,返回一个枚举类型
while (names.hasMoreElements()){
String key = names.nextElement();
System.out.println(key+":"+req.getHeader(key)); //根据请求头参数的key值获取对应的value值
}
}
还有一个request.getHeaders(key)的方法,获取的是具有相同key的请求头的value值,返回的一个枚举类型。
三、获取表单数据的方法
首先,我们先创建一个表单,如下:
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Register</title>
</head>
<body>
<form action="/java_web_day01/request/demo4" method="post">
用户名:<input type="text" name="userName"></br>
密 码:<input type="password" name="pwd"></br>
性 别:<input type="radio" name="sex" value="男" checked="checked">男
<input type="radio" name="sex" value="女">女</br>
爱 好:
<input type="checkbox" name="hobby" value="篮球">篮球
<input type="checkbox" name="hobby" value="唱歌">唱歌
<input type="checkbox" name="hobby" value="跳舞">跳舞
</br>
所在城市:
<select name="city">
<option>------请选择------</option>
<option value="BJ">北京</option>
<option value="SH">上海</option>
<option value="GZ">广州</option>
</select>
</br>
<input type="submit" value="注册">
</form>
</body>
</html>
1. 几个简单的方法介绍下用法:
request.getParameter(name):获取表单中,名字为name的值。
request.getParameterValues(name):获取复选框的值,返回一个数组类型;也可以获取只有一个值的表单。
request.getParameterNames():获取所有的表单的name,返回一个枚举类型。
2. 关于getParameterMap()的用法以及数据的封装
首先根据html表单中的数据封装一个User类,类中的属性要包含表单中所有name的数据,并且User类的属性名最好与name保持一致。注意这两点就可以了,这里就不放User类的创建代码了。
创建完成后,就可以写获取表单参数的代码,并封装成User对象,代码如下:
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
Map<String, String[]> map = req.getParameterMap();
//获取表单数据的map,返回的是一个Map<String, String[]>
User user = new User();
//创建User对象
for (String key : map.keySet()) {
String[] value = map.get(key);
try {
PropertyDescriptor p = new PropertyDescriptor(key, User.class);
//PropertyDescriptor这里运用到了反射的原理,key值是从表单中的name得到的
//我们创建的User对象的属性与name值是对应的,传入User的字节码对象,这时候,我们就可以通过反射拿到User中对应属性的get和set方法了。
Method method = p.getWriteMethod();
//getWriteMethod拿到的就是set方法(如果key是userName,那我们拿到的就是setUserName())
if(!key.equalsIgnoreCase("hobby")) {
//这里对hobby做一个区分,因为hobby是可以多选的,同样也可能只选了一个,所以直接区分是不是hobby,而不是区分value长度。
method.invoke(user, value[0]);
//不是hobby的那么,value里面只会有一个值,取出第一个值,就是我们要赋值给对应属性的值。
//反射的方法调用,传入user对象,以及set方法所需的参数,此时,我们就把拿到的表单数据的值保存在了User对象中
}else {
method.invoke(user, (Object)value);
//这里value是一个数组,根据反射传入参数的方式,这里必须强转成Object类型才可以传入数组
}
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(user);
}
上面这种方法需要我们自己对数据进行区分判断,有一点点麻烦,Apache提供了一个工具叫BeanUtils,用起来就非常简单了。下面是这种方法的实现:
首先,我们需要安装这个工具,因为我使用的是maven工程,所以直接找到这个工具的坐标加到pom.xml文件中,具体版本如下:
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
<!--maven仓库里还有更新的版本,我选了个使用人数相对更多的-->
</dependency>
代码如下:
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
try {
User user = new User();
BeanUtils.populate(user, req.getParameterMap());
//这里一行就可以搞定,非常简单
System.out.println(user);
} catch (Exception e) {
e.printStackTrace();
}
}
小结:这里显然更推荐使用Apache提供的工具了,完全不用考虑表单数据是一个值还是多个值,工具都会帮我处理好,我们只需要创建好我们自己的对象就可以了。不过还是把另一种方式记录下来,有助于我们理解反射的使用。
3. 使用getInputStream()获取表单数据
getInputStream()得到的是一个ServletStream对象,我们用读取流对象的方式来读取,用的表单还是上面那个,代码如下:
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
ServletInputStream inputStream = req.getInputStream();
int len;
byte[] arr = new byte[1024];
while ((len=inputStream.read(arr))!=-1){
String s = new String(arr, 0, len);
s = URLDecoder.decode(s,"UTF-8");
//这里需要对拿到的数据进行解码操作,要不然读取到的数据会是乱码的
System.out.println(s);
}
inputStream.close();
}
这种方式拿到的数据如下图:
四、请求的转发和包含
请求转发:一个servlet完成不了,把任务委派给其他servlet,共同完成一个请求。
请求转发和请求包含:
RequestDispatcher rd=request.getRequestDispatcher("/servlet");
请求转发:rd.forward(request,reponse);
请求包含:rd.include(request,reponse);
有时一个请求需要多个Servlet协作才能完成,所以需要在一个Servlet跳到另一个Servlet!
> 一个请求跨多个Servlet,需要使用转发和包含。
> 请求转发:由下一个Servlet完成响应体!当前Servlet可以设置响应头!(留头不留体)
> 请求包含:由两个Servlet共同未完成响应体!(都留)
> 无论是请求转发还是请求包含,都在一个请求范围内!使用同一个request和response!
request.getRequestDispatcher("/bservlet").forward(request, response);留头不留体
request.getRequestDispatcher("/bservlet").include(request, response);都留
最后我们来看下转发,之前我们了解了使用ServletContext实现转发,这里我们使用Request,达到的效果和之前是一样的。
代码如下:
@Override
//实现转发的代码
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("即将为您转发......");
req.getRequestDispatcher("/request/demo6").forward(req,resp);
System.out.println("业务处理完成啦!");
}
@Override
//实际业务处理的代码
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("业务正在处理中......");
}
输出结果:
面试题一 ServletContext域与Request域的生命周期比较?
ServletContext:
创建:服务器启动
销毁:服务器关闭
域的作用范围:整个web应用
request:
创建:访问时创建request
销毁:响应结束request销毁
域的作用范围:一次请求中
面试题二 转发与重定向的区别?
1)重定向两次请求,转发一次请求
2)重定向地址栏的地址变化,转发地址不变
3)重新定向可以访问外部网站 转发只能访问内部资源
4)转发的性能要优于重定向