Request
文章目录
1.概述
在Servlet API 中,定义了一个 HttpServlet Request 接口,它继承自 ServletRequest 专门用来封装 HTTP 请求消息。由于 HTTP 请求消息分为请求行、请求消息头和请求消息体三部分,因此,在 HttpServlet Request 接口中定义了获取请求行、请求头和请求消息体的相关方法
2.获取请求信息方法
当访问 Servler 时,会在请求消息的请求行中,包含请求方法、请求资源名,请求路径等信息,为了获取这些信息,在 HttpServler Request 接口中,定义了一系列用于获取请求行信息的方法
方法声明 | 功能描述 | |
---|---|---|
String getMethod() | 获取 http 请求消息中请求方式(GET、POST) | |
String getRequestURI() | 获取 URL 的主机和端口之后、参数部分之前的部分 | |
String getQueryString() | GET 提交 url 地址后的参数字符串 | |
String getServletPath() | 获取 Servlet 的名称或 Servlet 所映射的路径 | |
String getRemoteAddr() | 获取访问者 ip 地址 |
/**
*获取请求行的内容
*@author weidong */
public class LineServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
//1、获得请求方式
//2、获得请求的资源相关的内容
String requestURI = request.getRequestURI(); StringBuffer requestURL = request.getRequestURL();
System.out.println("uri:" + requestURI);
System.out.println("url:" + requestURL);
//获得web应用的名称
//地址后的参数的字符串
//3、获得客户机的信息---获得访问者IP地址
String remoteAddr = request.getRemoteAddr(); System.out.println("IP:" + remoteAddr);
}
}
3.获取请求消息头的方法
当请求 Servlet 时,需要通过请求头向服务器传递附加信息,例如,客户端可以接受的数据类型、压缩方式、语言等,为此,在 HttpServlet Request 接口中,定义了一系列用于获取 HTTP 请求头字段的方法。
方法声明 | 功能描述 |
---|---|
String getHeader(String name) | 获取 http 请求头的指定字段 |
Enumeration getHeaders(String name) | 获取指定头的多个值 |
Enumeration getHeaderNames() | 获取一个包含所有请求头字段的Enumeration 对象 |
String getContentLength() | 该方法用于获取 Content_Length 头字段的值,结果为 int 类型 |
int getContentType() | 获取 ContentType 头字段的值,结果为 String类型 |
String getCharacterEncoding() | 获取消息实体部分字符编码 |
/**
*获取头的方法
*@author weidong */
public class HeaderServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
PrintWriter writer = response.getWriter();
//1、获得指定的头
//2、获得所有的头的名称
Enumeration<String> headerNames = request.getHeaderNames(); writer.print(header);
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement(); String headerValue = request.getHeader(headerName); System.out.println(headerName + ":" + headerValue); writer.print(headerName + " : " + headerValue +"<br>");
}
}
}
4.获取请求参数
在实际开发中,经常需要获取用户提交的表单数据,例如,用户名、密码、电子邮件等,为了方便获取表单中的请求参数,在 HttpServlet Request 接口中,定义了一系列获取请求参数的方法。
方法 | 功能 | |
---|---|---|
String getParameter(String name) | 获取指定名称的参数 | |
String[] getParameterValues(String name); | 如果有多个参数同名,可以使用此方法获取 | |
Enumeration getParameterNames() | 获取所有参数对象 | |
Map<String,String[]> getParameterMap(); | 以键值对的形式获取参数 |
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
String username = request.getParameter("username"); System.out.println("用户名" + username);
String password = request.getParameter("password"); System.out.println("密码" + password);
System.out.println("------------------");
//获取参数名为" hobby"的值
String[] hobbys = request.getParameterValues("hobby");
for (int i = 0; i < hobbys.length; i++) { System.out.println("爱好:" + hobbys[i]);
}
System.out.println("------------------");
Enumeration<String> parameterNames = request.getParameterNames(); while (parameterNames.hasMoreElements()) {
System.out.println(parameterNames.nextElement());
}
System.out.println("------------------");
// 获得所有的参数 参数封装到一个 Map<String,String[]>
Map<String, String[]> parameterMap = request.getParameterMap(); for (Map.Entry<String, String[]> entry : parameterMap.entrySet())
{
System.out.println(entry.getKey());
for (String str : entry.getValue()) {
System.out.println(str);
}
System.out.println("---------------------------");
}
}
}
输出
4.1.请求参数乱码(POST)
上面的代码,如果输入的是中文字符,那么会产生乱码问题,如下图
是因为浏览器在传递请求参数时,默认采用 UTF-8 的编码,但是在解码时采用的是默认的ISO8859-1,因此导致控制台打印参数信息出现乱码
解决
在Http ServletRequest 接口中,提供了一个 sctCharacterEncoding()方法,该方法用于设置 request 对象的解码方式
此时取到的值为:
4.2.请求参数乱码(GET)
使用 sctCharacterEncoding()方法解决乱码问题只针对 POST 请求,而对 GET 请求是无效的,为了解决 GET 方式提交表单的中文乱码问题,可以先使用错误码表 ISO8859-1 将用户名重新编码,然后使用码表 UTF-8 进行解码。
//get 方式乱码解决
String username = request.getParameter("username");//乱码
//先用 iso8859-1 编码 在使用 utf-8 解码
username = new String(username.getBytes("iso8859-1"),"UTF-8");
5.Request 域存取和请求转发
5.1. Request 域对象
Reruest 对象不仅可以获取一系列数据,还可以通过属性传递数据,在 ServletRequest 接口中,定义了一系列操作属性的方法:
setAttribute(key, value)
向 request 域中存入一个值(键值对形式)
getAttribute(key)
获取 request 域中指定键的值
removeAttribute()
删除 request 域中指定键的值
getAttributeNames()
获取域中所有
注意:存储在 request 域中的对象数据只有当前请求有效,而对其它请求无效。
5.2.请求转发
在ServletI 中,如果当前 Web 资源不想处理它的访问请求,可以通过 forward()方法将当前请求传递给其他的 Web 资源进行处理,这种方式称为请求转发。
forward()方法工作原理
当客户端访问 Servlet1 时,可以通过 forward() 方法将请求转发给其它 Web 资源,其它 Web 资源处理完请求后,直接将响应结果返回到了客户端
相关方法介绍
request.getRequestDispatcher(path);
- 1.返回封装了某个路径所指定资源的 RequestDispatcher 对象
- 2.参数 path 必须以“/”开头,用于表示当前 Web 应用的根目录。
- 3.WEB-INF 目录中的内容对 RequestDispatcher 对象也是可见的,因此,传递给 getRequestDispatcher(String path)方法的资源可以是 WEB-INF 目录中的文件
RequestDispatcher 接口方法介绍
forward(ServletRequest request, ServletResponse response)
- 1.该方法用于将请求从一个 Servlet 传递给另外的一个 Web
- 2.注意:该方法必须在响应提交给客户端之前被调用,否则将抛出
IllegalStateException 异常
include(ServletRequest request,ServletResponse response)
1.该方法用于将其它资源当做响应内容包含进来
Servlet1
public class Servlet1 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
//想 request 域中存储数据
request.setAttribute("name", "LiSi");
//servlet1 将请求转发给 servlet2 RequestDispatcher dispatcher =
request.getRequestDispatcher("/servlet2");
//执行转发的方法
dispatcher.forward(request, response);
}
}
Servlet2
public class Servlet2 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
//从 request 域中取出数据
Object attribute = request.getAttribute("name"); response.getWriter().write("hello: " + attribute);
}
}
6.注册
注意:为了方便演示,这里需要将 login.html 页面转成 jsp 界面,关于 jsp 之后会有很详细的介绍,现在无需深究(和 html 没有太大的区别)
login.jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>登陆</title>
</head>
<body>
<div align="center">
<!-- jsp 代码取值,现阶段无需理解 -->
<div><%=request.getAttribute("msg")==null?"":request.getAttribute("m
sg")%></div>
<form action="http://localhost:8080/LoginDemo_Request/login" method="post">
用户名:<input type="text" name="username" /><br /> 密码:
<input type="text" name="password" /><br />
<input type="submit" value="登陆" /> </form>
</div>
</body>
</html>
需要添加的功能
1.注册功能,添加新的界面进行注册,完成注册后将数据保存到数据库中
添加注册 Servlet,设置请求参数编码为 UTF-8,解决参数提交乱码,获取提交过来的用户名和密码
public class RegisterServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
//获取所有参数
Map<String, String[]> maps = request.getParameterMap(); for (Entry<String,String[]> set : maps.entrySet()) {
System.out.println(set.getKey());
System.out.println(Arrays.toString(set.getValue()));
}
}
}
添加注册 jsp 界面
<div align="center">
<form
action="http://localhost:8080/LoginDemo_Request/registerServlet" method="post">
用户名:<input type="text" name="username" /><br /> 密码: <input type="text" name="password" /><br /> <input type="submit" value="注册" />
</form>
</div>
编写注册方法,在 UserDao 里面编写添加插入用户信息到数据库的方法,方法参数接受一个用户对象,返回 int 类型的值,调用者通过获取该值来判断是否插入成功
//添加用户的方法
public int addUser(User user) throws SQLException{
//操作数据库
QueryRunner runner = new QueryRunner(C3P0Utils.getDataSource());
String sql = "insert into user values(?,?,?)";
runner.update(sql,user.getId(),user.getUsername(),user.getPassword()
);
}
测试方法
另外,一般开发中,用户 id 是唯一的,这里使用了 UUID 工具类来帮我们生成一个唯一ID,关于 UUID 介绍,请看 UUID 文档
@Test
public void t2(){
UserDao userDao = new UserDao();
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setUsername("bbb");
user.setPassword("123456");
try {
userDao.addUser(user);
} catch (SQLException e) {
e.printStackTrace();
}
}
编写 Service 层,在 UserService 中添加注册方法,并调用 UserDao 的 addUser 方法进行完成注册逻辑。
/**
*注册
*@param user 用户信息对象
*@return
*@throws SQLException */
public int register(User user) throws SQLException{ UserDao dao=new UserDao();
return dao.addUser(user);
}
编写 RegisterServlet,获取到用户信息后,需要将用户信息进行封装,封装到 User 对象中,使用 BeanUtils 组件,帮助我们封装对象的属性(id 需要自己设置),之后调用 UserService 中的 register 方法,传入封装好的 User 对象,进行注册,代码如下:
public class RegisterServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
//获取所有参数
Map<String, String[]> maps = request.getParameterMap(); for (Entry<String, String[]> set : maps.entrySet()) {
System.out.println(set.getKey());
System.out.println(Arrays.toString(set.getValue()));
}
User user = new User();
try {
BeanUtils.populate(user, maps);
} catch (IllegalAccessException e) { e.printStackTrace();
} catch (InvocationTargetException e) { e.printStackTrace();
}
//手动添加 UUID
user.setId(UUID.randomUUID().toString());
UserService service = new UserService();
try {
service.register(user);
} catch (SQLException e) { e.printStackTrace();
}
//默认注册成功,跳转登陆界面
response.sendRedirect(request.getContextPath() + "/login.jsp");
}
}
7.登陆案例
修改之前的登陆逻辑:
如果登陆成功,请求转发到 index.jsp 界面,并显示登陆的账号名字
如果登陆失败,请求转发回到登陆界面,并给出相应的提示。
注意:
这里登陆成功后,往 request 域中存入了用户名,在 index.jsp 界面使用 jsp 来获取域中的值,现阶段只需要了解(之后会学到),只需理解 request 与可以存入值即可。
登陆失败后,向域里面存入消息,登陆的 jsp 进行取值显示
<%=request.getAttribute("msg")==null?"":request.getAttribut
e("msg")%>
//判断 user 是否为空 if (user == null) {
//若为空 写"用户名和密码不匹配"
//response.getWriter().print("用户名和密码不匹配,5 秒后自动跳转登陆界面
");
//response.setHeader("refresh", "5;url=/LoginDemo/login.html"); request.getRequestDispatcher("/login.jsp").forward(request,
response);
} else {
//在登录成功的代码中获得原来的次数并且+1,存回到 SErvletContext 域中。
//登录成功的时候 获得原来的次数 + 1
Integer count = (Integer)
this.getServletContext().getAttribute("count");
// 存回到 ServletContext 域中
this.getServletContext().setAttribute("count", ++count);
//向 request 域中存入用户名
request.setAttribute("username", user.getUsername());
//若不为空 写"xxx:欢迎回来"
//response.getWriter().print(user.getUsername() + ":欢迎回来");
//使用转发,转发到主界面
request.getRequestDispatcher("/index.jsp").forward(request,
response);
}
8.转发和重定向区别
- 使用重定后,之前的 request 中存放的变量全部失效,并进入一个新的 request 作用域。
- 试音转发后,之前的 request 中存放的变量不会失效,就像把两个页面拼到了一起。
- 重定向两次请求,转发一次请求
- 重定向地址栏的地址变化,转发地址不变
- 重新定向可以访问外部网站,转发只能访问内部资源
- 转发的性能要优于重定向
怎么选择是重定向还是转发呢?通常情况下转发更快,而且能保持 request 内的对象,所以他是第一选择。但是由于在转发之后,浏览器中 URL 仍然指向开始页面,此时如果重载当前页面,开始页面将会被重新调用。如果你不想看到这样的情况,则选择转发。
9.ServletContext 域与 Request 域的生命周期
ServletContext
- 创建:服务器启动
- 销毁:服务器关闭
- 域的作用范围:整个 web 应用
request
- 创建:访问时创建 request
- 销毁:响应结束 request 销毁
- 域的作用范围:一次请求中