Введение
Этот учебник проведет вас через обработку различных HTTP-кодов состояния в Python requests. HTTP-коды состояния необходимы для понимания того, была ли веб-заявка успешной или неудачной, а также для правильной реакции на различные сценарии. К концу этой лабораторной работы вы узнаете, как реализовать надежную обработку ошибок в ваших Python-приложениях, взаимодействующих с веб-сервисами.
Вы начнете с понимания HTTP-кодов состояния, а затем постепенно будете развивать свои навыки, реализуя базовые и продвинутые методы обработки ошибок. Эти знания критически важны для разработки надежных Python-веб-приложений, которые корректно обрабатывают различные ответы сервера.
Понимание HTTP-кодов состояния и настройка
Что такое HTTP-коды состояния?
HTTP-коды состояния — это трехзначные числа, возвращаемые сервером в ответ на запрос клиента. Они указывают, был ли конкретный HTTP-запрос успешно завершен. Эти коды сгруппированы в пять категорий:
- 1xx (Информационные): Запрос получен, и процесс продолжается
- 2xx (Успех): Запрос успешно получен, понят и принят
- 3xx (Перенаправление): Необходимо предпринять дальнейшие действия для завершения запроса
- 4xx (Ошибка клиента): Запрос содержит плохой синтаксис или не может быть выполнен
- 5xx (Ошибка сервера): Сервер не смог выполнить, казалось бы, допустимый запрос
Общие коды состояния, с которыми вы столкнетесь, включают:
| Код состояния | Название | Описание |
|---|---|---|
| 200 | OK | Запрос выполнен успешно |
| 201 | Created | Запрос выполнен успешно, ресурс создан |
| 400 | Bad Request | Сервер не может обработать запрос |
| 401 | Unauthorized | Требуется аутентификация |
| 404 | Not Found | Запрошенный ресурс не найден |
| 500 | Internal Server Error | На сервере произошла непредвиденная ошибка |
Настройка вашей среды Python
Прежде чем мы начнем обрабатывать HTTP-коды состояния, давайте установим библиотеку Python requests и создадим простой тестовый скрипт.
- Откройте терминал в среде LabEx и запустите:
pip install requests
- Создайте новую директорию для файлов нашего проекта:
mkdir -p ~/project/http_status_lab
cd ~/project/http_status_lab
- Используя WebIDE, создайте новый файл с именем
test_request.pyв директорииhttp_status_labсо следующим базовым кодом:
import requests
def make_request(url):
"""Make a basic GET request to the specified URL."""
response = requests.get(url)
print(f"Status Code: {response.status_code}")
print(f"Response: {response.text[:100]}...") ## Print first 100 characters
return response
## Test with a working URL
response = make_request("https://httpbin.org/status/200")
print(f"Request was successful: {response.ok}")
- Запустите тестовый скрипт, чтобы увидеть его в действии:
python test_request.py
Вы должны увидеть вывод, похожий на этот:
Status Code: 200
Response: ...
Request was successful: True
Это подтверждает, что вы успешно настроили среду Python и можете делать базовые HTTP-запросы. В следующих шагах вы узнаете, как обрабатывать различные HTTP-коды состояния и реализовывать более продвинутые методы обработки ошибок.
Обработка базовых HTTP-кодов состояния
Теперь, когда вы понимаете, что такое HTTP-коды состояния, и настроили свою среду, давайте реализуем базовую обработку ошибок для распространенных кодов состояния.
Создание инструмента для тестирования кодов состояния
Во-первых, давайте создадим инструмент, который позволит нам тестировать различные HTTP-коды состояния. Веб-сайт httpbin.org предоставляет конечные точки, которые возвращают определенные коды состояния, что идеально подходит для нашего тестирования.
Создайте новый файл с именем status_code_tester.py в вашей директории http_status_lab со следующим кодом:
import requests
import sys
def test_status_code(status_code):
"""Test a specific HTTP status code using httpbin.org."""
url = f"https://httpbin.org/status/{status_code}"
try:
response = requests.get(url)
print(f"Status Code: {response.status_code}")
print(f"Response OK: {response.ok}")
return response
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
return None
if __name__ == "__main__":
## Get status code from command line or use default
status_code = sys.argv[1] if len(sys.argv) > 1 else "200"
test_status_code(status_code)
Этот скрипт делает запрос к httpbin.org с определенным кодом состояния и выводит результат. Вы можете запустить его с аргументом командной строки, чтобы протестировать разные коды состояния.
Тестирование различных кодов состояния
Давайте протестируем наш скрипт с разными кодами состояния, чтобы увидеть, как библиотека requests их обрабатывает:
- Протестируйте успешный запрос (200 OK):
python status_code_tester.py 200
Ожидаемый вывод:
Status Code: 200
Response OK: True
- Протестируйте ошибку клиента (404 Not Found):
python status_code_tester.py 404
Ожидаемый вывод:
Status Code: 404
Response OK: False
- Протестируйте ошибку сервера (500 Internal Server Error):
python status_code_tester.py 500
Ожидаемый вывод:
Status Code: 500
Response OK: False
Обработка базовых категорий кодов состояния
Теперь давайте создадим более сложный скрипт, который надлежащим образом обрабатывает различные категории кодов состояния. Создайте новый файл с именем basic_handler.py в вашей директории http_status_lab:
import requests
def handle_response(url):
"""Handle different HTTP status codes with basic error handling."""
try:
response = requests.get(url)
## Check status code category
if 200 <= response.status_code < 300:
print(f"Success! Status code: {response.status_code}")
return response
elif 300 <= response.status_code < 400:
print(f"Redirection! Status code: {response.status_code}")
## For redirection, you might want to follow the redirect
return response
elif 400 <= response.status_code < 500:
print(f"Client error! Status code: {response.status_code}")
## Handle client errors
return response
elif 500 <= response.status_code < 600:
print(f"Server error! Status code: {response.status_code}")
## Handle server errors
return response
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
return None
## Test with different status codes
print("Testing 200 OK:")
handle_response("https://httpbin.org/status/200")
print("\nTesting 404 Not Found:")
handle_response("https://httpbin.org/status/404")
print("\nTesting 500 Internal Server Error:")
handle_response("https://httpbin.org/status/500")
Запустите скрипт, чтобы увидеть, как он обрабатывает разные коды состояния:
python basic_handler.py
Ожидаемый вывод:
Testing 200 OK:
Success! Status code: 200
Testing 404 Not Found:
Client error! Status code: 404
Testing 500 Internal Server Error:
Server error! Status code: 500
Этот базовый обработчик группирует коды состояния по категориям и выполняет разные действия в зависимости от категории. Это распространенный шаблон в Python-веб-приложениях, позволяющий вам надлежащим образом обрабатывать различные типы ответов.
Реализация расширенной обработки ошибок
Теперь, когда вы понимаете основы обработки HTTP-кодов состояния, давайте реализуем более продвинутые методы обработки ошибок для ваших приложений Python.
Использование raise_for_status()
Библиотека requests предоставляет удобный метод raise_for_status(), который вызывает исключение для кодов состояния 4xx и 5xx. Это простой, но эффективный способ обработки ошибок в вашем коде.
Создайте новый файл с именем raise_for_status_example.py в вашей директории http_status_lab:
import requests
def fetch_data(url):
"""Fetch data from a URL with error handling using raise_for_status()."""
try:
response = requests.get(url)
## This will raise an HTTPError if the HTTP request returned an unsuccessful status code
response.raise_for_status()
## If we get here, the request was successful
print(f"Success! Status code: {response.status_code}")
return response.json() if 'application/json' in response.headers.get('Content-Type', '') else response.text
except requests.exceptions.HTTPError as e:
print(f"HTTP Error: {e}")
return None
except requests.exceptions.ConnectionError as e:
print(f"Connection Error: {e}")
return None
except requests.exceptions.Timeout as e:
print(f"Timeout Error: {e}")
return None
except requests.exceptions.RequestException as e:
print(f"Request Exception: {e}")
return None
## Test with different URLs
print("Testing successful request:")
data = fetch_data("https://httpbin.org/json")
print(f"Received data type: {type(data)}")
print("\nTesting 404 error:")
data = fetch_data("https://httpbin.org/status/404")
print(f"Received data: {data}")
print("\nTesting 500 error:")
data = fetch_data("https://httpbin.org/status/500")
print(f"Received data: {data}")
Запустите скрипт, чтобы увидеть, как raise_for_status() обрабатывает ошибки:
python raise_for_status_example.py
Ожидаемый вывод покажет, что библиотека автоматически вызывает исключения для кодов состояния, отличных от 2xx, которые ваш код затем может перехватить и обработать соответствующим образом.
Создание пользовательских обработчиков исключений
Для более сложных приложений вам может потребоваться создать пользовательские обработчики исключений, чтобы обеспечить более специфическую обработку ошибок. Давайте создадим более продвинутый пример:
Создайте новый файл с именем custom_exception_handler.py в вашей директории http_status_lab:
import requests
import logging
## Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
## Custom exceptions
class APIError(Exception):
"""Base class for API request exceptions."""
pass
class ClientError(APIError):
"""Exception raised for client errors (4xx)."""
pass
class ServerError(APIError):
"""Exception raised for server errors (5xx)."""
pass
class NotFoundError(ClientError):
"""Exception raised for 404 Not Found errors."""
pass
class UnauthorizedError(ClientError):
"""Exception raised for 401 Unauthorized errors."""
pass
def api_request(url, method="get", **kwargs):
"""Make an API request with comprehensive error handling."""
try:
## Make the request using the specified HTTP method
response = getattr(requests, method.lower())(url, **kwargs)
## Log the response status
logger.info(f"Request to {url} returned status code {response.status_code}")
## Handle different status codes
if 200 <= response.status_code < 300:
return response
elif response.status_code == 401:
raise UnauthorizedError(f"Authentication required: {response.text}")
elif response.status_code == 404:
raise NotFoundError(f"Resource not found: {url}")
elif 400 <= response.status_code < 500:
raise ClientError(f"Client error {response.status_code}: {response.text}")
elif 500 <= response.status_code < 600:
raise ServerError(f"Server error {response.status_code}: {response.text}")
else:
raise APIError(f"Unexpected status code {response.status_code}: {response.text}")
except requests.exceptions.RequestException as e:
logger.error(f"Request failed: {e}")
raise APIError(f"Request failed: {e}")
## Test the custom exception handler
def test_api_request(url):
"""Test the api_request function with error handling."""
try:
response = api_request(url)
print(f"Success! Status code: {response.status_code}")
print(f"Response: {response.text[:100]}...") ## Print first 100 characters
return response
except UnauthorizedError as e:
print(f"Authentication Error: {e}")
except NotFoundError as e:
print(f"Not Found Error: {e}")
except ClientError as e:
print(f"Client Error: {e}")
except ServerError as e:
print(f"Server Error: {e}")
except APIError as e:
print(f"API Error: {e}")
return None
## Test with different status codes
print("Testing 200 OK:")
test_api_request("https://httpbin.org/status/200")
print("\nTesting 404 Not Found:")
test_api_request("https://httpbin.org/status/404")
print("\nTesting 401 Unauthorized:")
test_api_request("https://httpbin.org/status/401")
print("\nTesting 500 Internal Server Error:")
test_api_request("https://httpbin.org/status/500")
Запустите скрипт, чтобы увидеть, как работает обработка пользовательских исключений:
python custom_exception_handler.py
Этот расширенный пример демонстрирует несколько важных концепций:
- Пользовательские классы исключений, которые расширяют базовый класс
Exception - Иерархические исключения для разных типов ошибок
- Подробное ведение журнала для отладки и мониторинга
- Специфическая обработка для разных кодов состояния
Этот подход позволяет вам обрабатывать различные типы ошибок более структурированным и удобным для сопровождения способом, что необходимо для больших приложений.
Создание полноценного клиента Web API
На этом заключительном этапе мы объединим все, что вы узнали, чтобы создать полноценный клиент Web API с надежной обработкой ошибок. Мы создадим клиент для JSONPlaceholder API, который является бесплатным онлайн REST API, который вы можете использовать для тестирования.
Создание простого API-клиента
Давайте создадим файл с именем api_client.py в вашей директории http_status_lab:
import requests
import json
import logging
## Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
class APIClient:
"""A simple API client with robust error handling."""
def __init__(self, base_url):
"""Initialize the API client with a base URL."""
self.base_url = base_url
self.session = requests.Session()
def request(self, endpoint, method="get", params=None, data=None, headers=None):
"""Make a request to the API with error handling."""
url = f"{self.base_url}{endpoint}"
## Default headers for JSON APIs
if headers is None:
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
## Convert data to JSON if it's a dictionary
json_data = None
if data and isinstance(data, dict):
json_data = data
data = None
try:
## Log the request
logger.info(f"Making {method.upper()} request to {url}")
## Make the request
response = self.session.request(
method=method,
url=url,
params=params,
data=data,
json=json_data,
headers=headers
)
## Log the response status
logger.info(f"Received response with status code {response.status_code}")
## Check for HTTP errors
response.raise_for_status()
## Parse JSON response if applicable
try:
return response.json()
except json.JSONDecodeError:
return response.text
except requests.exceptions.HTTPError as e:
status_code = e.response.status_code
error_message = f"HTTP Error: {status_code}"
try:
## Try to get more details from the response
error_data = e.response.json()
error_message = f"{error_message} - {error_data.get('message', str(error_data))}"
except (json.JSONDecodeError, AttributeError):
error_message = f"{error_message} - {e.response.text if hasattr(e, 'response') else str(e)}"
logger.error(error_message)
## Re-raise with more context
if 400 <= status_code < 500:
logger.error(f"Client error: {error_message}")
raise Exception(f"Client error (HTTP {status_code}): {error_message}")
elif 500 <= status_code < 600:
logger.error(f"Server error: {error_message}")
raise Exception(f"Server error (HTTP {status_code}): {error_message}")
else:
raise
except requests.exceptions.ConnectionError as e:
logger.error(f"Connection Error: {e}")
raise Exception(f"Connection Error: Could not connect to {url}")
except requests.exceptions.Timeout as e:
logger.error(f"Timeout Error: {e}")
raise Exception(f"Timeout Error: Request to {url} timed out")
except requests.exceptions.RequestException as e:
logger.error(f"Request Exception: {e}")
raise Exception(f"Request Error: {str(e)}")
def get(self, endpoint, params=None, headers=None):
"""Make a GET request to the API."""
return self.request(endpoint, method="get", params=params, headers=headers)
def post(self, endpoint, data=None, headers=None):
"""Make a POST request to the API."""
return self.request(endpoint, method="post", data=data, headers=headers)
def put(self, endpoint, data=None, headers=None):
"""Make a PUT request to the API."""
return self.request(endpoint, method="put", data=data, headers=headers)
def delete(self, endpoint, headers=None):
"""Make a DELETE request to the API."""
return self.request(endpoint, method="delete", headers=headers)
## Test the API client with JSONPlaceholder
def test_api_client():
"""Test the API client with JSONPlaceholder."""
client = APIClient("https://jsonplaceholder.typicode.com")
try:
## Get a list of posts
print("\nGetting posts:")
posts = client.get("/posts")
print(f"Retrieved {len(posts)} posts")
print(f"First post: {posts[0]}")
## Get a single post
print("\nGetting a single post:")
post = client.get("/posts/1")
print(f"Retrieved post: {post}")
## Get a non-existent post (404)
print("\nTrying to get a non-existent post:")
try:
client.get("/posts/999999")
except Exception as e:
print(f"Expected error: {e}")
## Create a new post
print("\nCreating a new post:")
new_post = client.post("/posts", {
"title": "My New Post",
"body": "This is the content of my new post.",
"userId": 1
})
print(f"Created post: {new_post}")
## Update a post
print("\nUpdating a post:")
updated_post = client.put("/posts/1", {
"id": 1,
"title": "Updated Title",
"body": "Updated content.",
"userId": 1
})
print(f"Updated post: {updated_post}")
## Delete a post
print("\nDeleting a post:")
delete_response = client.delete("/posts/1")
print(f"Delete response: {delete_response}")
except Exception as e:
print(f"An error occurred: {e}")
if __name__ == "__main__":
test_api_client()
Запустите скрипт, чтобы увидеть, как наш полноценный API-клиент обрабатывает различные сценарии:
python api_client.py
Тестирование обработки ошибок
Давайте создадим отдельный файл для тестирования возможностей обработки ошибок нашего API-клиента. Создайте файл с именем test_error_handling.py в вашей директории http_status_lab:
from api_client import APIClient
import time
def test_different_errors():
"""Test different error scenarios with our API client."""
## Test with a valid API
print("\n1. Testing with a valid API:")
valid_client = APIClient("https://jsonplaceholder.typicode.com")
try:
data = valid_client.get("/posts/1")
print(f"Success! Retrieved data: {data}")
except Exception as e:
print(f"Unexpected error: {e}")
## Test with a 404 error
print("\n2. Testing with a non-existent endpoint (404):")
try:
valid_client.get("/non_existent_endpoint")
except Exception as e:
print(f"Expected error: {e}")
## Test with an invalid host
print("\n3. Testing with an invalid host (Connection Error):")
invalid_client = APIClient("https://this-does-not-exist-123456789.com")
try:
invalid_client.get("/anything")
except Exception as e:
print(f"Expected error: {e}")
## Test with a timeout
print("\n4. Testing with a timeout:")
timeout_client = APIClient("https://httpbin.org")
try:
## httpbin.org/delay/5 will delay the response for 5 seconds
## but we'll set the timeout to 2 seconds
timeout_client.session.request = lambda **kwargs: timeout_client.session.request_original(
**{**kwargs, 'timeout': 2}
)
timeout_client.session.request_original = timeout_client.session.request
timeout_client.get("/delay/5")
except Exception as e:
print(f"Expected error: {e}")
if __name__ == "__main__":
test_different_errors()
Скрипт выше может выдавать ошибки из-за обработки таймаута, поскольку он пытается изменить метод во время выполнения. Давайте упростим его, чтобы избежать этих проблем:
from api_client import APIClient
def test_different_errors():
"""Test different error scenarios with our API client."""
## Test with a valid API
print("\n1. Testing with a valid API:")
valid_client = APIClient("https://jsonplaceholder.typicode.com")
try:
data = valid_client.get("/posts/1")
print(f"Success! Retrieved data: {data}")
except Exception as e:
print(f"Unexpected error: {e}")
## Test with a 404 error
print("\n2. Testing with a non-existent endpoint (404):")
try:
valid_client.get("/non_existent_endpoint")
except Exception as e:
print(f"Expected error: {e}")
## Test with a server error
print("\n3. Testing with a server error (500):")
error_client = APIClient("https://httpbin.org")
try:
error_client.get("/status/500")
except Exception as e:
print(f"Expected error: {e}")
if __name__ == "__main__":
test_different_errors()
Запустите скрипт тестирования ошибок:
python test_error_handling.py
Этот скрипт демонстрирует, как наш API-клиент обрабатывает различные сценарии ошибок, обеспечивая надежную основу для реальных приложений.
Основные выводы
Создав этот полноценный API-клиент, вы изучили несколько важных методов:
- Создание многоразового класса API-клиента
- Реализация комплексной обработки ошибок
- Ведение журнала запросов и ответов для отладки
- Разбор различных форматов ответов
- Обработка различных HTTP-методов (GET, POST, PUT, DELETE)
Эти навыки необходимы для создания надежных приложений Python, которые взаимодействуют с веб-API, гарантируя, что ваш код сможет корректно обрабатывать различные сценарии ошибок и предоставлять пользователям содержательные сообщения об ошибках.
Резюме
В этой лабораторной работе вы узнали, как обрабатывать HTTP-коды состояния в запросах Python, переходя от базовых к продвинутым методам. Теперь у вас есть навыки:
- Понимать различные категории HTTP-кодов состояния и их значения
- Реализовывать базовую обработку ошибок с проверкой кодов состояния
- Использовать
raise_for_status()для простой обработки ошибок - Создавать пользовательские классы исключений для более специфической обработки ошибок
- Создавать полноценный API-клиент с комплексной обработкой ошибок
Эти навыки необходимы для разработки надежных приложений Python, взаимодействующих с веб-сервисами. Правильно обрабатывая HTTP-коды состояния и ошибки, вы можете создавать более надежные приложения, которые обеспечивают лучший пользовательский опыт и упрощают обслуживание.
Помните, что при работе с HTTP-запросами в ваших приложениях Python всегда следует учитывать обработку ошибок. Это важный аспект веб-разработки, который отличает профессиональный код от любительских реализаций.



