Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questo articolo ti mostra come adottare la serializzazione dei payload type-safe (chiamata anche type-aware) in un'app Durable Functions esistente che utilizza il modello di programmazione Python. La serializzazione type-safe valida i payload deserializzati rispetto a un tipo atteso e permette di optare per una modalità rigorosa e rigida che elimina il rischio di deserializzazione di payload non affidabile.
Adottare la serializzazione type-safe è una buona pratica raccomandata per ogni app Durable Functions che utilizza Python, comprese quelle non sensibili alla sicurezza. Ti aiuta a individuare tempestivamente gli errori di incompatibilità di tipo, perché l'SDK convalida ogni payload in base al tipo previsto dal tuo codice invece di ricostruire silenziosamente il tipo indicato nei dati memorizzati. La modalità Strict inoltre rende la tua app più resistente alla deserializzazione da payload non affidabile, rendendo il tuo codice più sicuro. L'SDK azure-functions pubblicizza la modalità rigorosa come best practice, e questo articolo ti guida nell'adozione graduale, partendo dai passaggi retrocompatibili.
La funzionalità viene fornita in due pacchetti che lavorano insieme:
-
azure-functionsfornisce ai serializer centralizzati (df_dumps/df_loads) validazione dei tipi opzionale e supporto per tipizzazione rigorosa. -
azure-functions-durablefa passare tutta la serializzazione dei payload di Durable Functions attraverso tali serializzatori e aggiunge il parametroexpected_typee il rilevamento automatico dei tipi alle API di orchestrazione e delle entità.
Per informazioni di base su quali dati Durable Functions persistono e come i tipi personalizzati vengono serializzati, vedi Data persistence and serialization in Durable Functions.
Cosa cambia
Prima di questa funzionalità, Durable Functions deserializzava i payload di oggetti personalizzati leggendo i campi __module__ e __class__ incorporati nel JSON archiviato e chiamando importlib.import_module() per individuare la classe. Non c'era alcun controllo che la classe nel payload corrispondesse al tipo che il tuo codice si aspettava.
La serializzazione type-safe aggiunge:
- Un argomento opzionale
expected_typenelle API di orchestrazione e di entità che deserializzano un payload. -
Rilevamento automatico dei tipi che legge l'annotazione del tipo restituito delle funzioni di attività e di sotto-orchestrazione decorate con v2 e la usa come
expected_typesenza apportare modifiche al codice. - Una modalità rigorosa, selezionata con la
AZURE_FUNCTIONS_DURABLE_STRICT_TYPINGvariabile ambiente, trasforma i disallineamenti di tipo in hard errors e deserializza oggetti personalizzati senza chiamareimportlib.import_module().
Il formato di serializzazione è rimasto invariato. I tipi integrati continuano a serializzare in JSON semplice, e gli oggetti personalizzati usano ancora questa {"__class__", "__module__", "__data__"} convenzione. Questo significa che la modalità loose è completamente retrocompatibile: le storie esistenti e le orchestrazioni in volo continuano a deserializzarsi come prima.
Prerequisiti
Un'app Durable Functions esistente che utilizza il modello di programmazione Python (v1 o v2).
Le seguenti versioni minime del pacchetto, che includono i serializzatori centralizzati
df_dumps/df_loads:Versione di Python Versione minima azure-functions3.13 e versioni successive 2.2.0 3.10 – 3.12 1.26.0 azure-functions-durable1.6.0 o successiva.
Note
Se il pacchetto installato azure-functions non fornisce df_dumps / df_loads, Durable Functions ripiega sulla pipeline di serializzazione precedente. Il formato JSON persistente rimane lo stesso, ma l'argomentazione expected_type e la modalità rigorosa non hanno effetto. Aggiornare alle versioni nella tabella precedente per abilitare la serializzazione convalidata per tipo.
Modalità sciolta rispetto a modalità rigorosa
La serializzazione type-safe ha due modalità.
| Behavior | Modalità sciolta (predefinita) | Modalità Strict |
|---|---|---|
| Consenso esplicito | Sempre attivo | Impostate AZURE_FUNCTIONS_DURABLE_STRICT_TYPING a 1, true, oppure yes |
| Incompatibilità di tipo | Registra un messaggio di avviso, quindi ripiega sul decoder legacy | Incrementi TypeError |
| Decodifica di oggetti personalizzati | Usa importlib.import_module() (percorso legacy) |
Chiama expected_type.from_json() direttamente; mai chiama import_module |
to_json
/
from_json Contratto |
Invariato | Deve essere simmetrico e produrre dati serializzabili nativamente in JSON (vedi Aggiornamento to_json e from_json) |
| Retrocompatibile | Yes | No. Richiede modifiche nel codice |
La modalità loose è sicura da adottare immediatamente perché non cambia mai il comportamento per i payload correttamente tipizzati. La modalità rigorosa è un cambiamento deliberato che rafforza la sicurezza e richiede i passaggi di migrazione che seguono.
Migrazione incrementale
Adottare la serializzazione con controllo dei tipi per fasi. I passaggi 1 e 2 sono retrocompatibili e sicuri da spedire da soli. Completa i passaggi 3 e 4 solo quando sei pronto ad attivare la modalità rigorosa.
Passo 1: Aggiorna i pacchetti
Aggiorna i requisiti della tua app alle versioni minime nei Prerequisiti. Ad esempio, in requirements.txt:
azure-functions>=2.2.0
azure-functions-durable>=1.6.0
Dopo l'aggiornamento, la tua app continua a funzionare in modalità sciolta senza alcun cambiamento di comportamento. Non è necessario fare altre modifiche per mantenere funzionante la tua app esistente.
Passo 2: Adottare la validazione del tipo a modalità lasciva
In modalità loose, fornire il tipo atteso affinché l'SDK possa validare i payload deerializzati e registrare un avviso su eventuali discorrispondenze. Puoi fornire il tipo in tre modi e mescolarli secondo necessità.
Aggiungi annotazioni di tipo return alle attività e ai sub-orchestratori. Nel modello di programmazione Python v2, l'SDK scopre automaticamente l'annotazione di ritorno e la utilizza per convalidare il risultato. Non è necessario cambiare sito di chiamata.
@myApp.activity_trigger(input_name="city")
def get_weather(city: str) -> WeatherReport:
return WeatherReport(city=city, temperature_c=21)
@myApp.orchestration_trigger(context_name="context")
def orchestrator(context: df.DurableOrchestrationContext):
# The WeatherReport return annotation on get_weather is discovered
# automatically and used to validate the result.
report = yield context.call_activity("get_weather", "Seattle")
return report.temperature_c
Passa expected_type esplicitamente. Un esplicito expected_type ha la precedenza su un'annotazione scoperta. Usalo quando il tipo di ritorno non è una classe concreta. Ad esempio, alias generici come list[Order] o Optional[Order] non possono essere scoperti automaticamente.
orders = yield context.call_activity("get_orders", customer_id, expected_type=list)
L'argomento expected_type è disponibile su queste API di orchestrazione:
-
call_activityecall_activity_with_retry -
call_sub_orchestratorecall_sub_orchestrator_with_retry call_entitywait_for_external_eventget_input
E su queste API di entità, attraverso DurableEntityContext:
get_stateget_input
Dichiara il tipo di input di orchestrazione sul trigger. Usa l'argomento input_type in orchestration_trigger modo che context.get_input() convalidi l'input. Un punto di chiamata expected_type su get_input() ha la precedenza.
@myApp.orchestration_trigger(context_name="context", input_type=OrderRequest)
def orchestrator(context: df.DurableOrchestrationContext):
request = context.get_input() # validated against OrderRequest
...
Dopo questo passaggio, esegui la tua app e controlla i log per verificare la presenza di avvisi di incompatibilità di tipo nel logger azure.functions.DurableFunctions. Risolvi eventuali avvertimenti prima di passare alla modalità rigorosa. Poiché questo passaggio aggiunge solo avvisi, è sicuro dispiegarlo da solo.
Suggerimento
La scoperta automatica del tipo risolve solo oggetti concreti type . Alias generici come list[Order], dict[str, Order] e Optional[Order] vengono risolti come "nessuna informazione di tipo" e la decodifica ripiega su una risoluzione basata solo sul modulo. Fornisci expected_type esplicitamente quando hai bisogno di validazione per queste forme.
Passo 3: Aggiorna to_json e from_json per la modalità rigorosa
La modalità Strict modifica il contratto per i tipi personalizzati. In modalità rigorosa, to_json() deve restituire un valore che json.dumps possa serializzare in modo nativo, come dizionari, liste, stringhe, numeri, valori booleani o None. Devi serializzare esplicitamente oggetti personalizzati annidati invece di restituirli come istanze, e from_json() devi ricostruirli simmetricamente.
Questo requisito rimuove le stringhe __module__ dai payload memorizzati a ogni livello di nidificazione, quindi per la deserializzazione non è più necessario risolvere i nomi dei tipi dai dati contenuti nel payload.
class Order:
def __init__(self, item, hat):
self.item = item
self.hat = hat
@staticmethod
def to_json(obj):
return {
"item": obj.item,
"hat": Hat.to_json(obj.hat), # explicit, not obj.hat
}
@staticmethod
def from_json(data):
return Order(
item=data["item"],
hat=Hat.from_json(data["hat"]), # symmetric
)
Gestire i payload legacy in volo durante il rollout. Se la tua app può ancora leggere payload scritti in modalità libera prima dell'aggiornamento, fai from_json in modo che tolleri entrambe le forme. Un valore annidato a codifica libera arriva come un'istanza già ricostruita (l'eredità object_hook si attiva), mentre un valore codificato strettamente arriva come un semplice ditto.
@staticmethod
def from_json(data):
hat_data = data["hat"]
if isinstance(hat_data, Hat):
hat = hat_data # loose-encoded: object already built
else:
hat = Hat.from_json(hat_data) # strict-encoded: plain dict
return Order(item=data["item"], hat=hat)
Passo 4: Abilita la modalità rigorosa
Imposta l'impostazione dell'applicazione AZURE_FUNCTIONS_DURABLE_STRICT_TYPING su 1, true o yes (senza distinzione tra maiuscole e minuscole).
Nel file locale local.settings.json:
{
"Values": {
"AZURE_FUNCTIONS_DURABLE_STRICT_TYPING": "true"
}
}
Oppure come impostazione applicativa nella tua app di funzione:
az functionapp config appsettings set --name <APP_NAME> --resource-group <RESOURCE_GROUP> --settings AZURE_FUNCTIONS_DURABLE_STRICT_TYPING=true
In modalità rigorosa:
- Le incompatibilità di tipo generano
TypeErrorinvece di registrare un avviso. - Gli oggetti personalizzati vengono deerializzati chiamando
expected_type.from_json()direttamente, quindiimport_modulenon vengono mai utilizzati. - Qualsiasi sito di chiamata che deserializza un oggetto personalizzato senza un
expected_typeaumentaTypeError. Assicurati che ogni sito di chiamata fornisca un tipo tramite uno dei meccanismi del Passo 2 prima di abilitare la modalità rigorosa. - Gli input delle funzioni di attività non possono essere oggetti personalizzati. Vedere la nota seguente.
Importante
In modalità strict, l'input di una funzione attività non può essere un oggetto personalizzato. Quando l'host invoca un'attività, il convertitore di trigger dell'attività azure-functions deserializza l'ingresso senza un expected_type, perché il worker Functions non inoltra l'annotazione del tipo di parametro dell'attività al convertitore. Un input di oggetto personalizzato quindi fallisce con un ValueError. Passate invece gli input dell'attività come valori serializzabili in JSON in modo nativo, ad esempio dizionari, liste, stringhe, numeri, valori booleani o None. Se devi inviare un oggetto personalizzato, convertilo con il suo to_json() metodo prima della chiamata e ricostruiscilo all'interno from_json() dell'attività. Questa limitazione si applica solo agli input di attività. I valori restituiti dalle attività, gli input delle orchestrazioni e delle entità, lo stato dell'entità e i payload degli eventi esterni supportano tutti i tipi personalizzati in modalità rigorosa quando si specifica un tipo.
Importante
Attiva la modalità rigorosa solo dopo che tutte le istanze dell'app sono state aggiornate e qualsiasi orchestrazione in volo che contiene storie codificate libere è stata scaricata o i tuoi from_json metodi tollerano entrambe le forme (Passo 3). Un'orchestrazione iniziata prima dell'aggiornamento ripropone la sua storia originale, codificata in modo approssimativo. Se il tuo codice non riesce a decodificare quella cronologia in modalità rigorosa, la riproduzione fallisce.
Implicazioni della gestione delle versioni per le orchestrazioni esistenti
L'aggiornamento alla serializzazione type-safe interrompe le orchestrazioni in esecuzione se i tipi di payload cambiano rispetto all'implementazione legacy. Ogni volta che un'orchestrazione prosegue, viene riprodotta la sua storia memorizzata. Se un sito di decodifica ora si aspetta un tipo che non corrisponde a quello memorizzato da un payload più vecchio, la modalità rigorosa solleva una TypeError che non era presente quando la storia è stata scritta, e quell'errore nuovo rompe l'orchestrazione. Due cambiamenti comuni nella migrazione introducono questa discrepanza:
- Un percorso che in precedenza aveva più di un tipo. Se un singolo percorso di deerializzazione, come un risultato di attività, poteva precedentemente restituire tipi di oggetti diversi, e ora lo annoti con un solo
expected_type, un payload memorizzato che usava un tipo diverso non corrisponde più e non decodifica. - Tipi personalizzati usati come input per le attività. Poiché gli input di attività non possono essere oggetti personalizzati in modalità strict, adottare la modalità strict richiede di cambiare quegli input in valori serializzabili in JSON, il che modifica la forma del payload che le istanze in esecuzione hanno mantenuto.
Più in generale, qualsiasi modifica che differisca il tipo memorizzato di un payload rispetto al tipo che un sito di decodifica ora si aspetta causa lo stesso errore. Ad esempio, rinominare o spostare una classe personalizzata dopo che le sue istanze sono state persistite introduce lo stesso disallineamento.
Per migrare in sicurezza, utilizza uno di questi approcci:
- Consigliato: dividere il rollout con la versionizzazione dell'orchestrazione. Usa il controllo delle versioni delle orchestrazioni con la
Strictstrategia di corrispondenza delle versioni, in modo che i tuoi nuovi worker in modalità rigorosa elaborino solo le orchestrazioni avviate con la nuova versione. Questa buona pratica permette a entrambe le versioni di coesistere durante un aggiornamento continuo ed evita fallimenti nelle rigiocazioni. - Alternativa: prima svuotare. Lascia che tutte le orchestrazioni in volo finiscano, poi abilita la modalità rigorosa.
Prima di abilitare la modalità rigorosa in produzione, verifica che ogni sito di decodifica di oggetti personalizzati fornisca un tipo e che le tue classi personalizzate mantengano lo stesso nome e modulo che avevano quando le istanze in esecuzione mantenevano i loro payload.
Per indicazioni più ampie su come implementare in sicurezza modifiche che influenzano l'esecuzione delle orchestrazioni, vedi Versioning in Durable Functions.
Protezione avanzata
La modalità rigorosa rafforza il modo in cui i payload di oggetti personalizzati vengono deserializzati. Invece di fidarsi dei nomi dei moduli e delle classi incorporati in un payload memorizzato o in entrata per individuare un tipo, la modalità stricta ricostruisce oggetti personalizzati usando i expected_type dati forniti dal tuo codice, e l'output in modalità to_json() stricta non persiste i nomi dei moduli a nessun livello di annidamento. Questo cambiamento elimina la necessità di risolvere nomi di tipo arbitrari a partire dai dati del payload durante la deserializzazione, il che rappresenta un miglioramento in termini di difesa in profondità rispetto al fare affidamento sulle informazioni sul tipo contenute nel payload.
Se i tuoi payload possono contenere dati sensibili, consulta anche Lavorare con dati sensibili.
Contenuti correlati
- Persistenza dei dati e serializzazione in Durable Functions
- binding Durable Functions
- Introduzione a Durable Functions