06_web_apis.py
Python/03_advanced_level/06_web_apis.py
# Web APIs Examples
import requests
import json
from flask import Flask, request, jsonify
from typing import Dict, List, Any
import time
print("Web APIs Examples")
print("=" * 20)
# 1. Consuming APIs with requests
print("1. Consuming APIs with requests")
print("-" * 30)
def get_weather_data(city: str) -> Dict[str, Any]:
"""Get weather data for a city."""
# Note: This is a mock example. In real usage, you'd use a real API key
api_key = "your_api_key_here"
url = f"http://api.openweathermap.org/data/2.5/weather"
params = {
'q': city,
'appid': api_key,
'units': 'metric'
}
try:
response = requests.get(url, params=params)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
return {"error": str(e)}
def get_github_user(username: str) -> Dict[str, Any]:
"""Get GitHub user information."""
url = f"https://api.github.com/users/{username}"
try:
response = requests.get(url)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
return {"error": str(e)}
def get_random_quote() -> Dict[str, Any]:
"""Get a random quote from an API."""
url = "https://api.quotable.io/random"
try:
response = requests.get(url)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
return {"error": str(e)}
# Test API consumption
print("Testing API consumption:")
print(f"Weather data for London: {get_weather_data('London')}")
print(f"GitHub user 'octocat': {get_github_user('octocat')}")
print(f"Random quote: {get_random_quote()}")
# 2. Building APIs with Flask
print("\n2. Building APIs with Flask")
print("-" * 25)
app = Flask(__name__)
# Sample data
users = [
{"id": 1, "name": "Alice", "email": "alice@example.com"},
{"id": 2, "name": "Bob", "email": "bob@example.com"},
{"id": 3, "name": "Charlie", "email": "charlie@example.com"}
]
posts = [
{"id": 1, "title": "First Post", "content": "This is the first post", "user_id": 1},
{"id": 2, "title": "Second Post", "content": "This is the second post", "user_id": 2},
{"id": 3, "title": "Third Post", "content": "This is the third post", "user_id": 1}
]
@app.route('/api/users', methods=['GET'])
def get_users():
"""Get all users."""
return jsonify(users)
@app.route('/api/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
"""Get a specific user."""
user = next((u for u in users if u['id'] == user_id), None)
if user:
return jsonify(user)
return jsonify({"error": "User not found"}), 404
@app.route('/api/users', methods=['POST'])
def create_user():
"""Create a new user."""
data = request.get_json()
if not data or 'name' not in data or 'email' not in data:
return jsonify({"error": "Name and email are required"}), 400
new_user = {
"id": max(u['id'] for u in users) + 1,
"name": data['name'],
"email": data['email']
}
users.append(new_user)
return jsonify(new_user), 201
@app.route('/api/users/<int:user_id>', methods=['PUT'])
def update_user(user_id):
"""Update a user."""
user = next((u for u in users if u['id'] == user_id), None)
if not user:
return jsonify({"error": "User not found"}), 404
data = request.get_json()
if not data:
return jsonify({"error": "No data provided"}), 400
user.update(data)
return jsonify(user)
@app.route('/api/users/<int:user_id>', methods=['DELETE'])
def delete_user(user_id):
"""Delete a user."""
global users
users = [u for u in users if u['id'] != user_id]
return jsonify({"message": "User deleted"}), 200
@app.route('/api/posts', methods=['GET'])
def get_posts():
"""Get all posts."""
return jsonify(posts)
@app.route('/api/posts/<int:post_id>', methods=['GET'])
def get_post(post_id):
"""Get a specific post."""
post = next((p for p in posts if p['id'] == post_id), None)
if post:
return jsonify(post)
return jsonify({"error": "Post not found"}), 404
@app.route('/api/posts', methods=['POST'])
def create_post():
"""Create a new post."""
data = request.get_json()
if not data or 'title' not in data or 'content' not in data or 'user_id' not in data:
return jsonify({"error": "Title, content, and user_id are required"}), 400
new_post = {
"id": max(p['id'] for p in posts) + 1,
"title": data['title'],
"content": data['content'],
"user_id": data['user_id']
}
posts.append(new_post)
return jsonify(new_post), 201
@app.route('/api/posts/<int:post_id>', methods=['PUT'])
def update_post(post_id):
"""Update a post."""
post = next((p for p in posts if p['id'] == post_id), None)
if not post:
return jsonify({"error": "Post not found"}), 404
data = request.get_json()
if not data:
return jsonify({"error": "No data provided"}), 400
post.update(data)
return jsonify(post)
@app.route('/api/posts/<int:post_id>', methods=['DELETE'])
def delete_post(post_id):
"""Delete a post."""
global posts
posts = [p for p in posts if p['id'] != post_id]
return jsonify({"message": "Post deleted"}), 200
@app.errorhandler(404)
def not_found(error):
"""Handle 404 errors."""
return jsonify({"error": "Not found"}), 404
@app.errorhandler(500)
def internal_error(error):
"""Handle 500 errors."""
return jsonify({"error": "Internal server error"}), 500
# 3. HTTP Methods and Status Codes
print("\n3. HTTP Methods and Status Codes")
print("-" * 30)
def demonstrate_http_methods():
"""Demonstrate different HTTP methods."""
# GET request
def make_get_request(url: str) -> Dict[str, Any]:
try:
response = requests.get(url)
return {
"status_code": response.status_code,
"data": response.json() if response.headers.get('content-type', '').startswith('application/json') else response.text
}
except requests.exceptions.RequestException as e:
return {"error": str(e)}
# POST request
def make_post_request(url: str, data: Dict[str, Any]) -> Dict[str, Any]:
try:
response = requests.post(url, json=data)
return {
"status_code": response.status_code,
"data": response.json() if response.headers.get('content-type', '').startswith('application/json') else response.text
}
except requests.exceptions.RequestException as e:
return {"error": str(e)}
# PUT request
def make_put_request(url: str, data: Dict[str, Any]) -> Dict[str, Any]:
try:
response = requests.put(url, json=data)
return {
"status_code": response.status_code,
"data": response.json() if response.headers.get('content-type', '').startswith('application/json') else response.text
}
except requests.exceptions.RequestException as e:
return {"error": str(e)}
# DELETE request
def make_delete_request(url: str) -> Dict[str, Any]:
try:
response = requests.delete(url)
return {
"status_code": response.status_code,
"data": response.json() if response.headers.get('content-type', '').startswith('application/json') else response.text
}
except requests.exceptions.RequestException as e:
return {"error": str(e)}
# PATCH request
def make_patch_request(url: str, data: Dict[str, Any]) -> Dict[str, Any]:
try:
response = requests.patch(url, json=data)
return {
"status_code": response.status_code,
"data": response.json() if response.headers.get('content-type', '').startswith('application/json') else response.text
}
except requests.exceptions.RequestException as e:
return {"error": str(e)}
return {
"get": make_get_request,
"post": make_post_request,
"put": make_put_request,
"delete": make_delete_request,
"patch": make_patch_request
}
# Test HTTP methods
http_methods = demonstrate_http_methods()
# Test with a mock API endpoint
test_url = "https://httpbin.org"
test_data = {"name": "Test", "value": 123}
print("Testing HTTP methods:")
print(f"GET request: {http_methods['get'](f'{test_url}/get')}")
print(f"POST request: {http_methods['post'](f'{test_url}/post', test_data)}")
print(f"PUT request: {http_methods['put'](f'{test_url}/put', test_data)}")
print(f"DELETE request: {http_methods['delete'](f'{test_url}/delete')}")
print(f"PATCH request: {http_methods['patch'](f'{test_url}/patch', test_data)}")
# 4. API Authentication
print("\n4. API Authentication")
print("-" * 22)
def api_key_authentication(api_key: str) -> Dict[str, str]:
"""API key authentication."""
return {"X-API-Key": api_key}
def basic_authentication(username: str, password: str) -> Dict[str, str]:
"""Basic authentication."""
import base64
credentials = f"{username}:{password}"
encoded_credentials = base64.b64encode(credentials.encode()).decode()
return {"Authorization": f"Basic {encoded_credentials}"}
def bearer_token_authentication(token: str) -> Dict[str, str]:
"""Bearer token authentication."""
return {"Authorization": f"Bearer {token}"}
def jwt_authentication(token: str) -> Dict[str, str]:
"""JWT authentication."""
return {"Authorization": f"Bearer {token}"}
def oauth2_authentication(access_token: str) -> Dict[str, str]:
"""OAuth 2.0 authentication."""
return {"Authorization": f"Bearer {access_token}"}
def session_authentication(session_id: str) -> Dict[str, str]:
"""Session-based authentication."""
return {"Cookie": f"session_id={session_id}"}
# Test authentication methods
print("Testing authentication methods:")
print(f"API Key: {api_key_authentication('your_api_key')}")
print(f"Basic Auth: {basic_authentication('username', 'password')}")
print(f"Bearer Token: {bearer_token_authentication('your_token')}")
print(f"JWT: {jwt_authentication('your_jwt_token')}")
print(f"OAuth 2.0: {oauth2_authentication('your_access_token')}")
print(f"Session: {session_authentication('your_session_id')}")
# 5. API Documentation
print("\n5. API Documentation")
print("-" * 20)
def create_api_documentation():
"""Create API documentation."""
api_docs = {
"title": "Sample API",
"version": "1.0.0",
"description": "A sample REST API for demonstration purposes",
"base_url": "https://api.example.com",
"endpoints": [
{
"path": "/api/users",
"methods": ["GET", "POST"],
"description": "Manage users",
"parameters": {
"GET": [],
"POST": [
{"name": "name", "type": "string", "required": True, "description": "User's name"},
{"name": "email", "type": "string", "required": True, "description": "User's email"}
]
},
"responses": {
"200": "Success",
"201": "Created",
"400": "Bad Request",
"500": "Internal Server Error"
}
},
{
"path": "/api/users/{id}",
"methods": ["GET", "PUT", "DELETE"],
"description": "Manage a specific user",
"parameters": {
"GET": [
{"name": "id", "type": "integer", "required": True, "description": "User ID"}
],
"PUT": [
{"name": "id", "type": "integer", "required": True, "description": "User ID"},
{"name": "name", "type": "string", "required": False, "description": "User's name"},
{"name": "email", "type": "string", "required": False, "description": "User's email"}
],
"DELETE": [
{"name": "id", "type": "integer", "required": True, "description": "User ID"}
]
},
"responses": {
"200": "Success",
"404": "Not Found",
"500": "Internal Server Error"
}
}
],
"authentication": {
"type": "API Key",
"header": "X-API-Key",
"description": "Include your API key in the X-API-Key header"
},
"rate_limiting": {
"limit": "1000 requests per hour",
"header": "X-RateLimit-Limit",
"remaining": "X-RateLimit-Remaining",
"reset": "X-RateLimit-Reset"
},
"examples": {
"python": {
"get_users": """
import requests
response = requests.get('https://api.example.com/api/users',
headers={'X-API-Key': 'your_api_key'})
users = response.json()
""",
"create_user": """
import requests
data = {'name': 'John Doe', 'email': 'john@example.com'}
response = requests.post('https://api.example.com/api/users',
json=data,
headers={'X-API-Key': 'your_api_key'})
user = response.json()
"""
},
"curl": {
"get_users": """
curl -X GET "https://api.example.com/api/users" \\
-H "X-API-Key: your_api_key"
""",
"create_user": """
curl -X POST "https://api.example.com/api/users" \\
-H "Content-Type: application/json" \\
-H "X-API-Key: your_api_key" \\
-d '{"name": "John Doe", "email": "john@example.com"}'
"""
}
},
"error_codes": {
"400": "Bad Request - Invalid input data",
"401": "Unauthorized - Invalid or missing API key",
"403": "Forbidden - Insufficient permissions",
"404": "Not Found - Resource not found",
"429": "Too Many Requests - Rate limit exceeded",
"500": "Internal Server Error - Server error"
}
}
return api_docs
# Create API documentation
api_docs = create_api_documentation()
print("API Documentation created:")
print(json.dumps(api_docs, indent=2))
# 6. Error Handling
print("\n6. Error Handling")
print("-" * 18)
def handle_api_errors(response: requests.Response) -> Dict[str, Any]:
"""Handle API errors."""
try:
if response.status_code == 200:
return {"success": True, "data": response.json()}
elif response.status_code == 201:
return {"success": True, "data": response.json(), "message": "Created"}
elif response.status_code == 400:
return {"success": False, "error": "Bad Request", "details": response.json()}
elif response.status_code == 401:
return {"success": False, "error": "Unauthorized", "message": "Invalid API key"}
elif response.status_code == 403:
return {"success": False, "error": "Forbidden", "message": "Insufficient permissions"}
elif response.status_code == 404:
return {"success": False, "error": "Not Found", "message": "Resource not found"}
elif response.status_code == 429:
return {"success": False, "error": "Too Many Requests", "message": "Rate limit exceeded"}
elif response.status_code == 500:
return {"success": False, "error": "Internal Server Error", "message": "Server error"}
else:
return {"success": False, "error": f"Unknown error: {response.status_code}"}
except Exception as e:
return {"success": False, "error": f"Error parsing response: {str(e)}"}
# Test error handling
test_responses = [
requests.Response(),
requests.Response(),
requests.Response(),
requests.Response(),
requests.Response()
]
# Set status codes for test responses
test_responses[0].status_code = 200
test_responses[1].status_code = 400
test_responses[2].status_code = 401
test_responses[3].status_code = 404
test_responses[4].status_code = 500
print("Testing error handling:")
for i, response in enumerate(test_responses):
print(f"Response {i+1}: {handle_api_errors(response)}")
# 7. Rate Limiting
print("\n7. Rate Limiting")
print("-" * 15)
class RateLimiter:
"""Rate limiter for API requests."""
def __init__(self, max_requests: int, time_window: int):
self.max_requests = max_requests
self.time_window = time_window
self.requests = []
def is_allowed(self) -> bool:
"""Check if request is allowed."""
current_time = time.time()
# Remove old requests outside the time window
self.requests = [req_time for req_time in self.requests
if current_time - req_time < self.time_window]
# Check if we can make another request
if len(self.requests) < self.max_requests:
self.requests.append(current_time)
return True
return False
def get_retry_after(self) -> int:
"""Get seconds to wait before retry."""
if not self.requests:
return 0
oldest_request = min(self.requests)
retry_after = self.time_window - (time.time() - oldest_request)
return max(0, int(retry_after))
# Test rate limiting
rate_limiter = RateLimiter(max_requests=5, time_window=60) # 5 requests per minute
print("Testing rate limiting:")
for i in range(7):
if rate_limiter.is_allowed():
print(f"Request {i+1}: Allowed")
else:
print(f"Request {i+1}: Rate limited. Retry after {rate_limiter.get_retry_after()} seconds")
# 8. API Testing
print("\n8. API Testing")
print("-" * 15)
def test_api_endpoint(url: str, method: str = 'GET', data: Dict[str, Any] = None,
headers: Dict[str, str] = None) -> Dict[str, Any]:
"""Test an API endpoint."""
try:
if method.upper() == 'GET':
response = requests.get(url, headers=headers)
elif method.upper() == 'POST':
response = requests.post(url, json=data, headers=headers)
elif method.upper() == 'PUT':
response = requests.put(url, json=data, headers=headers)
elif method.upper() == 'DELETE':
response = requests.delete(url, headers=headers)
else:
return {"error": f"Unsupported method: {method}"}
return {
"status_code": response.status_code,
"headers": dict(response.headers),
"data": response.json() if response.headers.get('content-type', '').startswith('application/json') else response.text,
"response_time": response.elapsed.total_seconds()
}
except requests.exceptions.RequestException as e:
return {"error": str(e)}
# Test API endpoints
test_endpoints = [
{"url": "https://httpbin.org/get", "method": "GET"},
{"url": "https://httpbin.org/post", "method": "POST", "data": {"test": "data"}},
{"url": "https://httpbin.org/put", "method": "PUT", "data": {"test": "data"}},
{"url": "https://httpbin.org/delete", "method": "DELETE"}
]
print("Testing API endpoints:")
for endpoint in test_endpoints:
result = test_api_endpoint(**endpoint)
print(f"{endpoint['method']} {endpoint['url']}: {result}")
print("\nWeb APIs examples completed!")
相关文章
01_getting_started.py
01_getting_started.py — python source code from the Python learning materials (Python/01_beginner_level/01_getting_started.py).
阅读文章 →02_core_syntax.py
02_core_syntax.py — python source code from the Python learning materials (Python/01_beginner_level/02_core_syntax.py).
阅读文章 →03_control_flow.py
03_control_flow.py — python source code from the Python learning materials (Python/01_beginner_level/03_control_flow.py).
阅读文章 →04_data_structures.py
04_data_structures.py — python source code from the Python learning materials (Python/01_beginner_level/04_data_structures.py).
阅读文章 →05_functions.py
05_functions.py — python source code from the Python learning materials (Python/01_beginner_level/05_functions.py).
阅读文章 →06_error_handling.py
06_error_handling.py — python source code from the Python learning materials (Python/01_beginner_level/06_error_handling.py).
阅读文章 →