Go
Servidor HTTP
Buscar..
Observaciones
http.ServeMux
proporciona un multiplexor que llama a los controladores para las solicitudes HTTP.
Las alternativas al multiplexor de biblioteca estándar incluyen:
HTTP Hello World con servidor personalizado y mux
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())
}
Presione Ctrl + C para detener el proceso.
Hola Mundo
La forma típica de comenzar a escribir servidores web en golang es usar el módulo net/http
biblioteca estándar.
También hay un tutorial para ello aquí .
El siguiente código también lo usa. Aquí está la implementación de servidor HTTP más simple posible. Responde "Hello World"
a cualquier solicitud HTTP.
Guarde el siguiente código en un archivo server.go
en sus server.go
de trabajo.
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))
}
Puede ejecutar el servidor utilizando:
$ go run server.go
O puedes compilar y correr.
$ go build server.go
$ ./server
El servidor escuchará el puerto especificado ( :8080
). Puedes probarlo con cualquier cliente HTTP. Aquí hay un ejemplo con 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!
Presione Ctrl + C para detener el proceso.
Usando una función de manejador
HandleFunc
registra la función del controlador para el patrón dado en el servidor mux (enrutador).
Puede pasar a definir una función anónima, como hemos visto en el ejemplo básico de Hello World :
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, world!")
}
Pero también podemos pasar un tipo HandlerFunc
. En otras palabras, podemos pasar cualquier función que respete la siguiente firma:
func FunctionName(w http.ResponseWriter, req *http.Request)
Podemos reescribir el ejemplo anterior pasando la referencia a un HandlerFunc
previamente definido. Aquí está el ejemplo completo:
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))
}
Por supuesto, puede definir varios manejadores de funciones para diferentes rutas.
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))
}
Aquí está la salida usando 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
Crear un servidor HTTPS
Generar un certificado
Para ejecutar un servidor HTTPS, es necesario un certificado. La generación de un certificado autofirmado con openssl
se realiza ejecutando este comando:
openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout key.pem -out cert.pem -subj "/CN=example.com" -days 3650`
Los parámetros son:
-
req
Usa la herramienta de solicitud de certificado -
x509
crea un certificado autofirmado -
newkey rsa:4096
Crea una nueva clave y certificado utilizando los algoritmos RSA con una longitud de clave de4096
bits -
sha256
Fuerza los algoritmos de hash SHA256 que los principales navegadores consideran seguros (en el año 2017) -
nodes
Desactiva la protección de contraseña para la clave privada. Sin este parámetro, su servidor tuvo que pedirle la contraseña cada vez que se inicia. -
keyout
Nombra el archivo donde escribir la clave -
out
los nombres del archivo donde escribir el certificado -
subj
define el nombre de dominio para el cual este certificado es válido -
days
Fow ¿cuántos días debe ser válido este certificado?3650
son aprox. 10 años.
Nota: Se puede usar un certificado autofirmado, por ejemplo, para proyectos internos, depuración, pruebas, etc. Cualquier navegador por ahí mencionará que este certificado no es seguro. Para evitar esto, el certificado debe estar firmado por una autoridad de certificación. En su mayoría, esto no está disponible de forma gratuita. Una excepción es el movimiento "Let's Encrypt": https://letsencrypt.org
El código Go necesario
Puede manejar la configuración de TLS para el servidor con el siguiente código. cert.pem
y key.pem
son su certificado y clave SSL, que se generaron con el comando anterior.
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))
}
Respondiendo a una solicitud HTTP usando plantillas
Las respuestas se pueden escribir en un http.ResponseWriter
usando plantillas en Go. Esto demuestra como una herramienta útil si desea crear páginas dinámicas.
(Para saber cómo funcionan las plantillas en Go, visite la página de documentación de Go Templates ).
Continuando con un ejemplo simple para utilizar el html/template
para responder a una solicitud 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)
}
}
Donde, los contenidos de
-
welcomeform.html
son:
<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
son:
<head>
<title> Greetings, {{.Name}} </title>
</head>
<body>
Greetings, {{.Name}}.<br>
We know you are a {{.nationality}}!
</body>
Nota:
Asegúrese de que los archivos
.html
estén en el directorio correcto.Cuando se puede visitar
http://localhost:8080/
después de iniciar el servidor.Como se puede ver después de enviar el formulario, el paquete de plantilla no pudo analizar el campo de nacionalidad no exportado de la estructura, como se esperaba.
Sirviendo contenido usando ServeMux
Un simple servidor de archivos estáticos se vería así:
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)
}
Manejo del método http, acceso a cadenas de consulta y cuerpo de solicitud
Aquí hay un ejemplo simple de algunas tareas comunes relacionadas con el desarrollo de una API, diferenciando el Método HTTP de la solicitud, accediendo a los valores de cadena de consulta y accediendo al cuerpo de la solicitud.
Recursos
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))
}
Ejemplo de salida de rizo:
$ 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