Go
HTTP-сервер
Поиск…
замечания
http.ServeMux
предоставляет мультиплексор, который вызывает обработчики для HTTP-запросов.
Альтернативы стандартного библиотечного мультиплексора включают:
HTTP Hello World с настраиваемым сервером и мультиплексором
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())
}
Нажмите Ctrl + C, чтобы остановить процесс.
Привет, мир
Типичным способом начать работу с веб-серверами в golang является использование стандартного модуля net/http
библиотеки.
Существует также учебник для него здесь .
Следующий код также использует его. Вот простейшая возможная реализация HTTP-сервера. Он отвечает "Hello World"
на любой HTTP-запрос.
Сохраните следующий код в файле server.go
в своих рабочих пространствах.
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))
}
Вы можете запустить сервер, используя:
$ go run server.go
Или вы можете компилировать и запускать.
$ go build server.go
$ ./server
Сервер будет прослушивать указанный порт ( :8080
). Вы можете протестировать его любым HTTP-клиентом. Вот пример с 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!
Нажмите Ctrl + C, чтобы остановить процесс.
Использование функции обработчика
HandleFunc
регистрирует функцию обработчика для данного шаблона в серверном мультиплексе (маршрутизаторе).
Вы можете передать определение анонимной функции, как мы видели в базовом примере Hello World :
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, world!")
}
Но мы также можем передать тип HandlerFunc
. Другими словами, мы можем передать любую функцию, которая соответствует следующей сигнатуре:
func FunctionName(w http.ResponseWriter, req *http.Request)
Мы можем переписать предыдущий пример, передав ссылку на ранее определенный HandlerFunc
. Вот полный пример:
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))
}
Конечно, вы можете определить несколько обработчиков функций для разных путей.
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))
}
Вот результат с использованием 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
Создание сервера HTTPS
Создать сертификат
Для запуска HTTPS-сервера необходим сертификат. Создание самозаверяющего сертификата с помощью openssl
выполняется с помощью этой команды:
openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout key.pem -out cert.pem -subj "/CN=example.com" -days 3650`
Параметры:
-
req
Используйте инструмент запроса сертификата -
x509
Создает самозаверяющий сертификат -
newkey rsa:4096
Создает новый ключ и сертификат с использованием алгоритмов RSA с длиной ключа4096
бит -
sha256
алгоритмы хэширования SHA256, которые основные браузеры считают безопасными (на 2017 год) -
nodes
Отключает защиту паролем для закрытого ключа. Без этого параметра ваш сервер должен был запрашивать пароль каждый раз при его запуске. -
keyout
файл, в который нужно записать ключ -
out
Имена файла, в котором следует записывать сертификат -
subj
Определяет имя домена, для которого этот сертификат действителен -
days
Сколько дней должен иметь этот сертификат?3650
- ок. 10 лет.
Примечание. Самозаверяющий сертификат можно использовать, например, для внутренних проектов, отладки, тестирования и т. Д. В любом браузере упоминается, что этот сертификат небезопасен. Чтобы этого избежать, сертификат должен быть подписан центром сертификации. В основном это бесплатно. Одним из исключений является движение «Давайте зашифровать»: https://letsencrypt.org
Необходимый код Go
Вы можете обрабатывать конфигурацию TLS для сервера со следующим кодом. cert.pem
и key.pem
- ваш SSL-сертификат и ключ, который генерируется с помощью указанной выше команды.
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))
}
Ответ на HTTP-запрос с использованием шаблонов
Ответы могут быть написаны на http.ResponseWriter
используя шаблоны в Go. Это удобно, если вы хотите создать динамические страницы.
(Чтобы узнать, как работают шаблоны в Go, перейдите на страницу документации шаблонов Go .)
Продолжая простой пример использования html/template
для ответа на 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)
}
}
Где, содержание
-
welcomeform.html
:
<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
:
<head>
<title> Greetings, {{.Name}} </title>
</head>
<body>
Greetings, {{.Name}}.<br>
We know you are a {{.nationality}}!
</body>
Замечания:
Убедитесь, что файлы
.html
находятся в правильном каталоге.Когда
http://localhost:8080/
может быть посещен после запуска сервера.Как можно видеть после отправки формы, невозвращенное национальное поле структуры не могло быть проанализировано пакетом шаблонов, как и ожидалось.
Обслуживание контента с использованием ServeMux
Простой статический файловый сервер будет выглядеть так:
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)
}
Обработка метода http, доступ к строкам запроса и тело запроса
Вот простой пример некоторых общих задач, связанных с разработкой API, дифференциацией между HTTP-методом запроса, доступом к строковым значениям запроса и доступом к телу запроса.
Ресурсы
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))
}
Выход пробного витка:
$ 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