Lua Academy · Les

Gestructureerde foutobjecten

Geef tables door als foutobjecten om type en context over te brengen

Les 4 van 412 stappen

Gestructureerde foutobjecten is een gratis Lua Academy-les op CoddyKit. Dit is les 4 van 4. Je kunt 3 lessen uit dit leerpad gratis volledig lezen — daarna ontgrendelt CoddyKit PRO alle lessen, plus praktische oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Lua Academy. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Lua Academy bevat in totaal 4 lessen.

Waarom gestructureerde fouten?

Eenvoudige tekenreeksfouten zijn moeilijk programmatisch af te handelen. Gestructureerde foutobjecten (tabellen) bevatten type-informatie en contextgegevens en kunnen door aanroepers worden onderzocht en verwerkt. Zo kun je fouten afhandelen op basis van hun type, zonder tekenreeksen te parseren.

-- 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"
})

Patroon voor foutconstructors

Maak voor elk fouttype een fabrieksfunctie voor fouten. De fabriek bouwt een tabel met vaste velden: type, message en relevante context. Met een __tostring-metamethode wordt de fout netjes afgedrukt.

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

Foutobjecten op type controleren

Controleer na het afvangen van een fout of deze een tabel is met een bekend typeveld. Zo kun je verschillende herstelstrategieën kiezen op basis van het fouttype, zonder afhankelijk te zijn van patroonherkenning in tekenreeksen.

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

Fouthiërarchie

Simuleer een fouthiërarchie door is_a-velden te controleren of metatabellen te gebruiken. Onderliggende fouttypen nemen de velden van het bovenliggende type over en kunnen door code die geen details nodig heeft als het bovenliggende type worden behandeld.

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

Fouten omwikkelen

Wanneer je een fout afvangt en opnieuw gooit, wikkel je de oorspronkelijke fout om context toe te voegen zonder deze kwijt te raken. De omwikkeling heeft een eigen type en bevat de oorspronkelijke fout als oorzaak.

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

Foutcodes versus fouttypen

Twee veelgebruikte conventies zijn foutcodes (numeriek, zoals HTTP-statuscodes) en tekenreeksen voor fouttypen (semantische namen). Foutcodes zijn eenvoudig numeriek te vergelijken; tekenreeksen voor typen beschrijven zichzelf. Veel systemen gebruiken beide.

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"))

Foutenstack (oorzakenketen)

Wanneer een fout door een andere fout is veroorzaakt, koppel je ze aan elkaar. Zo krijg je een volledig beeld van wat er in elke laag van de toepassing is misgegaan. Pak de keten uit om het volledige foutverhaal te loggen of weer te geven.

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

Fouten in callbackcontext

Wanneer fouten optreden in callbacks (gebeurtenisafhandelaars, iterators), worden ze doorgegeven aan de aanroeper van de callback. Gebruik pcall om ze af te vangen en te rapporteren met context over welke callback is mislukt.

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

Foutdetails afdrukken

Een helper die een foutobject op een gestructureerde, leesbare manier afdrukt en zowel tekenreeks- als tabelfouten verwerkt. Dit is nuttig op toepassingsgrenzen waar fouten worden gelogd of aan gebruikers worden getoond.

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

Asserties met gestructureerde fouten

Maak een assertT (assertie met getypeerde fouten) die een gestructureerde fout veroorzaakt in plaats van een gewone tekenreeks. Zo kun je in aanroepers eenvoudig op specifieke fouttypen testen.

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

Korte controle

Wat is het belangrijkste voordeel van het doorgeven van een tabel aan error() in plaats van een tekenreeks?

Samenvatting: gestructureerde fouten

Samenvatting:

  • Geef tabellen door aan error() voor gestructureerde, inspecteerbare fouten
  • Neem type, message en relevante contextvelden op
  • Voeg __tostring toe voor leesbare uitvoer
  • Wikkel fouten om context toe te voegen zonder de oorzaak kwijt te raken
  • Gebruik type-dispatch in handlers: controleer err.type, geen tekenreekspatronen
Gratis beginnen

Leer Lua met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
40
Lessen
159

Veelgestelde vragen

Is de les “Gestructureerde foutobjecten” gratis?

Ja — je kunt hier op het web alle 3 lessen van het leerpad Lua Academy, waaronder “Gestructureerde foutobjecten”, gratis volledig lezen. Daarna ontgrendelt CoddyKit PRO alle lessen, plus interactieve oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. De cursus Lua Academy bevat in totaal 4 lessen.

Wat leer ik in “Gestructureerde foutobjecten”?

Geef tables door als foutobjecten om type en context over te brengen Je oefent met Lua Academy door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met Lua Academy te beginnen?

Ervaring vooraf is niet nodig. Lua Academy op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 4 van 4.

Hoe lang duurt de les “Gestructureerde foutobjecten”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over Lua Academy?

Ja. Elke les over Lua Academy bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. De functie error()
  2. Beveiligde aanroepen met pcall
  3. xpcall en meldingshandlers
  4. Gestructureerde foutobjecten
← Terug naar Lua Academy