API 版本迭代过程中,我们希望使用者能保持使用新版本 API,并且知道他正在使用的旧版本 API 即将废弃。维护者与使用者关于这点的沟通通常通过下面的方式:
- 维护者定期更新网站中 API 版本信息,使用者定期关注
- 维护者通过邮件主动通知使用者
这两种方式并不可靠,如果双方开发人员可以在程序交互时就交流这个信息,会比依赖人的通知更为准时可靠。
目前社区打算引进一个新的 HTTP 响应头字段 Deprecation
来传递这个信息
字段的格式:Deprecation: 1688169599
应用案例
原始版本
下面的案例中存在两个版本,使用者使用的是旧版本的接口,版本的信息交互只能通过响应体中的 [Version 1]
来传递(实际应用中可能在 json 响应字段中或者其他地方),这种方式的问题在于它不具备普适性,每个外部依赖的 API 关于版本废弃的信息格式和位置都不一样,很难做基础开发。
python
from flask import Flask
app = Flask(__name__)
@app.route("/v1/list_books")
def get_book_list():
return "This is a book list! [Version 1]"
@app.route("/v2/list_books")
def get_book_list_v2():
return "This is a book list! [Version 2]"
shell
➜ ~ curl -i http://172.21.208.1:5000/v1/list_books
HTTP/1.1 200 OK
Server: Werkzeug/3.0.6 Python/3.11.9
Date: Mon, 18 Nov 2024 07:04:25 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 20
Connection: close
This is a book list!%
使用 Deprecated 字段版本
python
from datetime import datetime
from flask import Flask, make_response
app = Flask(__name__)
@app.route("/v1/list_books")
def get_book_list():
resp = make_response("This is a book list!")
deprecation_date = int(datetime(2024,11,20,23,59,59).timestamp())
resp.headers["Deprecation"] = deprecation_date
return resp
@app.route("/v2/list_books")
def get_book_list_v2():
return "This is a book list! [Vesion 2]"
➜ ~ curl -i http://172.21.208.1:5000/v1/list_books
HTTP/1.1 200 OK
Server: Werkzeug/3.0.6 Python/3.11.9
Date: Mon, 18 Nov 2024 08:36:03 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 20
Deprecation: 1732118399
Connection: close
这个版本只是增加了一个头字段,但对于API使用者来说可以做的发挥就增加了。
由于字段与时间表示方式遵循协议制定的格式,使用者可以做统一的开发,在响应头提取到 Deprecation 字段后:
- 使用者的系统对外部调用的API 统一监测响应头中的
Deprecation
字段,发出提示和告警,对于已经到达时间阈值内的发出告警 - 项目的请求中间件可以据此发出警告日志,从而使得在开发时得到提醒
下面我对这种应用做下基本演示:
python
# 客户端代码
import logging
import time
from datetime import timedelta
import requests
logger = logging.Logger("Detect")
def api_response_hook(func):
def new_func():
resp = func()
deprecation = resp.headers.get("Deprecation")
if deprecation:
# 对 deprecation 提取到的时间值做计算,开发者可以清楚感知到还剩几天过期
seconds = int(deprecation) - int(time.time())
remain_days = timedelta(seconds=seconds).days
logger.warning(f"Warning:{resp.request.url} will be deprecate after {remain_days} days!")
return resp
return new_func
@api_response_hook
def list_books():
resp = requests.get("http://127.0.0.1:5000/v1/list_books")
return resp
def main():
resp = list_books()
print(resp.text)
if __name__ == "__main__":
main()
Warning:http://127.0.0.1:5000/v1/list_books will be deprecate after 2 days!
This is a book list!