HTTP(超文本传输协议:Hyper Text Transfer Protocol)是一种应用层协议,主要用于在Web浏览器和服务器之间传输超文本(如HTML页面),也可以用于服务端与服务端之间的通信。它是一个无状态的请求-响应协议,客户端发送请求,服务器返回响应。
特点:
- 简单易用,广泛支持。
- 支持多种方法(GET、POST、PUT、DELETE等)。
- 支持RESTful API设计。
- 可以通过HTTPS提供安全传输。
- 基于TCP/IP的高级协议
- 默认端口号:80
- 基于请求/响应模型的:一次请求对应一次响应
- 无状态的:每次请求之间相互独立,不能交互数据。适合短连接的交互。
在1.0版本:每一次请求响应都会建立新的连接
在1.1版本:支持连接复用(当前主流)
适用场景:
- Web应用程序和API服务。
- 适用于短连接、无状态的请求-响应模式。
- 适用于需要简单、快速开发的应用。
1、请求消息格式
(1)、客户端发送给服务器端的数据
组成:
请求行,请求头,请求空行,请求体四个部分。
1、请求行(固定格式:请求方式 请求URI,请求协议/版本)
示例:
GET /login.html HTTP/1.1
2、请求头(告诉服务器一些信息,键值对形式)
常见的请求头:
- User-Agent:浏览器告诉服务器,使用的浏览器版本信息
- 可以在服务器端获取该头的信息,解决浏览器的兼容性问题
- Referer:http://localhost/login.html
- 告诉服务器,我(当前请求)从哪里来?主要作用有两个:防盗链和统计工作。
3、请求空行(一个空行)
用于分割POST请求的请求头,和请求体的
4、请求体(正文)
封装POST请求消息的请求参数的。(get方式会追加到url尾部)
5、完整HTTP请求消息示例
post请求示例:
POST /login.html HTTP/1.1 // 请求行
Host: localhost // 请求头
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://localhost/login.html
Connection: keep-alive
Upgrade-Insecure-Requests: 1
// 请求空行
username=zhangsan // 请求体
解释:
- 请求行指定了这是一个POST请求,请求的URI地址为/login.html,使用的是HTTP 1.1协议版本。
- Host: 指定请求的目标主机名和端口号(如果没有指定端口号,默认为 80)。
- User-Agent: 提供了客户端是浏览器的信息。这里的信息表明请求是由 Firefox 浏览器发起的,操作系统是 Windows 7(Windows NT 6.1),并且浏览器版本是 60.0。
- Accept: 客户端可以接受的内容类型。在这个例子中,客户端优先接受 text/html、application/xhtml+xml 和 application/xml 类型的内容。q 参数表示质量值(quality value),范围从 0 到 1,值越大表示优先级越高。/;q=0.8 表示客户端也可以接受其他任何类型的内容,但优先级较低。
- Accept-Language: 客户端可以接受的语言。这里指定了几种语言及其优先级:简体中文(zh-CN)、繁体中文(zh-TW 和 zh-HK)以及英语(en-US 和 en),其中 zh-CN 的优先级最高。
- Accept-Encoding: 客户端可以接受的内容编码方式。这里表示客户端支持 gzip 和 deflate 压缩格式,这有助于减少传输的数据量。
- Referer: 表示当前请求是从哪个页面发起的。在这个例子中,请求是从 http://localhost/login.html 发起的,通常用于跟踪用户的浏览历史。
- Connection: 指定连接是否保持打开状态。keep-alive 表示客户端希望在请求完成后保持连接打开,以便后续请求可以重用这个连接,从而提高性能。
- Upgrade-Insecure-Requests: 这个头部告诉服务器客户端希望升级不安全的请求(即通过 HTTP 发出的请求)到 HTTPS。值为 1 表示启用此功能。
- 中间的空行为请求空行,必须存在。
- 请求体(Body): 在 POST 请求中,数据通常放在请求体中。在这个例子中,请求体包含了一个简单的键值对 username=zhangsan,表示用户名字段的值是 zhangsan。实际应用中,登录表单可能还会包含密码等其他字段。
get请求示例:
GET /search?author=J.K.Rowling&category=fantasy&sort=price_asc&page=1 HTTP/1.1
Host: bookstore.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://bookstore.example.com/home
Connection: keep-alive
Upgrade-Insecure-Requests: 1
解释:
get请求和post请求参数整体上是差不多的,主要区别在于get请求的参数追加在URI后面,所以get请求没有请求体,因此也不需要请求空行了。
(2)、Request对象和使用
服务器端创建的,用来获取客户端发出的请求。
1、常用方法
//1. 获取请求方式 :GET
String method = request.getMethod();
System.out.println(method);
//2.(*)获取上下文:/day14
String contextPath = request.getContextPath();
System.out.println(contextPath);
//3. 获取Servleturl路径: /demo1
String servletPath = request.getServletPath();
System.out.println(servletPath);
//4. 获取get方式请求参数:name=zhangsan
String queryString = request.getQueryString();
System.out.println(queryString);
//5.(*)获取请求URI:/day14/demo1
String requestURI = request.getRequestURI(); // URI可控制所有IP的请求
StringBuffer requestURL = request.getRequestURL(); // URL可控制指定IP的请求
//6. 获取协议及版本:HTTP/1.1
String protocol = request.getProtocol();
System.out.println(protocol);
//7. 获取客户端的IP地址:
String remoteAddr = request.getRemoteAddr();
System.out.println(remoteAddr);
注意:
URL: 统一资源定位符。示例如:http://localhost/day14/demo1
URI: 统一资源标识符。示例如:/day14/demo1
2、获取请求头信息
//1.获取所有请求头名称
Enumeration<String> headerNames = request.getHeaderNames();
//2.遍历
while(headerNames.hasMoreElements()){
String name = headerNames.nextElement();
//根据名称获取请求头的值
String value = request.getHeader(name);
System.out.println(name+"---"+value);
}
3、防盗链示例
String referer = request.getHeader("referer");
System.out.println(referer); // 如:http://localhost/day14/login.html
if(referer != null ){
if(referer.contains("/day14")){
// 正常访问
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("播放电影....");
}else{
// 盗链,重定向
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("想看电影吗?来优酷吧...");
}
}
4、获取请求参数信息
// 获取指定名称参数值(多个仅获取第一个)
String username = request.getParameter("username");
// 获取指定名称参数数组(前端传多个时都可获取)
String[] hobbies = request.getParameterValues("hobby");
// 获取所有参数的map集合
Map<String, String[]> parameterMap = request.getParameterMap();
Set<String> keyset = parameterMap.keySet(); // keys
for (String name : keyset) {
//获取键获取值
String[] values = parameterMap.get(name); // values
System.out.println(name);
for (String value : values) {
System.out.println(value);
}
System.out.println("-----------------");
}
5、请求转发
request.getRequestDispatcher("/requestDemo9").forward(request,response);
请求转发说明:
- 浏览器地址栏路径不发生变化。
- 只能转发到当前服务器内部资源中。跨域不能转发
- 转发是一次请求。(可以使用request对象进行共享数据)
2、响应消息格式
(1)、服务器端发送给客户端的数据
组成:响应行,响应头,响应空行,响应体四个部分。
1、响应行(协议/版本 响应状态码 状态码描述)
示例:
HTTP/1.1 200 OK
解释:
状态码分类:
– 1xx:服务器接收客户端消息,但没有接收完成,等待一段时间后,发送1xx多状态码
– 2xx:响应成功,返回200;部分成功返回206等。
– 3xx:重定向。代表:302(重定向),304(访问缓存)
– 4xx:客户端错误。404:请求路径没有对应的资源。405:请求方式没有对应的doXxx方法等。
– 5xx:服务器端错误。代表:500(服务器内部出现异常)
2、响应头(告诉浏览器一些信息,键值对形式)
常见的响应头:
Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式
Content-disposition:服务器告诉客户端以什么格式打开响应体数据
- in-line:默认值,在当前页面内打开
- attachment;filename=xxx:以附件形式打开响应体。文件下载
3、 响应空行(一个空行)
用于分割响应头和响应体
4、响应体(正文)
传输的数据
5、完整响应示例:
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)、Response对象
服务器创建的对象,用来封装返回消息。
1、重定向示例
// 动态获取上下文
String contextPath = request.getContextPath();
// 简单的重定向
response.sendRedirect(contextPath+"/responseDemo2"); // 重定向相同域名
//response.sendRedirect("http://www.itcast.cn"); // 可以重定向到不同域
解释:(重定向和转发区别)
重定向的特点:(redirect)
- 地址栏发生变化
- 重定向可以访问其他站点(即:可跨服务访问)的资源
- 重定向是两次请求。不能使用request对象来共享数据
转发的特点:(forward) - 转发地址栏路径不变
- 转发只能访问当前服务器下的资源
- 转发是一次请求,可以使用request对象来共享数据
2、返回字符类型响应(如:返回字符串)
//获取流对象之前,设置流的默认编码:ISO-8859-1 设置为:GBK
response.setCharacterEncoding("utf-8");
//告诉浏览器,服务器发送的消息体数据的编码。建议浏览器使用该编码解码
response.setHeader("content-type","text/html;charset=utf-8");
// 设置content-type的简单的形式,实际相当于上一句的设置请求头
response.setContentType("text/html;charset=utf-8");
// 获取字符输出流
PrintWriter pw = response.getWriter();
// 设置字符串到响应体中
pw.write("你好啊啊啊 response");
//pw.write("<h1>hello response</h1>");
返回字节类型响应(如:返回图片)
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); // 边框也占1个像素,所以最后要减去1
String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789";
//生成随机角标
Random ran = new Random();
for (int i = 1; i <= 4; i++) {
int index = ran.nextInt(str.length());
char ch = str.charAt(index);//随机字符
//2.3写验证码
g.drawString(ch+"",width/5*i,height/2); // 字符值,和字符坐标
}
// 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());
3、ServletContext对象
ServletContext代表整个web应用,可以和运行程序的容器(服务器)来通信。
(1)、获取方式
1、通过request对象获取
request.getServletContext();
2、通过HttpServlet获取
this.getServletContext();
解释:
两者皆可获取,获取为同一个对象。使用时选择其中一个就行。
(2)、主要功能
1、获取MIME类型
MIME类型:在互联网通信过程中定义的一种文件数据类型。
格式: 大类型/小类型 如: text/html image/jpeg
示例:
//1、通过HttpServlet获取
ServletContext context = this.getServletContext();
//2、定义文件名称
String filename = "a.jpg";
//3、获取MIME类型
String mimeType = context.getMimeType(filename); // 返回:image/jpeg
2、域对象
(1)、共享数据
通过ServletContext可以设置和获取全局变量。不过通常不建议使用,要谨慎使用。
示例:
setAttribute(String name,Object value) // 设置全局属性
getAttribute(String name) // 获取全局属性
removeAttribute(String name) // 移除全局属性
(2)、获取文件的真实(服务器)路径
ServletContext相对ClassLoader据有更全面的类型加载,后者仅限于src目录下的java文件。前者可以加载整个web服务下的所有文件。
示例:
// 通过HttpServlet获取
ServletContext context = this.getServletContext();
// 获取文件的服务器路径
String b = context.getRealPath("/b.txt"); //web目录下资源访问(与WEB-INF同级目录的文件)
String c = context.getRealPath("/WEB-INF/c.txt"); //WEB-INF目录下的资源访问
String a = context.getRealPath("/WEB-INF/classes/a.txt"); //src目录下的资源访问
(3)、综合示例
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 一、获取文件,生成文件字节流
String filename = request.getParameter("filename"); // 入参文件名称
ServletContext servletContext = this.getServletContext(); // 获取web对象
String realPath = servletContext.getRealPath("/img/" + filename); // web对象获取到文件的真实路径
FileInputStream fis = new FileInputStream(realPath); // 获取文件的字节流
// 二、response设置,文件类型和输出方式
String mimeType = servletContext.getMimeType(filename); // 获取文件的mime类型
response.setHeader("content-type",mimeType); // 设置输出文件的mime类型
//解决中文文件名乱码问题
//1. 获取user-agent请求头,包含浏览器版本信息,可以识别请求发起的浏览器信息
String agent = request.getHeader("user-agent");
//2. 针对不同浏览器处理名称,防止中文乱码
filename = getFileName(agent,filename);
response.setHeader("content-disposition","attachment;filename="+filename); // 设置浏览器以文件方式打开响应值
// 三、文件流写入response字节流中
ServletOutputStream sos = response.getOutputStream();
byte[] buff = new byte[1024 * 8];
int len = 0;
while((len = fis.read(buff)) != -1){
sos.write(buff,0,len);
}
fis.close();
}
public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {
if (agent.contains("MSIE")) {
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
// 火狐浏览器
BASE64Encoder base64Encoder = new BASE64Encoder();
filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
4、会话技术–Cookie和Session
会话:
客户端和服务器之间的一次连接访问过程,包含多次请求和响应。
会话特点:
在一次会话的范围内的多次请求间,共享数据。
主要技术:
- 客户端会话技术:Cookie
- 服务器端会话技术:Session
(1)、Cookie技术
客户端会话技术,将数据保存到客户端。
1、原理
服务端在response中设置set- cookie,客户端在收到响应头包含set-cookie后,会自动添加该信息到会话的cookie中,之后会话中的每次的请求都会包含这个cookie的请求头信息。
代码示例:(服务端)
//1.创建Cookie对象
Cookie c = new Cookie("msg","hello");
//2.设置Cookie
response.addCookie(c); // 会返回set-cookie的响应头给客户端,值为msg:hello
//3. 获取Cookie
Cookie[] cs = request.getCookies();
//获取数据,遍历Cookies
if(cs != null){
for (Cookie c : cs) {
String name = c.getName();
String value = c.getValue();
System.out.println(name+":"+value);
}
}
2、使用细节
一次性可以设置多个cookie值:
Cookie c1 = new Cookie("msg","hello");
Cookie c2 = new Cookie("name","zhangsan");
response.addCookie(c1);
response.addCookie(c2);
设置保存时间:
默认情况下,当浏览器关闭后,Cookie数据被销毁,也可以自定义设置cookie的有效时间。
自定义设置cookie有效时间可以使用setMaxAge(int seconds)方法。
参数示例:
- 正数:将Cookie数据写到硬盘的文件中。持久化存储。并指定cookie存活时间,时间到后,cookie文件自动失效(注意:即使关闭浏览器再次打开,cookie也会在有效时间内存在)
- 负数:默认值(关闭浏览器,cookie消失)
- 0:删除cookie信息
//1.创建Cookie对象
Cookie c1 = new Cookie("msg","hahaha");
//2.设置cookie的存活时间
//c1.setMaxAge(30);// 将cookie持久化到硬盘,30秒后会自动删除cookie文件
//c1.setMaxAge(-1); // 默认,可不设置。关闭浏览器cookie失效
c1.setMaxAge(0);// 删除Cookie,通知浏览器立刻删除cookie
//3.发送Cookie
response.addCookie(c1);
cookie中是否能使用中文?
Tomcat 8之前不能存储中文;tomcat8及之后可以存中文。
如果tomcat8之前存中文,或存一些特色的字符,如空格怎么办?
解决方法:通过URL编码处理
// URL编码
str_date = URLEncoder.encode(str_date,"utf-8");
// URL解码:
String value = cookie.getValue();
value = URLDecoder.decode(value,"utf-8");
cookie共享问题:
默认不能实现共享。但是可以通过一些配置实现共享。
如果服务部署在同一个tomcat中:
setPath(String path):设置cookie的获取范围。
如果要共享,则可以将path设置为"/“。
如果服务部署在不同的tomcat中:
setDomain(String path):如果设置一级域名相同,那么多个服务器之间cookie可以共享。
解释:
setDomain(”.baidu.com"),那么tieba.baidu.com和news.baidu.com中cookie可以共享。
cookie大小和限制:
单个cookie 的大小有限制4kb(解释:1kb可以存1024个字节,即可以存1024个英文字符或512个中文字符)内,同一个域名最多不能超过20个。
(2)、session技术
服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中。如:HttpSession。
示例:
//1.获取session
HttpSession session = request.getSession();
//2.存储数据
session.setAttribute("msg","hello session");
//3.获取数据
Object msg = session.getAttribute("msg");
System.out.println(msg);
1、原理
Session的实现是依赖Cookie的。
服务器使用session时,会判断是否存在cookie(cookie携带唯一的JSESSIONID才能确保服务器能正确找到会话的session),如果没有cookie或者cookie不包含JSESSIONID,服务端则会给浏览器添加set-cookie:JSESSIONID=xxx的返回响应头,同时生成session对象,标记id为xxx。 之后浏览器携带cookie:JSESSIONID=xxx的请求头再次访问,后台则可以正确拿到session对象。
2、使用细节
当客户端关闭后再次打开客户端,服务器不关闭,两次获取session是否为同一个吗?
默认情况下不是同一个session,因为关闭浏览器会清除cookie缓存。
如何设置为同一个的方案:
创建Cookie,键为JSESSIONID,设置最大存活时间,让cookie持久化保存。
Cookie c = new Cookie("JSESSIONID",session.getId());
c.setMaxAge(60*60);
response.addCookie(c);
session什么时候被销毁?
(1)、服务器关闭
(2)、session对象调用invalidate() 。
(3)、session默认失效时间 30分钟。
示例:
选择性配置修改(tomcat的web.xml中配置,默认分钟)
<session-config>
<session-timeout>30</session-timeout>
</session-config>
乘风破浪会有时,直挂云帆济沧海!!!