Introdução
Este tutorial irá guiá-lo através do tratamento de diferentes códigos de status HTTP em requisições Python. Os códigos de status HTTP são essenciais para entender se uma requisição web foi bem-sucedida ou falhou, e como responder adequadamente a diferentes cenários. Ao final deste laboratório, você aprenderá como implementar um tratamento de erros robusto em suas aplicações Python que interagem com serviços web.
Você começará entendendo os códigos de status HTTP, e então construirá progressivamente suas habilidades implementando técnicas básicas e avançadas de tratamento de erros. Este conhecimento é crucial para o desenvolvimento de aplicações web Python confiáveis que lidam graciosamente com várias respostas do servidor.
Entendendo os Códigos de Status HTTP e Configuração
O que são Códigos de Status HTTP?
Os códigos de status HTTP são números de três dígitos retornados por um servidor em resposta à requisição de um cliente. Eles indicam se uma requisição HTTP específica foi concluída com sucesso. Esses códigos são agrupados em cinco categorias:
- 1xx (Informativo): A requisição foi recebida e o processo está continuando
- 2xx (Sucesso): A requisição foi recebida, entendida e aceita com sucesso
- 3xx (Redirecionamento): Ações adicionais devem ser tomadas para completar a requisição
- 4xx (Erro do Cliente): A requisição contém sintaxe incorreta ou não pode ser atendida
- 5xx (Erro do Servidor): O servidor falhou ao atender uma requisição aparentemente válida
Códigos de status comuns que você encontrará incluem:
| Código de Status | Nome | Descrição |
|---|---|---|
| 200 | OK | Requisição bem-sucedida |
| 201 | Created | Requisição bem-sucedida e recurso criado |
| 400 | Bad Request | Servidor não pode processar a requisição |
| 401 | Unauthorized | Autenticação é necessária |
| 404 | Not Found | O recurso solicitado não foi encontrado |
| 500 | Internal Server Error | O servidor encontrou uma condição inesperada |
Configurando Seu Ambiente Python
Antes de começarmos a lidar com códigos de status HTTP, vamos instalar a biblioteca requests do Python e criar um script de teste simples.
- Abra um terminal no ambiente LabEx e execute:
pip install requests
- Crie um novo diretório para os arquivos do nosso projeto:
mkdir -p ~/project/http_status_lab
cd ~/project/http_status_lab
- Usando o WebIDE, crie um novo arquivo chamado
test_request.pyno diretóriohttp_status_labcom este código básico:
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}")
- Execute o script de teste para vê-lo em ação:
python test_request.py
Você deve ver uma saída semelhante a esta:
Status Code: 200
Response: ...
Request was successful: True
Isso confirma que você configurou com sucesso o ambiente Python e pode fazer requisições HTTP básicas. Nos próximos passos, você aprenderá como lidar com diferentes códigos de status HTTP e implementar técnicas de tratamento de erros mais avançadas.
Lidando com Códigos de Status HTTP Básicos
Agora que você entende o que são códigos de status HTTP e configurou seu ambiente, vamos implementar o tratamento de erros básico para códigos de status comuns.
Criando uma Ferramenta de Teste de Códigos de Status
Primeiro, vamos criar uma ferramenta que nos permitirá testar diferentes códigos de status HTTP. O site httpbin.org fornece endpoints que retornam códigos de status específicos, o que é perfeito para nossos testes.
Crie um novo arquivo chamado status_code_tester.py no seu diretório http_status_lab com o seguinte código:
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)
Este script faz uma requisição para httpbin.org com um código de status específico e imprime o resultado. Você pode executá-lo com um argumento de linha de comando para testar diferentes códigos de status.
Testando Diferentes Códigos de Status
Vamos testar nosso script com diferentes códigos de status para ver como a biblioteca requests os lida:
- Teste uma requisição bem-sucedida (200 OK):
python status_code_tester.py 200
Saída esperada:
Status Code: 200
Response OK: True
- Teste um erro do cliente (404 Not Found):
python status_code_tester.py 404
Saída esperada:
Status Code: 404
Response OK: False
- Teste um erro do servidor (500 Internal Server Error):
python status_code_tester.py 500
Saída esperada:
Status Code: 500
Response OK: False
Lidando com Categorias de Códigos de Status Básicas
Agora, vamos criar um script mais abrangente que lida com diferentes categorias de códigos de status de forma apropriada. Crie um novo arquivo chamado basic_handler.py no seu diretório 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")
Execute o script para ver como ele lida com diferentes códigos de status:
python basic_handler.py
Saída esperada:
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
Este manipulador básico agrupa os códigos de status em categorias e toma diferentes ações com base na categoria. Este é um padrão comum em aplicações web Python, permitindo que você lide com diferentes tipos de respostas de forma apropriada.
Implementando Tratamento de Erros Avançado
Agora que você entende os conceitos básicos de tratamento de códigos de status HTTP, vamos implementar técnicas de tratamento de erros mais avançadas para suas aplicações Python.
Usando raise_for_status()
A biblioteca requests fornece um método conveniente chamado raise_for_status() que levanta uma exceção para códigos de status 4xx e 5xx. Esta é uma maneira simples, mas eficaz, de lidar com erros em seu código.
Crie um novo arquivo chamado raise_for_status_example.py no seu diretório 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}")
Execute o script para ver como raise_for_status() lida com erros:
python raise_for_status_example.py
A saída esperada mostrará que a biblioteca automaticamente levanta exceções para códigos de status que não são 2xx, que seu código pode então capturar e tratar apropriadamente.
Criando Manipuladores de Exceção Personalizados
Para aplicações mais complexas, você pode querer criar manipuladores de exceção personalizados para fornecer um tratamento de erros mais específico. Vamos criar um exemplo mais avançado:
Crie um novo arquivo chamado custom_exception_handler.py no seu diretório 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")
Execute o script para ver como o tratamento de exceção personalizado funciona:
python custom_exception_handler.py
Este exemplo avançado demonstra vários conceitos importantes:
- Classes de exceção personalizadas que estendem a classe base
Exception - Exceções hierárquicas para diferentes tipos de erros
- Log detalhado para depuração e monitoramento
- Tratamento específico para diferentes códigos de status
Esta abordagem permite que você lide com diferentes tipos de erros de uma maneira mais estruturada e sustentável, o que é essencial para aplicações maiores.
Construindo um Cliente de API Web Completo
Nesta etapa final, reuniremos tudo o que você aprendeu para construir um cliente de API web completo com tratamento de erros robusto. Criaremos um cliente para a API JSONPlaceholder, que é uma API REST online gratuita que você pode usar para testes.
Criando um Cliente de API Simples
Vamos criar um arquivo chamado api_client.py no seu diretório 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()
Execute o script para ver como nosso cliente de API completo lida com diferentes cenários:
python api_client.py
Testando o Tratamento de Erros
Vamos criar um arquivo separado para testar as capacidades de tratamento de erros do nosso cliente de API. Crie um arquivo chamado test_error_handling.py no seu diretório 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()
O script acima pode produzir erros devido ao tratamento de timeout, pois ele tenta modificar um método em tempo de execução. Vamos simplificá-lo para evitar esses problemas:
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()
Execute o script de teste de erros:
python test_error_handling.py
Este script demonstra como nosso cliente de API lida com diferentes cenários de erro, fornecendo uma base robusta para aplicações do mundo real.
Principais Conclusões
Ao construir este cliente de API completo, você aprendeu várias técnicas importantes:
- Criar uma classe de cliente de API reutilizável
- Implementar tratamento de erros abrangente
- Registrar requisições e respostas para depuração
- Analisar diferentes formatos de resposta
- Lidar com diferentes métodos HTTP (GET, POST, PUT, DELETE)
Essas habilidades são essenciais para construir aplicações Python robustas que interagem com APIs web, garantindo que seu código possa lidar graciosamente com vários cenários de erro e fornecer mensagens de erro significativas aos usuários.
Resumo
Neste laboratório, você aprendeu como lidar com códigos de status HTTP em requisições Python, progredindo de técnicas básicas para avançadas. Agora você tem as habilidades para:
- Entender diferentes categorias de códigos de status HTTP e seus significados
- Implementar tratamento de erros básico com verificação de código de status
- Usar
raise_for_status()para tratamento de erros simples - Criar classes de exceção personalizadas para tratamento de erros mais específico
- Construir um cliente de API completo com tratamento de erros abrangente
Essas habilidades são essenciais para desenvolver aplicações Python robustas que interagem com serviços web. Ao lidar adequadamente com códigos de status HTTP e erros, você pode criar aplicações mais confiáveis que proporcionam melhores experiências ao usuário e são mais fáceis de manter.
Lembre-se de sempre considerar o tratamento de erros ao trabalhar com requisições HTTP em suas aplicações Python. É um aspecto crucial do desenvolvimento web que distingue o código profissional das implementações amadoras.



