美文网首页
HTTP必学必会

HTTP必学必会

作者: chaos4fun | 来源:发表于2018-04-25 14:57 被阅读21次

    做Web开发最离不开的就是HTTP协议了.前后端交互的基本方式就是发送和响应HTTP请求,所以前端和后端都要了解HTTP协议.下面从使用者的角度来看下HTTP最基本的一些概念.

    从名称来看http的特点

    先来看下HTTP的全名,Hypertext Transfer Protocol 超文本传输协议.从名称来看这个协议的目的是用来传输超文本的.(这里需要注意下,把 transfer 翻译做 传输 其实是不准确的,因为传输容易让人误以为HTTP协议是传输层协议,实际上HTTP是一种应用层协议.鉴于 传输 这个翻译已经沿用了很多年,这里就继续沿用了,只是提醒大家别被误导).

    超文本就是 相互之间有联系 的各种文本,图片,音视频等等.

    超文本

    超文本的特性和传输超文本这一目的决定了HTTP的特性

    1. 无状态
    2. 请求/响应模式
    3. 简单,易扩展

    无状态是指每次文档传输都是独立的,与上一次传输了什么或者后续要传什么都没有关系,每次请求都是独立的.

    请求/响应模式对应的是客户端/服务器模式.从服务器角度来看我这里有很多文档,我哪知道你要看什么,你想看什么你告诉我啊.所以需要客户端先请求,然后服务端再响应.

    请求/响应模式

    只有简单的东西才不容易出错,易于实现,学习,使用和推广.但是简单也不能没能力啊,所以要易扩展,方便以后加功能.下面从HTTP消息的结构来看下HTTP是怎么做的简单,易扩展的

    HTTTP消息的结构

    start-line
    *( header-field CRLF )
    CRLF
    [ message-body ]
    

    HTTP消息有三个部分,第一部分表示消息的语义和协议版本号,第二部分表示消息的元信息也叫做头信息,第三部分表示具体消息也叫做消息体.一/二部分之间用一个换行分割,二/三部分之间用两个换行分割.
    这里为什么是两个换行,因为第二部分表示的是消息体的元信息,比如消息的格式之类的.为了方便以后扩展,头信息的个数没有规定死,头信息之间也是用换行分割的,所以用两个连续的换行来表示头信息部分结束了.
    在请求和响应中第二三部分是一样的,只有第一部分稍有不同.在请求时叫做请求行,包含 请求方法 空格 URL 空格 协议和版本号.在响应时叫做响应行,包含 协议和版本后 空格 状态码 空格 可选的状态码简单解释.
    头信息的格式是 key: value,消息体的格式由头信息来定.

    下面看个例子,
    客户端请求:

     GET /hello.txt HTTP/1.1
     User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
     Host: www.example.com
     Accept-Language: en, mi
    

    服务器响应:

     HTTP/1.1 200 OK
     Date: Mon, 27 Jul 2009 12:28:53 GMT
     Server: Apache
     Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
     ETag: "34aa387-d-1568eb00"
     Accept-Ranges: bytes
     Content-Length: 51
     Vary: Accept-Encoding
     Content-Type: text/plain
    
     Hello World! My payload includes a trailing CRLF.
    

    注意点: HTTP消息大体上是不区分大小写的,但是有几处特别的地方是区分大小写的.这些地方有请求行中的 请求方法(大写), 协议和版本号(大写), URL中的路径部分

    从实用角度来聊一聊HTTP消息各个部分的作用

    第一部分

    作为客户端,你是想简单获取内容呢,还是想修改或者删除一部分内容呢.你需要用不同的方法来告诉服务器你的各种目的.获取内容用GET,删除用DELETE,添加和修改用PUT和POST方法.这里需要注意下PUT和POST都既可以创建又可以修改服务器资源,但他们的语义稍有不同,这里先略过,我们再后续的章节再细聊.

    告诉了服务器你要怎么样之后,还得告诉服务器你要对谁怎样.这就需要URL了,用URL来标识某一个具体的文档或资源.URL就是用来标识一个具体资源的,类似操作系统里的文件路径,这里就不详细展开了

    URL

    需要注意的是HTTP第一行的路径部分只包含/dir/index.html?uid=1这一段信息.其它部分放在了头信息里.也就是HTTP消息的第二部分.

    HTTP协议也要发展啊,也会有一些变化,所以你要告诉对方你遵守的是那一版的HTTP协议,加上个版本号就可以了.

    请求方法,URL,版本号 组成了请求行.这些是双方交流中的最基本信息.

    第二部分

    除了最基本信息还得有其他一些辅助信息才能更好的交流.
    比如,最起码的礼貌要讲吧,你要告诉对方你是谁.User-Agent就是干这个用的,例如chrome的User-Agent是 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36 这里面包括了浏览器版本号,系统版本号,硬件架构等信息.
    Host就是上面URL中的服务器地址部分.
    还要告诉服务器你接受什么格式的文件,比如Accept: image/jpeg 表示希望接受一个jpg格式的图片,Accept: text/html 表示希望接受一个HTML文件.

    第三部分

    在详细展开各种头信息之前咱们来看下消息体,消息体是HTTP学习中最无足轻重的部分,它就是一坨字节码,至于如何解析这坨字节码完全由头信息来控制的.而且在HTTP消息中消息体也不是总是存在的,需要时才会有,比如GET请求中就没有消息体(请求方法,URL和头信息就足以表达清楚客户端的意图了,所以就不需要头信息了).

    请求方法

    主要使用的方法有四个,GET,POST,PUT,DELETE
    GET和DELETE很简单,一个是获取资源,一个是删除资源,这两个的请求都不带消息体,GET的返回一般都带消息体,DELETE的返回带不带消息体都可以.
    PUT和POST都可以用于创建和修改资源,它们的区别是,PUT的语义是把URL对应的资源改成消息体中所描述的样子.POST的语义是让URL对应的资源来处理下消息体中的内容,处理的结果没有具体规定.比如 URL是/myAccount/,消息体是100.PUT的语义是这个请求处理完我的账户就是100,POST处理完有可能是在原有基础上加100.所以PUT请求是冥等的,一次和多次请求的副作用是一样的,最终结果都是100.

    除了这四个常用的方法外,还有HEAD,OPTIONS,TRACE和CONNECT方法,这些方法都不常用.
    HEAD是GET的简化版,用来只获取头信息,HEAD的返回禁止携带消息体.
    OPTIONS是在真正的请求开始之前先协商下是否允许发送请求及允许那些请求.
    例如CORS就是利用了OPTIONS请求.
    CONNECT和TRACE一个是用来建立连接隧道的,一个是用来排查连接路径上有多少个代理的.

    头信息详解

    HTTP的头信息有很多,这里按照它们的目的分组介绍几种常用的头信息.

    内容协商

    客户端在请求时需要告诉服务器自己接受什么格式的内容,Accept和Accept-Charset可以达到这个目的.Accept的值是MIME列表中的一个或者多个,以逗号分割.比如text/plain代表的是纯文本文件,text/html代表的是html文件.
    Accept-Charset表明客户端接受的编码格式,例如UTF-8.编码格式也可以写在Accept中.例如text/html;utf-8
    对应的返回的头信息是Content-Type,用来告诉客户端服务器返回的内容是什么格式的,客户端要根据这个字段的值来解析消息体.

    压缩是解决网络性能问题的一个基本方法,所以HTTP的消息体也支持压缩,但是压缩算法有很多种,客户端要告诉对方自己支持的压缩算法,服务器要告诉对方内容是用什么算法压缩的,这就用到了Accept-Encoding和Content-Encoding.

    发送消息体的一方需要让对方知道是否已经全部接收到了消息体,Content-Length的值代表了消息体的长度.

    缓存相关

    缓存也是为了提高性能,做法是收到响应时把消息存储下来,再次发起同样的请求时可以直接从本地存储中返回消息.为了解决什么时候用本地缓存什么时候找服务器获取的问题,HTTP定义了Exprires,Date,Last-Modifed,Etag,If-None-Match,Cache-Control等一系列头信息.
    Expires的值是一个时间点,表示这个时间之前消息都是新鲜的,可以直接用本地缓存,过了这个时间点就别瞎用了.

    那不用本地缓存是不是就只有从服务器获取这一条路了呢,其实不是的,可以问下服务器我还能不能用之前缓存的内容,能用我就继续用本地缓存,不能用你再给我新的内容.这一步的学名叫验证,就是让服务器验证下缓存是否过期了.
    验证有两个方式,一是服务器在返回时通过Date告诉客户端内容产生的时间,客户端在发送验证请求时通过Last-Modifed把服务器返回的Date的值再告诉服务器,服务器就可以根据这个时间点之后内容有无变化来决定客户端缓存是否过期.
    二是服务器对返回内容进行Hash得到一个值Etag.把Etag返回给客户端,客户端在验证时通过If-None-Match等字段把上次的Etag告诉服务器,服务器对比下Etag是否相等,如果Etag相等就代表缓存中的内容和服务器的内容一致,可以继续使用.

    Cache-Control可以控制缓存策略的,值有很多,这里同样介绍几个常用的.
    Cache-Control:no-cache : 客户端可以缓存,但是每次需要先找服务器验证是否能用
    Cache-Control:no-store : 客户端就别缓存了
    Cache-Control:max-age=一个数字: 这个数字代表一段时间,单位是秒,指从接收到响应时开始计时,一段时间之内可以使用缓存.

    表明身份

    前面说到的User-Agent就是为了表明客户端的身份,服务器也可以表明自己的身份,用Server.例如 Server:Apache/2.2.22 (Debian),从中可以看出服务器的操作系统,HTTP服务应用名称和版本号.

    有时候服务器某些资源只对部分用户开放,所以服务器要验证用户的身份.客户端可以通过Authorization字段来表明自己的身份.这个字段的值是字符串username:password的base64编码,服务器根据这个字段的值来确定用户身份,如果客户端请求中没有Authorization字段,服务器在返回中会带一个WWW-Authorization头,来要求客户端发送请求时带上Authorization头.

    Session

    上面讲到通过Authorization来确定用户身份,这种方法要求客户端每次请求都带上用户名和密码,是很不安全的.所以一般很少用,现在流行用Cookie来确定用户身份.

    Cookie与其他头信息不同,其他头信息都是发送方根据自己的情况产生的,而Cookie的内容完全是服务器产生的,然后客户端每次在请求时把服务器上次产生的内容发送给服务器.
    利用Cookie,服务器可以给不同用户分配不同的sessionID,把sessionId通过 Set-Cookie 来发送给客户端.服务器从请求的Cookie中拿到sessionID,就可以根据sessionID来确定用户身份,然后查询到用户之前请求的一些状态了.(前提是它把之前的状态保存在了这个用户的sessionId下)

    Session

    最后

    HTTP基础东西就这些了.HTTP协议基本概念比较简单,但是现实应用中由于网络环境的问题,恶意破坏人员的存在等导致有很多细节问题需要处理.每个问题都是用一个或几个头信息的配合来解决的.现实应用中需要重点关注下头信息部分,某个头信息解决了什么问题,是怎么解决的,以及会有什么副作用.

    参考 :
    1)https://tools.ietf.org/html/rfc7230
    2)https://coolshell.cn/articles/4787.html
    3)https://gaohaoyang.gitbooks.io/http-notes/content/chapter1/section1.7.html

    相关文章

      网友评论

          本文标题:HTTP必学必会

          本文链接:https://www.haomeiwen.com/subject/muafjxtx.html