http-请求头介绍以及实例说明
请求
Introduction
使用python的包requests进行http/https的处理. 其中, 建立在TCP/IP协议之上的HTTP需要符合如下的规范:
1 | <method> <request-URI> <http-verison> |
注意, 一般而言request-URI
中存储的为URI,而并非URL, 可以通过headers中的HOST头来组成完成的URL请求头中的各类参数告知server不同的信息:
- 请求的信息类型, 例如表单application/x-www-form-urlencoded
- 请求的长度
- 客户端能够接受的响应类型
具体的各个字段的含义会在下文逐一说明, 对于一些特殊的设计较多知识的分类会以单章的形式呈现.
content-length
在requests的POST请求中, 传入data的值如果是unicode, 则会出现字符串长度被截断的现在发生, 因为在代码requests.models.prepare_body中, 使用len进行长度计算, 此时会出现问题.
1 | # coding:utf8 |
长度计算实例:
1 | len('狂想') == 6 |
Content Type
Intro
对于POST方法, 在传输数据时需要指明格式以便server
能够正确的解析请求数据. 这里主要介绍集中常见的Content-Type
并给出响应的python例子进行说明, 对于不同的格式, 请求体中的内容也是按照不同的格式进行编码.
注意, 一般而言, 服务端语言如PHP, Python, Java, 关联的框架在解析HTTP时会默认根据请求头Content-Type
解析数据并存储, 开发者仅仅按照约定或者Request对象中提取需要的数据即可.
当然, 返回的特定格式的Response, 则需要在Response Header
中指明, 以便Browser
能够识别解析, 这是一个反向的过程.
表单
application/x-www-form-urlencoded
, 最常见的表单提交方式, 浏览器原生的<form>
表单格式, 在JS中使用Ajax
方式提交数据时默认就使用application/x-www-form-urlencoded;charset=utf-8
来进行提交.
GET方法的表单提交包:
1 | GET /api/v2/monitor/upload?a=1&b=kuang |
此时在服务端需要根据如下代码进行解析
1 | from flask import request |
POST方法提交表单:
1 | POST /api/v2/monitor/upload |
此时的服务器解析代码:
1 | from urlparse import parse_qs |
输出如下, 注意, 此时的输出有一点特殊
1 | # 为何使用列表, 就是为了防止有相同key存在的情况 |
表单文件
1 |
|
下面先讲解一下表单文件的请求体中各个关键部分.
- boundary: 用于分割不同的文件之间, 这个非常重要
- Content-Type: 注意不是Header的Content-Type, 如果是字符串则无需指定
- Content-Disposition: 指明该部分上传文件的名称, 键值.
此时的服务器解析代码:
1 | # 提取非字符串类型 |
输出如下:
1 | Key: file3 Value: <FileStorage: u'test.txt' ('text/plain')> |
json
application/json
, 这是目前Restful API中最通用的方式了, 基于前后端分离, 以JSON格式返回给前端,无缝的利用JS来发送和解析数据. JSON能够简单的表达一个复杂的结构化数据(列表, 字典等).
请求:
1 | POST /api/v2/monitor/upload HTTP/1.1 |
此时的服务器解析代码:
1 | import json |
输出如下:
1 | {u'ip': u'127.0.0.1', u'monitors': [{u'country': u'ds160', u'type': u'error', u'task_id': u'Taskid123456', u'occur_time': u'2018-07-17 08:00:00'}, {u'country': u'singapore', u'type': u'error', u'task_id': u'Taskid123456898', u'occur_time': u'2018-07-19 08:00:00'}]} |
xml
application/xml
, 一种通用的XML包传输格式, 常常见于JAVA生态中, 相比json, 如果不是JAVA生态, 建议使用application/json
替代.
请求包:
1 | POST /api/v2/monitor/upload HTTP/1.1 |
服务器解析代码:
1 | import xml.dom.minidom |
输出如下: <DOM Element: Request at 0x102a32b90>
另外常见的XML请求格式还有: text/xml
, 例如在XML-RPC
作为调用方式, 传递函数方法以及参数
1 | POST http://www.example.com HTTP/1.1 |
缓存
操作
- 动作
F5/ctrl + R, cmd+R
: 刷新页面, 并在请求头加上cache-control: max-age=0
, 强行发起缓存验证, 但保留If-Modified-Since/If-Non-Match 请求头
, 页面其他相关联的请求(js, css, 图片等)不做任何特殊处理.
1 | Cache-Control: max-age=0 |
实际上此时js, css等资源也会请求一次, 不过请求头不会携带cache-control: max-age=0
, 响应状态码一般为304, 实际并未返回任何数据(正常一个文件2.1KB, 304返回的响应数据只有222B).
- 动作
CTRL + SHIFT + R, CMD+SHIFT+R
: 强制刷新, 在请求头加上cache-control: no-cache; pragma: no-cache
, 同时去掉If-Modified-Since/If-Non-Match
请求头, 其中no-cache
表示本地缓存失效需要重新进行验证, 此时所有资源的请求都会一视同仁.
头部说明
- 强缓存:
Cache-Control
,Expires
, 其中Expires返回一个固定的过期时间戳, 它有如下缺点:
- 服务器需要频繁的更改过期时间戳, 若是突然忘了更新过期时间戳, 则会造成浪涌或雪崩现象
- 过期时间戳受到本地时间的影响, 实际上cacheControl中的maxAge也会收到本地时间影响, 但其通过频繁的更新解决了该问题
Cache-Control
中的maxAge解决了Expires上述的问题, 其可选项说明如下:
no-cache
和no-store
: 前者会缓存资源, 但是每次请求都需要重新协商验证, 后者不使用缓存, 注意区别- public: 客户端和服务器均可缓存
- private: 客户端可缓存
max-age
: 缓存保质期, 一个相对时间
- 协商缓存:
Last-Modified/If-Modified-Since
,etag/If-None-Match
, 协商缓存仅仅在cache-control: max-age=0; 或者cache-control: no-cache
的情况发生
无效缓存
另外, 若在chrome使用https://IP:PORT
的方式进行访问, 发现强缓存并不生效, 但是在firefox中却是可以的, 此时进行如下操作:
- a. 修改本地hosts, 添加一个域名, 映射到IP
- b. 通过
https://domain:PORT
的方式进行访问, 此时在chrome中发现强缓存生效, 不知道具体原因
最后找到原因, 对于self-signed SSL Certificates
, chrome会忽略所有的缓存配置, 见下文参考.
状态码
任何一个 HTTP 请求, Server会按照 HTTP 协议返回指定的状态码, 用以响应该请求,其整体分类如下:
1**: 额外信息, 表示服务器收到请求, 需要请求者继续执行后续操作
2**: 成功, 表示服务器成功接收请求并处理
3**: 重定向, 表示需要进一步的请求才能完成最初的目的
4**: 客户端错误, 表示服务器接收到一个错误的请求
5**: 服务器错误, 表示服务器在处理请求的过程中出现错误, 可能由代码认为抛出
1XX
- 100: continue, 表示继续请求
- 101: switching protocols, 表示切换协议, 服务器根据Client的请求需要切换到高级协议
2XX
- 200: OK
- 201: Created, 成功请求并创建了新的资源
- 202: Accepted: 已接收请求, 但是为处理完成
- 203: Non-Authoritative: 非授权信息, 请求成功, 但是返回一个副本
- 204: No-content, 无内容
- 205: Reset Content, 重置内容, 服务器返回此码, 告知浏览器清除表单域
- 206: 部分内容, 成功处理了部分请求
3XX
- 300: Multiple Choices, 请求的资源可能有多个位置
- 301: Move Permanently, 永久移动
- 302: Found, 临时移动
- 303: See other, 查看其它地址
- 304: Not Modified, 未修改
- 305: Use Proxy, 表示使用的资源必须通过代理访问
- 306: Unused
- 307: Temporary Redirect: 临时重定向
其中301和302的区别:
- 301: 临时资源移动
- 302: 持久性资源移动
303/307提出原因: 用于细化302请求, 处理非GET/HEAD请求, 在HTTP1.1中使用. 非 GET/HEAD 请求: 理论上, 对于 POST 请求, 如果资源被Moved Temporarily, 则需要浏览器做出一定的操作, 让用户重新确认进行跳转, 但是实际上没有做, 浏览器直接 GET 请求.
4XX
- 400: Bad Request, 请求错误, Server无法理解
- 401: Unauthorized, 请求要求用户进行认证
- 402: Payment Required, 保留状态码
- 403: Forbidden, 服务器拒绝执行
- 404: Not found, 服务器无法找到资源
- 405: Method not allowed, 请求的方法被服务器禁止
- 406: Not Acceptable, 服务器无法根据客户端的内容特性完成请求
- 407: Proxy Authentication, 请求要求代理的身份认证
- 408: Request Timeout, 服务器等待客户端发送时间过程, 超时, Request body过大
- 409: Conflict, 处理冲突, 例如已经存在相同的唯一信息
- 410: Gone, 请求的资源已经丢失, 以前存在
- 411: Length Required: 无法处理没有Content-length的请求
- 412: Precondition Failed: 客户端请求信息的先决条件有问题
- 413: Request Entity Too Large: 请求实体过大, 服务器无法处理
- 414: Request URI too large, 请求的 URL 过长
- 415: Unsupported media type: 不支持的媒体格式
- 416: Requested range not satisfiable: 客户端请求的范围无效
- 417: Expectation Failed: 服务器无法满足Expect的请求头信息
5XX
- 500: 内部错误
- 501: Not implemented, 无法处理请求
- 502: Bad Gateway, 充当网关或者代理的服务器, 从远端服务器接收到一个无效请求
- 503: Service Unavailable, 由于系统超载, 服务器暂时无法处理
- 504: Gateway timeout, 充当网关或者代理的武器, 从远端服务器接收请求时超时
- 505: Http version: 服务器不支持的 HTTP 版本