HTTP接口设计
时间:2022-07-22
本文章向大家介绍HTTP接口设计,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
作者 | 陌无崖
转载请联系授权
HTTP/2
一个HTTP/2连接是运行在TCP连接上的应用层协议。客户端是TCP连接的发起者。
请求方法
- 如果请求头中存在
X-HTTP-Method-Override
或参数中存在_method
(拥有更高权重),且值为GET
,POST
,PUT
,DELETE
,PATCH
,OPTIONS
,HEAD
之一,则视作相应的请求方式进行处理 -
GET
,DELETE
,HEAD
方法,参数风格为标准的GET
风格的参数,如url?a=1&b=2
-
POST
,PUT
,PATCH
,OPTIONS
方法 - 默认情况下请求实体会被视作标准 json 字符串进行处理,当然,依旧推荐设置头信息的
Content-Type
为application/json
- 在一些特殊接口中(会在文档中说明),可能允许
Content-Type
为application/x-www-form-urlencoded
或者multipart/form-data
,此时请求实体会被视作标准POST
风格的参数进行处理
关于方法语义的说明:
-
OPTIONS
用于获取资源支持的所有 HTTP 方法 -
HEAD
用于只获取请求某个资源返回的头信息 -
GET
用于从服务器获取某个资源的信息 - 完成请求后返回状态码
200 OK
- 完成请求后需要返回被请求的资源详细信息
-
POST
用于创建新资源 - 创建完成后返回状态码
201 Created
- 完成请求后需要返回被创建的资源详细信息
-
PUT
用于完整的替换资源或者创建指定身份的资源,比如创建 id 为 123 的某个资源 - 如果是创建了资源,则返回
201 Created
- 如果是替换了资源,则返回
200 OK
- 完成请求后需要返回被修改的资源详细信息
-
PATCH
用于局部更新资源 - 完成请求后返回状态码
200 OK
- 完成请求后需要返回被修改的资源详细信息
-
DELETE
用于删除某个资源 - 完成请求后返回状态码
204 No Content
状态码
请求成功
- 200 OK : 请求执行成功并返回相应数据,如
GET
成功 - 201 Created : 对象创建成功并返回相应资源数据,如
POST
成功;创建完成后响应头中应该携带头标Location
,指向新建资源的地址 - 202 Accepted : 接受请求,但无法立即完成创建行为,比如其中涉及到一个需要花费若干小时才能完成的任务。返回的实体中应该包含当前状态的信息,以及指向处理状态监视器或状态预测的指针,以便客户端能够获取最新状态。
- 204 No Content : 请求执行成功,不返回相应资源数据,如
PATCH
,DELETE
成功。
重定向
重定向的新地址都需要在响应头 Location
中返回
- 301 Moved Permanently : 被请求的资源已永久移动到新位置
- 302 Found : 请求的资源现在临时从不同的 URI 响应请求
- 303 See Other : 对应当前请求的响应可以在另一个 URI 上被找到,客户端应该使用
GET
方法进行请求。比如在创建已经被创建的资源时,可以返回303
- 307 Temporary Redirect : 对应当前请求的响应可以在另一个 URI 上被找到,客户端应该保持原有的请求方法进行请求。
条件请求
- 304 Not Modified : 资源自从上次请求后没有再次发生变化,主要使用场景在于实现数据缓存
- 409 Conflict : 请求操作和资源的当前状态存在冲突。主要使用场景在于实现并发控制
- 412 Precondition Failed : 服务器在验证在请求的头字段中给出先决条件时,没能满足其中的一个或多个。主要使用场景在于实现并发控制
客户端错误
- 400 Bad Request : 请求体包含语法错误
- 401 Unauthorized : 需要验证用户身份,如果服务器就算是身份验证后也不允许客户访问资源,应该响应
403 Forbidden
。如果请求里有Authorization
头,那么必须返回一个 `WWW-Authenticate` 头 - 403 Forbidden : 服务器拒绝执行
- 404 Not Found : 找不到目标资源
- 405 Method Not Allowed : 不允许执行目标方法,响应中应该带有
Allow
头,内容为对该资源有效的 HTTP 方法 - 406 Not Acceptable : 服务器不支持客户端请求的内容格式,但响应里会包含服务端能够给出的格式的数据,并在
Content-Type
中声明格式名称 - 410 Gone : 被请求的资源已被删除,只有在确定了这种情况是永久性的时候才可以使用,否则建议使用
404 Not Found
- 413 Payload Too Large :
POST
或者PUT
请求的消息实体过大 - 415 Unsupported Media Type : 服务器不支持请求中提交的数据的格式
- 422 Unprocessable Entity : 请求格式正确,但是由于含有语义错误,无法响应
- 428 Precondition Required : 要求先决条件,如果想要请求能成功必须满足一些预设的条件
服务端错误
- 500 Internal Server Error : 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。
- 501 Not Implemented : 服务器不支持当前请求所需要的某个功能。
- 502 Bad Gateway : 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。
- 503 Service Unavailable : 由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是临时的,并且将在一段时间以后恢复。如果能够预计延迟时间,那么响应中可以包含一个
Retry-After
头用以标明这个延迟时间(内容可以为数字,单位为秒;或者是一个 HTTP 协议指定的时间格式)。如果没有给出这个Retry-After
信息,那么客户端应当以处理 500 响应的方式处理它。
501
与 405
的区别是:405
是表示服务端不允许客户端这么做,501
是表示客户端或许可以这么做,但服务端还没有实现这个功能
身份验证
OAuth 2.0
- 官网
- 理解OAuth 2.0 - 阮一峰 以及对文中 `state` 参数的介绍的修正
- JSON Web Token,一种 Token 的生成标准
- Json Web Tokens: Introduction
- Json Web Tokens: Examples
数据缓存
大部分接口应该在响应头中携带 Last-Modified
, ETag
, Vary
, Date
信息,客户端可以在随后请求这些资源的时候,在请求头中使用 If-Modified-Since
, If-None-Match
等请求头来确认资源是否经过修改。
如果资源没有进行过修改,那么就可以响应 304 Not Modified
并且不在响应实体中返回任何内容。
$ curl -i http://api.example.com/#{RESOURCE_URI}
HTTP/1.1 200 OK
Cache-Control: public, max-age=60
Date: Thu, 05 Jul 2012 15:31:30 GMT
Vary: Accept, Authorization
ETag: "644b5b0155e6404a9cc4bd9d8b1ae730"
Last-Modified: Thu, 05 Jul 2012 15:31:30 GMT
Content
$ curl -i http://api.example.com/#{RESOURCE_URI} -H "If-Modified-Since: Thu, 05 Jul 2012 15:31:30 GMT"
HTTP/1.1 304 Not Modified
Cache-Control: public, max-age=60
Date: Thu, 05 Jul 2012 15:31:45 GMT
Vary: Accept, Authorization
Last-Modified: Thu, 05 Jul 2012 15:31:30 GMT
$ curl -i http://api.example.com/#{RESOURCE_URI} -H 'If-None-Match: "644b5b0155e6404a9cc4bd9d8b1ae730"'
HTTP/1.1 304 Not Modified
Cache-Control: public, max-age=60
Date: Thu, 05 Jul 2012 15:31:55 GMT
Vary: Accept, Authorization
ETag: "644b5b0155e6404a9cc4bd9d8b1ae730"
Last-Modified: Thu, 05 Jul 2012 15:31:30 GMT
相关资料:
- RFC 7232
- HTTP 缓存 - Google Developers
- RFC 2616 中缓存过期时间的算法, MDN 版, 中文版
- HTTP 协议中 Vary 的一些研究
- Cache Control 與 ETag
并发控制
不严谨的实现,或者缺少并发控制的 PUT
和 PATCH
请求可能导致 “更新丢失”。这个时候可以使用 Last-Modified
和/或 ETag
头来实现条件请求,支持乐观并发控制。
下文只考虑使用 PUT
和 PATCH
方法更新资源的情况。
- 客户端发起的请求如果没有包含
If-Unmodified-Since
或者If-Match
头,那就返回状态码403 Forbidden
,在响应正文中解释为何返回该状态码 - 客户端发起的请求提供的
If-Unmodified-Since
或者If-Match
头与服务器记录的实际修改时间或ETag
值不匹配的时候,返回状态码412 Precondition Failed
- 客户端发起的请求提供的
If-Unmodified-Since
或者If-Match
头与服务器记录的实际修改时间或ETag
的历史值匹配,但资源已经被修改过的时候,返回状态码409 Conflict
- 客户端发起的请求提供的条件符合实际值,那就更新资源,响应
200 OK
或者204 No Content
,并且包含更新过的Last-Modified
和/或ETag
头,同时包含Content-Location
头,其值为更新后的资源 URI
相关资料:
- 《RESTful Web Services Cookbook 中文版》 10.4 节 《如何在服务器端实现条件 PUT 请求》
- RFC 7232 "Conditional Requests"
- Location vs. Content-Location
跨域
CORS
接口支持“跨域资源共享”(Cross Origin Resource Sharing, CORS),这里和这里和这份中文资料有一些指导性的资料。 简单示例:
$ curl -i https://api.example.com -H "Origin: http://example.com"
HTTP/1.1 302 Found
$ curl -i https://api.example.com -H "Origin: http://example.com"
HTTP/1.1 302 Found
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: ETag, Link, X-Total-Count
Access-Control-Allow-Credentials: true
预检请求的响应示例:
$ curl -i https://api.example.com -H "Origin: http://example.com" -X OPTIONS
HTTP/1.1 302 Found
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-Requested-With
Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE
Access-Control-Expose-Headers: ETag, Link, X-Total-Count
Access-Control-Max-Age: 86400
Access-Control-Allow-Credentials: true
JSON-P
如果在任何 GET
请求中带有参数 callback
,且值为非空字符串,那么接口将返回如下格式的数据
$ curl http://api.example.com/#{RESOURCE_URI}?callback=foo
foo({
"meta": {
"status": 200,
"X-Total-Count": 542,
"Link": [
{"href": "http://api.example.com/#{RESOURCE_URI}?cursor=0&count=100", "rel": "first"},
{"href": "http://api.example.com/#{RESOURCE_URI}?cursor=90&count=100", "rel": "prev"},
{"href": "http://api.example.com/#{RESOURCE_URI}?cursor=120&count=100", "rel": "next"},
{"href": "http://api.example.com/#{RESOURCE_URI}?cursor=200&count=100", "rel": "last"}
]
},
"data": // data
})
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 云原生分布式深度学习初探
- 【原创】经验分享:一个Content-Length引发的血案(almost....)
- SAP Spartacus里使用Observable访问Component数据
- SAP Spartacus的b2cLayoutConfig
- python判断工作日,节假日
- Shell高级用法之重定向绑定
- 使用Pyspark进行特征工程时的那些坑
- AutoML之自动化特征工程
- 使用Pandas_UDF快速改造Pandas代码
- pandas:根据行间差值进行数据合并
- DeepFM算法解析及Python实现
- 词嵌入技术解析(一)
- 初识TPOT:一个基于Python的自动化机器学习开发工具
- 为你的机器学习模型创建API服务
- Vim实用技巧——使用.swp文件恢复已删除文件