Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Den här artikeln visar hur du kan införa typsäker (även kallad type-aware) payload-serialisering i en befintlig Durable Functions-app som använder Python-programmeringsmodellen. Typsäker serialisering validerar deserialiserade nyttolaster mot en förväntad typ och gör att du kan aktivera ett härdat strikt läge som eliminerar risken vid deserialisering av opålitliga nyttolaster.
Att anta typsäker serialisering är en rekommenderad bästa praxis för varje Durable Functions-app som använder Python, inklusive appar som inte är säkerhetskänsliga. Det hjälper dig att upptäcka typ-mismatch-buggar tidigt, eftersom SDK:n validerar varje payload mot den typ din kod förväntar sig istället för att tyst rekonstruera vilken typ de lagrade datan heter. Strikt läge förstärker dessutom din app mot deserialisering av opålitlig nyttolast, vilket gör din kod säkrare. SDK:n azure-functions marknadsför strikt läge som bästa praxis, och den här artikeln guidar dig genom att införa det stegvis, med start i bakåtkompatibla steg.
Funktionen levereras över två paket som fungerar tillsammans:
-
azure-functionstillhandahåller de centraliserade serialiserarna (df_dumps/df_loads) med valfri typvalidering och stöd för strikt typning. -
azure-functions-durabledirigerar all nyttolastserialisering för Durable Functions genom dessa serialiserare och lägger till parameternexpected_typesamt automatisk typupptäckt i API:erna för orkestrering och entiteter.
För bakgrund om vilka data som lagras av Durable Functions och hur anpassade typer serialiseras, se Data persistence and serialization in Durable Functions.
Vilka ändringar
Innan den här funktionen deserialiserade Durable Functions nyttolaster för anpassade objekt genom att läsa __module__- och __class__-fälten som är inbäddade i den lagrade JSON-strukturen och anropa importlib.import_module() för att hitta klassen. Det fanns ingen kontroll om klassen i lasten matchade den typ din kod förväntade sig.
Typsäker serialisering lägger till:
- Ett valfritt
expected_type-argument i API:erna för orkestrering och entiteter som deserialiserar en nyttolast. -
Automatisk typidentifiering som läser returtypsannoteringen för dina v2-dekorerade aktivitets- och underorkestreringsfunktioner och använder den som
expected_typeutan någon kodändring. - Ett strikt läge, valt med miljövariabeln
AZURE_FUNCTIONS_DURABLE_STRICT_TYPING, som omvandlar typmismatcher till hårda fel och deserialiserar anpassade objekt utan att anropaimportlib.import_module().
Serialiseringsformatet är oförändrat. Inbyggda typer serialiserar fortfarande till vanlig JSON, och anpassade objekt använder fortfarande konventionen {"__class__", "__module__", "__data__"} . Detta innebär att loose mode är fullt bakåtkompatibelt: befintliga historier och orkestreringar under flygning fortsätter att deserialiseras som tidigare.
Förutsättningar
En befintlig Durable Functions-app som använder Python-programmeringsmodellen (v1 eller v2).
Följande lägsta paketversioner, som innehåller de centraliserade
df_dumps/df_loadsserialiserarna:Python-version Minimiversion azure-functions3.13 och senare 2.2.0 3.10 – 3.12 1.26.0 azure-functions-durable1.6.0 eller senare.
Note
Om det installerade azure-functions paketet inte tillhandahåller df_dumps / df_loads, faller Durable Functions tillbaka på den äldre serialiseringspipelinen. Det bestående JSON-formatet förblir detsamma, men argumentet expected_type och strikt läge har ingen effekt. Uppgradera till versionerna i föregående tabell för att möjliggöra typvaliderad serialisering.
Löst läge jämfört med strikt läge
Typsäker serialisering har två lägen.
| Behavior | Löst läge (standard) | Strikt läge |
|---|---|---|
| Anmäl dig | Alltid på | Sätt AZURE_FUNCTIONS_DURABLE_STRICT_TYPING till 1, true, eller yes |
| Matchningsfel för typ | Skriver en varning och använder sedan den äldre avkodaren | Höjer TypeError |
| Avkodning av anpassat objekt | Använder importlib.import_module() (äldre sökväg) |
Anropar expected_type.from_json() direkt; anropar aldrig import_module |
to_json
/
from_json Kontrakt |
Oförändrad | Måste vara symmetrisk och generera data som är inbyggt JSON-serialiserbar (se Update to_json and from_json) |
| Bakåtkompatibel | Yes | Nej. Kräver kodändringar |
Loose mode är säkert att ta i bruk omedelbart eftersom det aldrig ändrar beteendet för korrekt typade nyttolaster. Strikt läge är en avsiktlig, säkerhetsförstärkande förändring som kräver de migreringssteg som följer.
Migrera inkrementellt
Införa typsäker serialisering i faser. Steg 1 och 2 är bakåtkompatibla och säkra att skicka var för sig. Genomför steg 3 och 4 först när du är redo att aktivera strikt läge.
Steg 1: Uppgradera paketen
Uppdatera din apps krav till minimiversionerna i Prerequisites. Till exempel, i requirements.txt:
azure-functions>=2.2.0
azure-functions-durable>=1.6.0
Efter att du uppgraderat fortsätter din app att köras i löst läge utan någon beteendeförändring. Du behöver inte göra några andra ändringar för att hålla din befintliga app fungerande.
Steg 2: Använd typvalidering i löst läge
I löst läge, ange den förväntade typen så att SDK:n kan validera deserialiserade payloads och logga en varning vid eventuella missmatchningar. Du kan ange typen på tre sätt och kombinera dem efter behov.
Lägg till returtypsannoteringar för aktiviteter och underorkestrerare. I Python v2-programmeringsmodellen upptäcker SDK:n automatiskt returannoteringen och använder den för att validera resultatet. Ingen ändring av samtalsplats behövs.
@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
Klara expected_type det uttryckligen. En explicit expected_type har företräde framför en upptäckt annotation. Använd den när returtypen inte är en konkret klass. Till exempel generiska alias som list[Order] eller Optional[Order] kan inte upptäckas automatiskt.
orders = yield context.call_activity("get_orders", customer_id, expected_type=list)
Argumentet expected_type finns tillgängligt på dessa orkestrerings-API:er:
-
call_activityochcall_activity_with_retry -
call_sub_orchestratorochcall_sub_orchestrator_with_retry call_entitywait_for_external_eventget_input
Och i dessa entitets-API:er, genom DurableEntityContext:
get_stateget_input
Deklarera orkestreringsinmatningstypen på triggern. Använd argumentet input_type på orchestration_trigger så att det context.get_input() validerar indatan. En anropsplats expected_type på get_input() ges företräde.
@myApp.orchestration_trigger(context_name="context", input_type=OrderRequest)
def orchestrator(context: df.DurableOrchestrationContext):
request = context.get_input() # validated against OrderRequest
...
Efter detta steg, kör din app och håll koll på loggarna för typavvikelsevarningar under azure.functions.DurableFunctions loggaren. Åtgärda eventuella varningar innan du går vidare till strikt läge. Eftersom detta steg bara lägger till varningar är det säkert att använda det själv.
Tip
Automatisk typupptäckt löser endast konkreta type objekt. Generiska alias som list[Order], dict[str, Order] och Optional[Order] motsvarar ”ingen typinformation”, och avkodningen återgår till upplösning enbart på modulnivå. Ange expected_type explicit när du behöver validering för dessa former.
Steg 3: Uppdatera to_json och from_json för strikt läge
Strikt läge ändrar kontraktet för anpassade typer. I strikt läge måste to_json() returnera ett värde som json.dumps kan serialisera nativt, till exempel ordböcker, listor, strängar, tal, booleska värden eller None. Du måste uttryckligen serialisera nästlade anpassade objekt istället för att returnera dem som instanser, och from_json() måste rekonstruera dem symmetriskt.
Detta krav tar bort __module__ strängar från lagrade nyttolaster på varje nästlingsnivå, så deserialisering behöver inte längre lösa typnamn från nyttolastdata.
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
)
Hantera arrande laster under utrullningen. Om din app fortfarande kan komma att läsa payloader som skrevs i löst läge före uppgraderingen, låt from_json tolerera båda varianterna. Ett nästlat värde med lös kodning kommer som en redan rekonstruerad instans (den äldre object_hook utlöses), medan ett värde med strikt kodning kommer som en vanlig ordbok.
@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)
Steg 4: Aktivera strikt läge
Ställ in applikationsinställningen AZURE_FUNCTIONS_DURABLE_STRICT_TYPING på 1, true eller yes (inte skiftlägeskänsligt).
I din lokala local.settings.json:
{
"Values": {
"AZURE_FUNCTIONS_DURABLE_STRICT_TYPING": "true"
}
}
Eller som en applikationsinställning i din funktionsapp:
az functionapp config appsettings set --name <APP_NAME> --resource-group <RESOURCE_GROUP> --settings AZURE_FUNCTIONS_DURABLE_STRICT_TYPING=true
I strikt läge:
- Typkonflikter utlöser
TypeErrori stället för att logga en varning. - Anpassade objekt deserialiseras genom att anropa
expected_type.from_json()direkt, såimport_moduleanvänds aldrig. - Varje anropsplats som deserialiserar ett anpassat objekt utan en
expected_typeutlöserTypeError. Se till att varje sådan samtalsplats tillhandahåller en typ genom någon av mekanismerna i steg 2 innan du aktiverar strikt läge. - Aktivitetsfunktionsindata kan inte vara anpassade objekt. Se följande anteckning.
Important
I strikt läge kan en aktivitetsfunktion inte inmatning vara ett eget objekt. När värden anropar en aktivitet azure-functions deserialiserar aktivitetstriggeromvandlaren indatan utan ett expected_type, eftersom Functions-arbetaren inte vidarebefordrar aktivitetens parametertypannotation till omvandlaren. En indata för ett eget objekt misslyckas därför med en ValueError. Skicka i stället indata för aktiviteter som värden som kan JSON-serialiseras direkt, till exempel ordböcker, listor, strängar, tal, booleanska värden eller None. Om du behöver skicka ett anpassat objekt, konvertera det med dess to_json() metod före anropet och rekonstruera det med from_json() inuti aktiviteten. Denna begränsning gäller endast aktivitetsinmatningar. Aktivitetsreturnvärden, orkestrering och entitetsinmatningar, entitetstillstånd och externa händelsepayloads stöder alla anpassade typer i strikt läge när du tillhandahåller en typ.
Important
Aktivera strikt läge först när alla appinstanser har uppgraderats och alla pågående orkestreringar som har löst kodad historik har slutförts, eller när dina from_json metoder tolererar båda formaten (Steg 3). En orkestrering som började innan uppgraderingen återupprepar sin ursprungliga, löst kodade historia. Om din kod inte kan avkoda den historiken i strikt läge, misslyckas replay.
Versioneringsimplikationer för befintliga orkestreringar
Att uppdatera till typsäker serialisering gör att pågående orkestreringar slutar fungera om nyttolasttyperna ändras jämfört med den äldre implementationen. Varje gång en orkestrering fortsätter spelar den upp sin lagrade historia. Om ett avkodningsställe nu förväntar sig en typ som inte stämmer överens med den som lagrats i en äldre nyttolast, utlöser strikt läge en TypeError som inte fanns när historiken först skrevs, och det nya felet bryter orkestreringen. Två vanliga migrationsförändringar introducerar denna mismatch:
- En sökväg som tidigare hade mer än en typ. Om en och samma deserialiseringssökväg, till exempel ett aktivitetsresultat, tidigare kunde returnera olika objekttyper och du nu annoterar den med en enda
expected_type, kommer en lagrad nyttolast som använde en annan typ inte längre att stämma överens och kan därför inte avkodas. - Anpassade typer som används som aktivitetsinmatningar. Eftersom aktivitetsindata inte kan vara anpassade objekt i strikt läge, kräver du att du ändrar dessa indata till JSON-serialiserbara värden för att anta strikt läge, vilket ändrar payload-formen som körande instanser har kvar.
Mer generellt orsakar varje ändring som gör att en payloads lagrade typ skiljer sig från den typ en avkodningsplats nu förväntar sig samma fel. Till exempel leder det till samma diskrepans att byta namn på eller flytta en anpassad klass efter att dess instanser har sparats.
För att migrera säkert, använd en av dessa metoder:
- Rekommenderas: dela upp utrullningen med versionshantering för orkestrering. Använd orkestreringsversionering med strategin
Strictför versionsmatchning så att dina nya arbetare i strikt läge endast bearbetar de orkestreringar som startade i den nya versionen. Denna bästa praxis gör att båda versionerna samexisterar under en rullande uppgradering och undviker upprepningsfel. - Alternativ: töm först. Låt alla orkestreringar under flygning vara klara, och aktivera sedan strikt läge.
Innan du aktiverar strikt läge i produktion, kontrollera att varje avkodningsställe för anpassade objekt anger en typ och att dina anpassade klasser behåller samma namn och samma modul som de hade när körande instanser sparade sina nyttolaster.
För bredare vägledning om hur man säkert distribuerar förändringar som påverkar körande orkestreringar, se Versionering i Durable Functions.
Säkerhetshöjning
Strikt läge stärker hur egenutvecklade objektlaster deserialiseras. Istället för att lita på modul- och klassnamnen inbäddade i en lagrad eller inkommande payload för att hitta en typ, rekonstruerar strict mode anpassade objekt med hjälp av det expected_type som din kod tillhandahåller, och strict-mode to_json() output behåller inte modulnamn på någon nästlingsnivå. Denna förändring eliminerar behovet av att lösa godtyckliga typnamn från nyttolastdata under deserialisering, vilket är en djupgående förbättring jämfört med att förlita sig på typinformationen som bärs i nyttolasten.
Om dina laster kan innehålla känslig data, granska också Arbeta med känslig data.