So erstellen Sie Ihre erste Webanwendung mit Go

Hallo habr Ich präsentiere Ihnen die Übersetzung des Artikels "So erstellen Sie Ihre erste Webanwendung mit Go" von Ayooluwa Isaiah.


Dies ist der Leitfaden für Ihre erste Go-Webanwendung. Wir werden eine Nachrichtenanwendung erstellen , die die Nachrichten-API verwendet , um Nachrichtenartikel zu einem bestimmten Thema zu empfangen und am Ende auf dem Produktionsserver bereitzustellen.


Den vollständigen Code für dieses Tutorial finden Sie in diesem GitHub-Repository .


Anforderungen


Die einzige Voraussetzung für diese Aufgabe ist, dass Go auf Ihrem Computer installiert ist und Sie mit der Syntax und den Konstruktionen ein wenig vertraut sind. Die Go-Version, mit der ich die Anwendung erstellt habe, ist zum Zeitpunkt des Schreibens ebenfalls die neueste: 1.12.9 . Verwenden Sie den Befehl go version , um die installierte Version von Go anzuzeigen.


Wenn Ihnen diese Aufgabe zu schwer fällt, lesen Sie meine vorherige einführende Lektion , die Ihnen den Einstieg erleichtern soll.


Also fangen wir an!


Wir klonen das Startdatei-Repository auf GitHub und cd in das erstellte Verzeichnis. Wir haben drei Hauptdateien: In der Datei main.go schreiben wir den gesamten Go-Code für diese Aufgabe. Die Datei index.html ist die Vorlage, die an den Browser gesendet wird, und die für die Anwendung befinden sich in assets/styles.css .


Erstellen Sie einen einfachen Webserver


Beginnen wir mit der Erstellung eines Core-Servers, der den Text „Hello World!“ An den Browser sendet, wenn eine GET-Anforderung an den Server-Root ausgeführt wird. Ändern Sie Ihre main.go Datei folgendermaßen:


 package main import ( "net/http" "os" ) func indexHandler(w http.ResponseWriter, r *http.Request) { w.Write([]byte("<h1>Hello World!</h1>")) } func main() { port := os.Getenv("PORT") if port == "" { port = "3000" } mux := http.NewServeMux() mux.HandleFunc("/", indexHandler) http.ListenAndServe(":"+port, mux) } 

Die erste Zeile des package main - gibt an, dass der Code in der Datei main.go zum main.go . Danach haben wir das net/http Paket importiert, das HTTP-Client- und Server-Implementierungen zur Verwendung in unserer Anwendung bereitstellt. Dieses Paket ist Teil der Standardbibliothek und ist in jeder Go-Installation enthalten.


In der main erstellt http.NewServeMux() einen neuen HTTP-Anforderungsmultiplexer und weist ihn der Variablen mux . Im Wesentlichen ordnet der Anforderungsmultiplexer die URL der eingehenden Anforderungen der Liste der registrierten Pfade zu und ruft den entsprechenden Handler für den Pfad auf, wenn eine Übereinstimmung gefunden wird.


Als nächstes registrieren wir unsere erste Handler-Funktion für den Root-Pfad / . Diese HandleFunc ist das zweite Argument für HandleFunc und hat immer die Signaturfunktion func (w http.ResponseWriter, r * http.Request) .


Wenn Sie sich die indexHandler Funktion indexHandler , werden Sie indexHandler , dass sie eine solche Signatur hat, was sie zu einem gültigen zweiten Argument für HandleFunc . Der Parameter w ist die Struktur, mit der wir Antworten auf die HTTP-Anforderung senden. Es implementiert die Write() -Methode, die einen Byte-Slice benötigt und die kombinierten Daten als Teil der HTTP-Antwort schreibt.


Andererseits repräsentiert der Parameter r die vom Client empfangene HTTP-Anfrage. So greifen wir auf die vom Webbrowser auf dem Server gesendeten Daten zu. Wir benutzen es hier noch nicht, aber wir werden es definitiv später benutzen.


Schließlich haben wir die http.ListenAndServe() -Methode, mit der der Server an Port 3000 http.ListenAndServe() wird, wenn der Port nicht von der Umgebung festgelegt wurde. Sie können auch einen anderen Anschluss verwenden, wenn 3000 auf Ihrem Computer verwendet wird.


Kompilieren Sie dann den Code, den Sie gerade geschrieben haben, und führen Sie ihn aus:


 go run main.go 

Wenn Sie in Ihrem Browser zu http: // localhost: 3000 wechseln, sollte der Text „Hello World!“ Angezeigt werden.


Tapferer Browser mit Hello World-Text


Gehe zu Vorlagen


Schauen wir uns die Grundlagen des Templatings in Go an. Wenn Sie mit Vorlagen in anderen Sprachen vertraut sind, sollte dies leicht verständlich sein.


Vorlagen bieten eine einfache Möglichkeit, die Ausgabe Ihrer Webanwendung abhängig von der Route anzupassen, ohne den gleichen Code an verschiedenen Stellen schreiben zu müssen. Beispielsweise können wir eine Vorlage für die Navigationsleiste erstellen und auf allen Seiten der Site verwenden, ohne den Code zu duplizieren. Darüber hinaus haben wir die Möglichkeit, unseren Webseiten einige grundlegende Logik hinzuzufügen.


Go stellt in seiner Standardbibliothek zwei Vorlagenbibliotheken zur Verfügung: text/template und html/template . Beide bieten die gleiche Oberfläche, jedoch wird das html/template Paket verwendet, um HTML-Ausgaben zu generieren, die gegen Code-Injection geschützt sind. Deshalb werden wir es hier verwenden.


Importieren Sie dieses Paket in Ihre main.go Datei und verwenden Sie es wie folgt:


 package main import ( "html/template" "net/http" "os" ) var tpl = template.Must(template.ParseFiles("index.html")) func indexHandler(w http.ResponseWriter, r *http.Request) { tpl.Execute(w, nil) } func main() { port := os.Getenv("PORT") if port == "" { port = "3000" } mux := http.NewServeMux() mux.HandleFunc("/", indexHandler) http.ListenAndServe(":"+port, mux) } 

tpl ist eine Variable auf tpl , die die Definition einer Vorlage aus den bereitgestellten Dateien angibt. Der Aufruf template.ParseFiles analysiert die Datei index.html im Stammverzeichnis unseres Projektverzeichnisses und überprüft ihre Gültigkeit.


Wir verpacken den Aufruf template.ParseFiles in template.Must , dass der Code beim Auftreten eines Fehlers in Panik gerät. Der Grund, warum wir hier in Panik geraten, anstatt zu versuchen, den Fehler zu behandeln, ist, dass es keinen Sinn macht, den Code weiter auszuführen, wenn wir eine ungültige Vorlage haben. Dies ist ein Problem, das behoben werden muss, bevor der Server neu gestartet werden kann.


In der indexHandler Funktion führen wir die zuvor erstellte Vorlage aus, indem wir zwei Argumente indexHandler : Wo sollen die Ausgabe und die Daten geschrieben werden, die an die Vorlage übergeben werden sollen indexHandler


Im obigen Fall schreiben wir die Ausgabe in die ResponseWriter Schnittstelle und da wir derzeit keine Daten zur Übergabe an unsere Vorlage haben, wird nil als zweites Argument übergeben.


go run main.go Sie den laufenden Prozess in Ihrem Terminal mit Strg-C und starten Sie ihn erneut mit go run main.go Aktualisieren Sie dann Ihren Browser. Auf der folgenden Seite sollte der Text "News App Demo" angezeigt werden:


Brave Browser Zeigt News App Demo Text


Fügen Sie der Seite eine Navigationsleiste hinzu


Ersetzen Sie den Inhalt des <body> in Ihrer index.html -Datei wie folgt:


 <main> <header> <a class="logo" href="/">News Demo</a> <form action="/search" method="GET"> <input autofocus class="search-input" value="" placeholder="Enter a news topic" type="search" name="q"> </form> <a href="https://github.com/freshman-tech/news" class="button github-button">View on Github</a> </header> </main> 

Starten Sie dann den Server neu und aktualisieren Sie Ihren Browser. Sie sollten etwas Ähnliches sehen:


Browser mit nicht gestylter Navigationsleiste


Arbeiten Sie mit statischen Dateien


Bitte beachten Sie, dass die oben hinzugefügte Navigationsleiste keine Stile enthält, obwohl wir sie bereits im <head> unseres Dokuments angegeben haben.


Dies liegt daran, dass der Pfad / tatsächlich mit allen Pfaden übereinstimmt, die an keiner anderen Stelle verarbeitet werden. Wenn Sie zu http: // localhost: 3000 / assets / style.css gehen , erhalten Sie weiterhin die News Demo-Startseite anstelle der CSS-Datei, da die Route /assets/style.css nicht speziell deklariert wurde.


Die Notwendigkeit, explizite Handler für alle unsere statischen Dateien zu deklarieren, ist jedoch unrealistisch und kann nicht skaliert werden. Glücklicherweise können wir einen Handler für alle statischen Ressourcen erstellen.


Als Erstes müssen Sie eine Instanz des Dateiserverobjekts erstellen und das Verzeichnis übergeben, in dem sich alle statischen Dateien befinden:


 fs := http.FileServer(http.Dir("assets")) 

Als Nächstes müssen wir unseren Router anweisen, dieses Dateiserverobjekt für alle Pfade zu verwenden, die mit dem Präfix /assets/ :


 mux.Handle("/assets/", http.StripPrefix("/assets/", fs)) 

Nun alles zusammen:


 // main.go //   func main() { port := os.Getenv("PORT") if port == "" { port = "3000" } mux := http.NewServeMux() //     fs := http.FileServer(http.Dir("assets")) mux.Handle("/assets/", http.StripPrefix("/assets/", fs)) mux.HandleFunc("/", indexHandler) http.ListenAndServe(":"+port, mux) } 

Starten Sie den Server neu und aktualisieren Sie den Browser. Die Stile sollten sich wie folgt einschalten lassen:


Tapferer Browser mit gestalteter Navigationsleiste



Erstellen wir eine Route, die Suchanfragen nach Nachrichtenartikeln verarbeitet. Wir werden die Nachrichten-API verwenden , um Anfragen zu bearbeiten. Sie müssen sich daher registrieren, um hier einen kostenlosen API-Schlüssel zu erhalten.


Diese Route erwartet zwei Abfrageparameter: q stellt die Abfrage des Benutzers dar, und die page zum Blättern durch die Ergebnisse verwendet. Dieser Seitenparameter ist optional. Wenn es nicht in der URL enthalten ist, nehmen wir einfach an, dass die Seitenzahl der Ergebnisse auf "1" gesetzt ist.


Fügen indexHandler Ihrer main.go Datei den folgenden Handler unter indexHandler main.go :


 func searchHandler(w http.ResponseWriter, r *http.Request) { u, err := url.Parse(r.URL.String()) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal server error")) return } params := u.Query() searchKey := params.Get("q") page := params.Get("page") if page == "" { page = "1" } fmt.Println("Search Query is: ", searchKey) fmt.Println("Results page is: ", page) } 

Der obige Code extrahiert die Parameter q und page aus der Anforderungs-URL und zeigt beide im Terminal an.


Registrieren Sie dann die searchHandler Funktion wie folgt als /search Pfad-Handler:


 func main() { port := os.Getenv("PORT") if port == "" { port = "3000" } mux := http.NewServeMux() fs := http.FileServer(http.Dir("assets")) mux.Handle("/assets/", http.StripPrefix("/assets/", fs)) // Add the next line mux.HandleFunc("/search", searchHandler) mux.HandleFunc("/", indexHandler) http.ListenAndServe(":"+port, mux) } 

Denken Sie daran, die Pakete fmt und net/url von oben zu importieren:


 import ( "fmt" "html/template" "net/http" "net/url" "os" ) 

Starten Sie nun den Server neu, geben Sie die Abfrage in das Suchfeld ein und überprüfen Sie das Terminal. Sie sollten Ihre Anfrage im Terminal sehen, wie unten gezeigt:




Erstellen Sie ein Datenmodell


Wenn wir eine Anfrage an das News API/everything Endpoint stellen, erwarten wir eine json-Antwort im folgenden Format:


 { "status": "ok", "totalResults": 4661, "articles": [ { "source": { "id": null, "name": "Gizmodo.com" }, "author": "Jennings Brown", "title": "World's Dumbest Bitcoin Scammer Tries to Scam Bitcoin Educator, Gets Scammed in The Process", "description": "Ben Perrin is a Canadian cryptocurrency enthusiast and educator who hosts a bitcoin show on YouTube. This is immediately apparent after a quick a look at all his social media. Ten seconds of viewing on of his videos will show that he is knowledgeable about di…", "url": "https://gizmodo.com/worlds-dumbest-bitcoin-scammer-tries-to-scam-bitcoin-ed-1837032058", "urlToImage": "https://i.kinja-img.com/gawker-media/image/upload/s--uLIW_Oxp--/c_fill,fl_progressive,g_center,h_900,q_80,w_1600/s4us4gembzxlsjrkmnbi.png", "publishedAt": "2019-08-07T16:30:00Z", "content": "Ben Perrin is a Canadian cryptocurrency enthusiast and educator who hosts a bitcoin show on YouTube. This is immediately apparent after a quick a look at all his social media. Ten seconds of viewing on of his videos will show that he is knowledgeable about..." } ] } 

Um mit diesen Daten in Go arbeiten zu können, müssen wir eine Struktur generieren, die die Daten beim Dekodieren des Antwortkörpers widerspiegelt. Natürlich können Sie es manuell tun, aber ich bevorzuge die Verwendung der JSON-to-Go- Website, was diesen Vorgang wirklich einfach macht. Es wird eine Go-Struktur (mit Tags) generiert, die für diesen JSON funktioniert.


Alles, was Sie tun müssen, ist, das JSON-Objekt zu kopieren und in das mit JSON gekennzeichnete Feld einzufügen , dann die Ausgabe zu kopieren und in Ihren Code einzufügen. Folgendes erhalten wir für das obige JSON-Objekt:


 type AutoGenerated struct { Status string `json:"status"` TotalResults int `json:"totalResults"` Articles []struct { Source struct { ID interface{} `json:"id"` Name string `json:"name"` } `json:"source"` Author string `json:"author"` Title string `json:"title"` Description string `json:"description"` URL string `json:"url"` URLToImage string `json:"urlToImage"` PublishedAt time.Time `json:"publishedAt"` Content string `json:"content"` } `json:"articles"` } 

Tapferer Browser mit JSON to Go-Tool


Ich habe einige Änderungen an der AutoGenerated Struktur vorgenommen, indem ich das Articles Fragment in eine eigene Struktur aufgeteilt und den Strukturnamen aktualisiert habe. tpl die folgende tpl Variablendeklaration in main.go und fügen Sie das main.go Ihrem Import hinzu:


 type Source struct { ID interface{} `json:"id"` Name string `json:"name"` } type Article struct { Source Source `json:"source"` Author string `json:"author"` Title string `json:"title"` Description string `json:"description"` URL string `json:"url"` URLToImage string `json:"urlToImage"` PublishedAt time.Time `json:"publishedAt"` Content string `json:"content"` } type Results struct { Status string `json:"status"` TotalResults int `json:"totalResults"` Articles []Article `json:"articles"` } 

Wie Sie vielleicht wissen, erfordert Go, dass alle exportierten Felder in der Struktur mit einem Großbuchstaben beginnen. Es ist jedoch üblich, JSON-Felder mit camelCase oder snake_case darzustellen , die nicht mit einem Großbuchstaben beginnen.


Aus diesem Grund verwenden wir Strukturfeld-Tags wie json:"id" , um das Strukturfeld wie oben gezeigt explizit im JSON-Feld anzuzeigen. Bei Bedarf können Sie auch ganz andere Namen für das Strukturfeld und das entsprechende json-Feld verwenden.


Zuletzt erstellen wir für jede Suchabfrage einen anderen Strukturtyp. Fügen Sie dies unter der Results in main.go :


 type Search struct { SearchKey string NextPage int TotalPages int Results Results } 

Diese Struktur repräsentiert jede vom Benutzer vorgenommene Suchanfrage. SearchKey ist die Abfrage selbst. NextPage Feld NextPage können NextPage durch die Ergebnisse TotalPages - die Gesamtzahl der Seiten mit Abfrageergebnissen und Results - die aktuelle Seite mit Abfrageergebnissen.


Senden Sie eine Anfrage über die News-API und rendern Sie die Ergebnisse


Nachdem wir das Datenmodell für unsere Anwendung haben, fahren wir fort und stellen Anforderungen an die News-API. Anschließend werden die Ergebnisse auf der Seite gerendert.


Da für die News-API ein API-Schlüssel erforderlich ist, müssen wir eine Möglichkeit finden, ihn in unserer Anwendung zu übergeben, ohne den Code hart zu codieren. Umgebungsvariablen sind ein gängiger Ansatz, aber ich habe mich stattdessen für die Verwendung von Befehlszeilenflags entschieden. Go bietet ein flag Paket, das die grundlegende Analyse von Befehlszeilen-Flags unterstützt. Dies wird hier verwendet.


apiKey zuerst eine neue apiKey Variable unter der tpl Variablen:


 var apiKey *string 

Dann benutze es in der Hauptfunktion wie folgt:


 func main() { apiKey = flag.String("apikey", "", "Newsapi.org access key") flag.Parse() if *apiKey == "" { log.Fatal("apiKey must be set") } //    } 

Hier rufen wir die flag.String() -Methode auf, mit der wir ein String-Flag definieren können. Das erste Argument für diese Methode ist der Flagname, das zweite ist der Standardwert und das dritte ist die Verwendungsbeschreibung.


Nachdem Sie alle Flags definiert haben, müssen Sie flag.Parse() aufrufen, um sie tatsächlich zu flag.Parse() . Da apikey eine erforderliche Komponente für diese Anwendung ist, stellen wir schließlich sicher, dass das Programm abstürzt, wenn dieses Flag während der Programmausführung nicht gesetzt wird.


apikey Sie sicher, dass Sie das flag Paket zu Ihrem Import apikey , starten Sie den Server neu und übergeben Sie das erforderliche apikey Flag (siehe unten):


 go run main.go -apikey=<your newsapi access key> 

Als Nächstes setzen wir den searchHandler fort und aktualisieren ihn, sodass die Suchanfrage des Benutzers an newsapi.org gesendet und die Ergebnisse in unserer Vorlage angezeigt werden.


Ersetzen Sie die beiden Aufrufe der Methode fmt.Println() am Ende der Funktion fmt.Println() searchHandler folgenden Code:


 func searchHandler(w http.ResponseWriter, r *http.Request) { // beginning of the function search := &Search{} search.SearchKey = searchKey next, err := strconv.Atoi(page) if err != nil { http.Error(w, "Unexpected server error", http.StatusInternalServerError) return } search.NextPage = next pageSize := 20 endpoint := fmt.Sprintf("https://newsapi.org/v2/everything?q=%s&pageSize=%d&page=%d&apiKey=%s&sortBy=publishedAt&language=en", url.QueryEscape(search.SearchKey), pageSize, search.NextPage, *apiKey) resp, err := http.Get(endpoint) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } defer resp.Body.Close() if resp.StatusCode != 200 { w.WriteHeader(http.StatusInternalServerError) return } err = json.NewDecoder(resp.Body).Decode(&search.Results) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } search.TotalPages = int(math.Ceil(float64(search.Results.TotalResults / pageSize))) err = tpl.Execute(w, search) if err != nil { w.WriteHeader(http.StatusInternalServerError) } } 

Zunächst erstellen wir eine neue Instanz der SearchKey und setzen den Wert des SearchKey Felds auf den Wert des URL-Parameters q in der HTTP-Anforderung.


Danach konvertieren wir die NextPage in eine Ganzzahl und weisen das Ergebnis dem NextPage Feld NextPage . Dann erstellen wir die Variable pageSize und setzen ihren Wert auf 20. Diese Variable pageSize repräsentiert die Anzahl der Ergebnisse, die die Nachrichten-API in ihrer Antwort pageSize . Dieser Wert kann zwischen 0 und 100 liegen.


Dann erstellen wir den Endpunkt mit fmt.Sprintf() und stellen eine GET-Anfrage an ihn. Wenn die Antwort von der News-API nicht 200 OK ist , wird ein allgemeiner Serverfehler an den Client zurückgegeben. Andernfalls wird der Antworttext in search.Results analysiert.


Dann berechnen wir die Gesamtzahl der Seiten, indem TotalResults Feld " pageSize durch " pageSize . Wenn eine Abfrage beispielsweise 100 Ergebnisse zurückgibt und jeweils nur 20 angezeigt werden, müssen Sie einen Bildlauf durch fünf Seiten durchführen, um alle 100 Ergebnisse für diese Abfrage anzuzeigen.


Danach rendern wir unsere Vorlage und übergeben die Suchvariable als Datenschnittstelle. Auf diese Weise können wir auf Daten eines JSON-Objekts in unserer Vorlage zugreifen, wie Sie sehen werden.


index.html vor dem Wechsel zu index.html sicher, dass Sie Ihre Importe wie index.html aktualisieren:


 import ( "encoding/json" "flag" "fmt" "html/template" "log" "math" "net/http" "net/url" "os" "strconv" "time" ) 

Fahren wir fort und zeigen die Ergebnisse auf der Seite an, indem Sie die Datei index.html wie folgt ändern. Fügen Sie dies unter dem <header> :


 <section class="container"> <ul class="search-results"> {{ range .Results.Articles }} <li class="news-article"> <div> <a target="_blank" rel="noreferrer noopener" href="{{.URL}}"> <h3 class="title">{{.Title }}</h3> </a> <p class="description">{{ .Description }}</p> <div class="metadata"> <p class="source">{{ .Source.Name }}</p> <time class="published-date">{{ .PublishedAt }}</time> </div> </div> <img class="article-image" src="{{ .URLToImage }}"> </li> {{ end }} </ul> </section> 

Um auf das Strukturfeld in der Vorlage zuzugreifen, verwenden wir den Punktoperator. Dieser Operator verweist auf ein Strukturobjekt (in diesem Fall " search ). In der Vorlage geben wir dann einfach den Feldnamen an (als {{.Results}} ).


Mit dem range Block können wir in Go über ein Slice iterieren und HTML-Code für jedes Element im Slice ausgeben. Hier iterieren wir über das Segment der Article im Feld Articles und zeigen den HTML-Code bei jeder Iteration an.


Starten Sie den Server neu, aktualisieren Sie den Browser und suchen Sie nach Nachrichten zu einem beliebten Thema. Sie sollten eine Liste mit 20 Ergebnissen pro Seite erhalten, wie im folgenden Screenshot gezeigt.


Browser mit nachrichtenlisten


Suchabfrage im Ausland speichern


Beachten Sie, dass die Suchabfrage aus der Eingabe verschwindet, wenn die Seite mit den Ergebnissen aktualisiert wird. Im Idealfall sollte die Abfrage so lange aufbewahrt werden, bis der Benutzer eine neue Suche durchführt. So funktioniert beispielsweise die Google-Suche.


Wir können dies leicht beheben, indem wir das value Attribut des input Tags in unserer index.html Datei wie folgt aktualisieren:


 <input autofocus class="search-input" value="{{ .SearchKey }}" placeholder="Enter a news topic" type="search" name="q"> 

Starten Sie Ihren Browser neu und führen Sie eine neue Suche durch. Die Suchanfrage wird wie folgt gespeichert:



Veröffentlichungsdatum formatieren


Wenn Sie sich das Datum in jedem Artikel ansehen, werden Sie feststellen, dass es schlecht lesbar ist. Die aktuelle Ausgabe zeigt, wie die Nachrichten-API das Veröffentlichungsdatum des Artikels zurückgibt. Sie können dies jedoch problemlos ändern, indem Sie der Article eine Methode hinzufügen und das Datum damit formatieren, anstatt den Standardwert zu verwenden.


main.go wir den folgenden Code direkt unter der Article in main.go :


 func (a *Article) FormatPublishedDate() string { year, month, day := a.PublishedAt.Date() return fmt.Sprintf("%v %d, %d", month, day, year) } 

Hier wird eine neue FormatPublishedDate Methode in der Article Struktur erstellt. Diese Methode formatiert das PublishedAt Feld in Article und gibt eine Zeichenfolge im folgenden Format zurück: 10 2009 .


Ersetzen Sie .PublishedAt durch .FormatPublishedDate in Ihrer index.html Datei, um diese neue Methode in Ihrer Vorlage zu verwenden. Starten Sie dann den Server neu und wiederholen Sie die vorherige Suchabfrage. Dies wird die Ergebnisse mit einer korrekt formatierten Zeit ausgeben, wie unten gezeigt:


Tapferer Browser mit korrekt formatiertem Datum


Zeigt die Gesamtzahl der Ergebnisse an.


Lassen Sie uns die Benutzeroberfläche unserer Nachrichtenanwendung verbessern, indem wir oben auf der Seite die Gesamtzahl der Ergebnisse angeben und dann eine Meldung anzeigen, falls für eine bestimmte Abfrage keine Ergebnisse gefunden wurden.


Alles, was Sie tun müssen, ist, den folgenden Code als .search-results Element des .container direkt über dem .search-results Element in Ihrer index.html Datei .search-results :


 <div class="result-count"> {{ if (gt .Results.TotalResults 0)}} <p>About <strong>{{ .Results.TotalResults }}</strong> results were found.</p> {{ else if (ne .SearchKey "") and (eq .Results.TotalResults 0) }} <p>No results found for your query: <strong>{{ .SearchKey }}</strong>.</p> {{ end }} </div> 

Go , . gt , , TotalResults Results . , .


, SearchKey ( (ne .SearchKey "") ) TotalResults ( (eq .Results.TotalResults 0) ), «No results found».


, . «No results found».


Browser zeigt keine Ergebnisse gefunden Nachricht


. , :


Der Browser, der die Ergebnisse anzeigt, wird oben auf der Seite angezeigt



20 , , .


Next , . , , Search main.go :


 func (s *Search) IsLastPage() bool { return s.NextPage >= s.TotalPages } 

, NextPage , TotalPages Search . , NextPage , . So geht's:


 func searchHandler(w http.ResponseWriter, r *http.Request) { //   search.TotalPages = int(math.Ceil(float64(search.Results.TotalResults / pageSize))) //   if  if ok := !search.IsLastPage(); ok { search.NextPage++ } //    } 

, , . .search-results index.html .


 <div class="pagination"> {{ if (ne .IsLastPage true) }} <a href="/search?q={{ .SearchKey }}&page={{ .NextPage }}" class="button next-page">Next</a> {{ end }} </div> 

, Next .


, href /search q , NextPage page .


Previous . , 1. , CurrentPage() Search , . IsLastPage :


 func (s *Search) CurrentPage() int { if s.NextPage == 1 { return s.NextPage } return s.NextPage - 1 } 

NextPage - 1 , , NextPage 1. , 1 . :


 func (s *Search) PreviousPage() int { return s.CurrentPage() - 1 } 

, Previous , 1. .pagination index.html :


 <div class="pagination"> {{ if (gt .NextPage 2) }} <a href="/search?q={{ .SearchKey }}&page={{ .PreviousPage }}" class="button previous-page">Previous</a> {{ end }} {{ if (ne .IsLastPage true) }} <a href="/search?q={{ .SearchKey }}&page={{ .NextPage }}" class="button next-page">Next</a> {{ end }} </div> 

. , :





, , , , .


index.html :


 <div class="result-count"> {{ if (gt .Results.TotalResults 0)}} <p>About <strong>{{ .Results.TotalResults }}</strong> results were found. You are on page <strong>{{ .CurrentPage }}</strong> of <strong> {{ .TotalPages }}</strong>.</p> {{ else if (ne .SearchKey "") and (eq .Results.TotalResults 0) }} <p>No results found for your query: <strong>{{ .SearchKey }}</strong>.</p> {{ end }} </div> 

, , .


Browser, der die aktuelle Seite anzeigt


Heroku


, , Heroku. , , . . freshman-news .


, Heroku . heroku login , Heroku.


, git- . , git init , , heroku git-. freshman-news .


 heroku git:remote -a freshman-news 

Procfile ( touch Procfile ) :


 web: bin/news-demo -apikey $NEWS_API_KEY 

GitHub Go, , go.mod , . , , .


 module github.com/freshman-tech/news-demo go 1.12.9 

Settings Heroku Reveal Config Vars . NEWS_API_KEY , .


Heroku Konfigurationsvariablen


, Heroku :


 git add . git commit -m "Initial commit" git push heroku master 

https://__.herokuapp.com , .


Fazit


News Go -. , Heroku.


, . - , , .


Danke fürs Lesen!

Source: https://habr.com/ru/post/de475390/


All Articles