Wie man verschiedene HTTP-Statuscodes in Python requests behandelt

PythonPythonBeginner
Jetzt üben

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Introduction

This tutorial will guide you through handling different HTTP status codes in Python requests. HTTP status codes are essential for understanding whether a web request succeeded or failed, and how to properly respond to different scenarios. By the end of this lab, you will learn how to implement robust error handling in your Python applications that interact with web services.

You will start with understanding HTTP status codes, then progressively build your skills by implementing basic and advanced error handling techniques. This knowledge is crucial for developing reliable Python web applications that gracefully handle various server responses.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/ErrorandExceptionHandlingGroup(["Error and Exception Handling"]) python(("Python")) -.-> python/NetworkingGroup(["Networking"]) python/FunctionsGroup -.-> python/function_definition("Function Definition") python/ErrorandExceptionHandlingGroup -.-> python/catching_exceptions("Catching Exceptions") python/ErrorandExceptionHandlingGroup -.-> python/custom_exceptions("Custom Exceptions") python/NetworkingGroup -.-> python/http_requests("HTTP Requests") subgraph Lab Skills python/function_definition -.-> lab-398002{{"Wie man verschiedene HTTP-Statuscodes in Python requests behandelt"}} python/catching_exceptions -.-> lab-398002{{"Wie man verschiedene HTTP-Statuscodes in Python requests behandelt"}} python/custom_exceptions -.-> lab-398002{{"Wie man verschiedene HTTP-Statuscodes in Python requests behandelt"}} python/http_requests -.-> lab-398002{{"Wie man verschiedene HTTP-Statuscodes in Python requests behandelt"}} end

Understanding HTTP Status Codes and Setting Up

What are HTTP Status Codes?

HTTP status codes are three-digit numbers returned by a server in response to a client's request. They indicate whether a specific HTTP request has been successfully completed. These codes are grouped into five categories:

  • 1xx (Informational): The request was received, and the process is continuing
  • 2xx (Success): The request was successfully received, understood, and accepted
  • 3xx (Redirection): Further action must be taken to complete the request
  • 4xx (Client Error): The request contains bad syntax or cannot be fulfilled
  • 5xx (Server Error): The server failed to fulfill an apparently valid request

Common status codes you will encounter include:

Status Code Name Description
200 OK Request succeeded
201 Created Request succeeded and resource created
400 Bad Request Server cannot process the request
401 Unauthorized Authentication is required
404 Not Found The requested resource could not be found
500 Internal Server Error The server encountered an unexpected condition

Setting Up Your Python Environment

Before we begin handling HTTP status codes, let's install the Python requests library and create a simple test script.

  1. Open a terminal in the LabEx environment and run:
pip install requests
  1. Create a new directory for our project files:
mkdir -p ~/project/http_status_lab
cd ~/project/http_status_lab
  1. Using the WebIDE, create a new file called test_request.py in the http_status_lab directory with this basic code:
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}")
  1. Run the test script to see it in action:
python test_request.py

You should see output similar to this:

Status Code: 200
Response: ...
Request was successful: True

This confirms that you have successfully set up the Python environment and can make basic HTTP requests. In the next steps, you will learn how to handle different HTTP status codes and implement more advanced error handling techniques.

Handling Basic HTTP Status Codes

Now that you understand what HTTP status codes are and have set up your environment, let's implement basic error handling for common status codes.

Creating a Status Code Testing Tool

First, let's create a tool that will allow us to test different HTTP status codes. The website httpbin.org provides endpoints that return specific status codes, which is perfect for our testing.

Create a new file called status_code_tester.py in your http_status_lab directory with the following code:

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)

This script makes a request to httpbin.org with a specific status code and prints the result. You can run it with a command line argument to test different status codes.

Testing Different Status Codes

Let's test our script with different status codes to see how the requests library handles them:

  1. Test a successful request (200 OK):
python status_code_tester.py 200

Expected output:

Status Code: 200
Response OK: True
  1. Test a client error (404 Not Found):
python status_code_tester.py 404

Expected output:

Status Code: 404
Response OK: False
  1. Test a server error (500 Internal Server Error):
python status_code_tester.py 500

Expected output:

Status Code: 500
Response OK: False

Handling Basic Status Code Categories

Now, let's create a more comprehensive script that handles different categories of status codes appropriately. Create a new file called basic_handler.py in your http_status_lab directory:

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")

Run the script to see how it handles different status codes:

python basic_handler.py

Expected output:

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

This basic handler groups status codes into categories and takes different actions based on the category. This is a common pattern in Python web applications, allowing you to handle different types of responses appropriately.

Implementing Advanced Error Handling

Now that you understand the basics of handling HTTP status codes, let's implement more advanced error handling techniques for your Python applications.

Using raise_for_status()

The requests library provides a convenient method called raise_for_status() that raises an exception for 4xx and 5xx status codes. This is a simple but effective way to handle errors in your code.

Create a new file called raise_for_status_example.py in your http_status_lab directory:

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}")

Run the script to see how raise_for_status() handles errors:

python raise_for_status_example.py

Expected output will show that the library automatically raises exceptions for non-2xx status codes, which your code can then catch and handle appropriately.

Creating Custom Exception Handlers

For more complex applications, you might want to create custom exception handlers to provide more specific error handling. Let's create a more advanced example:

Create a new file called custom_exception_handler.py in your http_status_lab directory:

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")

Run the script to see how custom exception handling works:

python custom_exception_handler.py

This advanced example demonstrates several important concepts:

  1. Custom exception classes that extend the base Exception class
  2. Hierarchical exceptions for different types of errors
  3. Detailed logging for debugging and monitoring
  4. Specific handling for different status codes

This approach allows you to handle different error types in a more structured and maintainable way, which is essential for larger applications.

Building a Complete Web API Client

In this final step, we will bring together everything you have learned to build a complete web API client with robust error handling. We will create a client for the JSONPlaceholder API, which is a free online REST API that you can use for testing.

Creating a Simple API Client

Let's create a file called api_client.py in your http_status_lab directory:

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()

Run the script to see how our complete API client handles different scenarios:

python api_client.py

Testing Error Handling

Let's create a separate file to test the error handling capabilities of our API client. Create a file called test_error_handling.py in your http_status_lab directory:

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()

The script above might produce errors due to the timeout handling, as it attempts to modify a method at runtime. Let's simplify it to avoid those issues:

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()

Run the error testing script:

python test_error_handling.py

This script demonstrates how our API client handles different error scenarios, providing a robust foundation for real-world applications.

Key Takeaways

Through building this complete API client, you have learned several important techniques:

  1. Creating a reusable API client class
  2. Implementing comprehensive error handling
  3. Logging requests and responses for debugging
  4. Parsing different response formats
  5. Handling different HTTP methods (GET, POST, PUT, DELETE)

These skills are essential for building robust Python applications that interact with web APIs, ensuring that your code can gracefully handle various error scenarios and provide meaningful error messages to users.

Summary

In this lab, you learned how to handle HTTP status codes in Python requests, progressing from basic to advanced techniques. You now have the skills to:

  • Understand different categories of HTTP status codes and their meanings
  • Implement basic error handling with status code checking
  • Use raise_for_status() for simple error handling
  • Create custom exception classes for more specific error handling
  • Build a complete API client with comprehensive error handling

These skills are essential for developing robust Python applications that interact with web services. By properly handling HTTP status codes and errors, you can create more reliable applications that provide better user experiences and are easier to maintain.

Remember to always consider error handling when working with HTTP requests in your Python applications. It is a crucial aspect of web development that distinguishes professional code from amateur implementations.