HTTP Webserver

Go ofron suport për protokolin HTTP, me ç’rast nuk nevojitet kurrfarë serveri i jashtëm, për shembull siç është rasti me PHP.

package main

import (
    "fmt"
    "net/http"
)

func main() {
    fmt.Println("Duke e startuar serverin")
    http.HandleFunc("/", homeHandler)
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        panic(err)
    }
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Println("Pergjigja nga Web aplikacioni")
}

https://play.golang.org/p/n8CKMxzIp9z

I> Ky program nuk ekzekutohet në Go Playground, duhet të kompajlohet.

Rezultati:

Programi i mësipërm kthen përgjigje në konzolë, por në fakt ne do ta modifikojmë asisoj që përgjigjen ta kthejë si HTPP Response, pra t’ia dërgojë tekstin browserit në vend të konzolës.

Ashtu si është tani, programi startohet, shfaq tekstin “Duke e startuar serverin” në konzolë.

E hapim browserin dhe atje shënojmë si adresë:

http://localhost:8080

Sa herë bëjmë refresh në browser, në konzolë do të shfaqet teksti “Pergjigja nga Web aplikacioni”.

I> Në vend të 8080 mund të shënojë një numër arbitrar të lirë. Në produksion do të përdorim portin 80, për çka në fund të domainit nuk do të kemi nevojë ta shënojmë numrin e portit. Pra, nëse porti është 80, ky aplikacion do të thirrej me http://localhost, respektivisht me domain kur ta kemi bërë upload aplikacionin në Web server.

Funksioni HandleFunc nga pakoja http, shërben si një ruter bazik.

Si parametër të parë e shënojmë rutën e që në rastin konkret është “/” që d.m.th. home page, respektivisht faqja kryesore që hapet kur e shënojmë vetëm domainin, në rastin konkret http://localhost:8080.

Si parametër të dytë e shënojmë emrin e funksionit i cili thirret, dhe i cili do ta bëjë kthimin e përgjigjes (Response), në rastin konkret homeHandler.

Funksioni homeHandler (sikurse edhe funksionet tjera që do t’i definojmë te rutat tjera), i ka dy parametra; w të tipit http.ResponseWriter dhe r të tipit pointer i http.Request :

  • w http.ResponseWriter
  • r *http.Request

Nëse ruta ka parametra, ato lexohen nga http.Request, ndërsa në http.ResponseWriter dërgohet përmbajtja e dëshiruar në browser, zakonisht të dhëna në formatin HTML ose JSON.

Nga http.Request lexohen edhe të dhënat e fushave të formularit, nëse ajo rutë është thirrur si “action” i një formulari.

Funksioni ListenAndServe() e starton serverin në portin e cekur. Prej momentit kur fillon ekzekutimi i këtij funksioni, ne mund t’i qasemi serverit nëpërmes browserit. Vetë programi do të ngelet duke u ekzekutuar deri sa ta ndërprejmë me Ctrl-C. Asgjë nuk do të ekzekutohet pas këtij rreshti (rreshti 11), përveç nëse paraqitet ndonjë gabim, me ç’rast ai gabim raportohet në konzolë (rreshtat 12-14).

Për këtë shkak, të gjitha definicionet e rutave duhet të bëhet para thirrjes së ListenAndServe().

Meqë në këtë program konkret ende nuk kemi shënuar funksione që dërgojnë përgjigje në http.Response(), kur e hapim adresën në browser, browseri do të shfaqë faqe të zbrazët.

r.Method

Me r.Method mund ta determinojmë HTPP metodën e përdorur.

Konstantat e paradefinuara për secilën prej metodave me të cilat mund të krahasojmë:

  • MethodGet = “GET”
  • MethodPost = “POST”
  • MethodPut = “PUT”
  • MethodPatch = “PATCH” // RFC 5789
  • MethodDelete = “DELETE”
  • MethodOptions = “OPTIONS”
  • MethodHead = “HEAD”
  • MethodConnect = “CONNECT”
  • MethodTrace = “TRACE”

Dërgimi i kërkesës me metodën POST

resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf)

Dërgimi i kërkesës me metodën GET

resp, err := http.Get("http://example.com/")

Dërgimi i kërkesës me PostForm

resp, err := http.PostForm("http://example.com/form", url.Values{"key": {"Value"}, "id": {"123"}})

r.URL

Me r.URL e lexojmë URL-në e shënuar në address bar të browserit bashkë me query string.

r.URL.Path

Me r.URL.Path e lexojmë vetëm rutën, pa query string.

Zbërthimi i një URL

p := strings.Split(r.URL.Path, "/")

Definicioni i rutës duhet të përfundojë me /:

http.HandleFunc("/test/", homeHandler)

Parametrit në segmentin e dytë i qasemi me p[2].

Nëse e duam si integer, e konvertojmë:

id, _ := strconv.Atoi(p[2])

Pas kësaj mund ta përdorim për ta përcjellur si id në SQL query.

Numri i segmenteve

Numri e segmenteve e determinojmë me len(p).

HTML escape

html.EscapeString(r.URL.Path)

Shembull i ngjashëm

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/profile", profilePage)
    http.ListenAndServe(":8080", nil)
}

func profilePage(w http.ResponseWriter, r *http.Request) {
    name := r.FormValue("name")
    fmt.Fprint(w, "This is your profile page, "+name)
}

https://play.golang.org/p/nBi8gB-k683

Aplikacioni i mësipërm përmban vetëm një rutë, së cilës rutë i korrespondon një funksion.

http.HandleFunc("/profile", profilePage)

Thirret në këtë formë: http://localhost:8080/profile?name=Teuta

Në funksionin http.HandleFunc që i përket pakos net/http, si parametër të parë e shënojmë emërtimin e rutës, ndërsa si parametër të dytë e shënojmë emrin e funksionit i cili duhet të ekzekutohet kur thirret kjo rutë.

Të gjitha funksionet të cilat do të përdoren për procesimin e Web kërkesave (Web requests) për të kthyer më pastaj përgjigje (Web response), do t’i kenë dy parametra:

  • http.ResponseWriter
  • *http.Request

http.ResponseWriter do të përdoret për dërgimin e response, ndërsa *http.Request për ta lexuar kërkesën e cila ka mundur të vijë me ndonjërën nga HTTP metodat.

Ndaj variablit r në këtë shembull ku është vendosur HTTP kërkesa, respektivisht rezultati i kthyer nga strukti *http.Request, mund të zbatojmë metoda/funksione të ndryshme specifike për Web kërkesën. Në rastin konkret, thirret metoda FormValue() me anë të së cilës do të jemi në gjendje të lexojmë vlerat e query string të dërguar me ndonjërën nga HTTP metodat e cekura më sipër.

Dërgimin e përgjigjes (response) e bëjmë me fmt.Fprint(), e cila metodë kërkon dy parametra:

  • Variablin i cili i korrespondon struktit të response (w)
  • Përmbajtjen e cila dërgohet, që në shembullin konkret është string i rëndomtë

http.ListenAndServe(":8080", nil) e starton Web serverin në portin 8080, në mënyrë që ne të mund t’i qasemi Web aplikacionit nga browseri duke shënuar:

`localhost:8080/profile’

Në shembullin konkret, do të shtojmë edhe ?name=Golang në fund të URL-së për të demonstruar leximin equery string nga URL-ja. Pra, në browser shënojmë:

`localhost:8080/profile?name=Golang’

Pas kësaj, në browser do të shfaqet teksti:

This is your profile page, Golang

I> Ky program dhe të gjithë shembujt me Web server nuk do të funksionojnë në Go Playground, prandaj duhet të kompajlohen dhe startohen para se ta bëjmë kërkesën në browser. Pas çdo ndryshimi në kod, nevojitet që ta ri-kompajlojmë kodin.

Ndërtimi i rutave të tjera

Tash do t’i shtojmë pak më shumë funksionalitet aplikacionit tonë: regjistrimin e një anëtari, në këtë rast duke mos përdorur fare databazën, por vetëm duke i ruajtur të dhënat në memorje.

E formojmë një strukt për anëtarin:

type anetari struct {
    Emri   string
    Emaili string
}

E formojmë një map për anëtarët:

    anetarMap = make(map[string]anetari)

Si çelës i mapës do të përdoret emri i anëtarit, ndërsa si vlerë do të jetë strukti, i cili i përmban të dhënat e tjera të anëtarit (në këtë shembull, vetëm emrin dhe emailin).

E ndërtojmë funksionin për regjistrimin e anëtarit, duke lexuar të dhënat e kërkuara nga query string nga browseri, pra me metodën GET.

func regjistroAnetar(w http.ResponseWriter, r *http.Request) {
    Emri := r.URL.Query().Get("emri")
    Emaili := r.URL.Query().Get("emaili")
    anetarMap[Emri] = anetari{Emri, Emaili}
    w.Write([]byte("U regjistrua:" + Emri))
}

Vlerat hyrëse nga query string (apo nga formulari), lexohen me:

r.URL.Query().Get("emri")
r.URL.Query().Get("emaili")

Brenda Get() shënohet emri i fushës së formularit, ashtu siç është definuar me atributin name, sepse atributi name paraqitet si variabël në query string.

Funksioni i referohet ResponseWriter dhe Request nga pakoja http, prandaj nevojitet që kjo pako të importohet:

import (
    "fmt"
    "net/http"
)

Tash e shkruajmë funksonin për listimin e anëtarëve, i cili do ta formojë një string me emrat dhe emailat e anëtarëve, duke bërë iteracion nëpër mapën anetarMap me strukturën for.

func listaAnetareve(w http.ResponseWriter, r *http.Request) {
    lista := ""
    for _, ant := range anetarMap {
        if len(ant.Emri) > 0 {
            lista += ant.Emri + ":" + ant.Emaili + "\n"

        }
    }
    w.Write([]byte("Lista e anëtarëve:\n" + lista))
}

Në funksionin main() definojmë rutat për këto dy funksione:

    http.HandleFunc("/regjistro", regjistroAnetar)
    http.HandleFunc("/lista", listaAnetareve)

Programi komplet:

package main

import (
    "fmt"
    "net/http"
)

type anetari struct {
    Emri   string
    Emaili string
}

var anetarMap = make(map[string]anetari)

func main() {
    fmt.Println("Duke e startuar serverin")
    http.HandleFunc("/regjistro", regjistroAnetar)
    http.HandleFunc("/lista", listaAnetareve)
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        panic(err)
    }
    fmt.Println("test")
}

func regjistroAnetar(w http.ResponseWriter, r *http.Request) {
    Emri := r.URL.Query().Get("emri")
    Emaili := r.URL.Query().Get("emaili")
    anetarMap[Emri] = anetari{Emri, Emaili}
    w.Write([]byte("U regjistrua:" + Emri))
}

func listaAnetareve(w http.ResponseWriter, r *http.Request) {
    lista := ""
    for _, ant := range anetarMap {
        if len(ant.Emri) > 0 {
            lista += ant.Emri + ":" + ant.Emaili + "\n"

        }
    }
    w.Write([]byte("Lista e anëtarëve:\n" + lista))
}

https://play.golang.org/p/nKMyE2jgUGI

I> Programi duhet të kompajlohet.

Pasi të jetë startuar programi, e hapim browserin dhe e hapim URL-në si vijon:

http://localhost:8080/regjistro?emri=Granit&emaili=granit@gmail.com

Në query string, pas variablit emri e shënojmë një emër, ndërsa pas variablit emaili e shënojmë një email, pastaj shtypim Enter. Këtë e përsërisim aq herë sa anëtarë dëshirojmë të regjistrojmë.

Të njëjtën mund ta bëjmë edhe nëpërmes një formulari, tek i cili shënojmë si vlerë të atributit “action” URL-në e regjistrimit.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="get" action="http://localhost:8080/regjistro">
<p>Emri: <input type="text" name="emri"></p>
<p>Emaili: <input type="email" name="emaili"></p>
    <p><input type="submit" value="Ruaje"></p>
</form>
</body>
</html>

Ky formular mund të hapet si fajll statik drejtpërsëdrejti në browser.

Metoda e tretë është nëpërmes ndonjë programi për testimin e API-ve, siç janë Postman apo Insomnia.

Postman

Rezultati që kthehet është:
{lang=”text”, linenos=off}

U regjistrua: Alban

Lista e anëtarëve shfletohet duke e thirrur URL-në lista.

http://localhost:8080/lista

Rezultati:

{lang=”text”, linenos=off}

Lista e anëtarëve:
Tahir:tahir.hoxha@gmail.com
Arta:arta@gmail.com
Granit:granit@gmail.com
Alban:alban@gmail.com

B> Duhet të kemi parasysh se këto të dhëna, duke qenë se ruhet si map, ato ruhen në RAM memorje. Nëse programin e ndalim, të gjitha të dhënat do të fshihen!

Meqë rezultati në formë tekstuale nuk është i strukturuar, ne mundemi mapën e njëjtë ta konvertojmë në JSON, duke bërë ndryshime në funksion:

func listaAnetareve(w http.ResponseWriter, r *http.Request) {
    jAnetarMap, _ := json.Marshal(anetarMap)
    w.Header().Set("Content-Type", "application/json")
    w.Write([]byte(jAnetarMap))
}

Tash lista do të duket kështu:

{"Alban":{"Emri":"Alban","Emaili":"alban@gmail.com"},"Arta":{"Emri":"Arta","Emaili":"arta@gmail.com"},"Granit":{"Emri":"Granit","Emaili":"granit@gmail.com"}}

Ky rezultat mund të procesohet më tej me cilëndo gjuhë: Go, Java, C#, PHP, JavaScript, etj.

All Rights Reserved Theme by 404 THEME.