所有文章 > API术语解释 > API与端点:差异化细分
API与端点:差异化细分

API与端点:差异化细分

尽管 API 和端点是网络开发和编程中的基本概念,但它们也可能导致误解。尽管这些短语关系密切,有时还会相互交换,但它们的含义和目的却各不相同,从而导致误解。 这篇博文将详细介绍 API 和端点,解释它们的不同之处,并提供许多代码示例帮助您理解。

应用程序接口简介

应用程序编程接口简称 API。它是一个准则和程序系统,使各种软件程序能够相互对话。 通过端点,API 公开了数据或功能,并规定了开发人员从操作系统或其他应用程序请求服务的适当方法。

应用程序接口可用于各种场合:

  • 网络应用程序接口:实现网络服务器与客户端之间的通信。
  • 操作系统 API:允许应用程序使用操作系统的资源。
  • 图书馆 API:提供对软件库功能的访问。

示例:网络应用程序接口

考虑一个提供图书数据的简单网络应用程序接口。该应用程序接口允许客户获取图书列表、添加新书、更新图书详细信息或删除图书。

# Example of a simple Web API using Flask (Python)

from flask import Flask, jsonify, request
app = Flask(__name__)
books = [
{'id': 1, 'title': '1984', 'author': 'George Orwell'},
{'id': 2, 'title': 'To Kill a Mockingbird', 'author': 'Harper Lee'}
]
@app.route('/books', methods=['GET'])
def get_books():
return jsonify(books)
@app.route('/books', methods=['POST'])
def add_book():
new_book = request.get_json()
books.append(new_book)
return jsonify(new_book), 201
@app.route('/books/<int:id>', methods=['PUT'])
def update_book(id):
book = next((b for b in books if b['id'] == id), None)
if book is None:
return jsonify({'error': 'Book not found'}), 404
data = request.get_json()
book.update(data)
return jsonify(book)
@app.route('/books/<int:id>', methods=['DELETE'])
def delete_book(id):
global books
books = [b for b in books if b['id'] != id]
return '', 204
if __name__ == '__main__':
app.run(debug=True)

在本示例中,API 定义了多个用于管理图书的端点(路由)。每个端点都允许客户对图书资源执行特定操作。

什么是端点?

端点是提供特定服务的特定 URL 模式。它代表通信通道的一端,通常与应用程序接口公开的操作相对应。

端点是应用程序接口的重要组成部分。它们定义了访问或操作资源的位置和方式。

示例:网络应用程序接口中的端点

继续前面的例子,让我们来确定端点:

GET /books:获取所有书籍的列表。

POST /books:添加新书

PUT /books/<int:id>: 用指定的 ID 更新图书。

DELETE /books/<int:id>: 删除指定 ID 的图书。

每个端点都执行不同的操作,允许客户以特定方式与图书资源交互。

应用程序接口与端点的主要区别

  1. 范围:应用程序接口:应用程序接口(API)是一个更宽泛的概念,包括一套用于构建和集成软件应用程序的规则和定义。它包括多个端点。端点:端点是 API 中执行特定功能的特定 URL 模式。
  2. 功能:应用程序接口:应用程序接口(API)定义了不同软件组件的交互方式。它为访问应用程序的功能和数据提供了一个完整的接口。端点:端点是应用程序接口中执行操作的特定交互点。
  3. 结构:应用程序接口:应用程序接口由多个端点组成,每个端点处理应用程序功能的特定部分。端点:端点是应用程序接口中与特定操作相对应的一个点。

详细代码示例

为了进一步说明应用程序接口和端点之间的区别和互动,让我们扩展前面的示例。我们将添加更多的功能,并展示端点如何在更广泛的 API 环境中工作。

添加身份验证

让我们为应用程序接口添加身份验证。只有通过身份验证的用户才能添加、更新或删除图书。

from flask import Flask, jsonify, request, abort
app = Flask(__name__)
books = [
{'id': 1, 'title': '1984', 'author': 'George Orwell'},
{'id': 2, 'title': 'To Kill a Mockingbird', 'author': 'Harper Lee'}
]
users = {
'user1': 'password1',
'user2': 'password2'
}
def authenticate():
auth = request.authorization
if not auth or not users.get(auth.username) == auth.password:
return False
return True
@app.route('/books', methods=['GET'])
def get_books():
return jsonify(books)
@app.route('/books', methods=['POST'])
def add_book():
if not authenticate():
return jsonify({'error': 'Unauthorized access'}), 401
new_book = request.get_json()
books.append(new_book)
return jsonify(new_book), 201
@app.route('/books/<int:id>', methods=['PUT'])
def update_book(id):
if not authenticate():
return jsonify({'error': 'Unauthorized access'}), 401
book = next((b for b in books if b['id'] == id), None)
if book is None:
return jsonify({'error': 'Book not found'}), 404
data = request.get_json()
book.update(data)
return jsonify(book)
@app.route('/books/<int:id>', methods=['DELETE'])
def delete_book(id):
if not authenticate():
return jsonify({'error': 'Unauthorized access'}), 401
global books
books = [b for b in books if b['id'] != id]
return '', 204
if __name__ == '__main__':
app.run(debug=True)

在本例中,验证函数会检查请求是否包含有效的验证凭证。POST、PUT 和 DELETE 端点受保护,需要有效凭证才能访问。

添加错误处理

让我们通过添加更详细的错误处理来改进我们的应用程序接口。这样可以确保客户在出错时收到有意义的错误信息。

from flask import Flask, jsonify, request, abort
app = Flask(__name__)
books = [
{'id': 1, 'title': '1984', 'author': 'George Orwell'},
{'id': 2, 'title': 'To Kill a Mockingbird', 'author': 'Harper Lee'}
]
users = {
'user1': 'password1',
'user2': 'password2'
}
def authenticate():
auth = request.authorization
if not auth or not users.get(auth.username) == auth.password:
return False
return True
@app.errorhandler(400)
def bad_request(error):
return jsonify({'error': 'Bad request'}), 400
@app.errorhandler(401)
def unauthorized(error):
return jsonify({'error': 'Unauthorized access'}), 401
@app.errorhandler(404)
def not_found(error):
return jsonify({'error': 'Resource not found'}), 404
@app.route('/books', methods=['GET'])
def get_books():
return jsonify(books)
@app.route('/books', methods=['POST'])
def add_book():
if not authenticate():
abort(401)
if not request.json or not 'title' in request.json:
abort(400)
new_book = {
'id': books[-1]['id'] + 1 if books else 1,
'title': request.json['title'],
'author': request.json.get('author', "")
}
books.append(new_book)
return jsonify(new_book), 201
@app.route('/books/<int:id>', methods=['PUT'])
def update_book(id):
if not authenticate():
abort(401)
book = next((b for b in books if b['id'] == id), None)
if book is None:
abort(404)
if not request.json:
abort(400)
book['title'] = request.json.get('title', book['title'])
book['author'] = request.json.get('author', book['author'])
return jsonify(book)
@app.route('/books/<int:id>', methods=['DELETE'])
def delete_book(id):
if not authenticate():
abort(401)
global books
books = [b for b in books if b['id'] != id]
return '', 204
if __name__ == '__main__':
app.run(debug=True)

在这个改进版本中,我们为不同的 HTTP 状态代码添加了自定义错误处理程序。中止函数用于在必要时触发这些错误处理程序,为客户端提供更多的错误信息。

高级概念:版本控制和速率限制

随着应用程序接口的复杂性不断增加,管理不同版本和限制请求率以确保稳定性和向后兼容性变得至关重要。

应用程序接口版本化

应用程序接口版本管理允许您维护不同版本的应用程序接口,以支持传统客户端,同时为新客户端添加新功能。让我们为应用程序接口添加版本控制。

from flask import Flask, jsonify, request, abort
app = Flask(__name__)
books_v1 = [
{'id': 1, 'title': '1984', 'author': 'George Orwell'},
{'id': 2, 'title': 'To Kill a Mockingbird', 'author': 'Harper Lee'}
]
books_v2 = [
{'id': 1, 'title': '1984', 'author': 'George Orwell', 'published': '1949'},
{'id': 2, 'title': 'To Kill a Mockingbird', 'author': 'Harper Lee', 'published': '1960'}
]
users = {
'user1': 'password1',
'user2': 'password2'
}
def authenticate():
auth = request.authorization
if not auth or not users.get(auth.username) == auth.password:
return False
return True
@app.route('/v1/books', methods=['GET'])
def get_books_v1():
return jsonify(books_v1)
@app.route('/v1/books', methods=['POST'])
def add_book_v1():
if not authenticate():
abort(401)
if not request.json or not 'title' in request.json:
abort(400)
new_book = {
'id': books_v1[-1]['id'] + 1 if books_v1 else 1,
'title': request.json['title'],
'author': request.json.get('author', "")
}
books_v1.append(new_book)
return jsonify(new_book), 201
@app.route('/v1/books/<int:id>', methods=['PUT'])
def update_book_v1(id):
if not authenticate():
abort(401)
book = next((b for b in books_v1 if b['id'] == id), None)
if book is None:
abort(404)
if not request.json:
abort(400)
book['title'] = request.json.get('title', book['title'])
book['author'] = request.json.get('author', book['author'])
return jsonify(book)
@app.route('/v1/books/<int:id>', methods=['DELETE'])
def delete_book_v1(id):
if not authenticate():
abort(401)
global books_v1
books_v1 = [b for b in books_v1 if b['id'] != id]
return '', 204
@app.route('/v2/books', methods=['GET'])
def get_books_v2():
return jsonify(books_v2)
@app.route('/v2/books', methods=['POST'])
def add_book_v2():
if not authenticate():
abort(401)
if not request.json or not 'title' in request.json:
abort(400)
new_book = {
'id': books_v2[-1]['id'] + 1 if books_v2 else 1,
'title': request.json['title'],
'author': request.json.get('author', ""),
'published': request.json.get('published', "")
}
books_v2.append(new_book)
return jsonify(new_book), 201
@app.route('/v2/books/<int:id>', methods=['PUT'])
def update_book_v2(id):
if not authenticate():
abort(401)
book = next((b for b in books_v2 if b['id'] == id), None)
if book is None:
abort(404)
if not request.json:
abort(400)
book['title'] = request.json.get('title', book['title'])
book['author'] = request.json.get('author', book['author'])
book['published'] = request.json.get('published', book['published'])
return jsonify(book)
@app.route('/v2/books/<int:id>', methods=['DELETE'])
def delete_book_v2(id):
if not authenticate():
abort(401)
global books_v2
books_v2 = [b for b in books_v2 if b['id'] != id]
return '', 204
if __name__ == '__main__':
app.run(debug=True)

在本例中,我们创建了两个版本的应用程序接口(v1 和 v2)。每个版本都有自己的端点集,允许客户选择与哪个版本进行交互。 这种方法有助于保持向后兼容性,同时又能引入新的功能和改进。

速率限制

速率限制为客户在一定时间内向应用程序接口发出的请求数量设置了上限。这样可以确保客户端之间的公平使用,防止滥用。让我们使用 flask-limiter 扩展为我们的应用程序接口提供速率限制。

from flask import Flask, jsonify, request, abort
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
app = Flask(__name__)
limiter = Limiter(
get_remote_address,
app=app,
default_limits=["200 per day", "50 per hour"]
)
books = [
{'id': 1, 'title': '1984', 'author': 'George Orwell'},
{'id': 2, 'title': 'To Kill a Mockingbird', 'author': 'Harper Lee'}
]
users = {
'user1': 'password1',
'user2': 'password2'
}
def authenticate():
auth = request.authorization
if not auth or not users.get(auth.username) == auth.password:
return False
return True
@app.errorhandler(400)
def bad_request(error):
return jsonify({'error': 'Bad request'}), 400
@app.errorhandler(401)
def unauthorized(error):
return jsonify({'error': 'Unauthorized access'}), 401
@app.errorhandler(404)
def not_found(error):
return jsonify({'error': 'Resource not found'}), 404
@app.errorhandler(429)
def ratelimit_error(error):
return jsonify({'error': 'Too many requests'}), 429
@app.route('/books', methods=['GET'])
@limiter.limit("10 per minute")
def get_books():
return jsonify(books)
@app.route('/books', methods=['POST'])
@limiter.limit("5 per minute")
def add_book():
if not authenticate():
abort(401)
if not request.json or not 'title' in request.json:
abort(400)
new_book = {
'id': books[-1]['id'] + 1 if books else 1,
'title': request.json['title'],
'author': request.json.get('author', "")
}
books.append(new_book)
return jsonify(new_book), 201
@app.route('/books/<int:id>', methods=['PUT'])
@limiter.limit("5 per minute")
def update_book(id):
if not authenticate():
abort(401)
book = next((b for b in books if b['id'] == id), None)
if book is None:
abort(404)
if not request.json:
abort(400)
book['title'] = request.json.get('title', book['title'])
book['author'] = request.json.get('author', book['author'])
return jsonify(book)
@app.route('/books/<int:id>', methods=['DELETE'])
@limiter.limit("5 per minute")
def delete_book(id):
if not authenticate():
abort(401)
global books
books = [b for b in books if b['id'] != id]
return '', 204
if __name__ == '__main__':
app.run(debug=True)

在本例中,flask-limiter 扩展用于对不同端点应用速率限制。default_limits 参数设置了全局速率限制,而 @limiter.limit 装饰器则为单个端点应用了特定的速率限制。 如果客户端超过了允许的请求数,就会收到 429 请求过多的错误信息。

设计应用程序接口和端点的最佳实践

  1. 一致性:确保 API 端点遵循一致的命名规范和结构。这将使客户更容易理解和使用您的应用程序接口。
  2. 版本控制:使用版本管理来管理 API 的更改和改进,而不会破坏现有客户端。为清晰起见,请首选基于 URL 的版本(如 /v1/resource)。
  3. 文档:为您的应用程序接口提供全面的最新文档。包括有关可用端点、请求/响应格式、验证方法和错误处理的信息。
  4. 错误处理:实施有意义的错误信息和适当的 HTTP 状态代码。这有助于客户了解出错的原因以及如何修复。
  5. 安全性:使用验证和授权机制保护您的应用程序接口。确保使用 HTTPS 安全传输敏感数据。
  6. 速率限制:实施速率限制,防止滥用并确保公平使用。根据 API 和客户的需求自定义速率限制。
  7. 测试:彻底测试您的应用程序接口,确保其按预期运行。使用自动化测试工具来覆盖各种场景和边缘情况。
  8. 监控:监控您的应用程序接口,以便及时发现并解决问题。使用日志和监控工具跟踪性能、错误和使用模式。

结论

应用程序接口(API)和端点是网络开发中的基本概念,各自在实现软件应用程序之间的通信方面发挥着不同的作用。了解应用程序接口和端点之间的区别对于设计稳健高效的系统至关重要。

在这篇博文中,我们探讨了 API 和端点的概念,强调了它们的区别,并提供了详细的代码示例来说明它们的用法。我们还讨论了版本控制和速率限制等高级主题,以及设计 API 的最佳实践。

通过遵循这些指导原则和最佳实践,您可以创建不仅功能齐全,而且安全、可扩展和易于使用的应用程序接口。 无论您是在构建简单的应用程序还是复杂的系统,对应用程序接口和端点的扎实了解都将有助于您提供高质量的软件解决方案。

原文链接:https://medium.com/@nile.bits/apis-vs-endpoints-breaking-down-the-differences-ef80ef128844

推荐阅读:
了解异步API
API 安全策略和基础指南
在线API描述规范、发现与文档入门
API设计模式:粒度细化 vs 粒度粗化的利弊分析
#你可能也喜欢这些API文章!