Webbserverprogrammering 1 · område 4 · lektion 28
Async, loggning och felhantering
Tre övningar, en per tema. De är få men inte små: övning 72 är den som skiljer ett A-arbete från ett C-arbete i den här inlämningen, och den går att göra på en kvart.
Lektion 28 var genomgången. Så här tar du igen den, räkna med två timmar:
- Läs guidens steg 15. Varför tjänsten
svarar med
Taskredan innan något faktiskt väntar på något. - Läs steg 16 om loggning och steg 17 om felhantering, och skriv av koden.
- Kör felhanteringsexemplet från lektionen:
/fel-nivaoch/ratt-niva, med terminalen synlig bredvid webbläsaren. Skillnaden mellan de två sidorna är hela momentet, och den syns till hälften i terminalen. - Lös övning 70 till 72.
Två läsare, två sorters text
Det här området har en enda huvudtanke, och allt annat följer av den:
Undantaget är information till utvecklaren. Det ska loggas i sin helhet. Meddelandet på skärmen är information till användaren. Det ska säga vad som inte gick och vad som fortfarande fungerar.
| Loggposten | Meddelandet på skärmen | |
|---|---|---|
| Läses av | Dig, kanske månader senare | Besökaren, nu |
| Innehåller | Hela undantaget, med id och stacktrace | Vad som saknas och vad som fungerar |
| Hamnar | I terminalen där dotnet run körs | På sidan |
| Får innehålla | Tekniska detaljer | Inga klassnamn, inga radnummer |
70. Async hela vägen
Medel · tränar: Task<T> från gränssnitt till komponent
Ta en av dina synkrona tjänster och gör hela kedjan asynkron:
Task<T> i gränssnittet, Task.FromResult i
implementationen, async Task och await i
komponenten.
Klart när: projektet bygger utan varningar och sidan visar samma sak som förut.
Listan ligger i minnet. Svaret är klart direkt. Task.FromResult
gör om ett färdigt värde till ett löfte bara för att signaturen kräver det.
Skälet står i område 5: att läsa ur en databas tar tid, och under den tiden ska servern hinna svara andra besökare. Skriver du metoderna synkrona nu måste varje anropande komponent skrivas om när databasen kommer – precis den sortens ändring hela området handlar om att slippa.
Glömmer du ett await kraschar ingenting. Sidan skriver ut löftet i
stället för svaret, ungefär så här:
System.Threading.Tasks.Task`1[DittProjekt.Models.DinKlass]
Ser du den raden på en sida vet du precis vad som saknas.
71. Loggning som går att söka i
Medel · tränar: ILogger och strukturerade parametrar
Skriv två loggrader i en tjänst:
_logg.LogWarning($"Hittade inte {id}");
_logg.LogWarning("Hittade inte {Id}", id);
Kör och titta i terminalen. De ser exakt likadana ut – båda skriver
Hittade inte 99. Skillnaden finns, men syns inte i den vanliga
utskriften.
Byt därför loggaren, tillfälligt, högst upp i Program.cs:
builder.Logging.ClearProviders();
builder.Logging.AddJsonConsole();
Kör om och läs de två raderna igen. Nu ser de ut så här:
{"LogLevel":"Warning","Message":"Hittade inte 99",
"State":{"{OriginalFormat}":"Hittade inte 99"}}
{"LogLevel":"Warning","Message":"Hittade inte 99",
"State":{"Id":99,"{OriginalFormat}":"Hittade inte {Id}"}}
Klart när: du kan peka på vad som skiljer dem åt i JSON-utskriften, och säga vad den skillnaden är värd den dag loggen innehåller en miljon rader.
Ta bort de två raderna igen när du är klar – den vanliga utskriften är trevligare att läsa under utveckling.
I den första raden är 99 en del av texten. I den andra är den ett
eget fält med ett namn. Skillnaden är att den andra går att fråga:
visa alla varningar där Id är 99. Den första går bara att
söka i som text, och en textsökning på 99 träffar allt från
radnummer till årtal.
Det är därför alla loggexempel i kursen skriver {Id} som
parameter och aldrig bakar in värdet i strängen.
72. Felet som inte får ta ner sidan
Svår · tränar: try/catch i tjänstelagret och returtypen som bär informationen
Gör en tjänst med en metod som alltid kastar ett undantag, och en andra tjänst som anropar den.
Den andra tjänsten ska fånga felet, logga det med LogError, och
svara med int?. Sidan ska visa siffran om den finns och ett
begripligt meddelande om den inte gör det – utan att svara 500.
Klart när: sidan svarar 200, resten av sidan syns, och hela undantaget står i terminalen.
Fundera sedan på: varför int? och inte 0?
Hade metoden lämnat 0 när registret inte svarar hade sidan
påstått att noll poster finns – ett faktapåstående som råkar vara
falskt. null betyder vet inte, och det är sant.
Och try/catch hör inte hemma i komponenten.
Komponenten vet inte vad ett register är och kan därför inte avgöra vad som är
ett rimligt svar när det fattas. Tjänsten vet båda delarna – och skulle
felhanteringen ligga i komponenten måste varje sida som använder
tjänsten upprepa den.
Det är samma argument som hela område 4 vilar på.
Avprickning
Kryssen sparas i din webbläsare.
Lösta övningar i det här temat
Övning 72 är ett A-krav i inlämningen, nästan ord för ord. Gör den, och skriv sedan samma sak i ditt eget projekt.