HTTP 协议

HTTP 版本

  1. HTTP 1.0 一次连接只能获取一个资源

  2. HTTP 1.1 一次连接可以获取多个资源

HTTP 请求

请求的结构:

请求行包括:请求方式 请求资源名称 使用协议版本。注意这里的请求资源名称里面是会包含 ?XXX=XXX&XXX=XXX 这样的 GET 请求参数的。然后下面的请求实体会可能会包含 POST 请求参数 XXX=XXX&XXX=XXX

常用请求头:

Accept

Accept: text/html 浏览器可以接受服务器回发的类型为 text/html。

Accept: */* 代表浏览器可以处理所有类型,(一般浏览器发给服务器都是发这个)。

Accept-Encoding

Accept-Encoding: gzip, deflate 浏览器申明自己接收的编码方法,通常指定压缩方法,是否支持压缩,支持什么压缩方法(gzip,deflate),注意:这不是只字符编码。

Accept-Language

Accept-Language: zh-CN, zh; q=0.9  浏览器申明自己接收的语言。

Connection

Connection: keep-alive 当一个网页打开完成后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。

Connection: close 代表一个 Request 完成后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接会关闭, 当客户端再次发送 Request,需要重新建立 TCP 连接。

Host(发送请求时,该报头域是必需的)

Host: www.baidu.com 请求报头域主要用于指定被请求资源的 Internet 主机和端口号,它通常从HTTP URL中提取出来的。

Referer

Referer: https://www.baidu.com/?tn=62095104_8_oem_dg 当浏览器向web服务器发送请求的时候,一般会带上 Referer,告诉服务器我是从哪个页面链接过来的,服务器籍此可以获得一些信息用于处理。

User-Agent

User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36 告诉HTTP服务器,客户端使用的操作系统和浏览器的名称和版本。

Cache-Control

Cache-Control: private 默认为 private  响应只能够作为私有的缓存,不能再用户间共享。

Cache-Control: public 响应会被缓存,并且在多用户间共享。正常情况,如果要求 HTTP 认证,响应会自动设置为 private。

Cache-Control: must-revalidate 响应在特定条件下会被重用,以满足接下来的请求,但是它必须到服务器端去验证它是不是仍然是最新的。

Cache-Control: no-cache 响应不会被缓存,而是实时向服务器端请求资源。

Cache-Control: max-age=10 设置缓存最大的有效时间,但是这个参数定义的是时间大小(比如:60)而不是确定的时间点。单位是[秒 seconds]。

Cache-Control: no-store 在任何条件下,响应都不会被缓存,并且不会被写入到客户端的磁盘里,这也是基于安全考虑的某些敏感的响应才会使用这个。

Cookie: XXXXXXXXXXXXXXXXXXXXXXXXXXXX 是用来存储一些用户信息以便让服务器辨别用户身份的(大多数需要登录的网站上面会比较常见),比如 cookie 会存储一些用户的用户名和密码,当用户登录后就会在客户端产生一个 cookie 来存储相关信息,这样浏览器通过读取 cookie 的信息去服务器上验证并通过后会判定你是合法用户,从而允许查看相应网页。当然 cookie 里面的数据不仅仅是上述范围,还有很多信息可以存储是 cookie 里面,比如 sessionid 等。

Range(用于断点续传)

Range: bytes=0-5 指定第一个字节的位置和最后一个字节的位置。用于告诉服务器自己想取对象的哪部分。

HTTP 响应

状态行包括:通信协议 状态代码 状态信息

常见状态代码和状态信息:响应代码和状态信息手册

Cache-Control(对应请求中的Cache-Control)

Cache-Control: private 默认为private 响应只能够作为私有的缓存,不能再用户间共享。

Cache-Control: public 浏览器和缓存服务器都可以缓存页面信息。

Cache-Control: must-revalidate 对于客户机的每次请求,代理服务器必须想服务器验证缓存是否过时。

Cache-Control: no-cache 浏览器和缓存服务器都不应该缓存页面信息。

Cache-Control: max-age=10 是通知浏览器10秒之内不要烦我,自己从缓冲区中刷新。

Cache-Control: no-store 请求和响应的信息都不应该被存储在对方的磁盘系统中。

Content-Type

Content-Type:text/html; charset=UTF-8 告诉客户端,资源文件的类型,还有字符编码,客户端通过 UTF-8 对资源进行解码,然后对资源进行 HTML 解析。通常我们会看到有些网站是乱码的,往往就是服务器端没有返回正确的编码。

Content-Encoding

Content-Encoding: gzip 告诉客户端,服务端发送的资源是采用 gzip 编码的,客户端看到这个信息后,应该采用 gzip 对资源进行解码。

Date

Date: Tue, 03 Apr 2018 03:52:28 GMT 这个是服务端发送资源时的服务器时间,GMT 是格林尼治所在地的标准时间。HTTP 协议中发送的时间都是 GMT 的,这主要是解决在互联网上,不同时区在相互请求资源的时候,时间混乱问题。

Server

Server:Tengine/1.4.6  这个是服务器和相对应的版本,只是告诉客户端服务器信息。

Transfer-Encoding

Transfer-Encoding:chunked 这个响应头告诉客户端,服务器发送的资源的方式是分块发送的。一般分块发送的资源都是服务器动态生成的,在发送时还不知道发送资源的大小,所以采用分块发送,每一块都是独立的,独立的块都能标示自己的长度,最后一块是0长度的,当客户端读到这个0长度的块时,就可以确定资源已经传输完了。

Expires

Expires: Sun, 1 Jan 2000 01:00:00 GMT 这个响应头也是跟缓存有关的,告诉客户端在这个时间前,可以直接访问缓存副本,很显然这个值会存在问题,因为客户端和服务器的时间不一定会都是相同的,如果时间不同就会导致问题。所以这个响应头是没有 Cache-Control:max-age=* 这个响应头准确的,因为 max-age=date 中的 date 是个相对时间,不仅更好理解,也更准确。

Last-Modified

Last-Modified: Dec, 26 Dec 2015 17:30:00 GMT 所请求的对象的最后修改日期(按照 RFC 7231 中定义的“超文本传输协议日期”格式来表示)

Connection

Connection:keep-alive 这个字段作为回应客户端,告诉客户端服务器的 TCP 连接也是一个长连接,客户端可以继续使用这个 TCP 连接发送 HTTP 请求。

Etag

ETag: "737060cd8c284d8af7ad3082f209582d" 就是一个对象(比如 URL )的标志值,就一个对象而言,比如一个 HTML 文件,如果被修改了,其 Etag 也会别修改,所以,ETag 的作用跟 Last-Modified 的作用差不多,主要供 WEB 服务器判断一个对象是否改变了。比如前一次请求某个 HTML 文件时,获得了其 ETag,当这次又请求这个文件时,浏览器就会把先前获得 ETag 值发送给 WEB 服务器,然后 WEB 服务器会把这个 ETag 跟该文件的当前 ETag 进行对比,然后就知道这个文件有没有改变了。

Refresh

Refresh: 5; url=http://baidu.com 用于重定向,或者当一个新的资源被创建时。默认会在5秒后刷新重定向。

Access-Control-Allow-Origin

Access-Control-Allow-Origin: * * 号代表所有网站可以跨域资源共享,如果当前字段为 * 那么 Access-Control-Allow-Credentials 就不能为 true。

Access-Control-Allow-Origin: www.baidu.com 指定哪些网站可以跨域资源共享

Access-Control-Allow-Methods

Access-Control-Allow-Methods:GET, POST, PUT, DELETE 允许哪些方法来访问

Access-Control-Allow-Credentials

Access-Control-Allow-Credentials: true 是否允许发送 Cookie。默认情况下,Cookie 不包括在CORS 请求之中。设为 true,即表示服务器明确许可,Cookie 可以包含在请求中,一起发给服务器。这个值也只能设为 true,如果服务器不要浏览器发送 Cookie,删除该字段即可。如果 access-control-allow-origin 为 *,当前字段就不能为 true。

Content-Range

Content-Range: bytes 0-5/7877 指定整个实体中的一部分的插入位置,他也指示了整个实体的长度。在服务器向客户返回一个部分响应,它必须描述响应覆盖的范围和整个实体长度。

路径解析问题

base 标签

在 HTML 种,有一个 base 标签的:<base herf="http://localhost:8080/WebModule/"> 它可以定义相对路径跳转的前面部分,然后页面里面所有使用相对路径的跳转,都会加上 base 里面设置的 herf 组成一个绝对路径。

/ 的解析

对于服务器和客户端对相对路径的最前面的 / 解析是不同的,对于服务器来说这里会解析到当前 Servlet 所属的 Web 工程的目录,比如 http://localhost:8080/WebModule/,而对于客户端来说,会解析到当前主机的地址如 http://localhost:8080/

请求 HttpServletResquest

Tomcat 回将请求封装到 HttpServletResquest 类的实例中,并传入 service() 方法中,然后分流到 doPost() 和 doGet()。我们可以通过 HttpServletRequest 获取大量请求的相关信息。

编码

一般来说,互联网上发送的各种数据都是使用 UTF-8 来编码的,但因为 Tomcat7 及以下版本默认使用 iso-8859-1 来解码使用 URL 传输的文字,导致对于 Get 请求来的信息会乱码,那么为了解决这个问题,有两种方式:

  1. 设置 Tomcat 的 URL 解码方式为 UTF-8,在 server.xml 里面的 HTTP/1.1 的 <Connector> 标签里面加入属性: URIEncoding="UTF-8"

  2. 在通过 HttpServletResquest 获取 URL 中的中文信息(GET 参数)的时候,将获取的字符串用 iso-8859-1 编码为原本的 UTF-8 byte[] 信息,然后再用 UTF-8 编码解码这些 byte[] 为 String 即可。

对于 Post 比较简单,没有 Tomcat 乱解码,无脑标注使用 UTF-8 解码就可以了:request.setCharacterEncoding("UTF-8");

请求信息

getXXXX() 系列,可以获取各种请求头和 Get / Post 参数:

其中对于一个 name 对应多个 value 的参数,需要使用 getgetParameterValues() 获取 String[]。

设置传递信息

在进行信息转发的时候,因为整个过程传递的都是一个 HttpServletResquest 对象,所以可以在当前 Servlet 存储入一些信息,让其他的 Servlet 可以从这个对象中获取信息。

具体的设置方法就是使用 setAttribute(Name,Value) 保存信息,然后在转发到的 Servlet使用 getAttribute(Name) 获取保存的信息。

响应 HttpServletResponse

编码

首先对于要操作 HttpServletResponse 之前,先设置好编码:

  1. 无脑推荐版本:使用 setContentType("UTF-8") 来设置。这个方法不仅设置了输出信息使用 UTF-8 编码,还自动会添加响应头 Content-Type: text/html; charset=UTF-8 ,一步解决全部问题。

  2. 自己手动全部设置:先设置信息编码格式: setCharactorEncoding("UTF-8") 然后设置响应头:setHeader("Content-Type","text/html; charset=UTF-8") 这个比较麻烦,还是无脑用第一个吧。

输出

用一堆 set 方法设置响应头以外,就是输出响应体了。

响应体有两种方式:getWriter() getOutputStream() 获取 Write 实例和 OutputStream 实例来写响应体。

对于字符,可以使用 Write 来写,对于非字符(二进制文件)使用 OutputStream 来写。但是一定要注意的的是,这两个东西,只能同时持有一个实例,如果要用另一个,就需要 close() 关闭资源才可以。

这是因为如果同时持有两个输出实例,那么两者的输出合并就会成为一个大问题,所以为了避免混乱,就设计成两者互斥,只能同时持有一个输出实例。

请求重定向

  1. 无脑推荐: sendRedirect(/XXX/XX) 里面填入重定向页面。(会自动设置响应代码和重定向响应头)

  2. 自己手动设置响应代码和重定向头: setStatus(302) setHeader("Location","XXX/XX")