Décorateur @rate_limit
Décorateur @rate_limit
Le décorateur rate_limit limite le nombre d'appels à une méthode pendant une période. Au-delà de la limite, Dodock lève frappe.RateLimitExceededError et la réponse HTTP a le code 429.
Le décorateur agit seulement pendant une requête HTTP. Un appel depuis une tâche de fond ou la console n'est pas compté.
Principe
Dodock garde un compteur dans Redis pour chaque méthode et chaque identité. Le compteur expire à la fin de la période (seconds).
Paramètres
from frappe.rate_limiter import rate_limit
| Paramètre | Défaut | Rôle |
|---|---|---|
limit | 5 | Nombre maximal d'appels dans la période. Accepte une fonction qui renvoie un entier |
seconds | 86400 (24 h) | Durée de la période, en secondes |
methods | "ALL" | Méthodes HTTP comptées, par exemple ["POST"]. "ALL" compte toutes les méthodes |
ip_based | True | Identifie l'appelant par son adresse IP |
key | None | Nom d'un paramètre de la requête. Sa valeur est ajoutée à l'identité, par exemple l'adresse e-mail saisie |
user_based | False | Identifie l'appelant par son utilisateur connecté |
endpoint | Chemin de la fonction | Nom du compteur. Nécessaire si la fonction n'a pas de chemin pointé stable, par exemple un functools.partial |
Identité de l'appelant
| Réglage | Identité |
|---|---|
Par défaut (ip_based=True) | Adresse IP, plus la valeur du paramètre key si key est défini |
ip_based=False et key défini | Valeur du paramètre key seulement |
user_based=True | Utilisateur connecté. Pour un invité, adresse IP. Plus la valeur du paramètre key si key est défini |
Pourquoi suivre par utilisateur
Dans une entreprise, plusieurs personnes partagent souvent la même adresse IP publique. Avec un suivi par IP, une personne qui atteint la limite bloque aussi ses collègues. Avec user_based=True, chaque utilisateur connecté a son propre compteur.
Exemples
Limite par utilisateur
import frappe
from frappe.rate_limiter import rate_limit
@frappe.whitelist()
@rate_limit(limit=20, seconds=60, user_based=True)
def rechercher_clients(search_term: str):
"""Chaque utilisateur peut faire 20 recherches par minute."""
return frappe.get_list(
"Customer",
filters={"customer_name": ["like", f"%{search_term}%"]},
fields=["name", "customer_name"],
limit=50,
)
Deux utilisateurs derrière la même adresse IP ont chacun 20 recherches par minute.
Limite par IP et par paramètre, sur les seuls appels POST
@frappe.whitelist(allow_guest=True)
@rate_limit(key="email", limit=5, seconds=60 * 60, methods=["POST"])
def demander_rappel(email: str):
...
Chaque couple adresse IP et adresse e-mail peut envoyer 5 demandes par heure.
Limite dynamique
limit accepte une fonction. Dodock l'appelle à chaque requête.
@frappe.whitelist()
@rate_limit(
limit=lambda: frappe.db.get_single_value("Mes Parametres", "limite_appels"), # type de document fictif
seconds=300,
user_based=True,
)
def mon_point_acces():
...
Bonnes pratiques
- Placez
@frappe.whitelist()au-dessus de@rate_limit(...). - Utilisez
user_based=Truepour les méthodes réservées aux utilisateurs connectés. - Pour une méthode ouverte aux invités, gardez le suivi par IP et ajoutez
keysi un identifiant est saisi. - Choisissez un seuil adapté à l'usage réel. Trop bas, il bloque le travail normal. Trop haut, il ne protège plus.