Request与Response
request对象与response对象的原理
如上图:
- request对象和response都是由tomcat服务器创建,由我们程序员来使用它。
- request对象使用来获取用户从浏览器发过来的请求信息,response对象是用来对服务器设置响应消息并返回给浏览器的。
- request对象继承体系:
ServletRequest - - - -接口
| 继承
HttpServletRequest - - - 接口
| 实现
org.apache.catalina.connector.RequestFacade@2abb5747(tomcat实现类),这个类将为service方法创建request和response对象
request功能:获取请求消息数据
- 获取请求行数据
- GET/day03/requestDemo1?username=liuzeyu HTTP/1.1
- 方法:
-
获取请求方式 :GET
String getMethod() -
获取虚拟目录:/day03
String getContextPath() -
获取Servlet路径: /requestDemo1
String getServletPath() -
获取get方式请求参数:username=liuzeyu
String getQueryString() -
(*)获取请求URI:/day03/requestDemo1
String getRequestURI(): /day03/requestDemo1
StringBuffer getRequestURL() :
http://192.168.0.105/day03/requestDemo11. URL与URI区别 1. URL:统一资源定位符 : http://192.168.0.105/day03/requestDemo1 中华人民共和国 2. URI:统一资源标识符 : /day03/requestDemo1 共和国
-
获取协议及版本:HTTP/1.1
* String getProtocol() -
获取客户机的IP地址:192.168.0.100
* String getRemoteAddr()
-
@WebServlet("/requestDemo1")
public class RequestDemo1 extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取请求方式 :Get
String method = req.getMethod();
System.out.println(method);
//(*)获取虚拟目录:/day03
String contextPath = req.getContextPath();
System.out.println(contextPath);
//获取servlet路径:/requestDemo1
String servletPath = req.getServletPath();
System.out.println(servletPath);
//获取get请求信息 username=liuzeyu
String queryString = req.getQueryString();
System.out.println(queryString);
//获取URI,URL信息,URL:http://192.168.0.105/day03/requestDemo1 URI:/day03/requestDemo1
StringBuffer requestURL = req.getRequestURL();
String requestURI = req.getRequestURI();
System.out.println(requestURL);
System.out.println(requestURI);
//获取协议版本号 :HTTP/1.1
String protocol = req.getProtocol();
System.out.println(protocol);
//获取客户机IP地址 :192.168.0.100
String remoteAddr = req.getRemoteAddr();
System.out.println(remoteAddr);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
- 获取请求头数据
- 方法:
- (*)String getHeader(String name):通过请求头的名称获取请求头的值
- Enumeration< String> getHeaderNames():获取所有的请求头名称
- 方法:
/**
* 获取浏览器请求头信息
*/
@WebServlet("/requestDemo2")
public class RequestDemo2 extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取所有的请求头名字,Enumeration<String>类似于一个接口迭代器
Enumeration<String> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()){
String element = headerNames.nextElement(); //遍历获得每个请求头
String value = req.getHeader(element);
System.out.println(element + "--->"+value);
}
}
}
小案例:
需求:在tomcat部署两个不同的项目,拥有两个实现相同功能的界面,同时访问一个地址,制造防止盗取链接的功能
界面1对应tomcat项目1:192.168.0.105/day03/login.html
界面2对应tomcat项目2:192.168.0.105:8080/login.html
tomcat项目1(界面1):< a href="/day03/requestDemo4">点击看电影...< /a>
tomcat项目2(界面2,盗链接):< a href="http://192.168.0.105/day03/requestDemo4">点击看电影...< /a>
这里要注意同时启动两个项目时,HTTP port和JMX port都不可以相同,否则项目将启动不起来,可手动在Run/configurations中修改。
/**
* 获取浏览器请求头的referer信息进行处理过滤,防止盗链接
*/
@WebServlet("/requestDemo4")
public class RequestDemo4 extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String referer = req.getHeader("referer");
System.out.println(referer);
//过滤非法访问:
//只有从url中包含/day03才能访问看电影
if(referer.contains(("/day03"))){
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("可以看电影..!!");
}else{
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("不能看电影....");
}
/**
* 注意:直接从浏览器地址栏访问referer = null
*/
}
}
项目地址:
https://github.com/liuzeyu12a/referer
-
获取请求体数据
- 请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数
- 步骤:
-
获取流对象,方式由以下三种:
- BufferedReader getReader():获取字符输入流,只能操作字符数据
- ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据
- 学到文件上传知识点
-
再从流对象中拿数据
-
代码:
<!-- 表单数据:-->
<form action="/day03/requestDemo5" method="post">
<input type="text" name="username">
<input type="text" name="password">
<input type="submit" value="提交">
</form>
@WebServlet("/requestDemo5")
public class RequestDemo5 extends HttpServlet{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求体数据字符流
BufferedReader reader = req.getReader();
String s = null;
while((s = reader.readLine()) != null){
System.out.println(s);//按行读取
}
}
}
-
request其它功能
- 获取请求参数通用方式:不论get还是post请求方式都可以使用下列方法来获取请求参数
- String getParameter(String name):根据参数名称获取参数值 username=zs&password=123
- String[] getParameterValues(String name):根据参数名称获取参数值的数组 hobby=xx&hobby=game
- Enumeration< String> getParameterNames():获取所有请求的参数名称
- Map<String,String[]> getParameterMap():获取所有参数的map集合
** * 1. 获取请求参数通用方式:不论get还是post请求方式都可以使用下列方法来获取请求参数 1. String getParameter(String name):根据参数名称获取参数值 username=zs&password=123 2. String[] getParameterValues(String name):根据参数名称获取参数值的数组 hobby=xx&hobby=game 3. Enumeration<String> getParameterNames():获取所有请求的参数名称 4. Map<String,String[]> getParameterMap():获取所有参数的map集合 */ @WebServlet("/requestDemo6") public class RequestDemo6 extends HttpServlet{ @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.String getParameter(String name):根据参数名称获取参数值 String username = req.getParameter("username"); System.out.println(username); System.out.println("++++++++++++++++"); //2. String[] getParameterValues(String name):根据参数名称获取参数值的数组 hobby=xx&hobby=game String[] hobbies = req.getParameterValues("hobby"); for (String hobby : hobbies) { System.out.println(hobby); } System.out.println("++++++++++++++++"); //3. Enumeration<String> getParameterNames():获取所有请求的参数名称 Enumeration<String> parameterNames = req.getParameterNames(); while (parameterNames.hasMoreElements()){ String element = parameterNames.nextElement(); System.out.println(element); String value = req.getParameter(element); //getParameter只返回一个数组首元素 System.out.println(value); } System.out.println("++++++++++++++++"); //4. Map<String,String[]> getParameterMap():获取所有参数的map集合 Map<String, String[]> parameterMap = req.getParameterMap(); Set<String> keySet = parameterMap.keySet(); for (String name : keySet) { String[] values = parameterMap.get(name); System.out.println(name); for (String value : values) { System.out.println(value); } System.out.println("--------------------"); } } }
/** **中文乱码问题** * get方式:tomcat 8 已经将get方式乱码问题解决了 * post方式:会乱码 * 解决:在获取参数前,设置request的编码request.setCharacterEncoding("utf-8"); 如下: 解决POST方式发送请求数据时的中文乱码问题 */ @WebServlet("/requestDemo7") public class RequestDemo7 extends HttpServlet{ @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8"); //编码格式与html一致 String username = req.getParameter("username"); System.out.println(username); } }
- 请求转发(Servlet之间的信息转发)
- 步骤:
- 通过request对象获取请求转发器对象,RequestDispatcher getRequestDispatcher (String path)
- 使用RequestDispatcher 对象进行转发forward(ServletRequest request,ServletResponse response)
- 特点:
- 浏览器地址栏路径不会发生变化
- 只能转发到当前服务器内部资源
- 转发是一次请求
- 步骤:
/** 请求转发:一种在服务器内部的资源跳转方式 */ @WebServlet("/requestDemo8") public class RequestDemo8 extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("RequestDemo8...."); req.getRequestDispatcher("/requestDemo9").forward(req,resp); } }
** 接收RequestDemo8转发的数据 */ @WebServlet("/requestDemo9") public class RequestDemo9 extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("RequestDemo9...."); } }
- 共享数据
- 域对象:有一个作用范围的数据,可以在访问内共享数据
- request域:代表一次请求的范围,一般用于请求转发的多个资源共享数据
方法:- void setAttribute(String name,Object obj):存储数据
- String getAttribute(String name):通过键获取值
- void removeAttribute(String name):通过键移除键值对
** 请求转发:一种在服务器内部的资源跳转方式 */ @WebServlet("/requestDemo8") public class RequestDemo8 extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("RequestDemo8...."); //存储数据至requesst域中 req.setAttribute("msg","hello I am requestDemo8"); req.getRequestDispatcher("/requestDemo9").forward(req,resp); } }
/** 接收RequestDemo8转发的数据 */ @WebServlet("/requestDemo9") public class RequestDemo9 extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("RequestDemo9...."); //req.removeAttribute("msg"); 移除存储在msg里面的键值对 Object msg = req.getAttribute("msg"); System.out.println(msg); } }
- 获取ServletContext
ServletContext getServletContext()
- 获取请求参数通用方式:不论get还是post请求方式都可以使用下列方法来获取请求参数
案例:实现登录功能
- 用户登录案例需求
- 编写login.html登录界面 username&password
- 使用Druid数据库连接池技术操作mysql数据库,对应的表是user表
- 使用JdbcTemplate封装JDBC
- 登录成功将用户信息存储到request域中转发到SuccessServlet,展示:登录成功!用户名,欢迎您
- 登录失败将用户信息存储到request域中转发到FailServlet,展示:登录失败,请重新输入如用户名和密码
如下图:
-
开发步骤:
- 创建项目,导入jar包,编写login.html界面
- 创建数据库环境
create database day04; user day04; create table login_user( id int primary key auto_increment, username varchar(32) unique not null, passwd varchar(32) not null ); insert into login_user(id,username,passwd) value(1,'liuzeyu','809080');
- 创建domain包,创建实体类User
/** * 实体类对象 */ public class User { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
- 创建Druid连接池
/** * JDBC工具类使用Druid连接池 */ public class JDBCUtil { private static DataSource ds; //加载配置文件 static { try { //使用Properties对象 Properties pro = new Properties(); //使用getClassLoader获取字节输入流 InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream("druid.properties"); pro.load(is); //初始化连接池对象 ds = DruidDataSourceFactory.createDataSource(pro); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } //获取连接池对象 public static DataSource getDataSource(){ return ds; } //获取Connection对象 public static Connection getConnection(DataSource ds) throws SQLException { return ds.getConnection(); } }
- userDao层,与数据可建立连接
** * 操作数据库的User表 */ public class UserDao { //使用JDBCTemplate对象公用 private JdbcTemplate jdbcTemplate = new JdbcTemplate(JDBCUtil.getDataSource()); /** * 登录方法 * @param loginUser 只用用户名和密码 * @return User包含用户全部数据 */ public User login(User loginUser){ //1.编写sql语句 /** * 注意:数据库的实体类属性一定要和查询的字段名称一一对应,因为查询到后要创建实体类对象 * 如果对于不上的话,将不一致的属性将被赋值为null */ try{ String sql = "select * from login_user where username= ? and password= ? "; User user = jdbcTemplate. queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), loginUser.getUsername(), loginUser.getPassword()); return user; }catch (Exception e){ return null; } } }
- 编写servlet接口实现
loginServlet:
@WebServlet("/loginServlet") public class LoginServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.设置编码 req.setCharacterEncoding("utf-8"); // //2.获取请求参数 // String username = req.getParameter("username"); // String password = req.getParameter("password"); // //3.创建User对象 // User loginUser = new User(); // //4.封装User对象 // loginUser.setUsername(username); // loginUser.setPassword(password); //以上注释部分可以使用BeanUtils替换,如下 Map<String, String[]> map = req.getParameterMap(); User loginUser = new User(); try { BeanUtils.populate(loginUser,map); //populate封装 } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } //调用userDao方法 UserDao dao = new UserDao(); User user = dao.login(loginUser); //判断数据库有没有查找到 if(user == null){ //向FaliServlet转发消息 req.getRequestDispatcher("/failServlet").forward(req,resp); }else{ //发送共享消息 req.setAttribute("user",user); req.getRequestDispatcher("/successServlet").forward(req,resp); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req,resp); } }
FailServlet:
@WebServlet("/failServlet") public class FailServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.设置编码 resp.setContentType("text/html;charset=utf-8"); //输出 resp.getWriter().write("登录失败,请重新输入如用户名和密码"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req,resp); } }
SuccessServlet:
@WebServlet("/successServlet") public class SuccessServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.设置编码 resp.setContentType("text/html;charset=utf-8"); //2.获取request域***享的user对象 User user = (User)req.getAttribute("user"); //3.输出 resp.getWriter().write("登录成功!"+user.getUsername()+",欢迎您"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req,resp); } }
项目地址:https://github.com/liuzeyu12a/Servlet_Login
response功能:设置响应消息数据
功能:设置响应消息
- 设置响应行
- 格式:HTTP/1.1 200 OK
- 设置状态码:setStatus(int src)
- 设置响应头:setHeader(String name,String value)
- 设置响应体
使用步骤:
1. 获取输入流
1. 字节输出流:PrintWriter getWriter();
2. 字节输入流:ServletOutputStream getOutputstream
2. 使用输入流,将数据输出到客户端浏览器
案例:
-
完成重定向
重定向:资源跳转的方式
代码实现://responseDemo1 获得服务器返回的302状态码 //responseDemo2获得服务器返回的200状态码 @WebServlet("/responseDemo1") public class ResponseDemo1 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // resp.setStatus(302); // resp.setHeader("location","/day05/responseDemo2"); resp.sendRedirect("/day05/responseDemo2"); //一行替换上面两行 } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req,resp); } }
常见的面试题:redirect与forward之间的区别?
1. 重定向(redirect)特点: 1. 浏览器地址栏发生变化 2. 重定向可以访问其它站点服务器资源 3. 重定向发送的是两次请求数据,不能使用request对象来共享数据 2. 转发(forward)特点: 1. 浏览器地址栏不发生变化 2. 只能访问服务器内部资源 3. 转发是一次请求,可以使用request实现servlet之间共享数据
- 相对路径:通过相对路径不可以确定唯一资源
- 如 ./index.html
- 不以/开头,以.开头
- 规则:重点是找到当前资源和目标资源直接的相对位置关系(可以通过比对不同)
- ./:当前目录,可以省略
- …/ 后退上一级目录
- 例如:
- http://192.168.0.105/day05/index.html
跳转到
http://192.168.0.105/day05/responseDemo2
因此可以在index.html写跳转地址href="./responseDemo2" - http://192.168.0.105/day05/htmls/index.html
跳转到
http://192.168.0.105/day05/responseDemo2
因此可以在index.html写跳转地址
href=". ./responseDemo2"
- http://192.168.0.105/day05/index.html
- 绝对路径:通过绝对路径可以确定唯一资源
- 如:http://localhost/day05/responseDemo2
- 以 /开头
- 规则:判断定义的路径是给谁用的?判断请求将从那里发出?
- 给客户端浏览器使用:需要加上虚拟目录(项目的访问路径)
- 建议虚拟目录动态获取:request.getContextPath()
- < a >,< form> html界面借助jsp动态获取,重定向
- 给服务器使用:不需要加上虚拟目录
例如:转发(forward)
- 给客户端浏览器使用:需要加上虚拟目录(项目的访问路径)
- 相对路径:通过相对路径不可以确定唯一资源
-
服务器输出字符数据到浏览器
步骤:- 获取字符输出流:PrintWriter pw = response.getWriter()
- 输出内容到浏览器:pw.write(“hello responseDemo3…哈哈”);
注意:乱码问题
PrintWriter pw = response.getWriter(); 获取字符输出流,由于tomcat默认的字符集是ISO-8859-1,所以浏览器无法进行解析中文,因此需要在获取输出流之前进行设置请求头的ContentType字段
@WebServlet("/responseDemo3")
public class ResponseDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.设置编码并通知浏览器以什么样的解码来解析流数据
resp.setContentType("text/html;charset=utf-8");
//2.获取字符输出流
PrintWriter writer = resp.getWriter();
//3.写入数据
writer.write("hello responseDemo3....哈哈");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
- 服务器输出字节数据到浏览器
步骤:- 获取字节输出流:ServletOutputStream sos = resp.getOutputStream();
- 输出内容到浏览器:sos.write(;byte[]);
/**
* 服务器输出字节数据到浏览器
*/
@WebServlet("/responseDemo4")
public class ResponseDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.设置编码并通知浏览器以什么样的解码来解析流数据
resp.setContentType("text/html;charset=utf-8");
//2.获取字节输出流
ServletOutputStream sos = resp.getOutputStream();
//写出数据
sos.write("hello responseDemo3....哈哈".getBytes("utf-8")); //设置获取字节流的编码
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
- 验证码
1. 本质:就是一张图片
2. 目的:防止恶意表单注册。例如:如果有人可以通过死循环进行表单注册,然后数据库就会内存溢出导致程序崩溃,嵌入二维码图片就可以拦截这一步。项目地址:https://github.com/liuzeyu12a/checkCode