GNU/Linux >> Znalost Linux >  >> Linux

Směrování a ověřování HTTP požadavků pomocí gorila/mux

Síťová knihovna Go obsahuje http.ServeMux typ struktury, který podporuje multiplexování požadavků HTTP (směrování):Webový server směruje požadavek HTTP na hostovaný zdroj s URI, jako je /sales4today , na obsluhu kódu; obsluha provede příslušnou logiku před odesláním odpovědi HTTP, obvykle stránky HTML. Zde je náčrt architektury:

                 +------------+     +--------+     +---------+
HTTP request---->| web server |---->| router |---->| handler |
                 +------------+     +--------+     +---------+

Při volání ListenAndServe způsob spuštění serveru HTTP

http.ListenAndServe(":8888", nil) // args: port & router

druhý argument nil znamená, že DefaultServeMux se používá pro směrování požadavků.

gorilla/mux balíček má mux.Router zadejte jako alternativu k DefaultServeMux nebo přizpůsobený multiplexor požadavků. V ListenAndServe volání, mux.Router instance by nahradila nil jako druhý argument. Co dělá mux.Router takže přitažlivost je nejlépe ukázat na příkladu kódu:

1. Ukázková webová aplikace

surový webová aplikace (viz níže) podporuje čtyři operace CRUD (Create Read Update Delete), které odpovídají čtyřem metodám požadavku HTTP:POST, GET, PUT a DELETE. V hrubém aplikace, hostovaný zdroj je seznam párů klišé, z nichž každý je klišé a konfliktní klišé, jako je tento pár:

Out of sight, out of mind. Absence makes the heart grow fonder.

Lze přidávat nové páry klišé a stávající lze upravovat nebo mazat.

surový webová aplikace

package main

import (
   "gorilla/mux"
   "net/http"
   "fmt"
   "strconv"
)

const GETALL string = "GETALL"
const GETONE string = "GETONE"
const POST string   = "POST"
const PUT string    = "PUT"
const DELETE string = "DELETE"

type clichePair struct {
   Id      int
   Cliche  string
   Counter string
}

// Message sent to goroutine that accesses the requested resource.
type crudRequest struct {
   verb     string
   cp       *clichePair
   id       int
   cliche   string
   counter  string
   confirm  chan string
}

var clichesList = []*clichePair{}
var masterId = 1
var crudRequests chan *crudRequest

// GET /
// GET /cliches
func ClichesAll(res http.ResponseWriter, req *http.Request) {
   cr := &crudRequest{verb: GETALL, confirm: make(chan string)}
   completeRequest(cr, res, "read all")
}

// GET /cliches/id
func ClichesOne(res http.ResponseWriter, req *http.Request) {
   id := getIdFromRequest(req)
   cr := &crudRequest{verb: GETONE, id: id, confirm: make(chan string)}
   completeRequest(cr, res, "read one")
}

// POST /cliches
func ClichesCreate(res http.ResponseWriter, req *http.Request) {
   cliche, counter := getDataFromRequest(req)
   cp := new(clichePair)
   cp.Cliche = cliche
   cp.Counter = counter
   cr := &crudRequest{verb: POST, cp: cp, confirm: make(chan string)}
   completeRequest(cr, res, "create")
}

// PUT /cliches/id
func ClichesEdit(res http.ResponseWriter, req *http.Request) {
   id := getIdFromRequest(req)
   cliche, counter := getDataFromRequest(req)
   cr := &crudRequest{verb: PUT, id: id, cliche: cliche, counter: counter, confirm: make(chan string)}
   completeRequest(cr, res, "edit")
}

// DELETE /cliches/id
func ClichesDelete(res http.ResponseWriter, req *http.Request) {
   id := getIdFromRequest(req)
   cr := &crudRequest{verb: DELETE, id: id, confirm: make(chan string)}
   completeRequest(cr, res, "delete")
}

func completeRequest(cr *crudRequest, res http.ResponseWriter, logMsg string) {
   crudRequests<-cr
   msg := <-cr.confirm
   res.Write([]byte(msg))
   logIt(logMsg)
}

func main() {
   populateClichesList()

   // From now on, this gorountine alone accesses the clichesList.
   crudRequests = make(chan *crudRequest, 8)
   go func() { // resource manager
      for {
         select {
         case req := <-crudRequests:
         if req.verb == GETALL {
            req.confirm<-readAll()
         } else if req.verb == GETONE {
            req.confirm<-readOne(req.id)
         } else if req.verb == POST {
            req.confirm<-addPair(req.cp)
         } else if req.verb == PUT {
            req.confirm<-editPair(req.id, req.cliche, req.counter)
         } else if req.verb == DELETE {
            req.confirm<-deletePair(req.id)
         }
      }
   }()
   startServer()
}

func startServer() {
   router := mux.NewRouter()

   // Dispatch map for CRUD operations.
   router.HandleFunc("/", ClichesAll).Methods("GET")
   router.HandleFunc("/cliches", ClichesAll).Methods("GET")
   router.HandleFunc("/cliches/{id:[0-9]+}", ClichesOne).Methods("GET")

   router.HandleFunc("/cliches", ClichesCreate).Methods("POST")
   router.HandleFunc("/cliches/{id:[0-9]+}", ClichesEdit).Methods("PUT")
   router.HandleFunc("/cliches/{id:[0-9]+}", ClichesDelete).Methods("DELETE")

   http.Handle("/", router) // enable the router

   // Start the server.
   port := ":8888"
   fmt.Println("\nListening on port " + port)
   http.ListenAndServe(port, router); // mux.Router now in play
}

// Return entire list to requester.
func readAll() string {
   msg := "\n"
   for _, cliche := range clichesList {
      next := strconv.Itoa(cliche.Id) + ": " + cliche.Cliche + "  " + cliche.Counter + "\n"
      msg += next
   }
   return msg
}

// Return specified clichePair to requester.
func readOne(id int) string {
   msg := "\n" + "Bad Id: " + strconv.Itoa(id) + "\n"

   index := findCliche(id)
   if index >= 0 {
      cliche := clichesList[index]
      msg = "\n" + strconv.Itoa(id) + ": " + cliche.Cliche + "  " + cliche.Counter + "\n"
   }
   return msg
}

// Create a new clichePair and add to list
func addPair(cp *clichePair) string {
   cp.Id = masterId
   masterId++
   clichesList = append(clichesList, cp)
   return "\nCreated: " + cp.Cliche + " " + cp.Counter + "\n"
}

// Edit an existing clichePair
func editPair(id int, cliche string, counter string) string {
   msg := "\n" + "Bad Id: " + strconv.Itoa(id) + "\n"
   index := findCliche(id)
   if index >= 0 {
      clichesList[index].Cliche = cliche
      clichesList[index].Counter = counter
      msg = "\nCliche edited: " + cliche + " " + counter + "\n"
   }
   return msg
}

// Delete a clichePair
func deletePair(id int) string {
   idStr := strconv.Itoa(id)
   msg := "\n" + "Bad Id: " + idStr + "\n"
   index := findCliche(id)
   if index >= 0 {
      clichesList = append(clichesList[:index], clichesList[index + 1:]...)
      msg = "\nCliche " + idStr + " deleted\n"
   }
   return msg
}

//*** utility functions
func findCliche(id int) int {
   for i := 0; i < len(clichesList); i++ {
      if id == clichesList[i].Id {
         return i;
      }
   }
   return -1 // not found
}

func getIdFromRequest(req *http.Request) int {
   vars := mux.Vars(req)
   id, _ := strconv.Atoi(vars["id"])
   return id
}

func getDataFromRequest(req *http.Request) (string, string) {
   // Extract the user-provided data for the new clichePair
   req.ParseForm()
   form := req.Form
   cliche := form["cliche"][0]    // 1st and only member of a list
   counter := form["counter"][0]  // ditto
   return cliche, counter
}

func logIt(msg string) {
   fmt.Println(msg)
}

func populateClichesList() {
   var cliches = []string {
      "Out of sight, out of mind.",
      "A penny saved is a penny earned.",
      "He who hesitates is lost.",
   }
   var counterCliches = []string {
      "Absence makes the heart grow fonder.",
      "Penny-wise and dollar-foolish.",
      "Look before you leap.",
   }

   for i := 0; i < len(cliches); i++ {
      cp := new(clichePair)
      cp.Id = masterId
      masterId++
      cp.Cliche = cliches[i]
      cp.Counter = counterCliches[i]
      clichesList = append(clichesList, cp)
   }
}

Chcete-li se zaměřit na směrování a ověřování požadavků, crud aplikace nepoužívá stránky HTML jako odpovědi na požadavky. Místo toho mají požadavky za následek zprávy s odpovědí v prostém textu:Seznam párů klišé je odpovědí na požadavek GET, potvrzení, že do seznamu byl přidán nový pár klišé, je odpovědí na požadavek POST atd. Toto zjednodušení usnadňuje testování aplikace, zejména gorilla/mux komponenty pomocí nástroje příkazového řádku, jako je curl .

gorilla/mux balíček lze nainstalovat z GitHubu. surový aplikace běží neomezeně dlouho; proto by měl být ukončen Control-C nebo ekvivalentem. Kód pro crud aplikace spolu se souborem README a ukázkou kudrlinky testy, je k dispozici na mých webových stránkách.

2. Žádost o směrování

mux.Router rozšiřuje směrování ve stylu REST, které dává stejnou váhu metodě HTTP (např. GET) a URI nebo cestě na konci adresy URL (např. /cliches ). URI slouží jako podstatné jméno pro HTTP sloveso (metodu). Například v požadavku HTTP startovací řádek jako

GET /cliches

znamená získat všechny páry klišé , zatímco startovní čára jako

POST /cliches

znamená vytvořit klišé z dat v těle HTTP .

V hrubém webové aplikace, existuje pět funkcí, které fungují jako obsluha požadavků pro pět variant požadavku HTTP:

ClichesAll(...)    # GET: get all of the cliche pairs
ClichesOne(...)    # GET: get a specified cliche pair
ClichesCreate(...) # POST: create a new cliche pair
ClichesEdit(...)   # PUT: edit an existing cliche pair
ClichesDelete(...) # DELETE: delete a specified cliche pair

Každá funkce má dva argumenty:http.ResponseWriter pro odeslání odpovědi zpět žadateli a ukazatel na http.Request , který zapouzdřuje informace ze základního požadavku HTTP. gorilla/mux balíček usnadňuje registraci těchto obslužných programů na webovém serveru a provádění ověření založeného na regulárních výrazech.

startServer fungovat v crud aplikace registruje obsluhu požadavků. Zvažte tuto dvojici registrací s router jako mux.Router instance:

router.HandleFunc("/", ClichesAll).Methods("GET")
router.HandleFunc("/cliches", ClichesAll).Methods("GET")

Tyto příkazy znamenají, že požadavek GET buď na jedno lomítko / nebo /cliches by měl být směrován do ClichesAll funkce, která následně požadavek zpracuje. Například vlna request (s % jako příkazový řádek)

% curl --request GET localhost:8888/

vytvoří tuto odpověď:

1: Out of sight, out of mind.  Absence makes the heart grow fonder.
2: A penny saved is a penny earned.  Penny-wise and dollar-foolish.
3: He who hesitates is lost.  Look before you leap.

Tyto tři klišé páry jsou počátečními údaji v hrubém aplikace.

V této dvojici registračních výpisů

router.HandleFunc("/cliches", ClichesAll).Methods("GET")
router.HandleFunc("/cliches", ClichesCreate).Methods("POST")

URI je stejné (/cliches ), ale slovesa se liší:GET v prvním případě a POST ve druhém. Tato registrace je příkladem směrování ve stylu REST, protože samotný rozdíl ve slovesech stačí k odeslání požadavků dvěma různým obslužným rutinám.

V registraci je povolena více než jedna metoda HTTP, i když to narušuje ducha směrování ve stylu REST:

router.HandleFunc("/cliches", DoItAll).Methods("POST", "GET")

Požadavky HTTP mohou být směrovány na funkce kromě slovesa a URI. Například registrace

router.HandleFunc("/cliches", ClichesCreate).Schemes("https").Methods("POST")

vyžaduje přístup HTTPS pro požadavek POST k vytvoření nového páru klišé. Podobným způsobem může registrace vyžadovat požadavek na zadaný prvek záhlaví HTTP (např. ověřovací pověření).

3. Požádat o ověření

gorilla/mux balíček má snadný a intuitivní přístup k ověření požadavků prostřednictvím regulárních výrazů. Zvažte tento obslužný nástroj pro získání jednoho operace:

router.HandleFunc("/cliches/{id:[0-9]+}", ClichesOne).Methods("GET")

Tato registrace vylučuje požadavky HTTP, jako je

% curl --request GET localhost:8888/cliches/foo

protože foo není desetinná číslice. Výsledkem požadavku je známý stavový kód 404 (Nenalezeno). Zahrnutí vzoru regulárního výrazu do této registrace ovladače zajistí, že ClichesOne funkce se volá ke zpracování požadavku pouze v případě, že identifikátor URI požadavku končí desetinnou celočíselnou hodnotou:

% curl --request GET localhost:8888/cliches/3  # ok

Jako druhý příklad zvažte požadavek

% curl --request PUT --data "..." localhost:8888/cliches

Výsledkem tohoto požadavku je stavový kód 405 (Špatná metoda), protože /cliches URI je registrováno v crud aplikace, pouze pro požadavky GET a POST. Požadavek PUT, stejně jako požadavek GET one, musí obsahovat číselné ID na konci URI:

router.HandleFunc("/cliches/{id:[0-9]+}", ClichesEdit).Methods("PUT")

4. Problémy se souběhem

gorilla/mux router provádí každé volání registrovaného handleru požadavku jako samostatnou rutinu, což znamená, že souběžnost je zapečena do balíčku. Pokud například existuje deset současných požadavků, jako je

% curl --request POST --data "..." localhost:8888/cliches

pak mux.Router spustí deset goroutin ke spuštění ClichesCreate handler.

Z pěti operací požadavku GET all, GET one, POST, PUT a DELETE, poslední tři mění požadovaný zdroj, sdílený clichesList kde sídlí klišé páry. V souladu s tím surový aplikace musí zaručit bezpečnou souběžnost koordinací přístupu k clichesList . Jinými, ale ekvivalentními slovy, crud aplikace musí zabránit sporu na clichesList . V produkčním prostředí lze k uložení zdroje, jako je clichesList, použít databázový systém a bezpečný souběžný provoz by pak mohl být spravován prostřednictvím databázových transakcí.

surový aplikace využívá doporučený přístup Go k bezpečnému souběžnému běhu:

  • Pouze jeden gorutin, správce zdrojů začalo v hrubém aplikace startServer má přístup k clichesList jakmile webový server začne naslouchat požadavkům.
  • Obsluhy požadavků, jako je ClichesCreate a ClichesAll odeslat (ukazatel na) crudRequest instance do kanálu Go (ve výchozím nastavení bezpečné pro vlákna) a samotný správce prostředků čte z tohoto kanálu. Správce zdrojů poté provede požadovanou operaci na clichesList .

Architekturu bezpečného souběžného provozu lze načrtnout následovně:

                 crudRequest                   read/write
request handlers------------->resource manager------------>clichesList

S touto architekturou žádné explicitní zamykání clichesList je potřeba, protože pouze jedna rutina, správce zdrojů, přistupuje k clichesList jakmile začnou přicházet požadavky CRUD.

Chcete-li zachovat surovost pokud možno souběžně, je nezbytné mít efektivní rozdělení práce mezi zpracovatele požadavků na jedné straně a správce jediného zdroje na straně druhé. Zde je pro kontrolu ClichesCreate obsluha žádosti:

func ClichesCreate(res http.ResponseWriter, req *http.Request) {
   cliche, counter := getDataFromRequest(req)
   cp := new(clichePair)
   cp.Cliche = cliche
   cp.Counter = counter
   cr := &crudRequest{verb: POST, cp: cp, confirm: make(chan string)}
   completeRequest(cr, res, "create")
}

Další zdroje pro Linux

  • Cheat pro příkazy Linuxu
  • Cheat sheet pro pokročilé příkazy systému Linux
  • Bezplatný online kurz:Technický přehled RHEL
  • Síťový cheat pro Linux
  • Cheat sheet SELinux
  • Cheat pro běžné příkazy pro Linux
  • Co jsou kontejnery systému Linux?
  • Naše nejnovější články o Linuxu

Obslužný program požadavku ClichesCreate volá obslužnou funkci getDataFromRequest , který extrahuje nové klišé a protiklišé z požadavku POST. ClichesCreate pak vytvoří nový ClichePair , nastaví dvě pole a vytvoří crudRequest k odeslání do správce jediného zdroje. Tato žádost obsahuje potvrzovací kanál, který správce zdrojů používá k vrácení informací zpět obsluze žádosti. Veškerou práci s nastavením lze provést bez zapojení správce zdrojů, protože clichesList zatím není přístupný.

completeRequest pomocná funkce volaná na konci ClichesCreate funkce a další obslužné programy

completeRequest(cr, res, "create") // shown above

přináší do hry správce zdrojů vložením crudRequest do crudRequests kanál:

func completeRequest(cr *crudRequest, res http.ResponseWriter, logMsg string) {
   crudRequests<-cr          // send request to resource manager
   msg := <-cr.confirm       // await confirmation string
   res.Write([]byte(msg))    // send confirmation back to requester
   logIt(logMsg)             // print to the standard output
}

V případě požadavku POST správce zdrojů zavolá obslužnou funkci addPair , který změní clichesList zdroj:

func addPair(cp *clichePair) string {
   cp.Id = masterId  // assign a unique ID
   masterId++        // update the ID counter
   clichesList = append(clichesList, cp) // update the list
   return "\nCreated: " + cp.Cliche + " " + cp.Counter + "\n"
}

Správce prostředků volá podobné pomocné funkce pro ostatní operace CRUD. Stojí za to zopakovat, že správce zdrojů je jedinou rutinou pro čtení nebo zápis clichesList jakmile webový server začne přijímat požadavky.

Pro webové aplikace jakéhokoli typu, gorilla/mux balíček poskytuje směrování požadavků, ověřování požadavků a související služby v jednoduchém a intuitivním rozhraní API. surový webová aplikace zdůrazňuje hlavní funkce balíčku. Vyzkoušejte balíček a pravděpodobně se stanete kupujícím.


Linux
  1. Extrahování a zobrazení dat pomocí awk

  2. AWK a názvy souborů s mezerou.

  3. Co je to sport a dport?

  1. Co je příkaz cURL a jak jej používat?

  2. Trim s Lvm a Dm-crypt?

  3. Spuštění skriptu s „. “ A se „zdrojem“?

  1. Ruční požadavek HTTP(S).

  2. Najděte rozdíl pomocí mtime - a +

  3. dělení a subvol strategie s btrfs