杰瑞科技汇

Sanic如何快速构建RESTful服务?

Of course! Let's build a comprehensive guide to creating a REST API with Python and Sanic.

Sanic如何快速构建RESTful服务?-图1
(图片来源网络,侵删)

Sanic is a modern, fast, and simple Python web framework built on top of Python's asyncio. It's an excellent choice for building high-performance APIs because of its asynchronous nature, which allows it to handle many concurrent connections efficiently.

This guide will cover:

  1. Why Sanic? - The key advantages.
  2. Installation & Setup - Getting your environment ready.
  3. "Hello, World!" - Your first Sanic app.
  4. Building a RESTful API - The core concepts (routes, requests, responses).
  5. Advanced Features - URL parameters, request bodies, middleware, and static files.
  6. Project Structure - A scalable way to organize your application.
  7. Running with an ASGI Server - Production deployment.

Why Sanic?

  • Asynchronous First: Sanic uses async/await syntax, making it non-blocking by default. This is perfect for I/O-bound tasks like database calls, network requests, or file operations, leading to high throughput.
  • Blazing Fast: It's one of the fastest Python web frameworks, often compared to Go and Node.js in benchmarks.
  • Simple & Intuitive: The API is clean and easy to learn, especially if you're familiar with Flask or FastAPI.
  • Built-in Features: Includes support for WebSockets, static file serving, and middleware right out of the box.
  • Great Ecosystem: A growing number of extensions for databases (SQLAlchemy, TortoiseORM), authentication, and more.

Installation & Setup

First, you need to have Python 3.7 or newer installed.

Create a project directory and a virtual environment to keep your dependencies isolated.

Sanic如何快速构建RESTful服务?-图2
(图片来源网络,侵删)
# Create and navigate into your project directory
mkdir my_sanic_api
cd my_sanic_api
# Create a virtual environment
python -m venv venv
# Activate the virtual environment
# On macOS/Linux:
source venv/bin/activate
# On Windows:
.\venv\Scripts\activate
# Install Sanic
pip install "sanic[ext]"

The [ext] extra is recommended as it includes common extensions you might need.


"Hello, World!" - Your First Sanic App

Create a file named app.py and add the following code:

# app.py
from sanic import Sanic
from sanic.response import json
# 1. Create an instance of the Sanic class
app = Sanic("MyHelloWorldApp")
# 2. Define a route using the @app.route decorator
#    - The first argument is the URL path.
#    - The second argument is the HTTP method (defaults to 'GET').
@app.get("/")
async def hello_world(request):
    """
    This is an asynchronous function that handles the request.
    'request' is a Sanic request object containing all info about the incoming request.
    """
    # 3. Return a JSON response
    return json({"message": "Hello, World!"})
# 4. Run the app
if __name__ == "__main__":
    # The `host` and `port` arguments specify where the server will listen.
    app.run(host="0.0.0.0", port=8000, debug=True)

To run your app:

python app.py

You will see output like this:

[2025-10-27 10:30:00 -0400] [12345] [INFO] Goin' Fast @ http://0.0.0.0:8000
[2025-10-27 10:30:00 -0400] [12345] [INFO] Stopping worker after 0.5 seconds

Now, open your browser or a tool like curl or Postman and go to http://127.0.0.1:8000. You should see the JSON response: {"message": "Hello, World!"}.


Building a RESTful API: A Todo List Example

Let's build a simple REST API for managing a to-do list. We'll use a simple Python list as our "database" for this example.

Our API will have the following endpoints:

Method Path Description
GET /todos Get all to-do items.
POST /todos Create a new to-do item.
GET /todos/<id> Get a single to-do item by ID.
PUT /todos/<id> Update an existing to-do item.
DELETE /todos/<id> Delete a to-do item.

Update your app.py:

# app.py
from sanic import Sanic
from sanic.response import json, text
from sanic.exceptions import NotFound
# --- In-Memory "Database" ---
# In a real app, you would use a proper database.
todos = [
    {"id": 1, "task": "Build an API with Sanic", "completed": False},
    {"id": 2, "task": "Deploy the API", "completed": False},
]
next_todo_id = 3
# --- App Initialization ---
app = Sanic("TodoAPI")
# --- Helper Functions ---
def find_todo(todo_id):
    """Helper to find a todo by its ID."""
    for todo in todos:
        if todo["id"] == todo_id:
            return todo
    return None
# --- API Routes ---
# GET /todos - Get all todos
@app.get("/todos")
async def get_all_todos(request):
    return json(todos)
# POST /todos - Create a new todo
@app.post("/todos")
async def create_todo(request):
    global next_todo_id
    try:
        # 1. Get the JSON data from the request body
        data = request.json
        # 2. Basic validation
        if not data or "task" not in data:
            return json({"error": "Missing 'task' in request body"}, status=400)
        # 3. Create the new todo item
        new_todo = {
            "id": next_todo_id,
            "task": data["task"],
            "completed": data.get("completed", False) # Default to False
        }
        # 4. Add to our "database"
        todos.append(new_todo)
        next_todo_id += 1
        # 5. Return the created item with a 201 Created status
        return json(new_todo, status=201)
    except Exception:
        return json({"error": "Invalid JSON"}, status=400)
# GET /todos/<id> - Get a single todo
@app.get("/todos/<todo_id:int>")
async def get_todo(request, todo_id):
    todo = find_todo(todo_id)
    if todo:
        return json(todo)
    # If not found, return a 404 error
    raise NotFound(f"Todo with id {todo_id} not found")
# PUT /todos/<id> - Update a todo
@app.put("/todos/<todo_id:int>")
async def update_todo(request, todo_id):
    todo = find_todo(todo_id)
    if not todo:
        raise NotFound(f"Todo with id {todo_id} not found")
    data = request.json
    if not data:
        return json({"error": "Request body cannot be empty"}, status=400)
    # Update fields if they are provided in the request
    if "task" in data:
        todo["task"] = data["task"]
    if "completed" in data:
        todo["completed"] = data["completed"]
    return json(todo)
# DELETE /todos/<id> - Delete a todo
@app.delete("/todos/<todo_id:int>")
async def delete_todo(request, todo_id):
    todo = find_todo(todo_id)
    if not todo:
        raise NotFound(f"Todo with id {todo_id} not found")
    todos.remove(todo)
    # A successful DELETE request often returns a 204 No Content status
    return text("", status=204)
# --- Run the App ---
if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000, debug=True)

Key Concepts in this Example:

  • <todo_id:int>: This is a URL parameter. Sanic automatically converts it to an integer.
  • request.json: Accesses the parsed JSON body of a POST or PUT request.
  • json(data, status=code): A convenient way to return JSON responses with a specific HTTP status code.
  • text("", status=204): Returns an empty body with a 204 No Content status, which is standard for successful DELETE operations.
  • raise NotFound(...): Sanic has built-in exceptions. Raising them automatically generates the correct HTTP error response.

Advanced Features

Middleware

Middleware functions are executed on every request, either before it reaches your route handler (request middleware) or after the handler has processed it but before the response is sent (response middleware).

Here's a simple middleware to log all incoming requests.

# Add this to your app.py
@app.middleware("request")
async def log_request(request):
    print(f"Processing request: {request.method} {request.path}")
@app.middleware("response")
async def custom_headers(request, response):
    response.headers["Server"] = "MySuperCoolAPIServer"

Static Files

To serve static files (like CSS, JavaScript, or images), use the app.static method.

# Add this to your app.py
# Serve files from a 'static' directory in your project
app.static("/static", "./static")
# Now you can access a file at /static/style.css
# if you have a file named ./static/style.css

Project Structure

For any real application, you'll want to organize your code. Here's a common and scalable structure.

my_sanic_api/
├── venv/
├── app/
│   ├── __init__.py      # Initializes the Sanic app and registers blueprints
│   ├── routes/
│   │   ├── __init__.py
│   │   └── todos.py     # All todo-related routes
│   └── extensions.py    # For things like database connections
├── config.py            # Configuration settings
├── requirements.txt     # Your project dependencies
└── run.py               # The entry point to run the app

How it works:

  1. app/routes/todos.py: Move your todo routes here.

    # app/routes/todos.py
    from sanic import Blueprint
    from sanic.response import json, text
    from sanic.exceptions import NotFound
    # Create a Blueprint for the todo routes
    # A Blueprint is a way to organize a group of related routes
    todos_bp = Blueprint("todos", url_prefix="/todos")
    # ... (paste your todo route handlers here, but change @app.get to @todos_bp.get)
    # ... (paste find_todo helper here too)
  2. app/__init__.py: Initialize the app and register the blueprint.

    # app/__init__.py
    from sanic import Sanic
    from .routes import todos
    def create_app():
        app = Sanic("TodoAPI")
        # Register the blueprint
        app.blueprint(todos.todos_bp)
        return app
  3. run.py: The main script to run the app.

    # run.py
    from app import create_app
    app = create_app()
    if __name__ == "__main__":
        app.run(host="0.0.0.0", port=8000, debug=True)

This structure keeps your code modular and much easier to maintain as your API grows.


Running with an ASGI Server

The built-in app.run() is great for development, but it's not suitable for production. For production, you should use a production-grade ASGI (Asynchronous Server Gateway Interface) server like Uvicorn.

  1. Install Uvicorn:

    pip install uvicorn
  2. Run your app with Uvicorn: If you're using the project structure from the previous step, you would run:

    uvicorn run:app --host 0.0.0.0 --port 8000
    • run:app tells Uvicorn to look for an object named app inside the run.py file.
    • --host 0.0.0.0 makes the server accessible from any IP address on your machine.
    • --port 8000 specifies the port.

    You can also run it with multiple workers for better performance:

    uvicorn run:app --host 0.0.0.0 --port 8000 --workers 4

Summary

You've now learned the fundamentals of building a REST API with Sanic. You've covered:

  • Basic setup and "Hello, World".
  • Creating routes (GET, POST, PUT, DELETE).
  • Handling URL parameters and request bodies.
  • Returning JSON and status codes.
  • Using middleware and serving static files.
  • Organizing your code with blueprints for scalability.
  • Running your application in production with Uvicorn.

Sanic is a powerful and fun framework to work with, and its performance makes it a top contender for modern web development in Python. Happy coding

分享:
扫描分享到社交APP
上一篇
下一篇