1. HTTP

  • HTTP:Hyper Text Transfer Protocol 超文本传输协议。
  • 传输协议:定义了客户端和服务器端通信时,发送数据的格式。
  • HTTP 协议的特点:
    • 基于 TCP/IP 的高级协议
    • 默认端口号:80
    • 基于请求/响应模型的:一次请求对应一次响应
    • 无状态的:每次请求之间相互独立,不能交互数据
  • HTTP 版本:
    • 1.0:每一次请求响应都会建立新的连接
    • 1.1:复用连接
  • 请求方式:HTTP 协议有 7 中请求方式,常用的有 2 种
    • GET:
      • 请求参数在请求行中,在 url 后
      • 请求的 url 长度有限制的
      • 不太安全
      • 没有请求空行和请求体
    • POST:
      • 请求参数在请求体中
      • 请求的 url 长度没有限制的
      • 相对安全
      • 有请求体,参数在请求体中
  • 请求消息包含请求行、请求头、请求空行、请求体。
  • 请求行:请求方式 请求 url 请求协议/版本,GET /login.html HTPP/1.1,URL 不包括域名和端口。
  • 请求头:客户端浏览器告诉服务器一些消息,包括 IP/域名和端口。常见的请求头:
    • User-Agent:浏览器告诉服务器,我访问你使用的浏览器版本信息,可以在服务器端获取该头的信息,解决浏览器的兼容性问题。
    • Referer:告诉服务器,当前请求从哪里来?可以用来防盗链,通过浏览器直接访问网站的时候,请求头中不包括此行。
  • 请求空行:空行,用于分割 POST 请求的请求头和请求体。
  • 请求体(正文):封装 POST 请求消息的请求参数的。
  • HTTP 请求消息的例子:
    POST /javaweb_war_exploded/demo HTTP/1.1
    Host: localhost:8080
    Connection: keep-alive
    Content-Length: 15
    Cache-Control: max-age=0
    Origin: http://localhost:8080
    Upgrade-Insecure-Requests: 1
    Content-Type: application/x-www-form-urlencoded
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
    Referer: http://localhost:8080/javaweb_war_exploded/
    Accept-Encoding: gzip, deflate, br
    Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
    Cookie: JSESSIONID=4816F18659114CE9B38A053472E37E6B; Idea-2348c56=459a55fc-5646-4033-866a-eab90e249b20
    
    username=123456
    

  • 响应消息:服务器端发送给客户端的数据,包含响应行、响应头、响应空行、响应体。
  • 响应行:协议/版本 响应状态码 状态码描述,例如:HTTP/1.1 200 OK
  • 响应状态码:都是 3 位数字
    • 1xx:请求已被接受,需要继续处理,服务器发送 1xx 状态码询问客户端
    • 2xx:成功。例如:200
    • 3xx:重定向。例如:302(重定向),304(访问缓存)
    • 4xx:客户端错误。例如:404(请求路径没有对应的资源),405(请求方式没有对应的 doXxx 方法)
    • 5xx:服务器端错误。例如:500(服务器内部出现异常)
  • 响应头:
    • 格式:头名称: 值
    • 常见的响应头:
      • Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式,例如:Content-Type: text/html;charset=UTF-8
      • Content-disposition:服务器告诉客户端以什么格式打开响应体数据
        • in-line:默认值,在当前页面内打开
        • attachment;filename=xxx:以附件形式打开响应体,filename 为文件名。例如:文件下载
  • 响应空行:空行,用于分割响应头和响应体。
  • 响应体:传输的数据。例如:HTML 文档、图片
  • 响应消息例子:
    HTTP/1.1 200 OK
    Content-Type: text/html;charset=UTF-8
    Content-Length: 101
    Date: Wed, 06 Jun 2018 07:08:42 GMT
    
    <html>
      <head>
        <title>$Title$</title>
      </head>
      <body>
        hello , response
      </body>
    </html>
    

2. Request 和 Response 原理介绍

  • URI 和 URL :

    • URI (uniform resource identifier),统一资源标识符,URI 是一种抽象的高层次概念定义统一资源标识,URI 可以是绝对的,也可以是相对的。
    • URL (uniform resource locator),统一资源定位符,URL 是具体的资源标识的方式,是一种具体的 URI,URL 必须是绝对的,URL 不仅唯一标识资源,还提供了定位资源的信息。
  • request 对象和 response 对象的原理

    • request 和 response 对象是由服务器创建的,我们只是来使用它们
    • request 对象是来获取请求消息(请求消息封装在 request 中),response 对象是来设置响应消息。

3. Request

  • request 对象继承体系结构:org.apache.catalina.connector.RequestFacade实现类(tomcat) implements interface HttpServletRequest extends interface ServletRequest
  • 获取请求消息数据功能
    • 获取请求行数据
      • String getMethod():获取请求方式
      • String getContextPath():获取项目的虚拟目录,重点掌握
      • String getServletPath():获取 Servlet 路径,/demo1
      • String getQueryString():获取 get 方式请求参数,name=zhangsan&age=12
      • String getRequestURI():获取请求 URI,/day14/demo1,重点掌握
      • StringBuffer getRequestURL():获取请求 URL,http://localhost/day14/demo1
      • String getProtocol():获取协议及版本,HTTP/1.1
      • String getRemoteAddr():获取客户机的 IP 地址
    • 获取请求头数据
      • String getHeader(String name):通过请求头的名称获取请求头的值,不区分大小写,重点掌握
      • Enumeration<String> getHeaderNames():获取所有的请求头名称
    • 获取请求体数据
      • 只有 POST 请求方式,才有请求体,在请求体中封装了 POST 请求的请求参数
      • 请求体被封装在流对象中,通过 request 获取流对象,然后取出参数
      • BufferedReader getReader():获取字符输入流,只能操作字符数据
      • ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据
  • 其他功能
    • 获取请求参数:get 和 post 请求方式通用
      • 通用的好处:通用以后,get 请求的处理逻辑和 post 请求的处理逻辑无差别,可以在 doGet 方法中调用 doPost 方法
      • String getParameter(String name):根据参数名称获取参数值
      • String[] getParameterValues(String name):根据参数名称获取参数值的数组,适用于一个参数对应多个值的情况
      • Enumeration<String> getParameterNames():获取所有请求的参数名称
      • Map<String,String[]> getParameterMap():获取所有参数的 map 集合
      • 中文乱码问题:
        • get 方式:tomcat8 已经将 get 方式乱码问题解决了
          • 对于以前版本,因参数封装在请求行(URL)中,无法通过 setCharacterEncoding 设置编码,获取参数的时候,获取的是 iso-8859-1 编码的,需要通过 new String(param.getBytes("iso-8859-1"), "utf-8"); 还原回 utf-8 编码。
        • post 方式:post 数据封装在流中,getParameter 方法实际还是通过流获取数据,当流解码所用编码与所传数据的编码方式不一样的时候,就会出现乱码。
          • request.setCharacterEncoding("utf-8");:在获取参数前,设置 request 的编码,解决中文乱码,对 post 方法有效,因为设置的是请求体的获取数据的编码格式,post 方法的参数在请求体中,所以有效,get 方法的参数在请求行中,无法通过此方法设置获取请求行的编码。
    • 请求转发:一种在服务器内部的资源跳转方式
      • RequestDispatcher getRequestDispatcher(String path):通过 request 对象获取请求转发器对象。
      • forward(ServletRequest request, ServletResponse response):使用 RequestDispatcher 对象来进行转发。
      • 浏览器地址栏路径不发生变化
      • 只能转发到当前服务器内部资源中,不能转发至外部 URL
      • 转发是一次请求
    • 共享数据
      • 域对象:一个有作用范围的对象,可以在范围内共享数据。
      • request 域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据。
      • void setAttribute(String name,Object obj):存储数据
      • Object getAttitude(String name):通过键获取值
      • void removeAttribute(String name):通过键移除键值对
    • 获取 ServletContext
      • ServletContext getServletContext()
  • HTML 中 form 表单访问 Servlet 时,action 路径写法:虚拟路径 + Servlet 的资源路径
  • BeanUtils 工具类,apache 公司提供的,用于封装 JavaBean 的,可以简化数据封装。
    • JavaBean:标准的 Java 类
      • 类必须被 public 修饰
      • 必须提供空参的构造器
      • 成员变量必须使用 private 修饰
      • 提供公共 setter 和 getter 方法
    • 类中成员变量和属性的不同:
      • 成员变量:在类中定义的变量
      • 属性:setter 和 getter 方法截取后的产物,例如 getUsername() --> Username--> username,username 就是属性。
    • 从前端获取的 name 属性值,需要与 JavaBean 中成员变量名称一样,一样的值才会被填充,不一样的不会被填充,其值为默认值。
    • 方法:
      • setProperty(Object bean, String name, Object value):设置属性,为 JavaBean 重新赋值。
      • String getProperty(Object bean, String name):获取属性
      • populate(Object obj , Map map):将 map 集合的键值对信息,封装到对应的 JavaBean 对象中
    • 使用例子:
      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      	User loginUser = new User();
      	request.setCharacterEncoding("UTF-8");
      
      	/*String username = request.getParameter("username");
      	String password = request.getParameter("password");
      	loginUser.setUsername(username);
      	loginUser.setPassword(password);*/
      	//等价
      	Map<String, String[]> map = request.getParameterMap();
      	BeanUtils.populate(loginUser, map);
      }
      

4. Response

  • 设置响应行:
    • 格式:HTTP/1.1 200 ok
    • 设置状态码:setStatus(int sc)
  • 设置响应头:setHeader(String name, String value),不区分大小写
  • 设置响应体:
    • 响应体需要用流输出
    • 获取输出流
      • 字符输出流:PrintWriter getWriter()
      • 字节输出流:ServletOutputStream getOutputStream()
    • 使用输出流,将数据输出到客户端浏览器
    • 关于输出的乱码问题:getWriter() 获取的流是 Tomcat 提供的,默认编码为 ISO-8859-1,输出中文的时候,我们应当设置流的编码,并且告诉浏览器编码所用的字符集。
      • 设置流的字符集,需要在获取流之前设置,也就是说语句需要写在获取流之前,response.setCharacterEncoding("utf-8");
      • 告诉浏览器所用编码,需要设置响应头属性:response.setHeader("content-type","text/html;charset=utf-8");
      • 其实上面语句不仅可以告诉浏览器所用编码,也可以设置获取流的编码,所以可以省略第一条语句。
      • 为了方便起见,使用此语句 response.setContentType("text/html;charset=utf-8"); ,作用与上述相同
  • 重定向例子:
    //1. 设置状态码为302
    response.setStatus(302);
    //2.设置响应头location
    response.setHeader("location","/day15/responseDemo2");
    
  • 简单的重定向方法:response.sendRedirect("/day15/responseDemo2"); 与上面两个语句的作用相同,一般使用这种方法。
  • 重定向的特点:
    • 地址栏发生变化
    • 重定向可以访问其他站点(服务器)的资源
    • 重定向是两次请求。不能使用 request 对象来共享数据
  • request.forward() 为转发,特点与重定向相反。

5. 路径写法

  • 介绍项目中访问某个资源,如转发的时候,重定向的时候,action 中的路径怎么写。
  • 项目中路径写法分为相对路径和绝对路径。
  • 相对路径:通过相对路径不可以确定唯一资源。
    • 写相对路径需要确定当前所访问的路径和要访问路径的相对关系,比如当前路径为 http://localhost:8080/project1/login.html,现在要通过 action 访问 Servlet 路径 /loginServlet,则 action 中可以写相对路径 action="./loginServlet",等价于 action="loginServlet",因为访问此 Servlet 需要访问的路径为 http://localhost:8080/project1/loginServlet,与 login.html 处于同一级目录(project1 目录)下。
    • 同理,转发的时候,假设当前位于 Servlet 的 /loginServlet 下,其 URL 为 http://localhost:8080/project1/loginServlet,想要转发至 /successServlet,其 URL 为 http://localhost:8080/project1/successServlet,两者处于同一级目录下,可以写 forward("./successServlet") 或者 forward("successServlet")
  • 绝对路径:通过绝对路径可以确定唯一资源,在项目中的绝对路径即为 / 开头的路径。如,http://localhost:8080/project1/loginServlet,其绝对路径为 /project1/loginServlet
    • 那么问题来了,啥时候绝对路径加虚拟目录,啥时候不加?
    • 判断此请求是从哪发出的,从浏览器发出,此路径就是给浏览器用的,从服务器发出,此路径就是给服务器用的。
    • 如 a 和 form 标签,其中的请求就是从浏览器发出的,路径就是给浏览器用的。
    • 转发的时候,请求是从服务器发出的,路径就是给服务器用的。
    • 给客户端浏览器使用的时候需要加虚拟目录(项目的访问路径),给服务器使用的时候不需要加虚拟目录
    • 所以,a 和 form 中写相对路径的时候,就需要写成 /project1/loginServlet,转发使用相对路径的时候,就需要写成 /loginServlet
    • JSP 中建议用绝对路径。
    • 使用绝对路径的时候,为了防止项目的虚拟路径更改,可以使用 request.getContextPath() 动态获取虚拟路径,例如:response.sendRedirect(request.getContextPath() + "/loginServlet");

6. 验证码案例

  • package rainsheep;
    
    import javax.imageio.ImageIO;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.awt.*;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.util.Random;
    
    @WebServlet("/checkCodeServlet")
    public class CheckCodeServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            int width = 100;
            int height = 50;
            //1.创建一对象,在内存中图片(验证码图片对象)
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            //2.美化图片
            //2.1 填充背景色
            Graphics g = image.getGraphics();//画笔对象
            g.setColor(Color.PINK);//设置画笔颜色
            g.fillRect(0, 0, width, height);
    
            //2.2画边框
            g.setColor(Color.BLUE);
            g.drawRect(0, 0, width - 1, height - 1);
    
            String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789";
            //生成随机角标
            Random ran = new Random();
            StringBuilder sb = new StringBuilder();
            for (int i = 1; i < 5; i++) {
                int index = ran.nextInt(str.length());
                //获取字符
                char ch = str.charAt(index);
                sb.append(ch);
                //2.3写验证码
                g.drawString(ch + "", width / 5 * i, height / 2);
            }
            String checkCode_session = sb.toString();
            //将验证码存入session
            request.getSession().setAttribute("checkCode_session", checkCode_session);
            //2.4画干扰线
            g.setColor(Color.GREEN);
            //随机生成坐标点
            for (int i = 0; i < 10; i++) {
                int x1 = ran.nextInt(width);
                int x2 = ran.nextInt(width);
                int y1 = ran.nextInt(height);
                int y2 = ran.nextInt(height);
                g.drawLine(x1, y1, x2, y2);
            }
            //3.将图片输出到页面展示
            ImageIO.write(image, "jpg", response.getOutputStream());
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request, response);
        }
    }
    

7. ServletContext 对象

  • 代表整个 Web 应用,可以和程序的容器(服务器)来通信。
  • 获取方式:
    • 通过 reques t 对象获取:request.getServletContext();
    • 通过 HttpServlet 获取:this.getServletContext();
  • MIME 类型:在互联网通信过程中定义的一种文件数据类型
    • 格式:大类型/小类型,例如:text/htmlimage/jpeg
  • 功能:
    1. 获取 MIME 类型:String getMimeType(String file),类型的映射关系已经在 Tomcat 的 web.xml 定义好了,所以可以通过此对象来获取 MIME 类型。
      protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      	ServletContext servletContext = getServletContext();
      	String ans = servletContext.getMimeType("a.jpg");
      	System.out.println(ans);//image/jpeg
      }
      
    2. 域对象:共享数据,范围:所有用户所有请求的数据
      • setAttribute(String name,Object value)
      • getAttribute(String name)
      • removeAttribute(String name)
    3. 获取文件的真实(服务器)路径
      • class 和 classLoader 只能获取 src 目录下的文件,不能获取 Web 目录下的文件。
      • 此对象可以获取整个项目所有目录下的文件
      • String getRealPath(String path)
      • getRealPath("/"); 所对应的路径为 Tomcat 中的项目(web)所在的真实路径,例如:Tomcat 的 webapps 目录下或者配置的 doBase 所指向的目录,例如 IDEA 中 Tomcat 配置了 doBase :C:\Users\10766\Desktop\mycode\IDEA\workspace\out\artifacts\javaweb_war_exploded\
      • context.getRealPath("/b.txt"); Web 目录下资源访问
      • context.getRealPath("/WEB-INF/c.txt"); WEB-INF 目录下的资源访问
      • context.getRealPath("/WEB-INF/classes/a.txt"); src 目录下的资源访问
  • 文件下载案例的中文乱码问题:
    • 使用 response.setHeader("content-disposition","attachment;filename="+filename);,去设置文件名的时候,文件是中文会出现文件乱码问题。
    • response.setContentType("text/html;charset=utf-8"); 语句和 response.setCharacterEncoding("utf-8") 是设置响应体的编码,并不能设置响应头的编码!
    • 可以利用第三方工具类根据请求头中的 User-Agent 参数来获取用户使用的浏览器,根据浏览器的不同对中文进行编码,来解决响应头的中文乱码问题。