JavaWeb学习笔记(十)--HttpServletRequest
1. HttpServletRequest简介
HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中
2. HttpServletRequest常用方法
2.1获取客户机信息
- getRequestURL方法返回客户端发出请求时的完整URL
- getRequestURI方法返回请求行中的资源名
- getQueryString方法返回请求行中的参数部分
- getRemoteAddr方法返回发出请求的客户机的IP地址
- getRemoteHost方法返回发出请求的客户机的完整主机名,没有在DNS服务器上注册的时,返回IP地址
- getRemotePort方法返回客户机所使用的网络端口号
- getLocalAddr返回WEB服务器的IP地址
- getMethod返回请求的方法,GET POST等
1 @WebServlet(name = "HttpServletRequestDemo1") 2 public class HttpServletRequestDemo1 extends HttpServlet { 3 4 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 5 doGet(request, response); 6 } 7 8 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 9 response.setCharacterEncoding("utf-8"); 10 response.setContentType("text/html;charset=utf-8"); 11 PrintWriter out = response.getWriter(); 12 out.write("URI: " + request.getRequestURI()); 13 out.write("<br/>"); 14 out.write("URL: " + request.getRequestURL()); 15 out.write("<br/>"); 16 out.write("请求行的参数: " + request.getQueryString()); 17 out.write("<br/>"); 18 out.write("客户机的IP: " + request.getRemoteAddr()); 19 out.write("<br/>"); 20 out.write("客户机的主机名: " + request.getRemoteHost()); 21 out.write("<br/>"); 22 out.write("客户机的网络端口号: " + request.getRemotePort()); 23 out.write("<br/>"); 24 out.write("服务器的IP地址: " + request.getLocalAddr()); 25 out.write("<br/>"); 26 out.write("请求方法: " + request.getMethod()); 27 out.write("<br/>"); 28 } 29 }
运行结果:
2.2 获取客户机请求头
- String getHeader(String name) 方法返回指定请求头的值,如果有多个值,返回第一个
- Enumeration<String> getHeaders() 方法返回指定请求头所有值的枚举,如果没有指定的请求头,返回空Enumeration<String> ;如果servlet不允许使用此方法,则返回null
- Enumeration<String> getHeaderNames(String name) 方法返回请求包含的所有标头名称的枚举,如果没有指定的请求头,返回空Enumeration<String>;如果servlet不允许使用此方法,则返回null
1 /** 2 * 获取客户机请求头 3 */ 4 @WebServlet(name = "HttpServletRequestDemo2") 5 public class HttpServletRequestDemo2 extends HttpServlet { 6 7 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 8 response.setCharacterEncoding("utf-8"); 9 response.setContentType("text/html;charset=utf-8"); 10 PrintWriter out = response.getWriter(); 11 out.write("<b>getHeader()</b>"); 12 out.write("<br/>"); 13 out.write("Accept-Encoding:" + request.getHeader("Accept-Encoding") ); 14 out.write("<br/>"); 15 out.write("Host:" + request.getHeader("Host")); 16 out.write("<hr>"); 17 out.write("<b>getHeaders()</b>"); 18 out.write("<br/>"); 19 Enumeration<String> headers = request.getHeaders("Accept-Language"); 20 while (headers.hasMoreElements()) { 21 out.write("Accept-Language:" + headers.nextElement()); 22 out.write("<br/>"); 23 } 24 out.write("<hr>"); 25 out.write("<b>getHeaderNames()</b>"); 26 out.write("<br/>"); 27 Enumeration<String> headerNames = request.getHeaderNames(); 28 while (headerNames.hasMoreElements()) { 29 String name = headerNames.nextElement(); 30 String value = request.getHeader(name); 31 out.write(name + ": " + value); 32 out.write("<br/>"); 33 } 34 } 35 }
运行结果:
2.3 获取客户机请求参数
- String getParameter(String name) 方法以String返回请求参数的值,如果参数不存在返回null
- String[] getParameterValues(String name) 方法返回包含给定请求参数所具有的所有值的String对象数组,如果参数不存在返回null
- Enumeration<String> getParameterNames() 方法返回请求的所有参数名称,如果没有返回一个空枚举
- Map<String, String[]> getParameterMap() 方法返回请求参数的map,key为名称,value为String数组
创建一个表单:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 9 <form action="/HttpServletRequestDemo3" method="post"> 10 用户名:<input type="text" name="username"/><br> 11 密码:<input type="password" name="password"/><br> 12 性别: 13 <!--radio checkbox中name保持一致,这样才能确保是同一个radio或checkbox--> 14 <input type="radio" name="sex" value="男">男 15 <input type="radio" name="sex" value="女">女<br> 16 爱好: 17 <input type="checkbox" name="hobby" value="唱歌">唱歌 18 <input type="checkbox" name="hobby" value="跳舞">跳舞 19 <input type="checkbox" name="hobby" value="篮球">篮球 20 <input type="checkbox" name="hobby" value="足球">足球 21 <br> 22 所在地: 23 <select name="city"> 24 <option value="北京">北京</option> 25 <option value="深圳">上海</option> 26 <option value="上海">深圳</option> 27 </select> 28 <br> 29 简介:<br> 30 <textarea rows="5" cols="60" name="description"></textarea> 31 <br> 32 <input type="hidden" name="id" value="1"> 33 <input type="submit" value="提交"/> 34 </form> 35 </body> 36 </html>
使用getParameter,getParameterValues获取数据:
1 /** 2 * 获取请求数据,一般来说都要先检查后使用 3 */ 4 @WebServlet(name = "HttpServletRequestDemo3") 5 public class HttpServletRequestDemo3 extends HttpServlet { 6 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 7 doGet(request, response); 8 } 9 10 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 11 request.setCharacterEncoding("utf-8"); 12 response.setCharacterEncoding("utf-8"); 13 response.setContentType("text/html;charset=utf-8"); 14 15 String username = request.getParameter("username"); 16 String password = request.getParameter("password"); 17 String sex = request.getParameter("sex"); 18 String[] hobbies = request.getParameterValues("hobby"); 19 String hobbiesStr = ""; 20 // 先判空,防止空指针异常NullPointerException 21 for (int i = 0; hobbies != null && i < hobbies.length; i++) { 22 hobbiesStr += hobbies[i] + ","; 23 } 24 25 String description = request.getParameter("description"); 26 String hiddenId = request.getParameter("id"); 27 28 PrintWriter out = response.getWriter(); 29 out.write("用户名:" + username); 30 out.write("<br>"); 31 out.write("密码:" + password); 32 out.write("<br>"); 33 out.write("性别:" + sex); 34 out.write("<br>"); 35 out.write("爱好:" + hobbiesStr); 36 out.write("<br>"); 37 out.write("简介:" + description); 38 out.write("<br>"); 39 out.write("隐藏的ID:" + hiddenId); 40 out.write("<br>"); 41 } 42 }
运行结果:
使用getParameterNames获取数据:
1 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 2 request.setCharacterEncoding("utf-8"); 3 response.setCharacterEncoding("utf-8"); 4 response.setContentType("text/html;charset=utf-8"); 5 PrintWriter out = response.getWriter(); 6 Enumeration<String> names = request.getParameterNames(); 7 while(names.hasMoreElements()) { 8 String name = names.nextElement(); 9 String value = request.getParameter(name); 10 out.write(name + ": " + value); 11 out.write("<br>"); 12 } 13 }
运行结果:
使用getParameterMap获取数据,此方法常用于将表单数据封装到一个JavaBean中,我们使用BeanUtils对象进行bean的赋值和拷贝:
需要导入commons-beanutils,和其依赖的jar包commons-logging, commons-collections, http://commons.apache.org/proper/commons-beanutils/dependencies.html
按照依赖的版本进行下载,否则会报java.lang.NoClassDefFoundError: org/apache/commons/collections/FastHashMap错误
1 package com.domain; 2 3 public class User { 4 private String[] username; 5 private String password; 6 private String sex; 7 private String[] hobby; 8 private String city; 9 private String description; 10 private String id; 11 12 public String getSex() { 13 return sex; 14 } 15 16 public void setSex(String sex) { 17 this.sex = sex; 18 } 19 20 public String[] getHobby() { 21 return hobby; 22 } 23 24 public void setHobby(String[] hobby) { 25 this.hobby = hobby; 26 } 27 28 public String getCity() { 29 return city; 30 } 31 32 public void setCity(String city) { 33 this.city = city; 34 } 35 36 public String getDescription() { 37 return description; 38 } 39 40 public void setDescription(String description) { 41 this.description = description; 42 } 43 44 public String getId() { 45 return id; 46 } 47 48 public void setId(String id) { 49 this.id = id; 50 } 51 52 public String[] getUsername() { 53 return username; 54 } 55 56 public void setUsername(String[] username) { 57 this.username = username; 58 } 59 60 public String getPassword() { 61 return password; 62 } 63 64 public void setPassword(String password) { 65 this.password = password; 66 } 67 }
1 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 2 request.setCharacterEncoding("utf-8"); 3 response.setCharacterEncoding("utf-8"); 4 response.setContentType("text/html;charset=utf-8"); 5 PrintWriter out = response.getWriter(); 6 User formbean = new User(); 7 User user = new User(); 8 Map<String, String[]> map = request.getParameterMap(); 9 try { 10 BeanUtils.populate(formbean, map); // 用map集合填充bean,表单中的名字和bean的名字 11 BeanUtils.copyProperties(user, formbean); // 将formbean拷贝到user中 12 } catch (IllegalAccessException e) { 13 e.printStackTrace(); 14 } catch (InvocationTargetException e) { 15 e.printStackTrace(); 16 } 17 18 for (Map.Entry<String, String[]> entry : map.entrySet()) { 19 String name = entry.getKey(); 20 String[] values = entry.getValue(); 21 String valueStr = ""; 22 for (int i = 0; values != null && i < values.length; i++ ) { 23 valueStr += values[i] + ","; 24 } 25 out.write(name + ": " + valueStr); 26 out.write("<br>"); 27 } 28 }
运行结果:
打个断点,可以看到user中的数据:
3. request请求参数的中文乱码问题
乱码的根因:编码和解码使用的字符集不一样
request请求主要有以下三种方式:
- POST方式提交表单数据乱码问题
- GET方式提交表单数据乱码问题
- 超链接方式的请求
针对这三种分别展示乱码问题和解决方案,先创建一个html文件:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <a href="/HttpServletRequestDemo4?username=中国">乱码问题</a> <form action="/HttpServletRequestDemo4" method="post"> <br><br> 用户名: <input type="text" name="username"> <input type="submit" value="提交(post)"> </form> <br><br><br><br> <form action="/HttpServletRequestDemo4" method="get"> 用户名: <input type="text" name="username"> <input type="submit" value="提交(get)"> </form> </body> </html>
3.1 POST方式提交表单数据乱码问题
在Servlet中获取参数:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("Post方式:"); response.getWriter().write(request.getParameter("username")); }
浏览器中访问表单,并使用POST方式提交,可以看到乱码问题:
解决方案:设置编码格式与HTML post提交方式相同的编码,我们HTML使用的是UTF-8,那么我们也需要设置服务器以UTF-8的方式接收数据。
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("Post方式:"); request.setCharacterEncoding("UTF-8"); System.out.println(request.getParameter("username")); }
运行结果:
3.2 GET方式提交表单数据乱码问题
GET提交的编码不能通过request.setCharacterEncoding("utf-8")设置
Tomcat7以及以前的版本GET提交时URL使用的是ISO-8859-1编码,需要手动处理:
URIEncoding This specifies the character encoding used to decode the URI bytes, after %xx decoding the URL. If not specified, ISO-8859-1 will be used.
String username = request.getParameter("username"); username = new String(username.getBytes("ISO-8859-1"), "UTF-8") ;
运行结果:
Tomcat8以后默认提交使用UTF-8编码,无需转码,直接获取即可:
URIEncoding This specifies the character encoding used to decode the URI bytes, after %xx decoding the URL.
If not specified, UTF-8 will be used unless the org.apache.catalina.STRICT_SERVLET_COMPLIANCE system property is set to true in which case ISO-8859-1 will be used.
String username = request.getParameter("username");
疑问:试了下,将HTML改成GB2312,再进行转码操作,还是乱码,没有找到解决方案。。。
3.3 超链接方式的请求
<a href="/HttpServletRequestDemo4?username=中国">乱码问题</a>
此种方式和GET提交相同,因为超链接本身就是以GET方式提交请求的。说明:IE报404错误,Chrome正常。
4. request实现请求转发
请求转发指一个web资源收到客户端请求后,通知服务器去调用灵位一个web资源进行处理。
request对象提供了一个getRequestDispatcher方法,该方法返回一个RequestDispatcher对象,调用这个对象的forword方法可以实现请求转发。
request对象同时也是一个域对象,开发人员可以通过request对象在实现转发时,把数据通过request对象带给其他web资源处理。
- setAttribute方法
- getAttribute方法
- getAttributeNames方法
- removeAttribute方法
4.1 实现请求转发
访问HttpServletRequestDemo5,使用request将其转发到HttpServletRequestDemo6中,并在request中传递参数:
1 /** 2 * request实现请求转发,并传递数据 3 */ 4 @WebServlet(name = "HttpServletRequestDemo5") 5 public class HttpServletRequestDemo5 extends HttpServlet { 6 7 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 8 request.setAttribute("name", "bear"); 9 request.getRequestDispatcher("/HttpServletRequestDemo6").forward(request, response); 10 } 11 }
1 /** 2 * 获取request中存储的数据 3 */ 4 @WebServlet(name = "HttpServletRequestDemo6") 5 public class HttpServletRequestDemo6 extends HttpServlet { 6 7 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 8 response.getWriter().write((String)request.getAttribute("name")); 9 } 10 }
运行结果:
ServletContext对象也可实现请求转发(JavaWeb学习笔记(八)--ServletConfig和ServletContext对象介绍):
this.getServletContext().getRequestDispatcher("/HttpServletRequestDemo6").forward(request, response);
4.2 转发时需要注意的细节:
如果在调用forward方法之前,在Servlet程序中写入部分内容已经被真正的传送到了客户端,forword方法将抛出IllegalStateException异常:
例如,如下两种方式都会抛异常,所以最好在每个跳转后加上return语句:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); out.write("aaaa"); // 关闭流,将导致数据写到浏览器上 out.close(); request.getRequestDispatcher("/message.html").forward(request, response); }
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (true) { request.getRequestDispatcher("/form.html").forward(request, response); } // 下面这个会报java.lang.IllegalStateException: Cannot forward after response has been committed // 所以每个跳转后最好加上return语句 request.getRequestDispatcher("/message.html").forward(request, response); }
运行结果:
能输出aaaa或者跳转到form.html,但是代码会抛异常:
如果在调用forward方法之前向Servlet引擎的缓冲区中写入了内容,只要写入到缓冲区的内容还没有被真正的输出到客户端,forword方法就可以正常被执行,原来写入到输出缓冲区中的内容将被清空,但是,已经写入到HttpServletResponse对象中的响应头字段信息保持有效。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); // aaaa将不会在message.html中出现 out.write("aaaa"); request.getRequestDispatcher("/message.html").forward(request, response); }
运行结果:
4.3 请求重定向和请求转发的区别
一个web资源收到客户端请求后,通知服务器去调用另一个web资源进行处理,称之为请求转发。
一个web资源收到客户端请求后,通知浏览器去访问另一个web资源,称之为请求重定向。
RequestDispatcher.forward方法只能将请求转发给同一个web应用中的组件;而HttpServletResponse.sendRedirect方法还可以定位到同一站点的其他web应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。
HttpServletResponse.sendRedirect方法重定向后,浏览器地址栏的URL会发生变化,由初始的URL变成重定向后的目标URL;RequestDispatcher.forward请求转发,浏览器地址栏URL不会变化。
HttpServletResponse.sendRedirect会有两次访问请求;RequestDispatcher.forward只有一次访问请求。
5. 利用referer实现防盗链
我们经常会遇到别人在微信群发了一个连接,点击进去后发现直接跳转到了网站首页,通过首页才能跳转到想看的网页。这个就利用了referer实现防盗链:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title></title> </head> <body> 首页 <a href="/HttpServletRequestDemo7">消息显示</a> </body> </html>
/** * 利用referer实现防盗链 */ @WebServlet(name = "HttpServletRequestDemo7") public class HttpServletRequestDemo7 extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String referer = request.getHeader("referer"); System.out.println(referer); if (referer == null || !referer.startsWith("http://localhost")) { response.sendRedirect("/index.jsp"); return; } request.getRequestDispatcher("/message.html").forward(request, response); } }
运行结果:
可以看到都是调用HttpServletRequestDemo7,直接输入(referer为null)是不能获取到message.html的,通过首页跳转才可以。
6. web工程中各类地址的写法
写地址的时候最好以“/”开头,如果是给服务器用的地址,表示当前的web应用;如果是给浏览器用(地址栏)指的是web应用目录(虚拟映射目录)。
以下样例都设置当前web应用虚拟映射的目录为/aaa/bbb:
6.1 “/”代表当前web应用
1. 在Servlet中获取web应用的资源
this.getServletContext().getResource("/test.txt").getPath();
this.getServletContext().getRealPath("/test.txt");
2. 请求转发
request.getRequestDispatcher("/message.html").forward(request, response);
运行结果:
messag.html直接从当前web应用中查找,Servlet中无须写/aaa/bbb路径。
6.2 “/”代表当前web应用目录(虚拟映射目录)
1. 重定向
response.sendRedirect("/aaa/bbb/message.html");
这个是给浏览器用的,就必须写完整的映射路径,否则会找不到资源。但是“/aaa/bbb”映射的路径可能会变化,可通过下面的方式获取web应用的路径:
response.sendRedirect(request.getContextPath() + "/message.html");
运行结果:
2. 超链接
<a href="/aaa/bbb/HttpServletRequestDemo8">消息显示</a>
3. 表单提交
<form action="/aaa/bbb/HttpServletRequestDemo8">
<input type="submit" value="测试web地址写法">
</form>
没搞清楚HTML中如何获取当前web应用目录映射,只能先写死了。。。