Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Tento článek vám ukáže, jak zavést typově bezpečné (také nazývané typově nevědomé) serializaci payload v existující aplikaci Durable Functions, která využívá programovací model Python. Typově bezpečná serializace ověřuje deserializované datové zátěže proti očekávanému typu a umožňuje aktivovat zesílený režim strict mode, který odstraňuje riziko deserializace nedůvěryhodné datové zátěže.
Přijetí typově bezpečné serializace je doporučenou nejlepší praxí pro každou aplikaci Durable Functions, která používá Python, včetně aplikací, které nejsou citlivé na bezpečnost. Pomáhá vám to včas odhalit chyby způsobené nesouladem typů, protože SDK ověřuje každou datovou položku podle typu, který váš kód očekává, místo aby tiše rekonstruovalo typ, který je u uložených dat uveden. Přísný režim navíc odolní vaší aplikaci proti deserializaci nedůvěryhodného payloadu, což činí váš kód bezpečnějším. SDK propaguje azure-functions přísný režim jako nejlepší postup a tento článek vás provede postupným zaváděním, začínaje zpětně kompatibilními kroky.
Funkce je poskytována ve dvou balíčcích, které spolupracují:
-
azure-functionsposkytuje centralizované serializátory (df_dumps/df_loads) s volitelnou validací typů a podporou přísného typování. -
azure-functions-durablesměruje veškerou serializaci datové části v Durable Functions prostřednictvím těchto serializátorů a přidává parametrexpected_typea automatické zjišťování typů do rozhraní API pro orchestraci a entity.
Informace o tom, jaká data Durable Functions uchovává a jak se serializují vlastní typy, najdete v článku Uchovávání dat a serializace v Durable Functions.
Co se změní
Před zavedením této funkce Durable Functions deserializovaly payloady vlastních objektů čtením polí __module__ a __class__ vložených do uloženého formátu JSON a voláním importlib.import_module() za účelem nalezení třídy. Nebyla žádná kontrola, zda třída v payloadu odpovídá typu, který váš kód očekával.
Typově bezpečná serializace přidává:
- Volitelný argument
expected_typev rozhraních API pro orchestraci a entity, která deserializují datovou část. -
Automatické rozpoznání typu, které čte anotaci návratového typu vašich funkcí aktivit a suborchestrátorových funkcí dekorovaných pomocí v2 a používá ji jako
expected_typebez jakékoli změny kódu. -
Přísný režim, který se aktivuje pomocí proměnné prostředí
AZURE_FUNCTIONS_DURABLE_STRICT_TYPING, považuje nesoulad typů za závažné chyby a deserializuje uživatelsky definované objekty bez voláníimportlib.import_module().
Formát serializace zůstává nezměněn. Vestavěné typy se stále serializují jako prostý JSON a vlastní objekty stále používají konvenci {"__class__", "__module__", "__data__"}. To znamená, že volný režim je plně zpětně kompatibilní: existující historie a orchestrace během letu pokračují v deserializaci jako dříve.
Předpoklady
Existující aplikace Durable Functions, která používá programovací model Python (v1 nebo v2).
Následující minimální verze balíčků, které obsahují centralizované serializátory
df_dumps/df_loads:verze Pythonu Minimální azure-functionsverze3.13 a později 2.2.0 3.10 – 3.12 1.26.0 azure-functions-durable1.6.0 nebo později.
Poznámka
Pokud nainstalovaný balíček azure-functions neposkytuje df_dumps / df_loads, Durable Functions použije starší proces serializace. Přetrvávající formát JSON zůstává stejný, ale expected_type argument a přísný režim nemají žádný vliv. Upgradujte na verze uvedené v předchozí tabulce pro umožnění typově validované serializace.
Volný režim oproti přísnému režimu
Typově bezpečná serializace má dva režimy.
| Chování | Volný režim (výchozí) | Striktní režim |
|---|---|---|
| Přihlásit se | Vždy zapnuto | Nastavte AZURE_FUNCTIONS_DURABLE_STRICT_TYPING na 1, true, nebo yes |
| Neshoda typu | Zapíše varování do protokolu a poté přejde na starší dekodér | Zvýšení platu TypeError |
| Dekódování pomocí vlastních objektů | Používá importlib.import_module() (zastaralá cesta) |
Volá expected_type.from_json() přímo; nikdy nevolá import_module |
to_json
/
from_json smlouva |
Nezměněný | Musí být symetrický a generovat nativně JSON-serializovatelná data (viz Update to_json a from_json) |
| Zpětná kompatibilita | Ano | Ne. Vyžaduje změny kódu |
Volný režim lze bezpečně okamžitě zavést, protože nikdy nemění chování správně typovaných datových částí. Přísný režim je záměrná, bezpečnostní změna, která vyžaduje následující migrační kroky.
Migrujte postupně
Přijměte typově bezpečné serializaci ve fázích. Kroky 1 a 2 jsou zpětně kompatibilní a lze je bezpečně nasadit samostatně. Kroky 3 a 4 dokončujte jen tehdy, když budete připraveni zapnout přísný režim.
Krok 1: Upgradujte balíčky
Aktualizujte požadavky aplikace na minimální verze uvedené v části Požadavky. Například v requirements.txt:
azure-functions>=2.2.0
azure-functions-durable>=1.6.0
Po upgradu aplikace pokračuje v volném režimu bez změny chování. Nemusíte dělat žádné další změny, abyste udrželi stávající aplikaci funkční.
Krok 2: Přijměte validaci typů ve volném režimu
V volném režimu uveďte očekávaný typ, aby SDK mohlo ověřit deserializované payloady a zaznamenat varování při případném nesouladu. Typ můžete zadat třemi způsoby a podle potřeby je kombinovat.
Přidejte anotace návratového typu k aktivitám a suborchestrátorům. V programovacím modelu Python v2 SDK automaticky objeví návratovou anotaci a použije ji k ověření výsledku. Není potřeba měnit místo hovoru.
@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
Předejte expected_type explicitně. Explicitní expected_type má přednost před objevenou anotací. Použijte to, když návratový typ není konkrétní třídou. Například obecné aliasy jako list[Order] nebo Optional[Order] nelze automaticky objevit.
orders = yield context.call_activity("get_orders", customer_id, expected_type=list)
Argument expected_type je dostupný na těchto orchestračních API:
-
call_activityacall_activity_with_retry -
call_sub_orchestratoracall_sub_orchestrator_with_retry call_entitywait_for_external_eventget_input
A v rámci těchto API entit, prostřednictvím DurableEntityContext:
get_stateget_input
Na spouštěči označte typ orchestrace vstupu. Použijte u input_type argument orchestration_trigger, aby context.get_input() ověřil vstup. Místo volání expected_type na get_input() má přednost.
@myApp.orchestration_trigger(context_name="context", input_type=OrderRequest)
def orchestrator(context: df.DurableOrchestrationContext):
request = context.get_input() # validated against OrderRequest
...
Po tomto kroku spusťte aplikaci a sledujte logy, zda se u protokolovače azure.functions.DurableFunctions neobjeví varování na nesoulad typů. Odstraňte všechna varování, než přejdete do přísného režimu. Protože tento krok přidává jen varování, je bezpečné ho nasadit samostatně.
Tip
Automatické objevování typů řeší pouze konkrétní type objekty. Obecné aliasy, jako jsou list[Order], dict[str, Order] a Optional[Order], se vyhodnotí jako „bez informací o typu“ a dekódování se vrátí k rozlišení pouze podle modulu. Dodajte explicitně expected_type , když potřebujete ověření těchto tvarů.
Krok 3: Aktualizuj to_json a from_json pro přísný režim
Přísný režim mění smlouvu pro vlastní typy. Ve striktním režimu musí to_json() vracet hodnotu, kterou lze v json.dumps nativně serializovat, například slovníky, seznamy, řetězce, čísla, booleovské hodnoty nebo None. Musíte explicitně serializovat vnořené vlastní objekty místo jejich vracení jako instance a from_json() musíte je rekonstruovat symetricky.
Tento požadavek odstraňuje řetězce __module__ z uložených datových částí na všech úrovních vnoření, takže deserializace už nemusí vyhodnocovat názvy typů z datové části.
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
)
Zpracovat probíhající starší datové zátěže během nasazení. Pokud vaše aplikace stále čte payloady, které byly před upgradem napsány volně, nechte from_json tolerovat oba tvary. Volně kódovaná vnořená hodnota přichází jako už rekonstruovaná instance (aktivuje se starší mechanismus object_hook), zatímco přísně kódovaná hodnota přichází jako prostý slovník.
@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)
Krok 4: Zapněte přísný režim
Nastavte nastavení aplikace AZURE_FUNCTIONS_DURABLE_STRICT_TYPING na 1, true nebo yes (nerozlišují se malá a velká písmena).
V místním souboru local.settings.json:
{
"Values": {
"AZURE_FUNCTIONS_DURABLE_STRICT_TYPING": "true"
}
}
Nebo jako nastavení aplikace ve vaší aplikaci funkcí:
az functionapp config appsettings set --name <APP_NAME> --resource-group <RESOURCE_GROUP> --settings AZURE_FUNCTIONS_DURABLE_STRICT_TYPING=true
V přísném režimu:
- Nesoulad typů vyvolá
TypeErrormísto protokolování varování. - Vlastní objekty jsou deserializovány přímým voláním
expected_type.from_json(), takžeimport_modulese nikdy nepoužívají. - Jakékoli místo volání, které deserializuje vlastní objekt bez
expected_type, vyvoláTypeError. Ujistěte se, že každé takové místo volání specifikuje typ pomocí jednoho z mechanismů v Step 2, než zapnete přísný režim. - Vstupy funkcí aktivity nemohou být vlastní objekty. Podívejte se na následující poznámku.
Important
V přísném režimu nemůže být vstup funkce aktivity uživatelsky definovaný objekt. Když hostitel spustí aktivitu, převodník triggeru azure-functions deserializuje vstup bez expected_type, protože worker procesu Functions nepředává tomuto převodníku anotaci typu parametru aktivity. Vstup vlastního objektu tedy selže s ValueError. Místo toho předáváte vstupy aktivit jako nativně JSON-serializovatelné hodnoty, jako jsou slovníky, seznamy, řetězce, čísla, booleany nebo None. Pokud potřebujete odeslat vlastní objekt, převeďte jej před voláním pomocí metody to_json() a uvnitř aktivity jej znovu vytvořte pomocí from_json(). Toto omezení platí pouze pro vstupy aktivity. Návratové hodnoty aktivit, vstupy orchestrace a entit, stav entity i datové části externích událostí podporují v přísném režimu vlastní typy, když zadáte typ.
Important
Striktní režim zapněte až poté, co jsou všechny instance aplikací upgradovány a všechny orchestrace během letu s volně kódovanou historií jsou vyčerpány, nebo jakmile vaše from_json metody tolerují oba tvary (Krok 3). Orchestrace, která začala před upgradem, znovu přehrává svou původní, volně kódovanou historii. Pokud váš kód nedokáže tuto historii dekódovat v přísném režimu, opakování selže.
Dopady verzování na stávající orchestrace
Aktualizace na typově bezpečnou serializaci přeruší běžící orchestrace, pokud se typy payloadů změní oproti starší implementaci. Pokaždé, když orchestrace pokračuje, přehrává svou uloženou historii. Pokud místo dekódování nyní očekává typ, který neodpovídá tomu, co bylo uloženo ve starší datové zátěži, přísný režim vyvolá chybu TypeError, která se neobjevovala, když byla historie poprvé zapsána, a tato nová chyba rozbije orchestraci. Dvě běžné migrační změny přinášejí tento nesoulad:
- Cesta, která dříve obsahovala více než jeden typ. Pokud jedna cesta deserializace, například výsledek aktivity, mohla dříve vrátit různé typy objektů a nyní ji anotujete jedním
expected_type, uložený payload, který používal jiný typ, už neodpovídá a nedokáže dekódovat. - Vlastní typy používané jako vstupy pro aktivity. Protože vstupy aktivit nemohou být ve striktním režimu vlastní objekty, přijetí přísného režimu vyžaduje změnu těchto vstupů na hodnoty serializovatelné v JSON, což mění tvar payloadu, který běžící instance zachovávaly.
Obecněji jakákoli změna, která způsobí, že uložený typ payloadu se liší od typu, který nyní dekódovací site očekává, způsobuje stejnou chybu. Například přejmenování nebo přesun vlastní třídy po zachování jejích instancí zavede stejný nesoulad.
Pro bezpečnou migraci použijte jeden z těchto přístupů:
- Doporučeno: rozdělte zavádění pomocí verzování orchestrace. Použijte verzování orchestrací se strategií
Strictporovnávání verzí, aby vaši noví workeři ve striktním režimu zpracovávali pouze orchestrace, které byly spuštěny v nové verzi. Tato osvědčená praxe umožňuje oběma verzím koexistovat během plynulého upgradu a zabraňuje neúspěchům při přehrávání. - Alternativa: nejdřív vylijte. Nechte dokončit všechny orchestrace během letu, poté zapněte přísný režim.
Než v produkci povolíte striktní režim, ověřte, že každé místo dekódování vlastních objektů uvádí typ a že vaše vlastní třídy si zachovávají stejný název a modul, jaké měly v době, kdy běžící instance ukládaly své payloady.
Pro širší doporučení k bezpečnému nasazování změn, které ovlivňují běžící orchestrace, viz Versioning in Durable Functions.
Posílení zabezpečení
Přísný režim zpřísňuje způsob deserializace datových částí uživatelských objektů. Místo toho, aby se důvěřovalo jménům modulů a tříd vloženým do uloženého nebo příchozího payloadu, že najdou typ, přísný režim rekonstruuje vlastní objekty pomocí toho expected_type , co váš kód poskytuje, a výstup v přísném režimu to_json() neuchovává názvy modulů na žádné úrovni vnoření. Tato změna odstraňuje nutnost během deserializace určovat libovolné názvy typů z dat datové části, což představuje dodatečné posílení obrany oproti spoléhání se na informace o typu obsažené v datové části.
Pokud vaše payloady mohou obsahovat citlivá data, zkontrolujte také Work with sensitive data.