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.

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:

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:

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:
Starten Sie den Server neu und aktualisieren Sie den Browser. Die Stile sollten sich wie folgt einschalten lassen:

Wir erstellen eine Route / Suche
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))
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"` }

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) {
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.

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:

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:

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».

. , :

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-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>
, , .

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 :
git add . git commit -m "Initial commit" git push heroku master
https://__.herokuapp.com , .
Fazit
News Go -. , Heroku.
, . - , , .
Danke fürs Lesen!