existSanic
In , middleware refers to the code executed between a request and a response. They are a very powerful tool for handling tasks such as preprocessing of requests, post-processing of responses, global error handling, logging, authentication, permission verification, cross-domain resource sharing (CORS) and other tasks. Middleware is usually used for the global logic of an application and is executed before or after the request is processed.
Workflow of Sanic Middleware
The Sanic middleware is executed in the following two stages:
- Before the request is processed: Process the request data and execute before the request is routed. It can be used for verification, modification of requests and other operations.
- After the response process: Process the response data and execute before the response is sent to the client. It can be used to modify responses, record logs and other operations.
Use of middleware
existSanic
In , the middleware is defined by a decorator. You can define middleware for the entire application (global middleware) or for some specific routes.
1. Global middleware
Global middleware is called throughout the life cycle of the application and does not depend on specific routes. They are executed at all stages of the request and response.
Global middleware for the request phase
Global middleware is usually used to handle preprocessing of requests, such as authentication, logging, setting CORS, etc.
from sanic import Sanic from import json app = Sanic("MyApp") # Global middleware for the request phase@("request") async def log_request(request): print(f"Request received: {} {}") # You can modify the request here, such as authentication, setting CORS, etc. # If the request requires authentication, you can verify it here # Global middleware for the response phase@("response") async def log_response(request, response): print(f"Response status: {}") # You can modify the response here, such as adding CORS headers, logging, etc. # Process before the response is returned to the client ['X-Custom-Header'] = 'Sanic' # Can modify the response header return response @("/") async def hello(request): return json({"message": "Hello, Sanic!"}) if __name__ == "__main__": (host="0.0.0.0", port=8000)
In this example:
-
log_request
The middleware will be called when each request arrives at the application, printing the requested method and URL. -
log_response
The middleware will be called before each response is returned to the client, print the status code of the response, and add a custom header to the response header.
2. Routing middleware
You can also define middleware for specific routes or routing groups. This is the flexibility provided by Sanic that allows you to add extra logic to certain routes without affecting others.
@("request", route="/user/<user_id>") async def check_user_auth(request, user_id): # Suppose we authenticate through header if ("Authorization") != "Bearer my_token": return json({"error": "Unauthorized"}, status=401) print(f"Authenticated request for user {user_id}")
In this example,check_user_auth
Middleware will only be accessed/user/<user_id>
Execute during routing, check the request headerAuthorization
Whether the field contains a valid token.
3. Exception handling middleware
Sanic
The exception handling middleware in allows you to catch unhandled exceptions in the application and provide users with customized error information.
You can use@
The decorator catches exceptions of a specific type, and can also use middleware to handle errors globally.
Catch specific exceptions
from import SanicException @(SanicException) async def handle_sanic_exception(request, exception): return json({"error": f"Sanic error: {exception}"}, status=500)
Catch all exceptions
You can also define a global middleware that catches all exceptions and returns custom error messages.
@("response") async def handle_all_exceptions(request, response): if hasattr(request, 'exception') and : return json({"error": f"Unhandled error: {}"}, status=500) return response
In this example, if an exception is thrown by a request and is not caught by other middleware,handle_all_exceptions
A custom error response will be captured and returned.
4. Asynchronous middleware
becauseSanic
It is based onasyncio
Asynchronous framework, you can write asynchronous middleware so that they do not block the event loop. Middleware is asynchronous, which means you can perform I/O operations (such as database queries or HTTP requests) without blocking other requests.
@("request") async def async_middleware(request): # Asynchronous operations, such as querying databases asynchronously await (1) print("Async operation completed")
5. Priority and middleware order
Multiple middleware will be executed in the defined order. existSanic
In , the priority of the middleware is fixed and executed in the following order:
- Request middleware: Execute in the defined order, and the first defined request middleware is executed first.
- Response middleware: executes in the defined order, and the first defined response middleware is executed first.
6. Middleware running mechanism
Request phase
Request phase middleware refers to those middleware executed before the request route matches, which are usually used for request preprocessing, such as requesting data parsing, verification, authentication, etc.
Response phase
The middleware of the response phase is executed before the response is sent back to the client and is usually used to modify the response, such as adding additional response headers, modifying the response data, etc.
7. Use middleware for authentication
In web development, authentication is a very common requirement. Middleware can be used to implement authentication logic. For example, check whether the tokens or cookies in the user request header are valid.
@("request") async def check_auth(request): token = ("Authorization") if token != "Bearer valid_token": return json({"error": "Unauthorized"}, status=401)
8. Implement CORS using middleware
Cross-domain resource sharing (CORS) is a mechanism that allows servers to control which domains can access their resources. You can use middleware to add CORS headers.
@("response") async def add_cors_headers(request, response): ["Access-Control-Allow-Origin"] = "*" ["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS" ["Access-Control-Allow-Headers"] = "Content-Type, Authorization" return response
9. Multiple decorators for middleware
Sanic supports multiple middleware decorator applications on the same request or response phase. For example, you can apply multiple request middleware at the same time.
@("request") async def middleware_one(request): print("Middleware one executed") @("request") async def middleware_two(request): print("Middleware two executed")
In this example,middleware_one
Will execute first, thenmiddleware_two
。
Summarize
Sanic
The middleware in it is very powerful and flexible, and they allow you to insert custom logic into the request processing flow of your web application. Middleware usage scenarios are very wide, including request and response processing, authentication, permission control, logging, CORS processing, error processing, etc.
Main features:
- Request Middleware: Execute before requesting routing processing.
- Response Middleware: Execute before the response is returned to the client.
- Global middleware: Suitable for the entire application.
- Routing middleware: Applicable to specific routes only.
- Asynchronous operation: Supports asynchronous middleware, which can perform I/O operations without blocking event loops.
By using middleware rationally, you canSanic
Flexible handling of various needs in the application to improve the maintainability and scalability of the code.
Statistics the time spent per request
To count the execution time of a request, you can useSanic
Middleware to implement it. During the life cycle of request processing, we can record the timestamp at the beginning of the request, calculate and output the execution time of the request at the end of the request.
1. Use middleware to count request execution time
We can useRequest MiddlewareTo record the timestamp at the beginning of each request, and then useResponse MiddlewareTo calculate and output the execution time of the request.
Sample code
from sanic import Sanic from import json import time app = Sanic("TimingApp") # Request Middleware: Record the time when the request starts@("request") async def record_start_time(request): .start_time = () # Store the start time into the request context # Response middleware: calculate and output the request execution time@("response") async def calculate_execution_time(request, response): if hasattr(, 'start_time'): end_time = () execution_time = end_time - .start_time # Calculate execution time print(f"Request to {} took {execution_time:.4f} seconds") return response # Sample routing@("/") async def hello(request): return json({"message": "Hello, Sanic!"}) if __name__ == "__main__": (host="0.0.0.0", port=8000)
2. Code parsing
-
record_start_time
Request Middleware:- Triggered when the request reaches the application, use
()
Get the current time and store it in.start_time
middle.ctx
yesSanic
The provided context object is used to store data during the life of the request.
- Triggered when the request reaches the application, use
-
calculate_execution_time
Response middleware:- Triggered after the request is completed, and the time for the request to be executed is calculated. pass
()
Get the current time and with.start_time
Compare to get the execution time of the request. - The execution time of the output request is in seconds.
- Triggered after the request is completed, and the time for the request to be executed is calculated. pass
-
Sample routing:
- exist
/
In the routing, we return a simple JSON response to test the statistics of the execution time of the request.
- exist
3. Test
When you run the above code and accesshttp://localhost:8000/
When the terminal outputs something similar to the following:
Request to / took 0.0023 seconds
This means that the request has been reached/
The execution time is0.0023
Second.
4. More complex statistical methods
If you need more granular control (for example, counting the average execution time of multiple requests, the slowest request, the shortest request, etc.), you can further expand this function to record statistics into a log file or database.
For example, you can log execution time into a global statistics:
import time from sanic import Sanic from import json from collections import defaultdict app = Sanic("TimingApp") request_stats = defaultdict(list) # Used to store execution time for each route # Request Middleware: Record the time when the request starts@("request") async def record_start_time(request): .start_time = () # Response middleware: calculate the request execution time and save it@("response") async def calculate_execution_time(request, response): if hasattr(, 'start_time'): end_time = () execution_time = end_time - .start_time print(f"Request to {} took {execution_time:.4f} seconds") request_stats[].append(execution_time) # Record execution time return response # Get the route for average execution time@("/stats") async def stats(request): stats = {url: sum(times) / len(times) for url, times in request_stats.items()} return json(stats) # Sample routing@("/") async def hello(request): return json({"message": "Hello, Sanic!"}) if __name__ == "__main__": (host="0.0.0.0", port=8000)
5. Explanation of extended functions
-
Record the request execution time: We use one
defaultdict(list)
To record the execution time of each URL request. After each request is completed, we add the execution time to the list of the corresponding URLs. -
Statistics on average execution time: We created one
/stats
Routing, used to return the average execution time of each route.
6. Summary
- use
Sanic
InmiddlewareIt is very easy to count the execution time of the request. By recording the time of the start and end of the request, the execution time of the request can be easily calculated. - You can expand this function according to your needs, such as counting the slowest requests, recording logs, etc.
This approach can help you monitor the performance of your application, especially in high concurrency, and quickly identify potential performance bottlenecks.
This is the end of this article about the use of Sanic middleware in Python3. For more related content of Python3 Sanic middleware, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!