Ecco, le API che tornano 200 con corpo vuoto sono una delle cose che mi fanno bestemmiare in tre lingue. Ti capisco benissimo!
Il try-except è la strada giusta, ma possiamo renderlo più pulito. Io di solito faccio così:
```python
response = requests.get('https://api.ricette-vino.com/abbinamenti')
try:
dati = response.json() if response.content else None
except ValueError: # cattura sia JSONDecodeError che altri errori di parsing
dati = None
```
Oppure, se preferisci essere esplicito:
```python
if response.status_code == 200 and response.text.strip():
dati = response.json()
else:
dati = None # o un dizionario vuoto {}, dipende da come vuoi gestirlo
```
Però occhio: controllare solo `len(response.content)` può essere rischioso se l’API torna spazi bianchi o porcherie simili. `response.text.strip()` è più sicuro.
Ah, e se lavori spesso con API fatte male, valuta di aggiungere un timeout alle request, perché quelle che tornano 200 a vuoto a volte sono anche lentissime.
P.S. Ottima scelta organizzare degustazioni, io sul vino sono un disastro, finirei a bere tutto il barolo al primo giro!
Quando lavori con API instabili, devi mettere in conto che a volte ti rispondano con contenuti indecifrabili. Il problema qui è che `response.json()` prova a decodificare qualunque cosa trovi, anche stringhe vuote o malformate. Io aggiungerei un controllo più granulare prima di tentare il parsing:
```python
response = requests.get('https://api.ricette-vino.com/abbinamenti', timeout=5)
if response.status_code == 200 and response.text.strip().startswith('{'):
dati = response.json()
else:
dati = {} # o [] se ti aspetti una lista
```
Così filtri solo i casi in cui il contenuto abbia effettivamente una struttura JSON (iniziando con `{`). Però occhio: se l’API restituisce dati validi ma occasionalmente vuoti (es. `{}`), quel controllo escluderebbe risposte legittime. In quel caso, meglio gestire separatamente i due scenari:
```python
try:
dati = response.json() if response.text.strip() else {"errore": "nessun dato"}
except json.JSONDecodeError:
dati = {"errore": "json non valido"}
```
E, come detto, il timeout è un must. Ho avuto casi in cui l’API rispondevva dopo 30 secondi con un body vuoto… non far sprecare tempo al programma per errori server.
Madonna, ste API che ti fanno il regalo del 200 con il body vuoto... un classico che ti fa venire voglia di lanciare la tastiera! Concordo con @bluamato10 sul controllo di `response.text.strip()`, ma evita l'approccio di @ernestosala di controllare `startswith('{')`: se un giorno l'API inizia a restituire array (come `[]`), il codice esplode.
La mia soluzione preferita? Un ibrido efficienza-eleganza:
```python
response = requests.get(url, timeout=3)
try:
dati = response.json() if response.content else {}
except json.JSONDecodeError:
dati = {} # logga l'errore se serve
```
Spiegazione lampo:
- `timeout` previene attese infinite (API vinicole a volte son lente come trattori)
- `response.content` è più veloce di `response.text.strip()` per verificare il vuoto
- Il `try-except` cattura JSON malformati (es. "null" o virgole pazze)
- Assegni un dizionario vuoto per evitare crash nel codice successivo
Se l'API è particolarmente ballerina, crea una funzione wrapper che gestisca questi casi sporchi. Così quando organizzi le tue degustazioni, almeno il codice non ti rovina la serata!
Mi sa che stai brancolando nel buio col rischio di romperti il naso, ma ci sto passando anch'io con API inaffidabili. Quel `JSONDecodeError` quando ti sparano un 200 vuoto è una presa per il culo bella e buona.
La soluzione meno farraginosa? Copia spudoratamente l'approccio di @gabriellalombardo80, ma con due aggiustamenti da chi ci è già scottato:
```python
try:
response = requests.get(url, timeout=5)
response.raise_for_status() # Aggiungi QUESTA per catturare 400/500 mascherati
dati = response.json() if response.content else None
except (requests.exceptions.RequestException, json.JSONDecodeError):
dati = None # o fallback a un dizionario vuoto se preferisci
```
Perché funziona meglio:
- `raise_for_status()` blocca subito i codici HTTP falsi (404, 500, ecc.) che a volte quelle API vinicole ti rifilano come se niente fosse
- Controllare `response.content` è più rapido di `text.strip()` e ti evita rogne con encoding
- Il doppio except piglia sia errori di rete che JSON zoppi
Se poi vuoi essere paranoico, aggiungi un logger nell'except per tracciare gli URL che ti fanno il dispetto. E per l'amor del vino, usa sempre il timeout: le API enologiche hanno la reattività di una bottiglia di Barolo '82 stappata ieri.
Grazie @gaiapellegrini, hai centrato proprio il mio dilemma! Il paragone col Barolo '82 è spietatamente vero 😂 Apprezzo tantissimo i tuoi aggiustamenti:
- **raise_for_status()** è oro, non mi ero accorta che certe API vinicole nascondono errori dietro falsi 200
- **response.content** invece del text.strip() è una svolta per l'encoding (ho già avuto rogne con caratteri strani nelle schede tecniche dei vini)
- Il **doppio except** è quel che cercavo: semplice ma copre tutto, dai timeout ai JSON corrotti
Manderò in produzione con il logger come suggerisci: traccerò gli URL "bugiardi" mentre sorseggio un buon rosso! Direi che il codice è pronto per la prossima degustazione 🍷
Ottimo lavoro @proserpinagatti34, la soluzione con `raise_for_status()` e il doppio except è solida come una barrique di Brunello. Solo due appunti dal mio vissuto con API "creative":
1. **Logging differenziato**:
```python
except requests.exceptions.RequestException as e:
logger.error(f"Errore rete: {e} - URL: {url}")
except json.JSONDecodeError:
logger.warning(f"JSON malformato - URL: {url} - Contenuto: {response.content[:50]}")
```
Così distingui i problemi di rete da quelli dei dati, fondamentale se usi provider diversi.
2. **Fallback intelligente**:
Invece di `dati = None`, potresti avere un dizionario predefinito con suggerimenti d'abbinamento di riserva tipo {"consiglio": "Prova con un Sangiovese secco"}. Così l'app non si blocca e gli amici hanno comunque un'idea.
Sul vino... quel Barolo '82 è un'ottima metafora: come certe API, anche i grandi rossi a volte hanno sedimenti da filtrare 😉. Se incontri altri bug, fai come col vino giovane: lascialo ossigenare prima di debuggarci.
Hai ragione su tutto, Max! Il **logger differenziato** è un must quando l’API ti risponde come un sommelier ubriaco: errori di rete sono rogne fisiche, JSON vuoti sono tradimenti spirituali. Separarli? Meglio che dividere la diramazione del Nebbiolo dal suo sedimente.
Sul **fallback intelligente**, poi, non hai sbagliato un colpo. L’utente vuole un consiglio, non un crash. Un dizionario di riserva con abbinamenti tipo Sangiovese o Aglianico è come tenere una bottiglia di emergenza in cantina: non elegante, ma salva la serata.
Io aggiungerei un dettaglio: dopo il fallback, magari notifica con un logger.warning che stai usando la riserva. Così chi usa l’app sa che il consiglio è “autoprodotto”, non ufficiale. E se gli amici protestano, rispondi: “Era o il vino o il debug”.
P.S. Sul Barolo ’82 hai centrato il paradosso: certe API sono come i grandi rossi, ti fanno innamorare ma poi ti costringono a filtrare notti intere. Buona vendemmia, e se vedi un Chianti con pH 3.0… buttalo via. 🍷