complete chapter 4 translation to spanish
This commit is contained in:
20
es/03.4.md
20
es/03.4.md
@@ -176,21 +176,21 @@ En escenarios mas complejos también podemos utilizar este método, la ventaja e
|
||||
|
||||
Vamos a echar un vistazo a la lista de flujo de ejecución en conjunto.
|
||||
|
||||
- Se llama http.HandleFunc
|
||||
- Se llama `http.HandleFunc`
|
||||
1. Se Llama HandleFunc de DefaultServeMux
|
||||
2. Se Llama Handle de DefaultServeMux
|
||||
3. Se agregan las reglas del enrutamiento a map[string]muxEntry de DefaultServeMux
|
||||
- Se llama http.ListenAndServe (":9090" , nil )
|
||||
- Se llama `http.ListenAndServe(":9090" , nil)`
|
||||
1. Se instancia el servidor
|
||||
2. Llama ListenAndServe del Servidor
|
||||
3. Llama net.Listen ( " tcp" , addr ) para escuchar en el puerto .
|
||||
2. Se Llama ListenAndServe del Servidor
|
||||
3. Se Llama net.Listen ( "tcp" , addr ) para escuchar en el puerto.
|
||||
4. Iniciar un bucle, y aceptar las solicitudes en el cuerpo del bucle.
|
||||
5. Instanciada una Conn se empieza una goroutine para cada solicitud : ir c.serve ().
|
||||
6. Lee petición de datos : w , err : = c.readRequest ().
|
||||
7. Comprueba si el controlador está vacío, si está vacíoutiliza DefaultServeMux .
|
||||
8. Llama al controlador de ServeHTTP
|
||||
9. Ejecutar código en DefaultServeMux en este caso.
|
||||
10. Elije el controlador URL y ejecutar código del controlador en esta seccion: mux.handler.ServeHTTP ( w , r)
|
||||
5. Instanciar una Conn y se empieza una goroutine para cada solicitud : `go c.serve ()`
|
||||
6. Se Lee petición de datos : `w , err : = c.readRequest ()`
|
||||
7. Se Comprueba si el controlador está vacío, si está vacíoutiliza DefaultServeMux .
|
||||
8. Se Llama al controlador de ServeHTTP
|
||||
9. Se Ejecuta el código en DefaultServeMux en este caso.
|
||||
10. Elije el controlador URL y ejecutar código del controlador en esta seccion: `mux.handler.ServeHTTP (w , r)`
|
||||
11. Cómo elegir handler:
|
||||
A. Normas de router de verificación para esta URL.
|
||||
B. Llamar ServeHTTP en ese controlador, si es que existe.
|
||||
|
||||
141
es/04.2.md
Normal file
141
es/04.2.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# 4.2 Verificando las entradas
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## Campos requeridos
|
||||
|
||||
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.
|
||||
```
|
||||
if len(r.Form["username"][0])==0{
|
||||
// code for empty field
|
||||
}
|
||||
```
|
||||
`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.
|
||||
|
||||
## Números
|
||||
|
||||
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:
|
||||
```
|
||||
getint,err:=strconv.Atoi(r.Form.Get("age"))
|
||||
if err!=nil{
|
||||
// error occurs when convert to number, it may not a number
|
||||
}
|
||||
|
||||
// check range of number
|
||||
if getint >100 {
|
||||
// too big
|
||||
}
|
||||
```
|
||||
Otra manera de realizar esto es usando expresiones regulares:
|
||||
```
|
||||
if m, _ := regexp.MatchString("^[0-9]+$", r.Form.Get("age")); !m {
|
||||
return false
|
||||
}
|
||||
```
|
||||
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.
|
||||
|
||||
## Chino
|
||||
|
||||
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:
|
||||
```
|
||||
if m, _ := regexp.MatchString("^[\\x{4e00}-\\x{9fa5}]+$", r.Form.Get("realname")); !m {
|
||||
return false
|
||||
}
|
||||
```
|
||||
## Letras del Inglés
|
||||
|
||||
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.
|
||||
```
|
||||
if m, _ := regexp.MatchString("^[a-zA-Z]+$", r.Form.Get("engname")); !m {
|
||||
return false
|
||||
}
|
||||
```
|
||||
## Correo electrónico
|
||||
|
||||
Si queremos si el usuario ha ingresado una dirección de correo electrónico válida, puedes usar la siguiente expresión regular:
|
||||
```
|
||||
if m, _ := regexp.MatchString(`^([\w\.\_]{2,10})@(\w{1,}).([a-z]{2,4})$`, r.Form.Get("email")); !m {
|
||||
fmt.Println("no")
|
||||
}else{
|
||||
fmt.Println("yes")
|
||||
}
|
||||
```
|
||||
## Lista desplegable
|
||||
|
||||
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?
|
||||
|
||||
Suponga que tenemos el siguiente `<select>`
|
||||
```
|
||||
<select name="fruit">
|
||||
<option value="apple">apple</option>
|
||||
<option value="pear">pear</option>
|
||||
<option value="banana">banana</option>
|
||||
</select>
|
||||
```
|
||||
Podemos usar la siguiente estrategia para limpiar la entrada:
|
||||
```
|
||||
slice:=[]string{"apple","pear","banana"}
|
||||
|
||||
for _, v := range slice {
|
||||
if v == r.Form.Get("fruit") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
```
|
||||
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)
|
||||
|
||||
## Botones circulares
|
||||
|
||||
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.
|
||||
```
|
||||
<input type="radio" name="gender" value="1">Femenino
|
||||
<input type="radio" name="gender" value="2">Masculino
|
||||
```
|
||||
Y usualmente utilizamos el siguiente código para validar las entradas:
|
||||
```
|
||||
slice:=[]int{1,2}
|
||||
|
||||
for _, v := range slice {
|
||||
if v == r.Form.Get("gender") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
```
|
||||
## Cajas de chequeo
|
||||
|
||||
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:
|
||||
```
|
||||
<input type="checkbox" name="interest" value="football">Football
|
||||
<input type="checkbox" name="interest" value="basketball">Basketball
|
||||
<input type="checkbox" name="interest" value="tennis">Tennis
|
||||
```
|
||||
En este caso la estrategia de limpieza es un poco diferente a la validación de botones de chequeo y listas desplegables:
|
||||
```
|
||||
slice:=[]string{"football","basketball","tennis"}
|
||||
a:=Slice_diff(r.Form["interest"],slice)
|
||||
if a == nil{
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
```
|
||||
## Fecha y Hora
|
||||
|
||||
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.
|
||||
```
|
||||
t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
|
||||
fmt.Printf("Go launched at %s\n", t.Local())
|
||||
```
|
||||
Después de eso, también puedes utilizar el paquete `time` para mas operaciones, dependiende de tus necesidades.
|
||||
|
||||
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.
|
||||
|
||||
## Enlaces
|
||||
|
||||
- [Índice](preface.md)
|
||||
- Sección anterior: [Procesando la entrada de los formularios](04.1.md)
|
||||
- Siguiente sección: [Cross site scripting](04.3.md)
|
||||
69
es/04.3.md
Normal file
69
es/04.3.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# 4.3 Cross site scripting
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
Si deseas prevenir este tipo de ataques, deberías combinar las siguientes dos aproximaciones
|
||||
|
||||
- Validar toda la información de los usuarios, que discutimos en la sección pasada.
|
||||
- Manejar cuidadosamente la información enviada por los clientes en orden de prevenir cualquier inyección de scripts hecha desde los navegadores.
|
||||
|
||||
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:
|
||||
|
||||
- `func HTMLEscape(w io.Writer, b []byte)` escapa b to w.
|
||||
- `func HTMLEscapeString(s string) string` retorna una cadena después de escaparla de s
|
||||
- `func HTMLEscaper(args ...interface{}) string` retorna una cadena después de escaparla de algunos argumentos.
|
||||
|
||||
Vamos a cambiar el ejemplo de la sección 4.1:
|
||||
```
|
||||
fmt.Println("username:", template.HTMLEscapeString(r.Form.Get("username"))) // Imprime en el lado del servidor
|
||||
fmt.Println("password:", template.HTMLEscapeString(r.Form.Get("password")))
|
||||
template.HTMLEscape(w, []byte(r.Form.Get("username"))) // Respuesta a los clientes
|
||||
```
|
||||
Si alguien trata de ingresar el nombre de usuario como `<script>alert()</script>` podremos ver el siguiente contenido en el navegador.
|
||||
|
||||

|
||||
|
||||
Figure 4.3 JavaScript después del escape
|
||||
|
||||
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`.
|
||||
```
|
||||
import "text/template"
|
||||
...
|
||||
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
|
||||
err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
|
||||
```
|
||||
Salida:
|
||||
```
|
||||
Hello, <script>alert('you have been pwned')</script>!
|
||||
```
|
||||
O puedes usar el tipo `template.HTML`:
|
||||
El contenido variable no va a ser escapado si es del tipo `template.HTML`
|
||||
Variable content will not be escaped if its type is `template.HTML`.
|
||||
```
|
||||
import "html/template"
|
||||
...
|
||||
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
|
||||
err = t.ExecuteTemplate(out, "T", template.HTML("<script>alert('you have been pwned')</script>"))
|
||||
```
|
||||
Salida:
|
||||
```
|
||||
Hello, <script>alert('you have been pwned')</script>!
|
||||
```
|
||||
Un ejemplo mas de escape:
|
||||
```
|
||||
import "html/template"
|
||||
...
|
||||
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
|
||||
err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
|
||||
```
|
||||
Salida:
|
||||
```
|
||||
Hello, <script>alert('you have been pwned')</script>!
|
||||
```
|
||||
## Enlaces
|
||||
|
||||
- [Índice](preface.md)
|
||||
- Sección anterior: [Verificando las entradas](04.2.md)
|
||||
- Siguiente sección: [Envíos duplicados](04.4.md)
|
||||
57
es/04.4.md
Normal file
57
es/04.4.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# 4.4 Envíos duplicados
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
Vamos a mejorar el ejemplo de la sección 4.2:
|
||||
```
|
||||
<input type="checkbox" name="interest" value="football">Futbol
|
||||
<input type="checkbox" name="interest" value="basketball">Basquetbol
|
||||
<input type="checkbox" name="interest" value="tennis">Tenis
|
||||
Nombre de usuario:<input type="text" name="username">
|
||||
Contraseña:<input type="password" name="password">
|
||||
<input type="hidden" name="token" value="{{.}}">
|
||||
<input type="submit" value="Ingresar">
|
||||
```
|
||||
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.
|
||||
```
|
||||
func login(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println("method:", r.Method) // get request method
|
||||
if r.Method == "GET" {
|
||||
crutime := time.Now().Unix()
|
||||
h := md5.New()
|
||||
io.WriteString(h, strconv.FormatInt(crutime, 10))
|
||||
token := fmt.Sprintf("%x", h.Sum(nil))
|
||||
|
||||
t, _ := template.ParseFiles("login.gtpl")
|
||||
t.Execute(w, token)
|
||||
} else {
|
||||
// log in request
|
||||
r.ParseForm()
|
||||
token := r.Form.Get("token")
|
||||
if token != "" {
|
||||
// check token validity
|
||||
} else {
|
||||
// give error if no token
|
||||
}
|
||||
fmt.Println("username length:", len(r.Form["username"][0]))
|
||||
fmt.Println("username:", template.HTMLEscapeString(r.Form.Get("username"))) // print in server side
|
||||
fmt.Println("password:", template.HTMLEscapeString(r.Form.Get("password")))
|
||||
template.HTMLEscape(w, []byte(r.Form.Get("username"))) // respond to client
|
||||
}
|
||||
}
|
||||
```
|
||||

|
||||
|
||||
Figure 4.4 El contenido del navegador después de agregar el Token
|
||||
|
||||
Puedes refrescar la página y verás un token diferente cada vez. Esto asegura que cada formulario es único.
|
||||
|
||||
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.
|
||||
|
||||
## Enlaces
|
||||
|
||||
- [Índice](preface.md)
|
||||
- Sección anterior: [Cross site scripting](04.3.md)
|
||||
- Siguiente sección: [Subida de archivos](04.5.md)
|
||||
168
es/04.5.md
Normal file
168
es/04.5.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# 4.5 Subida de archivos
|
||||
|
||||
Supón que tienes un sitio web como Instagram y quieres que tus usuarios suban sus fotos. ¿Cómo implementarías esa funcionalidad?
|
||||
|
||||
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
|
||||
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:
|
||||
|
||||
```
|
||||
application/x-www-form-urlencoded. Transforma todos los caracteres antes de subirlos (Por defecto).
|
||||
multipart/form-data Sin transformación. Debes usar esta valor cuando vas a subir fotos.
|
||||
text/plain Convierte los espacios en "+" sin transformar los caracteres.
|
||||
```
|
||||
|
||||
Entonces el contenido HTML para subir un archivo debería verse como esto:
|
||||
|
||||
```
|
||||
<html>
|
||||
<head>
|
||||
<title>Upload file</title>
|
||||
</head>
|
||||
<body>
|
||||
<form enctype="multipart/form-data" action="http://127.0.0.1:9090/upload" method="post">
|
||||
<input type="file" name="uploadfile" />
|
||||
<input type="hidden" name="token" value="{{.}}"/>
|
||||
<input type="submit" value="Subit" />
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
Necesitamos añadir la funcionalidad al servidor para manejar este formulario.
|
||||
|
||||
```
|
||||
http.HandleFunc("/upload", upload)
|
||||
|
||||
// lógica de subida
|
||||
func upload(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println("method:", r.Method)
|
||||
if r.Method == "GET" {
|
||||
crutime := time.Now().Unix()
|
||||
h := md5.New()
|
||||
io.WriteString(h, strconv.FormatInt(crutime, 10))
|
||||
token := fmt.Sprintf("%x", h.Sum(nil))
|
||||
|
||||
t, _ := template.ParseFiles("upload.gtpl")
|
||||
t.Execute(w, token)
|
||||
} else {
|
||||
r.ParseMultipartForm(32 << 20)
|
||||
file, handler, err := r.FormFile("uploadfile")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
fmt.Fprintf(w, "%v", handler.Header)
|
||||
f, err := os.OpenFile("./test/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
io.Copy(f, file)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
Usamos tres pasos para subir archivos:
|
||||
|
||||
1. Agregar `enctype="multipart/form-data"` a tu formulario
|
||||
2. Llamar `r.ParseMultipartForm` en el lado del servidor para guardar el archivo en memoria o en un archivo temporal.
|
||||
3. Llamar `r.FormFile` para obtener el archivo y guardarlo en el sistema de ficheros.
|
||||
|
||||
El manejador del archivo es `multipart.FileHeader`. Este usa la siguiente estructura:
|
||||
|
||||
```
|
||||
type FileHeader struct {
|
||||
Filename string
|
||||
Header textproto.MIMEHeader
|
||||
// contains filtered or unexported fields
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
Figure 4.5 Imprimir la información del servidor después de recibir el archivo.
|
||||
|
||||
## Cliente para subir archivos
|
||||
|
||||
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.
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
func postFile(filename string, targetUrl string) error {
|
||||
bodyBuf := &bytes.Buffer{}
|
||||
bodyWriter := multipart.NewWriter(bodyBuf)
|
||||
|
||||
// este paso es muy importante
|
||||
fileWriter, err := bodyWriter.CreateFormFile("uploadfile", filename)
|
||||
if err != nil {
|
||||
fmt.Println("error writing to buffer")
|
||||
return err
|
||||
}
|
||||
|
||||
// Abrir el archivo para manejarlo
|
||||
fh, err := os.Open(filename)
|
||||
if err != nil {
|
||||
fmt.Println("error opening file")
|
||||
return err
|
||||
}
|
||||
|
||||
//iocopy
|
||||
_, err = io.Copy(fileWriter, fh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
contentType := bodyWriter.FormDataContentType()
|
||||
bodyWriter.Close()
|
||||
|
||||
resp, err := http.Post(targetUrl, contentType, bodyBuf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
resp_body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(resp.Status)
|
||||
fmt.Println(string(resp_body))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ejemplo de uso
|
||||
func main() {
|
||||
target_url := "http://localhost:9090/upload"
|
||||
filename := "./astaxie.pdf"
|
||||
postFile(filename, target_url)
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
## Enlaces
|
||||
|
||||
- [Índice](preface.md)
|
||||
- Sección anterior: [Envíos duplicados](04.4.md)
|
||||
- Siguiente sección: [Resumen](04.6.md)
|
||||
11
es/04.6.md
Normal file
11
es/04.6.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# 4.6 Resumen
|
||||
|
||||
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.
|
||||
|
||||
Espero que ahora sepas mas sobre el proceso de comunicación entre cliente y servidor.
|
||||
|
||||
## Enlaces
|
||||
|
||||
- [Índice](preface.md)
|
||||
- Sección anterior: [Subida de archivos](04.5.md)
|
||||
- Siguiente sección: [Base de datos](05.0.md)
|
||||
Reference in New Issue
Block a user