Skip to content

Commit cc95a03

Browse files
author
ma0
committedDec 17, 2016
complete chapter 4 translation to spanish
1 parent d98aaa2 commit cc95a03

File tree

6 files changed

+456
-10
lines changed

6 files changed

+456
-10
lines changed
 

‎es/03.4.md

+10-10
Original file line numberDiff line numberDiff line change
@@ -176,21 +176,21 @@ En escenarios mas complejos también podemos utilizar este método, la ventaja e
176176

177177
Vamos a echar un vistazo a la lista de flujo de ejecución en conjunto.
178178

179-
- Se llama http.HandleFunc
179+
- Se llama `http.HandleFunc`
180180
1. Se Llama HandleFunc de DefaultServeMux
181181
2. Se Llama Handle de DefaultServeMux
182182
3. Se agregan las reglas del enrutamiento a map[string]muxEntry de DefaultServeMux
183-
- Se llama http.ListenAndServe (":9090" , nil )
183+
- Se llama `http.ListenAndServe(":9090" , nil)`
184184
1. Se instancia el servidor
185-
2. Llama ListenAndServe del Servidor
186-
3. Llama net.Listen ( " tcp" , addr ) para escuchar en el puerto .
185+
2. Se Llama ListenAndServe del Servidor
186+
3. Se Llama net.Listen ( "tcp" , addr ) para escuchar en el puerto.
187187
4. Iniciar un bucle, y aceptar las solicitudes en el cuerpo del bucle.
188-
5. Instanciada una Conn se empieza una goroutine para cada solicitud : ir c.serve ().
189-
6. Lee petición de datos : w , err : = c.readRequest ().
190-
7. Comprueba si el controlador está vacío, si está vacíoutiliza DefaultServeMux .
191-
8. Llama al controlador de ServeHTTP
192-
9. Ejecutar código en DefaultServeMux en este caso.
193-
10. Elije el controlador URL y ejecutar código del controlador en esta seccion: mux.handler.ServeHTTP ( w , r)
188+
5. Instanciar una Conn y se empieza una goroutine para cada solicitud : `go c.serve ()`
189+
6. Se Lee petición de datos : `w , err : = c.readRequest ()`
190+
7. Se Comprueba si el controlador está vacío, si está vacíoutiliza DefaultServeMux .
191+
8. Se Llama al controlador de ServeHTTP
192+
9. Se Ejecuta el código en DefaultServeMux en este caso.
193+
10. Elije el controlador URL y ejecutar código del controlador en esta seccion: `mux.handler.ServeHTTP (w , r)`
194194
11. Cómo elegir handler:
195195
A. Normas de router de verificación para esta URL.
196196
B. Llamar ServeHTTP en ese controlador, si es que existe.

‎es/04.2.md

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# 4.2 Verificando las entradas
2+
3+
Uno de los principios mas importantes en el desarrollo web es que no puedes confiar en nada de lo que viene en los formularios de usuario. Necesitas validar todos los datos de entrada antes de usarlos. Muchos sitios web son afectados por este problema, lo que es simplemente crucial.
4+
5+
Existen dos maneras de verificar datos de formularios, que son usadas comunmente. La primera es la validación del lado del cliente en. el front end, y la segunda es la validación del lado del servidor, en el back end. En esta sección vamos a hablar sobre la validación del lado del servidor.
6+
7+
## Campos requeridos
8+
9+
Algunas veces requerimos que los usuarios ingresen algunos campos, pero ellos fallan completándolos. Por ejemplo, en la sección anterior, nosotros requerimos un nombre de usuario. Puedes usar la función `len` para obtener el tamaño de un campo para asegurarte que se ha ingresado algo.
10+
```
11+
if len(r.Form["username"][0])==0{
12+
// code for empty field
13+
}
14+
```
15+
`r.Form` trata diferente a las entradas que son de distinto tipo cuando están vacíos. Para cuadros de textos, áreas de texto y campos de archivos, retorna una cadena vacía, para botones circulares y cajas de chequeo, a veces ni siquiera se crean los tipos. En cambio, vas a tener problemas accediendo a los elementos correspondientes. Por esto, es mas seguro usar `r.Form.Get()` para obtener los valores de los campos de una manera que siempre van a retornar vacío si el valor no existe. Por otra parte, `r.Form.Get()` solo puede obtener un valor al tiempo, así que necesitas usar `r.Form` para obtener un mapa de los valores.
16+
17+
## Números
18+
19+
Algunas veces necesitamos números mas allá de texto en un campo. Por ejemplo, digamos que necesitas la edad de un usuario como un entero solamente, es decir: 50 o 10, en vez de "lo suficientemente viejo" u "hombre jóven". Si requerimos un número positivo, podemos convertir el valor al tipo `int` y luego procesarlo:
20+
```
21+
getint,err:=strconv.Atoi(r.Form.Get("age"))
22+
if err!=nil{
23+
// error occurs when convert to number, it may not a number
24+
}
25+
26+
// check range of number
27+
if getint >100 {
28+
// too big
29+
}
30+
```
31+
Otra manera de realizar esto es usando expresiones regulares:
32+
```
33+
if m, _ := regexp.MatchString("^[0-9]+$", r.Form.Get("age")); !m {
34+
return false
35+
}
36+
```
37+
Para propósitos de eficiencia, las expresiones regulares no son eficientes, sin embarglo las expresiones regulares simples son lo suficientemente rápidas. Si estas familiarizado con las expresiones regulares, es una manera muy conveniente de verificar datos. Nota que Go usa la sintaxis [RE2](http://code.google.com/p/re2/wiki/Syntax), entonces todos los caracteres de UTF-8 están soportados.
38+
39+
## Chino
40+
41+
Algunas veces, necesitamos que los usuarios ingresen su nombre en chino, y necesitamos verificar que todos estén en chino, en vez de caracteres al azar. Para la verificación del chino, las expresiones regulares son la única manera de conseguirlo:
42+
```
43+
if m, _ := regexp.MatchString("^[\\x{4e00}-\\x{9fa5}]+$", r.Form.Get("realname")); !m {
44+
return false
45+
}
46+
```
47+
## Letras del Inglés
48+
49+
Algunas veces necesitamos que los usuarios solo ingresen letras del Inglés, por ejemplo algún nombre en inglés como astaxie en vez de asta谢. Podemos verificar esto usando la siguiente expresión regular.
50+
```
51+
if m, _ := regexp.MatchString("^[a-zA-Z]+$", r.Form.Get("engname")); !m {
52+
return false
53+
}
54+
```
55+
## Correo electrónico
56+
57+
Si queremos si el usuario ha ingresado una dirección de correo electrónico válida, puedes usar la siguiente expresión regular:
58+
```
59+
if m, _ := regexp.MatchString(`^([\w\.\_]{2,10})@(\w{1,}).([a-z]{2,4})$`, r.Form.Get("email")); !m {
60+
fmt.Println("no")
61+
}else{
62+
fmt.Println("yes")
63+
}
64+
```
65+
## Lista desplegable
66+
67+
Digamos que vamos a requerir un elemento de una lista desplegable, pero en vez de esto, tenemos un valor fabricado por hackers. ¿Cómo evitamos que esto pase?
68+
69+
Suponga que tenemos el siguiente `<select>`
70+
```
71+
<select name="fruit">
72+
<option value="apple">apple</option>
73+
<option value="pear">pear</option>
74+
<option value="banana">banana</option>
75+
</select>
76+
```
77+
Podemos usar la siguiente estrategia para limpiar la entrada:
78+
```
79+
slice:=[]string{"apple","pear","banana"}
80+
81+
for _, v := range slice {
82+
if v == r.Form.Get("fruit") {
83+
return true
84+
}
85+
}
86+
return false
87+
```
88+
Todas las funciones que he mostrado arriba están en mi proyecto de código abierto para operar con segmentos y mapas: [https://github.com/astaxie/beeku](https://github.com/astaxie/beeku)
89+
90+
## Botones circulares
91+
92+
Si queremos saber cuando un usuario es femenino o masculino, podríamos usar un botón circular, retornando 1 para femenino y 2 para masculino. Sin embargo, algún niño que acaba de leer su primer libro sobre HTTP decide enviar un 3. ¿Nuestro programa levantará una excepción? Como puedes ver, necesitamos usar el mismo método que usamos para la lista desplegable para asegurarnos sobre los valores que son ingresados a nuestro botón circular.
93+
```
94+
<input type="radio" name="gender" value="1">Femenino
95+
<input type="radio" name="gender" value="2">Masculino
96+
```
97+
Y usualmente utilizamos el siguiente código para validar las entradas:
98+
```
99+
slice:=[]int{1,2}
100+
101+
for _, v := range slice {
102+
if v == r.Form.Get("gender") {
103+
return true
104+
}
105+
}
106+
return false
107+
```
108+
## Cajas de chequeo
109+
110+
Supon que existen algunas cajas de chequeo para los intereses de los usuarios, y tu no quieres valores extraños aquí tampoco, puedes validarlos de la siguiente manera:
111+
```
112+
<input type="checkbox" name="interest" value="football">Football
113+
<input type="checkbox" name="interest" value="basketball">Basketball
114+
<input type="checkbox" name="interest" value="tennis">Tennis
115+
```
116+
En este caso la estrategia de limpieza es un poco diferente a la validación de botones de chequeo y listas desplegables:
117+
```
118+
slice:=[]string{"football","basketball","tennis"}
119+
a:=Slice_diff(r.Form["interest"],slice)
120+
if a == nil{
121+
return true
122+
}
123+
124+
return false
125+
```
126+
## Fecha y Hora
127+
128+
Supón que quieres que los usuarios ingresen fechas y horas. Go tiene un paquete `time` para convertir año, mes y día a la hora correspondiente. Después de eso, es fácil verificarlo.
129+
```
130+
t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
131+
fmt.Printf("Go launched at %s\n", t.Local())
132+
```
133+
Después de eso, también puedes utilizar el paquete `time` para mas operaciones, dependiende de tus necesidades.
134+
135+
En esta sección hemos discutido algunos métodos comunes para validar los datos del lado del servidor. Espero que ahora entiendas un poco mas sobre la validación de datos en Go, especialmente como usar las evntajas de las expresiones regulares.
136+
137+
## Enlaces
138+
139+
- [Índice](preface.md)
140+
- Sección anterior: [Procesando la entrada de los formularios](04.1.md)
141+
- Siguiente sección: [Cross site scripting](04.3.md)

‎es/04.3.md

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# 4.3 Cross site scripting
2+
3+
Hoy los sitios web tienen mucho contenido dinámico en orden de mejorar la experiencia de usuario, lo que significa que debemos proveer información dinámica en función de cada comportamiento individual, desafortunadamente, los sitios web dinámicos son suceptibles a ataques maliciosos conocidos como "Cross site scripting" (abreviado "XSS"). Los sitios web estáticos no son vulnerables a este tipo de ataque.
4+
5+
Los atacantes usualmente inyectan código malicioso como Javascript, VBScript, ActiveX o Flash en sitios que tienen vulnerabilidades. Una vez que han conseguido enyectar sus scripts, la información del usuario puede ser robada y tu sitio web puede ser inundado de Spam. Los atacantes también pueden cambiar las configuraciones de los usuarios como sea que ellos quieran.
6+
7+
Si deseas prevenir este tipo de ataques, deberías combinar las siguientes dos aproximaciones
8+
9+
- Validar toda la información de los usuarios, que discutimos en la sección pasada.
10+
- Manejar cuidadosamente la información enviada por los clientes en orden de prevenir cualquier inyección de scripts hecha desde los navegadores.
11+
12+
Entonces ¿Cómo podemos haces estas dos cosas en Go? Afortunadamente, el paquete `http/template` tiene algunas funciones que nos permiten escapar los datos como sigue:
13+
14+
- `func HTMLEscape(w io.Writer, b []byte)` escapa b to w.
15+
- `func HTMLEscapeString(s string) string` retorna una cadena después de escaparla de s
16+
- `func HTMLEscaper(args ...interface{}) string` retorna una cadena después de escaparla de algunos argumentos.
17+
18+
Vamos a cambiar el ejemplo de la sección 4.1:
19+
```
20+
fmt.Println("username:", template.HTMLEscapeString(r.Form.Get("username"))) // Imprime en el lado del servidor
21+
fmt.Println("password:", template.HTMLEscapeString(r.Form.Get("password")))
22+
template.HTMLEscape(w, []byte(r.Form.Get("username"))) // Respuesta a los clientes
23+
```
24+
Si alguien trata de ingresar el nombre de usuario como `<script>alert()</script>` podremos ver el siguiente contenido en el navegador.
25+
26+
![](images/4.3.escape.png?raw=true)
27+
28+
Figure 4.3 JavaScript después del escape
29+
30+
Las funciones en el paquete `html/template` nos ayudan a escapar todas las etiquetas HTML. Y ¿qué pasa si queremos imprimir `<script>alert()</script>` en los navegarores? Deberías usar el paquete `text/template`.
31+
```
32+
import "text/template"
33+
...
34+
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
35+
err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
36+
```
37+
Salida:
38+
```
39+
Hello, <script>alert('you have been pwned')</script>!
40+
```
41+
O puedes usar el tipo `template.HTML`:
42+
El contenido variable no va a ser escapado si es del tipo `template.HTML`
43+
Variable content will not be escaped if its type is `template.HTML`.
44+
```
45+
import "html/template"
46+
...
47+
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
48+
err = t.ExecuteTemplate(out, "T", template.HTML("<script>alert('you have been pwned')</script>"))
49+
```
50+
Salida:
51+
```
52+
Hello, <script>alert('you have been pwned')</script>!
53+
```
54+
Un ejemplo mas de escape:
55+
```
56+
import "html/template"
57+
...
58+
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
59+
err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
60+
```
61+
Salida:
62+
```
63+
Hello, &lt;script&gt;alert(&#39;you have been pwned&#39;)&lt;/script&gt;!
64+
```
65+
## Enlaces
66+
67+
- [Índice](preface.md)
68+
- Sección anterior: [Verificando las entradas](04.2.md)
69+
- Siguiente sección: [Envíos duplicados](04.4.md)

‎es/04.4.md

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# 4.4 Envíos duplicados
2+
3+
No se si alguna vez has visto blogs que tienen uno o mas de un post que son exactamente iguales, pero puedo decirte que es porque un usuario envió dos veces el mismo formulario. Existen muchas cosas que pueden cambiar envíos duplicados; algunas veces los usuarios hacen doble click en el botón de enviar, o quieren modificar el contenido después de postear y usan el botón de atrás. En algunos casos es por una acción intencioanl de usuarios malicioso. Es facil ver como los envíos duplicados pueden generar muchos problemas. Afortunadamente tenemos herramientas para prevenirlos.
4+
5+
La solución es añadir un campo oculto con un único token al formulario, y siempre verificar si el token ha sido procesado con anterioridad. También si quieres usar ajax para enviar un formulario, puedes usar javascript para deshabilitar el botón una vez se ha presionado.
6+
7+
Vamos a mejorar el ejemplo de la sección 4.2:
8+
```
9+
<input type="checkbox" name="interest" value="football">Futbol
10+
<input type="checkbox" name="interest" value="basketball">Basquetbol
11+
<input type="checkbox" name="interest" value="tennis">Tenis
12+
Nombre de usuario:<input type="text" name="username">
13+
Contraseña:<input type="password" name="password">
14+
<input type="hidden" name="token" value="{{.}}">
15+
<input type="submit" value="Ingresar">
16+
```
17+
Nosotros usamos un hash MD5 con la hora actual para generar el token, y agregamos esto a un campo oculto del lado del cliente y a una cookie en el lado del servidor (Capítulo 6). Nosotros usamos este token para verificar si el formulario ha sido enviado.
18+
```
19+
func login(w http.ResponseWriter, r *http.Request) {
20+
fmt.Println("method:", r.Method) // get request method
21+
if r.Method == "GET" {
22+
crutime := time.Now().Unix()
23+
h := md5.New()
24+
io.WriteString(h, strconv.FormatInt(crutime, 10))
25+
token := fmt.Sprintf("%x", h.Sum(nil))
26+
27+
t, _ := template.ParseFiles("login.gtpl")
28+
t.Execute(w, token)
29+
} else {
30+
// log in request
31+
r.ParseForm()
32+
token := r.Form.Get("token")
33+
if token != "" {
34+
// check token validity
35+
} else {
36+
// give error if no token
37+
}
38+
fmt.Println("username length:", len(r.Form["username"][0]))
39+
fmt.Println("username:", template.HTMLEscapeString(r.Form.Get("username"))) // print in server side
40+
fmt.Println("password:", template.HTMLEscapeString(r.Form.Get("password")))
41+
template.HTMLEscape(w, []byte(r.Form.Get("username"))) // respond to client
42+
}
43+
}
44+
```
45+
![](images/4.4.token.png?raw=true)
46+
47+
Figure 4.4 El contenido del navegador después de agregar el Token
48+
49+
Puedes refrescar la página y verás un token diferente cada vez. Esto asegura que cada formulario es único.
50+
51+
Por ahora, puedes prevenir ataques de envíos duplicados añadiendo tokens a tus formularios, pero no puedes prevenir todos los ataques de este tipo, aún hay mucho trabajo por hacer.
52+
53+
## Enlaces
54+
55+
- [Índice](preface.md)
56+
- Sección anterior: [Cross site scripting](04.3.md)
57+
- Siguiente sección: [Subida de archivos](04.5.md)

‎es/04.5.md

+168
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# 4.5 Subida de archivos
2+
3+
Supón que tienes un sitio web como Instagram y quieres que tus usuarios suban sus fotos. ¿Cómo implementarías esa funcionalidad?
4+
5+
Tienes que agregar la propuedad `enctype` al formulario que vas a usar para subir fotos. Aquí están los tres posibles valores para esta propiedad
6+
You have to add property `enctype` to the form that you want to use for uploading photos. There are three possible values for this property:
7+
8+
```
9+
application/x-www-form-urlencoded. Transforma todos los caracteres antes de subirlos (Por defecto).
10+
multipart/form-data Sin transformación. Debes usar esta valor cuando vas a subir fotos.
11+
text/plain Convierte los espacios en "+" sin transformar los caracteres.
12+
```
13+
14+
Entonces el contenido HTML para subir un archivo debería verse como esto:
15+
16+
```
17+
<html>
18+
<head>
19+
<title>Upload file</title>
20+
</head>
21+
<body>
22+
<form enctype="multipart/form-data" action="http://127.0.0.1:9090/upload" method="post">
23+
<input type="file" name="uploadfile" />
24+
<input type="hidden" name="token" value="{{.}}"/>
25+
<input type="submit" value="Subit" />
26+
</form>
27+
</body>
28+
</html>
29+
```
30+
31+
32+
Necesitamos añadir la funcionalidad al servidor para manejar este formulario.
33+
34+
```
35+
http.HandleFunc("/upload", upload)
36+
37+
// lógica de subida
38+
func upload(w http.ResponseWriter, r *http.Request) {
39+
fmt.Println("method:", r.Method)
40+
if r.Method == "GET" {
41+
crutime := time.Now().Unix()
42+
h := md5.New()
43+
io.WriteString(h, strconv.FormatInt(crutime, 10))
44+
token := fmt.Sprintf("%x", h.Sum(nil))
45+
46+
t, _ := template.ParseFiles("upload.gtpl")
47+
t.Execute(w, token)
48+
} else {
49+
r.ParseMultipartForm(32 << 20)
50+
file, handler, err := r.FormFile("uploadfile")
51+
if err != nil {
52+
fmt.Println(err)
53+
return
54+
}
55+
defer file.Close()
56+
fmt.Fprintf(w, "%v", handler.Header)
57+
f, err := os.OpenFile("./test/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
58+
if err != nil {
59+
fmt.Println(err)
60+
return
61+
}
62+
defer f.Close()
63+
io.Copy(f, file)
64+
}
65+
}
66+
```
67+
68+
69+
Como puedes ver, llamamo a `r.ParseMultipartForm` para subir archivos. La función ParseMultipartForm toma el argumento `maxMemory`. Después de llamar `ParseMultipartForm`, el archivo puede ser guardado en el servidor con tamaño `maxMemory`. Si el tamaño del archivo es mayor que `maxMemory`, el resto del archivo será guardado en un archivo temporal del sistema. Puedes usar `r.FormFile` para obtener el archivo y usar `io.Copy` para guardarlo en tu sistema.
70+
71+
Puede que no necesites llamar a `r.ParseForm` cuando acceses a otros campos, porque Go llamará a este método si es necesario. También llamar a `ParseMultipartForm` una vez es suficiente, múltiples llamadas no hacen diferencia.
72+
73+
Usamos tres pasos para subir archivos:
74+
75+
1. Agregar `enctype="multipart/form-data"` a tu formulario
76+
2. Llamar `r.ParseMultipartForm` en el lado del servidor para guardar el archivo en memoria o en un archivo temporal.
77+
3. Llamar `r.FormFile` para obtener el archivo y guardarlo en el sistema de ficheros.
78+
79+
El manejador del archivo es `multipart.FileHeader`. Este usa la siguiente estructura:
80+
81+
```
82+
type FileHeader struct {
83+
Filename string
84+
Header textproto.MIMEHeader
85+
// contains filtered or unexported fields
86+
}
87+
```
88+
89+
![](images/4.5.upload2.png?raw=true)
90+
91+
Figure 4.5 Imprimir la información del servidor después de recibir el archivo.
92+
93+
## Cliente para subir archivos
94+
95+
Les mostré en el ejemplo anterior como usar un formulario para subir un archivo. Nosotros podemos usar un cliente en Go para emular la subida de archivos.
96+
97+
```
98+
package main
99+
100+
import (
101+
"bytes"
102+
"fmt"
103+
"io"
104+
"io/ioutil"
105+
"mime/multipart"
106+
"net/http"
107+
"os"
108+
)
109+
110+
func postFile(filename string, targetUrl string) error {
111+
bodyBuf := &bytes.Buffer{}
112+
bodyWriter := multipart.NewWriter(bodyBuf)
113+
114+
// este paso es muy importante
115+
fileWriter, err := bodyWriter.CreateFormFile("uploadfile", filename)
116+
if err != nil {
117+
fmt.Println("error writing to buffer")
118+
return err
119+
}
120+
121+
// Abrir el archivo para manejarlo
122+
fh, err := os.Open(filename)
123+
if err != nil {
124+
fmt.Println("error opening file")
125+
return err
126+
}
127+
128+
//iocopy
129+
_, err = io.Copy(fileWriter, fh)
130+
if err != nil {
131+
return err
132+
}
133+
134+
contentType := bodyWriter.FormDataContentType()
135+
bodyWriter.Close()
136+
137+
resp, err := http.Post(targetUrl, contentType, bodyBuf)
138+
if err != nil {
139+
return err
140+
}
141+
defer resp.Body.Close()
142+
resp_body, err := ioutil.ReadAll(resp.Body)
143+
if err != nil {
144+
return err
145+
}
146+
fmt.Println(resp.Status)
147+
fmt.Println(string(resp_body))
148+
return nil
149+
}
150+
151+
// Ejemplo de uso
152+
func main() {
153+
target_url := "http://localhost:9090/upload"
154+
filename := "./astaxie.pdf"
155+
postFile(filename, target_url)
156+
}
157+
```
158+
159+
El código de arriba muestra como usar un cliente para subir archivos. El usa `multipart.Write` para escribir archivos en un cache y enviarlos al servidor a través de un método POST.
160+
161+
Si tienes otros campos que necesitas escribir en el cuerpo del mensaje, como un nombre d eusuario, llama a `multipart.WriteField` cada que lo necesites.
162+
163+
164+
## Enlaces
165+
166+
- [Índice](preface.md)
167+
- Sección anterior: [Envíos duplicados](04.4.md)
168+
- Siguiente sección: [Resumen](04.6.md)

‎es/04.6.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# 4.6 Resumen
2+
3+
En este capítulo mayormente hemos aprendido como procesar datos de formularios en Go a través de varios ejemplos, como autenticar un usuario y subir archivos. También hemos enfatizado que validar la información es extremadamente importante para la seguridad del sitio web, y también hemos usado una sección para filtrar datos a través de expresiones regulares.
4+
5+
Espero que ahora sepas mas sobre el proceso de comunicación entre cliente y servidor.
6+
7+
## Enlaces
8+
9+
- [Índice](preface.md)
10+
- Sección anterior: [Subida de archivos](04.5.md)
11+
- Siguiente sección: [Base de datos](05.0.md)

0 commit comments

Comments
 (0)
Please sign in to comment.