Web og API

Denne manualen viser hvordan du bygger en liten, praktisk web-løsning i Norscode. Målet er ikke bare å få en server til å svare, men å bygge noe som er lett å lese, lett å teste og enkelt å videreutvikle når prosjektet vokser.

For mange prosjekter er web-laget stedet der flere hensyn møtes samtidig: routing, validering, HTML, JSON, statuskoder og dokumentasjon. Derfor er det nyttig å se disse delene som ett samlet arbeidsmønster i stedet for som mange små teknikker hver for seg.

Oppsettet under bygger en liten løsning i fire faser:

  1. Definer handlerfunksjoner (HTML og JSON)
  2. Registrer ruter
  3. Legg på validering og OpenAPI
  4. Legg til enkle tester

1) Handler-funksjoner: inngangsporten

Handler-funksjonen er inngangsporten til web-laget. Det er her requesten først blir tolket, og det er her du bestemmer om kallet skal svare med HTML, JSON, feil eller en annen responsform.

Norscode mottar standardparametere i web-handleren:

bruk std.web som web

funksjon hjem_handler(method: tekst, path: tekst, query: tekst, body: tekst) -> tekst {
    hvis method != "GET" da {
        returner web.http_respons_tekst(405, web.http_content_type_json(), "{\"error\":\"Metode ikke tillatt\"}")
    }
    returner web.http_respons_tekst(
        200,
        "text/html; charset=utf-8",
        "<h1>Norscode</h1><p>API klart.</p>"
    )
}

Dette enkle mønsteret er nyttig fordi det holder ansvaret tydelig. Handleren vet hvilken metode som er lov, og returnerer en eksplisitt respons. Det gjør koden lettere å teste og lettere å lese senere, både for deg selv og andre.

2) Enkle JSON-endepunkter

Bruk http_json_respons for strukturert API-svar. Når du holder JSON-svarene enkle og konsekvente, blir de lettere å bruke fra frontend, lettere å dokumentere og lettere å verifisere i tester.

funksjon status_handler(method: tekst, path: tekst, query: tekst, body: tekst) -> tekst {
    hvis method != "GET" da {
        returner web.http_respons_tekst(405, web.http_content_type_json(), "{\"error\":\"Kun GET tillatt\"}")
    }
    returner web.http_json_respons("{\"ok\":true,\"service\":\"norscode\",\"version\":\"1.0.0\"}")
}

Selv et lite status-endepunkt er nyttig som mønster. Det lærer deg å skille mellom en HTML-side for mennesker og et JSON-svar for systemer. Den skillen blir bare viktigere jo flere ruter og klienter løsningen får.

3) Parse body og bygg validering

Her begynner web-laget å bli mer realistisk. Når du tar imot data, må du ikke bare lese bodyen, men også definere hva som er gyldig, hva som skal avvises og hvordan feilsvaret bør se ut.

Eksempel på POST med JSON-felt og enkel validering:

funksjon kontakt_handler(method: tekst, path: tekst, query: tekst, body: tekst) -> tekst {
    hvis method != "POST" da {
        returner web.http_respons_tekst(405, web.http_content_type_json(), "{\"error\":\"Kun POST tillatt\"}")
    }

    la navn: tekst = web.http_body_json_felt_tekst(body, "navn")
    hvis navn == "" da {
        returner web.http_typed_input_feil_respons("body", "navn", "ikke tom", navn)
    }

    returner web.http_json_respons("{\"mottatt\":\"" + navn + "\"}")
}

Det viktigste her er ikke bare å lese feltet, men å etablere forventningene tydelig. Når ugyldig input behandles bevisst og forutsigbart, blir hele API-et enklere å stole på og enklere å dokumentere.

4) Legg på OpenAPI samtidig

Når API-et har en kontrakt, slipper du å holde manuell dokumentasjon løsrevet fra koden. OpenAPI gjør det enklere å vise hvilke ruter som finnes, hva de gjør og hvilke svar som kan forventes.

funksjon kontakt_openapi() -> tekst {
    la api: liste_tekst = web.openapi_tom()
    web.openapi_info_registrer(api, "title", "Norscode Kontakt")
    web.openapi_info_registrer(api, "version", "1.0.0")
    web.openapi_info_registrer(api, "description", "Enkel kontakt- og status-API")
    web.openapi_rute_registrer(api, "GET", "/api/status", "Hent status", "Returnerer tjenestestatus", "status", "StatusResponse")
    web.openapi_rute_registrer(api, "POST", "/api/kontakt", "Opprett henvendelse", "Tar i mot navn som body", "kontakt", "KontaktResponse")
    returner web.openapi_json(api)
}

Dette er særlig nyttig når API-et skal brukes av andre enn bare deg selv. En eksplisitt kontrakt reduserer misforståelser og gjør endringer lettere å oppdage i tide.

5) Full ruteoppsett

Serveren trenger en tabell over METHOD\tPATH\tHANDLER. Dette gjør rutene eksplisitte i stedet for magiske, og det gir deg ett tydelig sted å lese hvordan web-laget faktisk er satt sammen.

funksjon ruter() -> liste_tekst {
    returner [
        "GET\t/\t__main__.hjem_handler",
        "GET\t/api/status\t__main__.status_handler",
        "POST\t/api/kontakt\t__main__.kontakt_handler",
        "GET\t/api/openapi\t__main__.kontakt_openapi"
    ]
}

funksjon start() -> heltall {
    returner web.web_server_start(ruter(), "127.0.0.1", 8080)
}

Når ruteoppsettet holdes ryddig, blir det også lettere å teste at viktige endepunkter fortsatt finnes og å oppdage når en rute mangler etter en refaktorering.

6) Mini-case: produktive steg

Bruk denne rekkefølgen i egne prosjekter:

  1. Definer datafelt og svarformat
  2. Lag én handler per endpoint
  3. Legg til OpenAPI-ruter
  4. Valider med tester før deploy

Poenget med denne rekkefølgen er at du bygger web-laget uten å hoppe mellom for mange nivåer samtidig. Først bestemmer du data og ansvar, så bygger du rutene, deretter kontrakten og til slutt sikkerhetsnettet rundt det hele.

7) Slik tester du web-koden

Webkode blir mer robust når du tester handlerne direkte, i stedet for å vente til hele systemet er koblet sammen. Da er det enklere å verifisere både responsform, kontrakter og valideringsregler tidlig.

test "API-svar er gyldig" {
    la status: tekst = status_handler("GET", "/api/status", "", "")
    assert(tekst_inneholder(status, "\"ok\":true"))

    la openapi: tekst = kontakt_openapi()
    assert(tekst_inneholder(openapi, "\"/api/status\""))
    assert(tekst_inneholder(openapi, "\"/api/kontakt\""))
}

Denne typen test er spesielt nyttig fordi den dekker mer enn bare én funksjon. Den bekrefter også at dokumentasjon og ruteoppsett fortsatt peker i samme retning.

8) Vanlige feil i startfasen

En annen vanlig feil er å blande domenelogikk og web-responsbygging for tett. Da blir det vanskeligere å teste hver del for seg, og handlerne ender ofte opp med å eie mer ansvar enn de burde.

9) Råd før produksjon

Før produksjon er det også lurt å spørre om web-laget er lett å lese for en annen utvikler. Hvis ruter, statuskoder og validering er tydelige, blir systemet enklere å drifte og enklere å feilsøke når noe faktisk går galt.

Etter dette

Gå videre til Eksempelsamlingen for komplette kodesnutter du kan lime rett inn i egne filer. Det er også naturlig å koble dette videre til testing og moduler, siden webkode blir sterkes