Lua Academy · Lektion

Strukturerade felobjekt

Skicka tabeller som felobjekt för att förmedla typ och sammanhang.

Lektion 4 av 412 steg

Strukturerade felobjekt är en gratis lektion i Lua Academy på CoddyKit. Detta är lektion 4 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för Lua Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Lua Academy innehåller totalt 4 lektioner.

Varför strukturerade fel?

Vanliga strängfel är svåra att hantera programmatiskt. Strukturerade felobjekt (tabeller) innehåller typinformation och sammanhangsdata och kan granskas och åtgärdas av anropande kod. Det möjliggör felbaserad dirigering utan strängtolkning.

-- Plain string: hard to handle programmatically
error("database error: connection refused")

-- Structured: type + data
error({
  type = "DatabaseError",
  code = "CONN_REFUSED",
  host = "localhost",
  port = 5432,
  message = "connection refused"
})

Mönster för felkonstruktorer

Skapa en fabriksfunktion för fel för varje feltyp. Fabriken bygger en tabell med konsekventa fält: typ, meddelande och relevant sammanhang. En __tostring-metametod gör att felet skrivs ut på ett tydligt sätt.

local ErrorMT = {__tostring = function(e)
  return string.format("[%s] %s", e.type, e.message)
end}

local function makeError(errType, msg, data)
  local e = {type=errType, message=msg}
  if data then for k,v in pairs(data) do e[k]=v end end
  return setmetatable(e, ErrorMT)
end

local E = {
  notFound = function(name) return makeError("NOT_FOUND","not found: "..name,{name=name}) end,
  badInput = function(msg,field) return makeError("BAD_INPUT",msg,{field=field}) end,
}

local ok, err = pcall(error, E.notFound("user:42"))
print(tostring(err))   -- [NOT_FOUND] not found: user:42

Typkontroll av felobjekt

Efter att ni har fångat ett fel kontrollerar ni om det är en tabell med ett känt typfält. På så sätt kan ni välja olika återställningsstrategier utifrån feltypen utan att förlita er på strängmönstermatchning.

local function handleRequest(fn)
  local ok, err = pcall(fn)
  if ok then return true end
  if type(err) == "table" then
    if err.type == "NOT_FOUND" then
      print("404: " .. err.message)
    elseif err.type == "BAD_INPUT" then
      print("400: " .. err.message .. " (field: " .. (err.field or "?") .. ")")
    else
      print("500: unhandled error: " .. tostring(err))
    end
  else
    print("500: " .. tostring(err))
  end
  return false
end

Felhierarki

Simulera en felhierarki genom att kontrollera is_a-fält eller använda metatabeller. Underordnade feltyper ärver den överordnade typens fält och kan behandlas som den överordnade typen av kod som inte behöver känna till detaljerna.

local function isError(e, errType)
  if type(e) ~= "table" then return false end
  return e.type == errType or e.parentType == errType
end

local function makeDbError(code, msg)
  return {type="DbError:"..code, parentType="DbError", code=code, message=msg}
end

local err = makeDbError("TIMEOUT","query timed out")
print(isError(err, "DbError"))          -- true
print(isError(err, "DbError:TIMEOUT"))  -- true
print(isError(err, "NetworkError"))     -- false

Omsluta fel

När ni fångar och kastar om ett fel kan ni omsluta originalfelet för att lägga till sammanhang utan att förlora det. Omslaget har en egen typ och innehåller originalfelet som orsak.

local function wrapError(msg, cause)
  return {
    type = "WrappedError",
    message = msg,
    cause = cause,
  }
end

local function loadConfig(path)
  local ok, err = pcall(function()
    local f = assert(io.open(path,"r"))
    local content = f:read("a")
    f:close()
    return content
  end)
  if not ok then
    error(wrapError("failed to load config: "..path, err))
  end
end

local ok2, e = pcall(loadConfig, "missing.cfg")
if not ok2 then
  print(e.message)
  print("Caused by:", tostring(e.cause))
end

Felkoder jämfört med feltyper

Två vanliga konventioner är felkoder (numeriska, till exempel HTTP-statuskoder) och feltypssträngar (semantiska namn). Felkoder är enkla att jämföra numeriskt, medan typssträngar beskriver sig själva. Många system använder båda.

local STATUS = {OK=200, NOT_FOUND=404, SERVER_ERROR=500, BAD_REQUEST=400}

local function makeStatusError(status, msg)
  return {status=status, message=msg, type="HTTPError"}
end

local function handleError(e)
  if e.status == STATUS.NOT_FOUND then
    print("Resource not found:", e.message)
  elseif e.status >= 500 then
    print("Server error:", e.message)
  else
    print("Error", e.status, e.message)
  end
end

handleError(makeStatusError(404, "user not found"))

Felstack (orsakskedja)

När ett fel orsakas av ett annat kan ni kedja ihop dem. Det ger en fullständig bild av vad som gick fel på varje lager i applikationen. Packa upp kedjan för att logga eller visa hela felberättelsen.

local function unwindCause(e, depth)
  depth = depth or 0
  local pad = string.rep("  ", depth)
  if type(e) == "table" then
    print(pad .. (e.type or "Error") .. ": " .. (e.message or "?"))
    if e.cause then unwindCause(e.cause, depth+1) end
  else
    print(pad .. tostring(e))
  end
end

local inner = {type="IoError", message="permission denied"}
local outer = {type="ConfigError", message="cannot load config", cause=inner}
unwindCause(outer)
-- ConfigError: cannot load config
--   IoError: permission denied

Fel i callback-sammanhang

När fel uppstår i callbacks (händelsehanterare, iteratorer) propageras de till den kod som anropade callbacken. Använd pcall för att fånga dem och rapportera med sammanhang om vilken callback som misslyckades.

local function runCallbacks(callbacks, data)
  local errors = {}
  for name, fn in pairs(callbacks) do
    local ok, err = pcall(fn, data)
    if not ok then
      errors[#errors+1] = {callback=name, error=err}
    end
  end
  return errors
end

local cbs = {
  validate = function(d) assert(d.name, "name required") end,
  transform = function(d) d.name = d.name:upper() end,
}

local errs = runCallbacks(cbs, {})
for _, e in ipairs(errs) do
  print(e.callback, "->", e.error)
end

Skriva ut feldetaljer

En hjälpfunktion som skriver ut ett felobjekt på ett strukturerat och lättläst sätt och hanterar både sträng- och tabellfel. Detta är användbart vid applikationsgränser där fel loggas eller visas för användare.

local function printError(err, prefix)
  prefix = prefix or "Error"
  if type(err) ~= "table" then
    print(prefix .. ": " .. tostring(err))
    return
  end
  print(prefix .. " [" .. (err.type or "unknown") .. "]")
  print("  Message: " .. (err.message or "?"))
  for k, v in pairs(err) do
    if k ~= "type" and k ~= "message" and k ~= "cause" then
      print("  " .. k .. ": " .. tostring(v))
    end
  end
  if err.cause then printError(err.cause, "  Caused by") end
end

Assert med strukturerade fel

Skapa en assertT (assert med typade fel) som utlöser ett strukturerat fel i stället för en vanlig sträng. Då blir det enkelt för anropande kod att testa specifika feltyper.

local function assertT(cond, errType, msg, data)
  if not cond then
    local e = {type=errType, message=msg}
    if data then for k,v in pairs(data) do e[k]=v end end
    error(e, 2)
  end
  return cond
end

local function createUser(name, age)
  assertT(type(name)=="string", "BAD_INPUT", "name must be string", {field="name"})
  assertT(age >= 0 and age <= 150, "BAD_INPUT", "invalid age", {field="age", value=age})
  return {name=name, age=age}
end

local ok, err = pcall(createUser, "Alice", -5)
if not ok then print(err.type, err.field, err.value) end

Snabbkontroll

Vilken är den främsta fördelen med att skicka en tabell till error() i stället för en sträng?

Sammanfattning: strukturerade fel

Sammanfattning:

  • Skicka tabeller till error() för strukturerade och inspekterbara fel
  • Inkludera typ, meddelande och relevanta sammanhangsfält
  • Lägg till __tostring för lättläst utskrift
  • Omslut fel för att lägga till sammanhang utan att förlora orsaken
  • Välj åtgärd utifrån typ i hanterare: kontrollera err.type, inte strängmönster
Gratis att börja

Lär dig Lua med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
40
Lektioner
159

Vanliga frågor

Är lektionen ”Strukturerade felobjekt” gratis?

Ja – du kan läsa vilka 3 lektioner som helst i lärvägen Lua Academy, inklusive ”Strukturerade felobjekt”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i Lua Academy innehåller totalt 4 lektioner.

Vad lär jag mig i ”Strukturerade felobjekt”?

Skicka tabeller som felobjekt för att förmedla typ och sammanhang. Ni övar på Lua Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Lua Academy?

Du behöver inga förkunskaper. Utbildningen i Lua Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 4 av 4.

Hur lång tid tar lektionen ”Strukturerade felobjekt”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Lua Academy-lektionen?

Ja. Varje Lua Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Funktionen error()
  2. Skyddade anrop med pcall
  3. xpcall och meddelandehanterare
  4. Strukturerade felobjekt
← Tillbaka till Lua Academy