头部字段
请求行或状态行再加上头部字段集合就构成了 HTTP 报文里完整的请求头或响应头。
- 通用报文头。可以用在请求头和响应头中。
- 请求报文头。仅能出现在请求头里,进一步说明请求信息或者额外的附加条件。
- 响应报文头。仅能出现在响应头里,补充说明响应报文的信息。
- 实体报文头。它实际上属于通用字段,但专门描述 body 的额外信息。
在http/1.1中一共规范了47种报文头字段。
一些常见报文头字段
- host: HTTP/1.1 里唯一要求必须提供的头字段是 Host,它必须出现在请求头里,标记虚拟主机名。表示相同ip下的不同域名,让服务器返回当前指定的host域名下的资源。
- Date 字段是一个通用字段,但通常出现在响应头里,表示 HTTP 报文创建的时间,客户端可以使用这个时间再搭配其他字段决定缓存策略。
- Server 字段是响应字段,只能出现在响应头里。它告诉客户端当前正在提供 Web 服务的软件名称和版本号。一般服务器响应头里都返回一个不相关的信息。防止被攻击。
- Content-Length,它表示报文里 body 的长度,也就是请求头或响应头空行后面数据的长度。服务器看到这个字段,就知道了后续有多少数据,可以直接接收。如果没有这个字段,那么 body 就是不定长的,需要使用 chunked 方式分段传输。
http请求方法
请求方法是客户端发出的、要求服务器执行的、对资源的一种操作。请求方法是对服务器的“指示”,真正应如何处理由服务器来决定。HTTP/1.1 规定了八种方法,单词都必须是大写的形式
- GET: 取资源,可以理解为读取或者下载数据;
- HEAD:获取资源的元信息。服务器不返回实体部分。
- POST:向资源提交数据,相当于写入或上传数据;
- PUT:类似 POST;
- DELETE:删除资源;
- CONNECT:建立特殊的连接隧道;
- OPTIONS:列出可对资源实行的方法;
- TRACE:追踪请求 - 响应的传输路径。
安全和幂等
- 安全。指请求方法不会“破坏”服务器上的资源,即不会对服务器上的资源造成实质的修改。
按照这个定义,只有 GET 和 HEAD 方法是“安全”的,因为它们是“只读”操作,只要服务器不故意曲解请求方法的处理方式,无论 GET 和 HEAD 操作多少次,服务器上的数据都是“安全的”。而 POST/PUT/DELETE 操作会修改服务器上的资源,增加或删除数据,所以是“不安全”的。
- 幂等。多次执行相同的操作,结果也都是相同的,即多次“幂”后结果“相等”。 很显然,GET 和 HEAD 既是安全的也是幂等的,DELETE 可以多次删除同一个资源,效果都是“资源不存在”,所以也是幂等的。按照 RFC 里的语义,POST 是“新增或提交数据”,多次提交数据会创建多个资源,所以不是幂等的。而 PUT 是“替换或更新数据”,多次更新一个资源,资源还是会第一次更新的状态,所以是幂等的。
URI和URL
uri的一般形式
- scheme: 协议名,方案名。表示资源应该使用哪种协议来访问。例如http, https, ftp、ldap、file、news等。
- authority: 表示资源所在的主机名,通常的形式是“host:port”,即主机名加端口号。有时端口号会被省略。HTTP 的默认端口号是 80,HTTPS 的默认端口号是 443。
- path:标记资源所在位置。必须以
/
开头。
- query: 表示对资源附加的额外要求。 注意file协议之后,允许省略主机名,默认是主机localhost。但对于 HTTP 或 HTTPS 这样的网络通信协议,主机名是绝对不能省略的。会导致浏览器无法找到服务器。
客户端和服务器看到的 URI 是不一样的。客户端看到的必须是完整的 URI,使用特定的协议去连接特定的主机,而服务器看到的只是报文请求行里被删除了协议名和主机名的 URI。
uri的完整形式
- User:passwd@ : 身份信息,但是不推荐这样使用,泄露重要信息 。
- #fragment :片段标识符。标识URI所定位的资源内部的一个锚点,浏览器可以跳转到它指示的位置。服务器看不到#fragment。
url编码
如果url中出现了非ASCII码以外的字符或者url中保留字符,那么它将被编码。
把字符(unicode)编码成utf-8,utf-8是用1-4个字节表示的,所以每个字节转换成16进制并在前面用百分号(%)连接,最后并把每个字节转换的结果连接起来。但是在浏览器输入框中并不会显示转以后的字符。
状态码
状态码在响应报文里表示了服务器对请求的处理结果。 使用以表示网页服务器超文本传输协议响应状态的3位数字代码。
状态码的整体分类
- 1××:提示信息,表示目前是协议处理的中间状态,还需要后续的操作;
- 2××:成功,报文已经收到并被正确处理;
- 3××:重定向,资源位置发生变动,需要客户端重新发送请求;
- 4××:客户端错误,请求报文有误,服务器无法处理;
- 5××:服务器错误,服务器在处理请求时内部发生了错误。
常见的状态码
- 1xx 101 Switching Protocols。它的意思是客户端使用 Upgrade 头字段,要求在 HTTP 协议的基础上改成其他的协议继续通信,比如 WebSocket。而如果服务器也同意变更协议,就会发送状态码 101,但这之后的数据传输就不会再使用 HTTP 了。
- 2xx 204 No Content。它的含义与“200 OK”基本相同,但响应头后没有 body 数据。所以对于 Web 服务器来说,正确地区分 200 和 204 是很必要的。
- 3xx 301 和 302 都会在响应头里使用字段 Location 指明后续要跳转的 URI,最终的效果很相似,浏览器都会重定向到新的 URI。两者的根本区别在于语义,一个是“永久”,一个是“临时”,所以在场景、用法上差距很大。
304 Not Modified 它用于 If-Modified-Since 等条件请求,表示资源未修改,用于缓存控制。它不具有通常的跳转含义,但可以理解成“重定向已到缓存的文件”(即“缓存重定向”)。
- 4xx 400 Bad Request 是一个通用的错误码,表示请求报文有错误,但具体是数据格式错误、缺少请求头还是 URI 超长它没有明确说,只是一个笼统的错误。所以,在开发 Web 应用时应当尽量避免给客户端返回 400,而是要用其他更有明确含义的状态码。
405 Method Not Allowed:不允许使用某些方法操作资源,例如不允许 POST 只能 GET;
406 Not Acceptable:资源无法满足客户端请求的条件,例如请求中文但只有英文;
408 Request Timeout:请求超时,服务器等待了过长的时间;
409 Conflict:多个请求发生了冲突,可以理解为多线程并发时的竞态;
413 Request Entity Too Large:请求报文里的 body 太大;
414 Request-URI Too Long:请求行里的 URI 太大;
429 Too Many Requests:客户端发送了太多的请求,通常是由于服务器的限连策略;
431 Request Header Fields Too Large:请求头某个字段或总体太大;
- 5xx 503 Service Unavailable 表示服务器当前很忙,暂时无法响应服务,我们上网时有时候遇到的“网络服务正忙,请稍后重试”的提示信息就是状态码 503。
503 是一个“临时”的状态,很可能过几秒钟后服务器就不那么忙了,可以继续提供服务,所以503 响应报文里通常还会有一个“Retry-After”字段,指示客户端可以在多久以后再次尝试发送请求。
字符集与编码
编码规范
- 字库表:他主要是存放该编码对应的字符。
- 字符集:存放的是字符编码的二进制地址。他和字库表中的字符一一对应。
- 编码方式: 这个编码规范对应的编码格式。 一个二进制数据通过指定的编码方式将其转化成字符集中对应的地址,然后再在字库表中找到对应的字符。
常见的编码规范
- ASCII码:只是对应的ASCII码中的字符。
- GBK:存放日韩中全部的字符。
- ISO-8859-1: 这个编码规范中不存在中文。
- Unicode: 这个包括全球的字符。
URL编码
浏览器仅对url中的非ASCII码字符进行编码。并且也对保留字进行编码。
一般,我们从网上下东西,然后名字中有汉语,那么将使用这种编码规则进行编码。
一些思考题
- 如果拼 HTTP 报文的时候,在头字段后多加了一个 CRLF,导致出现了一个空行,会发生什么? 在header 下面第一个空行以后都会被当作body 体
- 讲头字段时说“:”后的空格可以有多个,那为什么绝大多数情况下都只使用一个空格呢? 头部多一个空格就会多一个传输的字节,去掉无用的信息,保证传输的头部字节数尽量小
- HTTP 协议允许在在请求行里使用完整的 URI,但为什么浏览器没有这么做呢? 因为请求头部字段中host已经标识了请求的主机名,请求行中也表示了请求的协议了。所以为了减少传输量,没必要。
- URI 的查询参数和头字段很相似,都是 key-value 形式,都可以任意自定义,那么它们在使用时该如何区别呢? query参数针对的是资源(uri),而字段针对的是本次请求,也就是报文。 一个是长期、稳定的,一个是短期、临时的。
- 你在开发 HTTP 客户端,收到了一个非标准的状态码,比如 4××、5××,应当如何应对呢? 给个错误页面、或者弹窗报个错误,然后做跳转。
- 你在开发 HTTP 服务器,处理请求时发现报文里缺了一个必需的 query 参数,应该如何告知客户端错误原因呢? http响应报文一般都是200,也有400的,并且在响应体中给出提示。