目录
HTTP协议
Web应用中的基本单位是Web资源。Web资源分布在网络中的任意主机上,所以本机的浏览器要获取这些Web资源,就必须通过一种网络协议——HTTP协议。
HTTP协议是以Web资源为基本单位。
每次请求-响应只能针对一个Web资源进行。
HTTP的发展
HTTP: Hyper(超)Text (文本)Transfer(传输)Protocol(协议)
HTTP协议设计肇始,是为了传输超文本(HTML的前身),随着互联网的发展,HTTP协议传输的不仅仅是超文本了,目前其实任意类型都可以。围绕着超文本这套类型发展出来的前端三剑客(HTML、CSS、JavaScript)还是其主流;除此之外,其他多媒体资源也是非常常见的:图片、音频、视频等。
HTTP协议是一套应用层协议(一开始专注于业务,但现在HTTP协议本身也成为基础协议之一了),所以HTTP协议必须在一套传输层协议上进行传输。传输层的候选协议:UDP、TCP。
HTTP在1.0、1.1、2.0是基于TCP协议之上进行工作的。(HTTP协议中本身没有设计任何提供可靠性的机制,依赖传输层及以下网络已经提供可靠服务)
HTTP 在3.0之后,开始基于UDP协议上进行了。(HTTP协议内部自己做可靠性工作了)HTTP协议的客户端,一般称为浏览器,同时又被称为用户代理(User-Agent)等名称,网景浏览器可以说是最早的浏览器了。
Web资源
Web资源:某些内容或者是执行一些动作,以服务的形式体现。购买一本书(书就是 web资源)、发个快递(发快递动作是web资源)。
HTTP协议的完整流程
重点是理解HTTP格式:请求格式和响应格式
请求
客户端
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; /** * 只能进行一次请求-响应的 HTTP 客户端 */ public class MyHttpClient { public static void main(String[] args) throws IOException { // 主机 127.0.0.1 // 端口 8080 // 资源路径 /hello.html try (Socket socket = new Socket("127.0.0.1", 8080)) { // 准备 HTTP 请求内容 // 文本 String // 格式:请求行 String requestLine = "GET /hello.html HTTP/1.0\r\n"; // 请求头:完全可以没有,但必须一个空行结尾 // 请求头中共有 1对 key-value String requestHeader1 = "Name: xxx\r\n\r\n"; // 请求头中共有 2对 key-value String requestHeader2 = "Name: xxx\r\nAge: 1993\r\n\r\n"; // 请求头中共有 0 对 key-value String requestHeader3 = "\r\n"; // 请求体,GET 是没有请求体 // 最终的请求 —— 要发送给服务器的东西 String request = requestLine + requestHeader3; // 发送服务器的过程 byte[] requestBytes = request.getBytes("ISO-8859-1"); // 字符集编码 // 发送(数据会经由 TCP、IP、以太网发送给服务器) OutputStream os = socket.getOutputStream(); os.write(requestBytes); os.flush(); // 请求既然已经发送,我们要做的就是等待响应 InputStream is = socket.getInputStream(); // 响应的前面字符集应该是 ISO-8859-1,后边是 UTF-8 Scanner scanner = new Scanner(is, "UTF-8"); while (scanner.hasNextLine()) { String line = scanner.nextLine(); System.out.println(line); } } } }
响应
服务器
import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class MyHttpServer { public static void main(String[] args) throws Exception { // 我们也监听在 8080 端口上 try (ServerSocket serverSocket = new ServerSocket(8080)) { while (true) { Socket socket = serverSocket.accept(); // 三次握手完成 // 读取用户的请求 :这里就不管用户的请求了,一律采用相同的方式返回响应 // 发送响应 // Content-Type: 浏览器应该按照什么格式来看到我们响应的资源内容的(资源内容放在响应体中) // 响应体(资源的内容) String responseBody = "<h1>Welcome MyHttpServer</h1>"; byte[] responseBodyBytes = responseBody.getBytes("UTF-8"); int contentLength = responseBodyBytes.length; System.out.println("发送响应"); // 响应头 String response = "HTTP/1.0 200 OK\r\n" + "Server: xxx\r\n" + "Content-Type: text/plain; charset=utf-8\r\n" // 响应体的类型 + "Content-Length: " + contentLength + "\r\n" // 响应体的长度 + "\r\n"; byte[] responseBytes = response.getBytes("ISO-8859-1"); OutputStream os = socket.getOutputStream(); // TCP 是面向字节流的一种协议,所以只要按顺序发即可,不要管分几次发送 os.write(responseBytes); // 先发送前面部分(响应行 和 响应头) os.write(responseBodyBytes); // 再发送响应体 os.flush(); // 发送完成之后,直接关闭 TCP 连接(短连接的处理模式) socket.close(); } } } }
URI和URL
经常将ip(域名)+port 合起来称为主机 ( host)
资源路径
动态资源
URL字符的要求
HTTP请求中的关键信息
1.请求方法
2.请求的资源路径3.请求版本
4.请求头
5.可能存在的请求体
请求方法
演示1:通过<form>发送POST 请求
![]()
实验中涉及了两个web 资源
/post-form.html(静态资源)我们采用GET请求方法
/demo(动态)我们采用POST请求方法
演示2:
ajax请求/xhr请求
重点!
通过JavaScript 发起HTTP请求——俗称ajax请求/xhr请求
我们使用比较频繁,尤其是在现代 web开发的前后端分离开发模式下。
使用方式(以下代码是JavaScript代码)︰
// 1. 实例化一个 XMLHttpRequest 对象,简称 XHR 对象 var xhr = new XMLHttpRequest() // JS 语法下的实例化对象 // 2. 调用 对象 的 open(...) 方法,设置 发起请求的 1)方法 2)资源路径 //xhr.open('get', '/demo') // GET /demo xhr.open('post', '/demo') // POST /demo // 3. 设置回调函数,当 /demo 资源的响应返回时,应该干什么 // 通过事件绑定机制 // 事件源:xhr 对象 // 事件:load 事件(当响应返回时) // 事件处理:回调函数 xhr.onload = function() { console.log(xhr.status) // 打印响应的状态(成功是 200) console.log(xhr.responseText) // 打印响应的响应体 // 当然响应的其他信息我们都可以获取到,只是一般不关心 } // 4. 真正发送请求出去 xhr.send()
<!DOCTYPE html> <html lang="zh-hans"> <head> <meta charset="UTF-8"> <title>发起 ajax 请求</title> </head> <body> <!-- js 代码无法直接运行,必须挂靠在一个 html 上才可以 --> <!-- 浏览器会启动 GET /js/ajax-demo.js --> <!-- 并且在 JS 响应后运行 JS 代码 --> <!-- 运行代码过程中,会发 ajax 请求 GET /demo --> <!-- 最终是 3 个 HTTP 请求响应 --> <!-- GET /ajax-demo.html --> <!-- GET /js/ajax-demo.js --> <!-- GET /demo --> <script src="/js/ajax-demo.js"></script> </body> </html>
执行顺序
这些代码中有的是JS代码,有的是Java 代码;有的运行在浏览器进程中,有的运行在Tomcat 进程。但这些不重要的,重要的这些语句会按照一定的顺序执行。
请问执行顺序是什么∶
答:
非常常见的一道面试题
幂等性
面试题小结
演示3: 使用ajax发送post+携带请求体的请求
GET在query string中无论携带什么数据,总体上还是得遵守key=value的形式。但post则非常灵活。
使用ajax发送post+携带请求体的请求。
@Controller public class MyController { @RequestMapping("/collect") @ResponseBody public String collect() { return "OK"; } }
var xhr = new XMLHttpRequest() xhr.open('post', '/collect') xhr.onload = function() { console.log(xhr) console.log(this) } // 区别在这里 xhr.setRequestHeader('Content-Type', 'text/xxxxxxx') xhr.send('我随便写,按照 content-type 的格式去写就行')
<!DOCTYPE html> <html lang="zh-hans"> <head> <meta charset="UTF-8"> <title>发送有请求体的 ajax 请求</title> </head> <body> <script src="/js/ajax-send-request-body.js"></script> </body> </html>
form表单逻辑上只是一种提交数据的形式。但实际中使用较多,所以form提交的一些行为,代替了get/post 请求的行为。
HTTP响应中的关键信息
重定向
重定向分类
同理,通过 ajax发起HTTP 404或者其他错误,你是无法在页面中直接看到错误的,所以打开开发者工具去查看。
响应头
form表单提交
总之,文件上传时(通过form表单上传),必须指定enctype为multipart/form-data这样,请求体中才不仅仅携带要上传文件的文件名,还包含文件内容信息。这样,后端才可以读取到信息,做进—步处理。