1. HTTP
- HTTP:Hyper Text Transfer Protocol 超文本传输协议。
- 传输协议:定义了客户端和服务器端通信时,发送数据的格式。
- HTTP 协议的特点:
- 基于 TCP/IP 的高级协议
- 默认端口号:80
- 基于请求/响应模型的:一次请求对应一次响应
- 无状态的:每次请求之间相互独立,不能交互数据
- HTTP 版本:
- 1.0:每一次请求响应都会建立新的连接
- 1.1:复用连接
- 请求方式:HTTP 协议有 7 中请求方式,常用的有 2 种
- GET:
- 请求参数在请求行中,在 url 后
- 请求的 url 长度有限制的
- 不太安全
- 没有请求空行和请求体
- POST:
- 请求参数在请求体中
- 请求的 url 长度没有限制的
- 相对安全
- 有请求体,参数在请求体中
- GET:
- 请求消息包含请求行、请求头、请求空行、请求体。
- 请求行:请求方式 请求 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 为文件名。例如:文件下载
- Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式,例如:
- 响应空行:空行,用于分割响应头和响应体。
- 响应体:传输的数据。例如: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 编码。
- 对于以前版本,因参数封装在请求行(URL)中,无法通过 setCharacterEncoding 设置编码,获取参数的时候,获取的是
- post 方式:post 数据封装在流中,getParameter 方法实际还是通过流获取数据,当流解码所用编码与所传数据的编码方式不一样的时候,就会出现乱码。
request.setCharacterEncoding("utf-8");
:在获取参数前,设置 request 的编码,解决中文乱码,对 post 方法有效,因为设置的是请求体的获取数据的编码格式,post 方法的参数在请求体中,所以有效,get 方法的参数在请求行中,无法通过此方法设置获取请求行的编码。
- get 方式:tomcat8 已经将 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()
- 获取请求参数:get 和 post 请求方式通用
- 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); }
- JavaBean:标准的 Java 类
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();
- 通过 reques t 对象获取:
- MIME 类型:在互联网通信过程中定义的一种文件数据类型
- 格式:大类型/小类型,例如:
text/html
和image/jpeg
- 格式:大类型/小类型,例如:
- 功能:
- 获取 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 }
- 域对象:共享数据,范围:所有用户所有请求的数据
setAttribute(String name,Object value)
getAttribute(String name)
removeAttribute(String name)
- 获取文件的真实(服务器)路径
- 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 目录下的资源访问
- 获取 MIME 类型:
- 文件下载案例的中文乱码问题:
- 使用
response.setHeader("content-disposition","attachment;filename="+filename);
,去设置文件名的时候,文件是中文会出现文件乱码问题。 response.setContentType("text/html;charset=utf-8");
语句和response.setCharacterEncoding("utf-8")
是设置响应体的编码,并不能设置响应头的编码!- 可以利用第三方工具类根据请求头中的
User-Agent
参数来获取用户使用的浏览器,根据浏览器的不同对中文进行编码,来解决响应头的中文乱码问题。
- 使用