Go
Serwer HTTP
Szukaj…
Uwagi
http.ServeMux zapewnia multiplekser, który wywołuje http.ServeMux obsługi żądań HTTP.
Alternatywy dla standardowego multipleksera bibliotecznego obejmują:
HTTP Hello World z niestandardowym serwerem i multiplekserem
package main
import (
"log"
"net/http"
)
func main() {
// Create a mux for routing incoming requests
m := http.NewServeMux()
// All URLs will be handled by this function
m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, world!"))
})
// Create a server listening on port 8000
s := &http.Server{
Addr: ":8000",
Handler: m,
}
// Continue to process new requests until an error occurs
log.Fatal(s.ListenAndServe())
}
Naciśnij Ctrl + C, aby zatrzymać proces.
Witaj świecie
Typowym sposobem na rozpoczęcie pisania serwerów WWW w golang jest użycie standardowego modułu biblioteki net/http .
Jest też poradnik dla niego tutaj .
Poniższy kod również go używa. Oto najprostsza możliwa implementacja serwera HTTP. Odpowiada "Hello World" na każde żądanie HTTP.
Zapisz następujący kod w pliku server.go w swoich server.go roboczych.
package main
import (
"log"
"net/http"
)
func main() {
// All URLs will be handled by this function
// http.HandleFunc uses the DefaultServeMux
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, world!"))
})
// Continue to process new requests until an error occurs
log.Fatal(http.ListenAndServe(":8080", nil))
}
Możesz uruchomić serwer za pomocą:
$ go run server.go
Lub możesz skompilować i uruchomić.
$ go build server.go
$ ./server
Serwer będzie nasłuchiwał na określonym porcie ( :8080 ). Możesz to przetestować za pomocą dowolnego klienta HTTP. Oto przykład z cURL :
curl -i http://localhost:8080/
HTTP/1.1 200 OK
Date: Wed, 20 Jul 2016 18:04:46 GMT
Content-Length: 13
Content-Type: text/plain; charset=utf-8
Hello, world!
Naciśnij Ctrl + C, aby zatrzymać proces.
Korzystanie z funkcji obsługi
HandleFunc rejestruje funkcję modułu obsługi dla danego wzorca w HandleFunc serwera (routerze).
Możesz przejść zdefiniować anonimową funkcję, jak widzieliśmy w podstawowym przykładzie Hello World :
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, world!")
}
Ale możemy również przekazać typ HandlerFunc . Innymi słowy, możemy przekazać dowolną funkcję, która szanuje następujący podpis:
func FunctionName(w http.ResponseWriter, req *http.Request)
Możemy przepisać poprzedni przykład przekazując odwołanie do wcześniej zdefiniowanego HandlerFunc . Oto pełny przykład:
package main
import (
"fmt"
"net/http"
)
// A HandlerFunc function
// Notice the signature of the function
func RootHandler(w http.ResponseWriter, req *http.Request) {
fmt.Fprintln(w, "Hello, world!")
}
func main() {
// Here we pass the reference to the `RootHandler` handler function
http.HandleFunc("/", RootHandler)
panic(http.ListenAndServe(":8080", nil))
}
Oczywiście można zdefiniować kilka programów obsługi funkcji dla różnych ścieżek.
package main
import (
"fmt"
"log"
"net/http"
)
func FooHandler(w http.ResponseWriter, req *http.Request) {
fmt.Fprintln(w, "Hello from foo!")
}
func BarHandler(w http.ResponseWriter, req *http.Request) {
fmt.Fprintln(w, "Hello from bar!")
}
func main() {
http.HandleFunc("/foo", FooHandler)
http.HandleFunc("/bar", BarHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Oto wynik za pomocą cURL :
➜ ~ curl -i localhost:8080/foo
HTTP/1.1 200 OK
Date: Wed, 20 Jul 2016 18:23:08 GMT
Content-Length: 16
Content-Type: text/plain; charset=utf-8
Hello from foo!
➜ ~ curl -i localhost:8080/bar
HTTP/1.1 200 OK
Date: Wed, 20 Jul 2016 18:23:10 GMT
Content-Length: 16
Content-Type: text/plain; charset=utf-8
Hello from bar!
➜ ~ curl -i localhost:8080/
HTTP/1.1 404 Not Found
Content-Type: text/plain; charset=utf-8
X-Content-Type-Options: nosniff
Date: Wed, 20 Jul 2016 18:23:13 GMT
Content-Length: 19
404 page not found
Utwórz serwer HTTPS
Wygeneruj certyfikat
Aby uruchomić serwer HTTPS, niezbędny jest certyfikat. Generowanie samopodpisanego certyfikatu za pomocą openssl odbywa się poprzez wykonanie tego polecenia:
openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout key.pem -out cert.pem -subj "/CN=example.com" -days 3650`
Parametry to:
-
reqUżyj narzędzia żądania certyfikatu -
x509Tworzy samopodpisany certyfikat -
newkey rsa:4096Tworzy nowy klucz i certyfikat przy użyciu algorytmów RSA o długości klucza4096bitów -
sha256Wymusza algorytmy mieszające SHA256, które główne przeglądarki uważają za bezpieczne (na rok 2017) -
nodesWyłącza ochronę hasła dla klucza prywatnego. Bez tego parametru serwer musiał prosić o hasło przy każdym uruchomieniu. -
keyoutNazwa pliku, w którym ma zostać zapisany klucz -
outNazwa pliku, w którym ma zostać zapisany certyfikat -
subjDefiniuje nazwę domeny, dla której ten certyfikat jest ważny -
daysIle dni powinien obowiązywać ten certyfikat?3650to ok. 10 lat.
Uwaga: samopodpisany certyfikat może być wykorzystany np. Do wewnętrznych projektów, debugowania, testowania itp. Każda przeglądarka wspomina, że ten certyfikat nie jest bezpieczny. Aby tego uniknąć, certyfikat musi zostać podpisany przez urząd certyfikacji. Przeważnie nie jest to dostępne za darmo. Jednym wyjątkiem jest ruch „Let's Encrypt”: https://letsencrypt.org
Niezbędny kod Go
Możesz obsługiwać konfigurację TLS dla serwera za pomocą następującego kodu. cert.pem i key.pem to Twój certyfikat i klucz SSL, które zostały wygenerowane za pomocą powyższej komendy.
package main
import (
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, world!"))
})
log.Fatal(http.ListenAndServeTLS(":443","cert.pem","key.pem", nil))
}
Odpowiadanie na żądanie HTTP za pomocą szablonów
Odpowiedzi można zapisać na http.ResponseWriter przy użyciu szablonów w Go. Jest to przydatne narzędzie do tworzenia dynamicznych stron.
(Aby dowiedzieć się, jak działają szablony w Go, odwiedź stronę Dokumentacja szablonów Go ).
Kontynuując prosty przykład wykorzystania html/template do odpowiedzi na żądanie HTTP:
package main
import(
"html/template"
"net/http"
"log"
)
func main(){
http.HandleFunc("/",WelcomeHandler)
http.ListenAndServe(":8080",nil)
}
type User struct{
Name string
nationality string //unexported field.
}
func check(err error){
if err != nil{
log.Fatal(err)
}
}
func WelcomeHandler(w http.ResponseWriter, r *http.Request){
if r.Method == "GET"{
t,err := template.ParseFiles("welcomeform.html")
check(err)
t.Execute(w,nil)
}else{
r.ParseForm()
myUser := User{}
myUser.Name = r.Form.Get("entered_name")
myUser.nationality = r.Form.Get("entered_nationality")
t, err := template.ParseFiles("welcomeresponse.html")
check(err)
t.Execute(w,myUser)
}
}
Gdzie zawartość
-
welcomeform.htmlto:
<head>
<title> Help us greet you </title>
</head>
<body>
<form method="POST" action="/">
Enter Name: <input type="text" name="entered_name">
Enter Nationality: <input type="text" name="entered_nationality">
<input type="submit" value="Greet me!">
</form>
</body>
-
welcomeresponse.htmlto:
<head>
<title> Greetings, {{.Name}} </title>
</head>
<body>
Greetings, {{.Name}}.<br>
We know you are a {{.nationality}}!
</body>
Uwaga:
Upewnij się, że pliki
.htmlznajdują się we właściwym katalogu.Gdy
http://localhost:8080/można odwiedzić po uruchomieniu serwera.Jak można zobaczyć po przesłaniu formularza, niewyportowane pole narodowości struktury nie mogło zostać przeanalizowane przez pakiet szablonów, zgodnie z oczekiwaniami.
Udostępnianie treści za pomocą ServeMux
Prosty statyczny serwer plików wyglądałby tak:
package main
import (
"net/http"
)
func main() {
muxer := http.NewServeMux()
fileServerCss := http.FileServer(http.Dir("src/css"))
fileServerJs := http.FileServer(http.Dir("src/js"))
fileServerHtml := http.FileServer(http.Dir("content"))
muxer.Handle("/", fileServerHtml)
muxer.Handle("/css", fileServerCss)
muxer.Handle("/js", fileServerJs)
http.ListenAndServe(":8080", muxer)
}
Obsługa metody http, uzyskiwanie dostępu do ciągów zapytań i treści żądania
Oto prosty przykład niektórych typowych zadań związanych z tworzeniem interfejsu API, rozróżniania metody HTTP żądania, uzyskiwania dostępu do wartości ciągu zapytania i uzyskiwania dostępu do treści żądania.
Zasoby
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
)
type customHandler struct{}
// ServeHTTP implements the http.Handler interface in the net/http package
func (h customHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// ParseForm will parse query string values and make r.Form available
r.ParseForm()
// r.Form is map of query string parameters
// its' type is url.Values, which in turn is a map[string][]string
queryMap := r.Form
switch r.Method {
case http.MethodGet:
// Handle GET requests
w.WriteHeader(http.StatusOK)
w.Write([]byte(fmt.Sprintf("Query string values: %s", queryMap)))
return
case http.MethodPost:
// Handle POST requests
body, err := ioutil.ReadAll(r.Body)
if err != nil {
// Error occurred while parsing request body
w.WriteHeader(http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte(fmt.Sprintf("Query string values: %s\nBody posted: %s", queryMap, body)))
return
}
// Other HTTP methods (eg PUT, PATCH, etc) are not handled by the above
// so inform the client with appropriate status code
w.WriteHeader(http.StatusMethodNotAllowed)
}
func main() {
// All URLs will be handled by this function
// http.Handle, similarly to http.HandleFunc
// uses the DefaultServeMux
http.Handle("/", customHandler{})
// Continue to process new requests until an error occurs
log.Fatal(http.ListenAndServe(":8080", nil))
}
Przykładowy wynik zwijania:
$ curl -i 'localhost:8080?city=Seattle&state=WA' -H 'Content-Type: text/plain' -X GET
HTTP/1.1 200 OK
Date: Fri, 02 Sep 2016 16:36:24 GMT
Content-Length: 51
Content-Type: text/plain; charset=utf-8
Query string values: map[city:[Seattle] state:[WA]]%
$ curl -i 'localhost:8080?city=Seattle&state=WA' -H 'Content-Type: text/plain' -X POST -d "some post data"
HTTP/1.1 200 OK
Date: Fri, 02 Sep 2016 16:36:35 GMT
Content-Length: 79
Content-Type: text/plain; charset=utf-8
Query string values: map[city:[Seattle] state:[WA]]
Body posted: some post data%
$ curl -i 'localhost:8080?city=Seattle&state=WA' -H 'Content-Type: text/plain' -X PUT
HTTP/1.1 405 Method Not Allowed
Date: Fri, 02 Sep 2016 16:36:41 GMT
Content-Length: 0
Content-Type: text/plain; charset=utf-8