Request与Response

request对象与response对象的原理

如上图:

  1. request对象和response都是由tomcat服务器创建,由我们程序员来使用它。
  2. request对象使用来获取用户从浏览器发过来的请求信息,response对象是用来对服务器设置响应消息并返回给浏览器的。
  3. request对象继承体系:
    ServletRequest - - - -接口
    | 继承
    HttpServletRequest - - - 接口
    | 实现
    org.apache.catalina.connector.RequestFacade@2abb5747(tomcat实现类),这个类将为service方法创建request和response对象

request功能:获取请求消息数据

  1. 获取请求行数据
    • GET/day03/requestDemo1?username=liuzeyu HTTP/1.1
    • 方法:
      1. 获取请求方式 :GET
        String getMethod()

      2. 获取虚拟目录:/day03
        String getContextPath()

      3. 获取Servlet路径: /requestDemo1
        String getServletPath()

      4. 获取get方式请求参数:username=liuzeyu
        String getQueryString()

      5. (*)获取请求URI:/day03/requestDemo1
        String getRequestURI(): /day03/requestDemo1
        StringBuffer getRequestURL() :
        http://192.168.0.105/day03/requestDemo1

         1. URL与URI区别
         		1. URL:统一资源定位符 : http://192.168.0.105/day03/requestDemo1	中华人民共和国
         		2. URI:统一资源标识符 : /day03/requestDemo1	 共和国
        
      6. 获取协议及版本:HTTP/1.1
        * String getProtocol()

      7. 获取客户机的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);
    }
}
  1. 获取请求头数据
    • 方法:
      • (*)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

  1. 获取请求体数据

    • 请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数
    • 步骤:
      1. 获取流对象,方式由以下三种:

        • BufferedReader getReader():获取字符输入流,只能操作字符数据
        • ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据
        • 学到文件上传知识点
      2. 再从流对象中拿数据

    代码:

<!-- 表单数据:-->
<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);//按行读取
        }

    }
}
  1. request其它功能

    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集合
    **
     *	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);
        }
    }
    
    1. 请求转发(Servlet之间的信息转发)
      1. 步骤:
        1. 通过request对象获取请求转发器对象,RequestDispatcher getRequestDispatcher (String path)
        2. 使用RequestDispatcher 对象进行转发forward(ServletRequest request,ServletResponse response)
      2. 特点:
        1. 浏览器地址栏路径不会发生变化
        2. 只能转发到当前服务器内部资源
        3. 转发是一次请求
    /**
     请求转发:一种在服务器内部的资源跳转方式
     */
    @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....");
        }
    }
    
    1. 共享数据
      1. 域对象:有一个作用范围的数据,可以在访问内共享数据
      2. request域:代表一次请求的范围,一般用于请求转发的多个资源共享数据
        方法:
        1. void setAttribute(String name,Object obj):存储数据
        2. String getAttribute(String name):通过键获取值
        3. 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);
        }
    }
    
    1. 获取ServletContext
      ServletContext getServletContext()

案例:实现登录功能

  1. 用户登录案例需求
    1. 编写login.html登录界面 username&password
    2. 使用Druid数据库连接池技术操作mysql数据库,对应的表是user表
    3. 使用JdbcTemplate封装JDBC
    4. 登录成功将用户信息存储到request域中转发到SuccessServlet,展示:登录成功!用户名,欢迎您
    5. 登录失败将用户信息存储到request域中转发到FailServlet,展示:登录失败,请重新输入如用户名和密码

如下图:

  1. 开发步骤:

    1. 创建项目,导入jar包,编写login.html界面
    2. 创建数据库环境
    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');
    
    1. 创建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;
        }
    }
    
    1. 创建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();
        }	
    }
    
    
    1. 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;
            }
        }
    }
    
    1. 编写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功能:设置响应消息数据

功能:设置响应消息

  1. 设置响应行
    1. 格式:HTTP/1.1 200 OK
    2. 设置状态码:setStatus(int src)
  2. 设置响应头:setHeader(String name,String value)
  3. 设置响应体
    使用步骤:
    1. 获取输入流
    1. 字节输出流:PrintWriter getWriter();
    2. 字节输入流:ServletOutputStream getOutputstream
    2. 使用输入流,将数据输出到客户端浏览器

案例

  1. 完成重定向
    重定向:资源跳转的方式
    代码实现:

    //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之间共享数据
    
    1. 相对路径:通过相对路径不可以确定唯一资源
      1. 如 ./index.html
      2. 不以/开头,以.开头
      3. 规则:重点是找到当前资源和目标资源直接的相对位置关系(可以通过比对不同)
        1. ./:当前目录,可以省略
        2. …/ 后退上一级目录
        3. 例如:
          1. http://192.168.0.105/day05/index.html
            跳转到
            http://192.168.0.105/day05/responseDemo2
            因此可以在index.html写跳转地址href="./responseDemo2"
          2. http://192.168.0.105/day05/htmls/index.html
            跳转到
            http://192.168.0.105/day05/responseDemo2
            因此可以在index.html写跳转地址
            href=". ./responseDemo2"
    2. 绝对路径:通过绝对路径可以确定唯一资源
      1. 如:http://localhost/day05/responseDemo2
      2. 以 /开头
      3. 规则:判断定义的路径是给谁用的?判断请求将从那里发出?
        1. 给客户端浏览器使用:需要加上虚拟目录(项目的访问路径)
          1. 建议虚拟目录动态获取:request.getContextPath()
          2. < a >,< form> html界面借助jsp动态获取,重定向
        2. 给服务器使用:不需要加上虚拟目录
          例如:转发(forward)
  2. 服务器输出字符数据到浏览器
    步骤:

    1. 获取字符输出流:PrintWriter pw = response.getWriter()
    2. 输出内容到浏览器: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);
    }
}
  1. 服务器输出字节数据到浏览器
    步骤:
    1. 获取字节输出流:ServletOutputStream sos = resp.getOutputStream();
    2. 输出内容到浏览器: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. 验证码
    1. 本质:就是一张图片
    2. 目的:防止恶意表单注册。例如:如果有人可以通过死循环进行表单注册,然后数据库就会内存溢出导致程序崩溃,嵌入二维码图片就可以拦截这一步。

    项目地址:https://github.com/liuzeyu12a/checkCode