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:
-
req
Użyj narzędzia żądania certyfikatu -
x509
Tworzy samopodpisany certyfikat -
newkey rsa:4096
Tworzy nowy klucz i certyfikat przy użyciu algorytmów RSA o długości klucza4096
bitów -
sha256
Wymusza algorytmy mieszające SHA256, które główne przeglądarki uważają za bezpieczne (na rok 2017) -
nodes
Wyłącza ochronę hasła dla klucza prywatnego. Bez tego parametru serwer musiał prosić o hasło przy każdym uruchomieniu. -
keyout
Nazwa pliku, w którym ma zostać zapisany klucz -
out
Nazwa pliku, w którym ma zostać zapisany certyfikat -
subj
Definiuje nazwę domeny, dla której ten certyfikat jest ważny -
days
Ile dni powinien obowiązywać ten certyfikat?3650
to 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.html
to:
<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.html
to:
<head>
<title> Greetings, {{.Name}} </title>
</head>
<body>
Greetings, {{.Name}}.<br>
We know you are a {{.nationality}}!
</body>
Uwaga:
Upewnij się, że pliki
.html
znajdują 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