Académique Documents
Professionnel Documents
Culture Documents
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
ElLibroparaPrincipiantes
enNode.js
UntutorialdeNode.jspor:ManuelKiessling&HermanA.Junge
SobreelTutorial
El objetivo de este documento es ayudarte a empezar con el desarrollo de
aplicaciones para Node.js, ensendote todo lo que necesites saber acerca de
JavaScript"avanzado"sobrelamarcha.Estetutorialvamuchomsalldeltpico
manual"HolaMundo".
Status
Estsleyendolaversinfinaldeestelibro,esdecir,lasactualizacionessolosern
hechas para corregir errores o para reflejar cambiar en nuevas versiones de
Node.js.
Lasmuestrasdecdigodeestelibroestnprobadasparafuncionarconlaversin
0.6.11deNode.js.
AudienciaObjetivo
Estedocumentoprobablementesermejorentendidoporloslectoresquetengan
un trasfondo similar al mo: Programadores experimentados en al menos un
lenguaje orientado al objeto, como Ruby, Python, PHP o Java poca experiencia
conJavaScript,yningunaexperienciaenNode.js.
Elqueestedocumentoestorientadoadesarrolladoresqueyatienenexperiencia
con otros lenguajes de programacin significa que no vamos a cubrir temas
realmente bsicos como tipos de datos, variables, estructuras de control y
similares.Debessaberacercadeestostpicosparaentenderestedocumento.
Sinembargo,dadoquelasfuncionesyobjetosenJavaScriptsondiferentesdesus
contrapartes en la mayora de los lenguajes, estos sern explicados con ms
detalle.
Estructuradeestedocumento
http://www.nodebeginner.org/indexes.html
1/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
AlTrminodeestedocumento,habrscreadounaaplicacinWebcompleta,que
permitaalosusuariosdestaelverpginaswebysubirarchivos.
La cual, por supuesto, no va ser nada como la "aplicacin que va a cambiar el
mundo", no obstante eso, nosotros haremos la milla extra y no vamos slo a
codificarunaaplicacinlo"suficientementesimple"parahacerestoscasosdeuso
posible,sinoquecrearemosunframeworksencillo,perocompleto,afindepoder
separarlosdistintosaspectosdenuestraaplicacin.Versloqueestosignificaen
pocotiempo.
EmpezaremospormirarcmoeldesarrolloenJavaScriptenNode.jsesdiferente
deldesarrolloenJavaScriptenunbrowser.
Luego,nosmantendremosconlaviejatradicindeescribirunaaplicacin"Hola
Mundo",lacualeslaaplicacinmsbsicadeNode.jsque"hace"algo.
Enseguida, discutiremos que tipo de "aplicacin del mundo real" queremos
construir, disectaremos las diferentes partes que necesitan ser implementadas
paraensamblarestaaplicacin,yempezaremostrabajando en cada una de estas
partespasoapaso.
Talycualloprometido,aprenderemossobrelamarchaacercadealgunosdelos
muchos conceptos avanzados de JavaScript, como hacer uso de ellos, y ver el
porqu tiene sentido el hacer uso de estos conceptos en vez de los que ya
conocemosporotroslenguajesdeprogramacin.
TabladeContenidos
JavaScriptyNode.js
JavaScriptyT
Antesquehablemosdetodalapartetcnica,tommonosunminutoyhablemos
acerca de ti y tu relacin con JavaScript. Este captulo est aqu para permitirte
estimarsitienesentidoelquesigasonoleyendoestedocumento.
Si eres como yo, empezaste con el "desarrollo" HTML hace bastante tiempo,
escribiendo documentos HTML. Te encontraste en el camino con esta cosa
simptica llamada JavaScript, pero solo la usabas en una forma muy bsica,
agregandointeractividadatuspginasdecuandoencuando.
http://www.nodebeginner.org/indexes.html
2/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
Lo que realmente quisiste era "la cosa real", queras saber cmo construir sitios
webcomplejosAprendisteunlenguajedeprogramacincomoPHP,Ruby,Java,
yempezasteaescribircdigo"backend".
No obstante, mantuviste un ojo en JavaScript, y te diste cuenta que con la
introduccin de jQuery, Prototype y otros, las cosas se fueron poniendo ms
avanzadasenlasTierrasdeJavaScript,yqueestelenguajeerarealmentemsque
hacerunwindow.open().
Sinembargo,estoeratodocosadelfrontend,yaunqueeraagradablecontarcon
jQuery a tu disposicin en cualquier momento que te sintieras con nimo de
sazonarunapginaweb,al final del da, lo que eras a lo ms, era un usuario de
JavaScript,perono,undesarrolladordeJavaScript.
YentoncesllegNode.js.JavaScriptenelservidor,Quhayconeso?
DecidistequeerayatiempoderevisarelnuevoJavaScript.Peroespera:Escribir
aplicaciones Node.js es una cosa Entender el porqu ellas necesitan ser escritas
enlamaneraquelosonsignificaentenderJavaScript!Yestavezesenserio.
Yaquestelproblema:YaqueJavaScriptrealmentevivedos,otalveztresvidas
(ElpequeoayudanteDHTMLdemediadosdelos90's,lascosasmsseriastales
comojQueryy similares, y ahora, el lado del servidor), no es tan fcil encontrar
informacinqueteayudeaaprenderJavaScriptdela"maneracorrecta",deforma
depoderescribiraplicacionesdeNode.jsenunaaparienciaquetehagasentirque
nosloestsusandoJavaScript,sinoquetambinestndesarrollandoconl.
Porque ah est el asunto: Ya eres un desarrollador experimentado,ynoquieres
aprender una nueva tcnica simplemente metiendo cdigo aqu y all mal
aprovechndolo Quieres estar seguro que te ests enfocando en un ngulo
correcto.
Hay,porsupuesto,excelentedocumentacinafuera.Peroladocumentacinpors
solanoessuficiente.Loquesenecesitaesunagua.
Miobjetivoesproveerteestagua.
UnaAdvertencia
HayalgunaspersonasrealmenteexcelenteenJavaScript.Nosoyunadeellas.
Yosoyrealmenteeltipodelquetehehabladoenlosprrafosprevios.Sunpar
http://www.nodebeginner.org/indexes.html
3/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
JavaScriptdelLadodelServidor
LasprimerasencarnacionesdeJavaScriptvivanenlosbrowsers.Peroestoesslo
elcontexto.Defineloquepuedeshacerconellenguaje,peronodicemuchoacerca
deloqueellenguaje mismo puede hacer. JavaScript es un lenguaje "completo":
Lo puedes usar en muchos contextos y alcanzar con ste, todo lo que puedes
alcanzarconcualquierotrolenguaje"completo".
Node.jsrealmenteesslootrocontexto:tepermitecorrercdigoJavaScriptenel
backend,fueradelbrowser.
Para ejecutar el cdigo JavaScript que tu pretendes correr en el backend, este
necesita ser interpretado y, bueno, ejecutado, Esto es lo que Node.js realiza,
haciendousodelaMaquinaVirtualV8deGoogle,elmismoentornodeejecucin
paraJavaScriptqueGoogleChromeutiliza.
Adems,Node.jsvieneconmuchosmdulostiles,demaneraquenotienes que
escribirtododecero,comoporejemplo,algoquepongaunstringalaconsola.
Entonces,Node.jsesenrealidaddoscosas:unentornodeejecucinyunalibrera.
Para hacer uso de stas (la librera y el entorno), necesitas instalar Node.js. En
lugar de repetir el proceso aqu, te ruego visitar las instrucciones oficiales de
instalacin.PorfavorvuelveunavezquetengastuversindeNode.jscorriendo.
"HolaMundo"
Ok. Saltemos entonces al agua fra y escribamos nuestra primera aplicacin
Node.js:"HolaMundo".
Abre tu editor favorito y crea un archivo llamado holamundo.js. Nosotros
queremosescribir"HolaMundo"aSTDOUT,yaquestelcdigonecesariopara
http://www.nodebeginner.org/indexes.html
4/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
haceresto:
console.log("HolaMundo");
Grabaelarchivo,yejectaloatravsdeNode.js:
nodeholamundo.js
EstedeberaretornarHolaMundoentumonitor.
Ok,estoesaburrido,deacuerdo?Asqueescribamosalgunacosareal.
UnaAplicacinWebCompletaconNode.js
LoscasosdeUso
Mantengmoslosimple,perorealista:
ElUsuariodeberasercapazdeocuparnuestraaplicacinconunbrowser.
ElUsuariodeberaverunapginadebienvenidacuandosolicita
http://dominio/inicio,lacualdespliegaunformulariodesbida.
Eligiendounarchivodeimagenparasubiryenviandoelformulario,la
imagendeberasersubidaahttp://dominio/subir,dondeesdesplegadauna
vezquelasbidaestefinalizada.
Muy bien. Ahora, tu puedes ser capaz de alcanzar este objetivo googleando y
programandoloquesea,peroesonoesloquequeremoshaceraqu.
Msqueeso,noqueremosescribirsimplementeelcdigomsbsicoposiblepara
alcanzaresteobjetivo,noimportaloeleganteycorrectoquepuedaserestecdigo.
Nosotros agregaremos intencionalmente ms abstraccin de la necesaria de
maneradepodertenerunaideadeloqueesconstruiraplicacionesmscomplejas
deNode.js.
LaPiladeAplicaciones
Hagamos un desglose a nuestra aplicacin. Qu partes necesitan ser
implementadasparapodersatisfacernuestroscasosdeuso?
http://www.nodebeginner.org/indexes.html
5/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
Queremosservirpginasweb,demaneraquenecesitamosunServidor
HTTP.
Nuestroservidornecesitarresponderdirectamentepeticiones(requests),
dependiendodequURLseapedidaenesterequerimiento,esque
necesitaremosalgntipodeenrutador(router)demanerademapearlos
peticionesaloshandlers(manejadores)destos.
Parasatisfaceralospeticionesquellegaronalservidoryhansidoruteados
usandoelenrutador,necesitaremosdehechohandlers(manejadores)
depeticiones
ElEnrutadorprobablementedeberatratarcualquierinformacinPOSTque
llegueydrselaaloshandlersdepeticionesenunaformaconveniente,luego
necesitaremosmanipulacindedatadepeticin
NosotrosnosoloqueremosmanejarpeticionesdeURLs,sinoquetambin
queremosdesplegarcontenidocuandoestasURLsseanpedidas,loque
significaquenecesitamosalgntipodelgicaenlasvistasaserutilizada
porloshandlersdepeticiones,demaneradepoderenviarcontenidoal
browserdelUsuario.
Porltimo,peronomenosimportante,elUsuariosercapazdesubir
imgenes,asquenecesitaremosalgntipodemanipulacindesubidas
quienseharcargodelosdetalles.
ConstruyendolaPiladeAplicaciones
UnServidorHTTPBsico
CuandollegualpuntodondequeraempezarconmiprimeraaplicacinNode.js
http://www.nodebeginner.org/indexes.html
6/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
"real", me pregunt no solo como la iba a programar, sino que tambin, como
organizarmicdigo.
Necesitar tenerlo todo en un archivo? Muchos tutoriales en la Web que te
enseancmoescribirunservidorHTTPbsicoenNode.jstienentodalalgica
enunsololugar.Qupasasiyoquieroasegurarmequemicdigosemantenga
lebleamedidaquelevayaagregandomscosas?
Resulta,queesrelativamentefcildemantenerlosdistintosaspectosdetucdigo
separados,ponindolosenmdulos.
Esto te permite tener un archivo main limpio, en el cual ejecutas Node.js, y
mdulos limpios que pueden ser utilizados por el archivo main entre muchos
otros.
As que vamos a crear un archivo main el cual usaremos para iniciar nuestra
aplicacin, y un archivo de mdulo dnde residir el cdigo de nuestro servidor
HTTP.
Miimpresinesqueesmsomenosunestndarnombraratuarchivoprincipal
comoindex.js.Tienesentidotambinquepongamosnuestromdulodeservidor
enunarchivollamadoserver.js.
Empecemosconelmdulodelservidor.Creaelarchivoserver.jseneldirectorio
razdetuproyecto,yllnaloconelcdigosiguiente:
varhttp=require("http");
http.createServer(function(request,response){
response.writeHead(200,{"ContentType":"text/html"});
response.write("HolaMundo");
response.end();
}).listen(8888);
nodeserver.js
7/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
AnalizandonuestroservidorHTTP
Bueno,entonces,analicemosqueestpasandoaqu.
Laprimeralnearequire,requierealmdulohttpquevieneincluidoconNode.jsy
lohaceaccesibleatravsdelavariablehttp.
Luegollamamosaunadelasfuncionesqueelmdulohttpofrece:createServer.
Esta funcin retorna un objeto, y este objeto tiene un mtodo llamado listen
(escucha), y toma un valor numrico que indica el nmero de puerto en que
nuestroservidorHTTPvaaescuchar.
Porfavorignoraporunsegundoaladefinicindefuncinquesiguealallavede
aperturadehttp.createServer.
Nosotrospodramoshaberescritoelcdigoqueiniciaanuestroservidorylohace
escucharalpuerto8888delasiguientemanera:
varhttp=require("http");
varserver=http.createServer();
server.listen(8888);
PasandoFuncionesdeunLadoaOtro
Puedes,porejemplo,haceralgocomoesto:
functiondecir(palabra){
http://www.nodebeginner.org/indexes.html
8/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
console.log(palabra);
}
functionejecutar(algunaFuncion,valor){
algunaFuncion(valor);
}
ejecutar(decir,"Hola");
Leeestocuidadosamente!Loqueestamoshaciendoaques,nosotrospasamosla
funcindecir() como el primer parmetro de la funcin ejecutar. No el valor de
retornodedecir,sinoquedecir()misma!
Entonces, decir se convierte en la variable local algunaFuncion dentro de
ejecutar, y ejecutar puede llamar a la funcin en esta variable usando
algunaFuncion()(agregandollaves).
Por supuesto, dado que decir toma un parmetro, ejecutar puede pasar tal
parmetrocuandollamaaalgunaFuncion.
Nosotrospodemos,talcomolohicimos,pasarunafuncinpor su nombre como
parmetroaotrafuncin.Peronoestamosobligadosatenerquedefinirlafuncin
primero y luego pasarla. Podemos tambin definir y pasar la funcin como un
parmetroaotrafuncintodoalmismotiempo:
functionejecutar(algunaFuncion,valor){
algunaFuncion(valor);
}
ejecutar(function(palabra){console.log(palabra)},"Hola");
(N.delT.:functionesunapalabraclavedeJavaScript).
Nosotrosdefinimoslafuncinquequeremospasaraejecutarjustoahenellugar
dondeejecutaresperasuprimerparmetro.
De esta manera, no necesitamos darle a la funcin un nombre, por lo que esta
funcinesllamadafuncinannima.
EstaesunaprimeraojeadaaloquemegustallamarJavaScript"avanzado".Pero
tommoslo paso a paso. Por ahora, aceptemos que en JavaScript, nosotros
podemospasarunafuncincomounparmetrocuandollamamosaotrafuncin.
Podemos hacer esto asignando nuestra funcin a una variable, la cual luego
pasamos,odefiniendolafuncinapasarenelmismolugar.
http://www.nodebeginner.org/indexes.html
9/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
DeQumaneraelpasarfuncioneshaceque
nuestroservidorHTTPfuncione
Conesteconocimiento,VolvamosanuestroservidorHTTPminimalista:
varhttp=require("http");
http.createServer(function(request,response){
response.writeHead(200,{"ContentType":"text/html"});
response.write("HolaMundo");
response.end();
}).listen(8888);
A estas alturas, debera quedar claro lo que estamos haciendo ac: Estamos
pasndolealafuncincreateServerunafuncinannima.
Podemosllegaralomismorefactorizandonuestrocdigoas:
varhttp=require("http");
functiononRequest(request,response){
response.writeHead(200,{"ContentType":"text/html"});
response.write("HolaMundo");
response.end();
}
http.createServer(onRequest).listen(8888);
CallbacksManejadasporEventos
La respuesta a) No es algo fcil de explicar (al menos para m), y b) Yace en la
naturaleza misma de como Node.js trabaja: Est orientado al evento, esa es la
razndeporquestanrpido.
Podras tomarte un tiempo para leer este excelente post (en ingls) de Felix
Geisendrdfer:Understandingnode.jsparaalgunaexplicacindetrasfondo.
AlfinaltodosereducealhechoqueNode.jstrabajaorientadoalevento.Ah,ys,
yo tampoco s exactamente qu significa eso. Pero voy a hacer un intento de
explicar, el porqu esto tiene sentido para nosotros, que queremos escribir
aplicacioneswebenNode.js.
http://www.nodebeginner.org/indexes.html
10/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
Cuandonosotrosllamamosalmtodohttp.createServer,porsupuestoquenoslo
queremosqueelservidorsequedeescuchandoenalgnpuerto,sinoquetambin
queremoshaceralgocuandohayunapeticinHTTPaesteservidor.
El problema es, que esto sucede de manera asincrnica: Puede suceder en
cualquier momento, pero solo tenemos un nico proceso en el cual nuestro
servidorcorre.
Cuando escribimos aplicaciones PHP, esto no nos molesta en absoluto: cada vez
que hay una peticin HTTP, el servidor web (por lo general Apache) genera un
nuevo proceso solo para esta peticin, y empieza el script PHP indicado desde
cero,elcualesejecutadodeprincipioafin.
Asquerespectoalcontroldeflujo,estamosenelmediodenuestroprogramaen
Node.js, cuando una nueva peticin llega al puerto 8888: Cmo manipulamos
estosinvolvernoslocos?
Bueno,estaeslapartedondeeldiseoorientadoaleventodeNode.js/JavaScript
de verdad ayuda, aunque tengamos que aprender nuevos conceptos para poder
dominarlo. Veamos como estos conceptos son aplicados en nuestro cdigo de
servidor.
Nosotroscreamoselservidor,ypasamosunafuncinalmtodoquelocrea.Cada
vez que nuestro servidor recibe una peticin, la funcin que le pasamos ser
llamada.
Nosabemosquesloquevaasuceder,peroahoratenemosunlugardondevamos
apodermanipularlapeticinentrante.Eslafuncinquepasamos,sinimportarsi
ladefinimososilapasamosdemaneraannima.
Esteconceptoesllamadouncallback(N.delT.:delingls:call=llamaryback=
devuelta).Nosotrospasamosunafuncinaalgnmtodo,yelmtodoocupaesta
funcin para llamar (call) de vuelta (back) si un evento relacionado con este
mtodoocurre.
Almenosparam,estotomalgntiempoparaserentendido.Leeelarticulodel
blogdeFelixdenuevositodavanotesientesseguro.
Juguemosunpococonestenuevoconcepto.Podemosprobarquenuestrocdigo
continadespusdehabercreadoelservidor,inclusosinohasucedidoninguna
peticinHTTPylafuncincallbackquepasamosnohasidollamada?Probemos:
varhttp=require("http");
http://www.nodebeginner.org/indexes.html
11/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
functiononRequest(request,response){
console.log("PeticionRecibida.");
response.writeHead(200,{"ContentType":"text/html"});
response.write("HolaMundo");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("ServidorIniciado.");
Noten que utilizo console.log para entregar un texto cada vez que la funcin
onRequest(nuestrocallback)esgatillada,yotrotextodespusdeiniciarnuestro
servidorHTTP.
Cuando iniciamos esta aplicacin (con node server.js, como siempre). Esta
inmediatamenteescribirenpantalla"ServidorIniciado"enlalneadecomandos.
Cada vez que hagamos una peticin a nuestro servidor (abriendo
http://localhost:8888/ennuestrobrowser),elmensaje"PeticionRecibida."vaa
serimpresoenlalneadecomandos.
Esto es JavaScript del lado del servidor asincrnico y orientado al evento con
callbacksenaccin:)
(Toma en cuenta que nuestro servidor probablemente escribir "Peticin
Recibida."aSTDOUTdosvecesalabrirlapginaenunbrowser.Estoesporquela
mayora de los browsers van a tratar de cargar el favicon mediante la peticin
http://localhost:8888/favicon.icocadavezqueabrashttp://localhost:8888/).
ComonuestroServidormanipulalaspeticiones
OK, analicemos rpidamente el resto del cdigo de nuestro servidor, esto es, el
cuerpodenuestrafuncindecallbackonRequest().
Cuando la callback es disparada y nuestra funcin onRequest() es gatillada, dos
parmetrossonpasadosaella:requestyresponse.
Estos son objetos, y puedes usar sus mtodos para manejar los detalles de la
peticinHTTPocurridayresponderalapeticin(enotraspalabrasenviaralgode
vueltaalbrowserquehizolapeticinatuservidor).
Yesoesloquenuestrocdigohace:cadavezqueunapeticinesrecibida,usala
funcin response.writeHead() para enviar un estatus HTTP 200 y un content
type (parmetro que define que tipo de contenido es) en el encabezado de la
http://www.nodebeginner.org/indexes.html
12/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
respuestaHTTP,ylafuncinresponse.write()paraenviareltexto"HolaMundo"
enelcuerpodelarespuesta.
Porltimo,nosotrosllamamosresponse.end()parafinalizarnuestrarespuesta.
Hastaelmomento,nonoshemosinteresadoporlosdetallesdelapeticin,yese
eselporqunohemosocupadoelobjetorequestcompletamente.
Encontrandounlugarparanuestromdulode
servidor
OK,prometquevolveramosaalCmoorganizarnuestraaplicacin.Tenemosel
cdigodenuestroservidorHTTPmuybsicoenelarchivoserver.js,ymencion
que es comn tener un archivo principal llamado index.js, el cual es usado para
arrancar y partir nuestra aplicacin haciendo uso de los otros mdulos de la
aplicacin(comoelmdulodeservidorHTTPqueviveenserver.js).
Hablemosdecomopodemoshacerquenuestroserver.jsseaunverdaderomdulo
Node.jsyquepuedaserusadopornuestroprontoaserescritoarchivoprincipal
index.js.
Comohabrnnotado,yahemosusadomdulosennuestrocdigo,comoste:
varhttp=require("http");
...
http.createServer(...);
EnalgnlugardentrodeNode.jsviveunmdulollamado"http",ypodemoshacer
uso de ste en nuestro propio cdigo requirindolo y asignando el resultado del
requerimientoaunavariablelocal.
Esto transforma a nuestra variable local en un objeto que acarrea todos los
mtodospblicosqueelmdulohttpprovee.
Es prctica comn elegir el nombre del mdulo como nombre para nuestra
variablelocal,perosomoslibresdeescogercualquieraquenosguste:
varfoo=require("http");
...
http://www.nodebeginner.org/indexes.html
13/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
foo.createServer(...);
Bien. Ya tenemos claro como hacer uso de los mdulos internos de Node.js.
Cmohacemosparacrearnuestrospropiosmdulos,yCmolosutilizamos?
Descubrmoslotransformandonuestroscriptserver.jsenunmduloreal.
Sucedeque,notenemosquetransformarlotanto.Hacerquealgncdigoseaun
Mdulo, significa que necesitamos exportar las partes de su funcionalidad que
queremosproveeraotrosscriptsquerequierannuestromdulo.
Por ahora, la funcionalidad que nuestro servidor HTTP necesita exportar es
simple:Permitiralosscriptsqueutilicenestemduloarrancarelservidor.
Parahacerestoposible,dotaremosalcdigodenuestroservidordeunafuncin
llamadainicio,yexportaremosestafuncin:
varhttp=require("http");
functioniniciar(){
functiononRequest(request,response){
console.log("PeticinRecibida.");
response.writeHead(200,{"ContentType":"text/html"});
response.write("HolaMundo");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("ServidorIniciado.");
}
exports.iniciar=iniciar;
Deestemodo,Podemoscrearnuestropropioarchivoprincipalindex.js,yarrancar
nuestro servidor HTTP all, aunque el cdigo para el servidor este en nuestro
archivoserver.js.
Creaunarchivoindex.jsconelsiguientecontenido:
varserver=require("./server");
server.iniciar();
Como puedes ver, nosotros utilizamos nuestro mdulo de servidor tal como
cualquier otro mdulo interno: requiriendo el archivo donde est contenido y
http://www.nodebeginner.org/indexes.html
14/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
asignndolo a una variable, con las funciones que tenga 'exportadas' disponibles
paranosotros.
Eso es. Podemos ahora arrancar nuestra aplicacin por medio de nuestro script
principal,yvaahacerexactamentelomismo:
nodeindex.js
Bien,ahorapodemosponerlasdiferentespartesdenuestraaplicacinenarchivos
diferentesyenlazarlasjuntasatravsdelacreacindeestosmdulos.
Tenemosslolaprimerapartedenuestraaplicacinensulugar:Podemosrecibir
peticionesHTTP.Peronecesitamoshaceralgoconellasnecesitamos reaccionar
de manera diferente, dependiendo de que URL el browser requiera de nuestro
servidor.
Para una aplicacin muy simple, podras hacer esto directamente dentro de una
funcin de callback OnRequest(). Pero, como dije, agreguemos un poco ms de
abstraccin,demaneradehacernuestraaplicacinmsinteresante.
HacerdiferentespeticionesHTTPirapartesdiferentesdenuestrocdigosellama
"ruteo" (N. del T.: routing, en ingls) bueno, entonces, cremos un mdulo
llamadorouter.
Qusenecesitapara"rutear"peticiones?
NecesitamossercapacesdeentregarlaURLrequeridaylosposiblesparmetros
GET o POST adicionales a nuestro router, y basado en estos, el router debe ser
capazdedecidirqucdigoejecutar(este"cdigoaejecutar"eslatercerapartede
nuestra aplicacin: una coleccin de manipuladores de peticiones que harn el
verdaderotrabajocuandounapeticinesrecibida).
Asque,NecesitamosmirarenlaspeticionesHTTPyextraerlaURLrequerida,as
como los parmetros GET/POST de ellos. Se puede discutir acerca de si este
procedimientodebeserpartedelrouterodelservidor(osilohacemosunmdulo
por s mismo), pero hagamos el acuerdo de hacerlo parte de nuestro servidor
HTTPporahora.
Todalainformacinquenecesitamosestdisponibleenelobjetorequest,elque
es pasado como primer parmetro a nuestra funcin callback onRequest(). Pero
para interpretar esta informacin, necesitamos algunos mdulos adicionales
http://www.nodebeginner.org/indexes.html
15/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
Node.js,llamadosurlyquerystring.
El mdulo url provee mtodos que nos permite extraer las diferentes partes de
una URL (como por ejemplo la ruta requerida y el string de consulta), y
querystringpuede,encambio,serusadopara parsear el string de consulta para
losparmetrosrequeridos:
url.parse(string).query
|
url.parse(string).pathname|
||
||
http://localhost:8888/iniciar?foo=bar&hello=world
||
||
querystring(string)["foo"]|
|
querystring(string)["hello"]
varhttp=require("http");
varurl=require("url");
functioniniciar(){
functiononRequest(request,response){
varpathname=url.parse(request.url).pathname;
console.log("Peticinpara"+pathname+"recibida.");
response.writeHead(200,{"ContentType":"text/html"});
response.write("HolaMundo");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("ServidorIniciado.");
}
exports.iniciar=iniciar;
MuyBien.Nuestraaplicacinpuedeahoradistinguirpeticionesbasadasenlaruta
URLrequeridaestonospermitemapearpeticioneshacianuestromanipuladores
de peticiones, basndonos en la ruta URL usando nuestro (pronto a ser escrito)
router.Luego,podemos construir nuestra aplicacin en una forma REST (N. del
T.:RESTfulwayenIngls),yaqueahorapodemosimplementarunainterfazque
http://www.nodebeginner.org/indexes.html
16/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
sigue los principios que guan a la Identificacin de Recursos (ve por favor el
artculodeWikipediaacercadelaTransferenciadelEstadoRepresentacionalpara
informacindetrasfondo.
En el contexto de nuestra aplicacin, esto significa simplemente que seremos
capacesdetenerpeticionesparalasURLs/iniciary/subirmanejadasporpartes
diferentesdenuestrocdigo.Veremosprontocomotodoestoencaja.
OK,eshoradeescribirnuestrorouter.Vamosacrearunnuevoarchivollamado
router.js,conelsiguientecontenido:
functionroute(pathname){
console.log("Apuntoderutearunapeticionpara"+pathname);
}
exports.route=route;
Por supuesto, este cdigo no est haciendo nada, pero eso est bien por ahora.
Empecemosavercomovamosaencajaresterouterconnuestroservidorantesde
ponermslgicaenelrouter.
Nuestro servidor HTTP necesita saber y hacer uso de nuestro router. Podemos
escribir directamente esta dependencia a nuestro servidor, pero como hemos
aprendido de la manera difcil en nuestras experiencias, vamos a acoplar de
maneradbil(loosecouplingenIngls)al router y su servidor va inyeccin por
dependencia.Paraunareferenciadefondo,leerelArtculodeMartinFowler(en
Ingls).
Primeroextendamosnuestrafuncininiciar()demaneradepermitirnospasarla
funcinderuteoaserusadacomoparmetro:
varhttp=require("http");
varurl=require("url");
functioniniciar(route){
functiononRequest(request,response){
varpathname=url.parse(request.url).pathname;
console.log("Peticionpara"+pathname+"recibida.");
route(pathname);
response.writeHead(200,{"ContentType":"text/html"});
response.write("HolaMundo");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("ServidorIniciado.");
}
http://www.nodebeginner.org/indexes.html
17/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
exports.iniciar=iniciar;
Yextendamosnuestroindex.jsadecuadamente,estoes,inyectandolafuncinde
ruteodenuestrorouterenelservidor:
varserver=require("./server");
varrouter=require("./router");
server.iniciar(router.route);
Nuevamente,estamospasandounafuncincomoparmetros,peroestoyanoes
unanovedadparanosotros.
Siarrancamosnuestraaplicacinahora(nodeindex.jscomosiempre),yhacemos
unapeticinparaunaURL,puedesverahoraporlasrespuestasdela aplicacin
quenuestroservidorHTTPhaceusodenuestrorouteryleentregaelnombrede
rutarequerido:
bash$nodeindex.js
Peticinpara/foorecibida.
Apuntoderutearunapeticionpara/foo
Heomitidolamolestarespuestadelapeticinpara/favicon.ico
Ejecucinenelreinodelosverbos
Puedodivagarunvezmsporunmomentoyhablaracercadelaprogramacin
funcionaldenuevo?
Pasar funciones no es slo una consideracin tcnica. Con respecto al diseo de
software, esto es casi filosfico. Tan solo piensa en ello: en nuestro archivo de
index, podramos haber entregado el objeto router al servidor, y el servidor
hubiesellamadoalafuncinroutedeesteobjeto.
De esta manera, podramos haber pasado una cosa, y el servidor hubiese usado
esacosaparahaceralgo.Oye,"CosaRouter",Podrasporfavor rutear esto por
m?
Peroelservidornonecesitalacosa.Slonecesitahaceralgo,y para que algo se
http://www.nodebeginner.org/indexes.html
18/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
Ruteandoalosverdaderosmanipuladoresde
peticiones
Volviendo al tema. Nuestro servidor HTTP y nuestro router de peticiones son
ahoralosmejoresamigosyconversanentreellos,talycomopretendimos.
Por supuesto, esto no es suficiente, "Rutear" significa que nosotros queremos
manipularlaspeticionesadistintasURLsdemanera,diferente.Nosgustaratener
la "lgicas de negocios" para peticiones de /inicio manejadas en otra funcin,
distintaalaquemanejalaspeticionespara/subir.
Porahora,elruteo"termina"enelrouter,yelrouternoesellugardondeseest
"haciendo algo" con las peticiones, ya que esto no escalara bien una vez que
nuestraaplicacinsehagamscompleja.
Llamemos a estas funciones, donde las peticiones estn siendo ruteadas,
manipuladoresdepeticiones(requesthandlers).Yprocedamosconstosahora,
porque,amenosquenolostengamosensulugar,nohaytienemuchosentidoen
hacernadaconelrouterporahora.
Nuevapartedelaaplicacin,significanuevomdulonocreoquehayasorpresa
ac.CreemosunmdulollamadorequestHandlers(N.delT.:pormanipuladores
de peticin), agreguemos un funcin de ubicacin para cada manipulador de
peticin,yexportemosestoscomomtodosparaelmdulo:
functioniniciar(){
console.log("Manipuladordepeticin'iniciar'hasidollamado.");
}
functionsubir(){
console.log("Manipuladordepeticin'subir'hasidollamado.");
}
exports.iniciar=iniciar;
exports.subir=subir;
http://www.nodebeginner.org/indexes.html
19/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
Estonospermitiratarlosmanipuladoresdepeticinalrouter,dndoleanuestro
routeralgoquerutear.
Llegado a este punto, necesitamos tomar una decisin: Ingresaremos las rutas
del mdulo requestHandlers dentro del cdigo del router (hardcoding), o
queremosalgomsdedependenciaporinyeccin?Aunqueenladependenciapor
inyeccin, como cualquier otro patrn, no debera ser usada simplemente por
usarla, en este caso tiene sentido acoplar el router dbilmente a sus
manipuladores de peticin, as, de esta manera hacemos que el router sea
reutilizable.
Estosignificaquenecesitamospasarlosmanipuladoresdepeticindesdenuestro
serveralrouter,peroestosesienteequivocado,dadoque,Porqutenemosque
hacerelcaminolargoyentregarlosmanipuladoresdesde el archivo principal al
servidorydeahalrouter?
Cmo vamos a pasarlos? Ahora tenemos slo dos manipuladores, pero en una
aplicacinreal,estenmerosevaaincrementaryvariar,ynosotrosnoqueremos
estar a cada momento mapeando peticiones a manipuladores cada vez que una
nueva URL o manipulador de peticin sea agregado. Y si tenemos un cdigo del
tipoifpeticion==xthenllamamanipuladoryenelrouter,estosepondracada
vezmsfeo.
Unnmerovariabledetems,cadaunodeellosmapeadosaunstring?(en este
casolaURLrequerida)Bueno,estosuenacomoqueunarrayasociativoharael
truco.
Bueno,estedescubrimientoesobscurecidoporelhechoqueJavaScriptnoprovee
arrays asociativos o s? !Resulta que lo que necesitamos usar son objetos si
necesitamosunarrayasociativo!
Unabuenaintroduccinaestoest(enIngls)enhttp://msdn.microsoft.com/en
us/magazine/cc163419.aspx,Djamecitartelaparterelevante:
En C++ o C#, cuando hablamos acerca de objetos, nos estamos
refiriendo a instancias de clases de estructuras. Los objetos tienen
distintas propiedades y mtodos, dependiendo en las plantillas (esto
es,lasclases)desde donde stos sean instanciados. Este no es el caso
con los objetos de JavaScript. En JavaScript, los objetos son slo
colecciones de pares nombre/valor piensa en un objeto JavaScript
comoenundiccionarioconllavesdestring.
Si los objetos JavaScript son slo colecciones de pares nombre/valor, Cmo
http://www.nodebeginner.org/indexes.html
20/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
puedenentoncestenermtodos?Bueno,losvalorespuedenserstrings,nmeros,
etc...OFunciones!
OK,ahora,volviendofinalmentealcdigo.Hemosdecididoquequeremospasarla
lista de requestHandlers (manipuladores de peticin) como un objeto, y para
lograresteacoplamientodbil,necesitamosusarlatcnicadeinyectaresteobjeto
enlaroute()(ruta).
Empecemosconponerelobjetoennuestroarchivoprincipalindex.js:
varserver=require("./server");
varrouter=require("./router");
varrequestHandlers=require("./requestHandlers");
varhandle={}
handle["/"]=requestHandlers.iniciar;
handle["/iniciar"]=requestHandlers.iniciar;
handle["/subir"]=requestHandlers.subir;
server.iniciar(router.route,handle);
(N.delT.:SeOptapordejarlosverbosenIngls'route'pararuteary'handle'para
manipular).
Aunquehandleesmsuna"cosa"(unacoleccindemanipuladoresde peticin),
propongo que lo nombremos como un verbo, ya que esto resultar en una
expresinfluidaennuestrorouter,comoveremosacontinuacin:
Como puedez ver, es realmente simple mapear diferentes URLs al mismo
manipulador de peticiones: Mediante la adicin de un par llave/valor de "/" y
requestHandlers.iniciar,podemosexpresarenunaformaagradableylimpiaque
noslopeticionesa/start,sinoquetambinpeticionesa/puedensermanejadas
porelmanipuladorinicio.
Despusdedefinirnuestroobjeto,selopasamosalservidorcomounparmetro
adicional.Modifiquemosnuestroserver.jsparahacerusodeeste:
varhttp=require("http");
varurl=require("url");
functioniniciar(route,handle){
functiononRequest(request,response){
varpathname=url.parse(request.url).pathname;
console.log("Peticionpara"+pathname+"recibida.");
route(handle,pathname);
response.writeHead(200,{"ContentType":"text/html"});
http://www.nodebeginner.org/indexes.html
21/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
response.write("HolaMundo");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("ServidorIniciado.");
}
exports.iniciar=iniciar;
Loquehacemosaqu,eschequearsiunmanipuladordepeticionesparaunaruta
dada existe, y si es as, simplemente llamamos a la funcin adecuada. Dado que
podemos acceder a nuestras funciones manipuladoras de peticin desde nuestro
objetodelamismamaneraquehubisemospodidoaccederaunelementodeun
array asociativo, es que tenemos la expresin fluida handle[pathname]() de la
que habl antes, que en otras palabras es: "Por favor, handle (maneja) este(a)
pathname(ruta)".
Bien,Estoestodoloquenecesitamosparaatarservidor,routerymanipuladores
depeticionesjuntos!Unavezquearranquemosnuestraaplicacinyhagamosuna
peticinennuestrobrowserdehttp://localhost:8888/iniciar,vamosaprobarque
elmanipuladordepeticincorrectofue,dehecho,llamado:
ServidorIniciado.
Peticionpara/iniciarrecibida.
Apuntoderutearunapeticinpara/iniciar
Manipuladordepeticion'iniciar'hasidollamado.
HaciendoquelosManipuladoresdePeticiones
respondan
Muybien.Ahora,sitansololosmanipuladoresdepeticinpudieranenviaralgo
devueltaalbrowser,estoseramuchomejor,cierto?
Recuerda,queel"HolaMundo"quetubrowserdespliegaanteunapeticindeuna
pgina,anvienedesdelafuncinonRequestennuestroarchivoserver.js.
"Manipular Peticiones" no significa otra cosa que "Responder a las Peticiones"
despus de todo, as que necesitamos empoderar a nuestros manipuladores de
peticiones para hablar con el browser de la misma manera que la funcin
onRequestlohace.
http://www.nodebeginner.org/indexes.html
22/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
Cmonosedebehaceresto?
LaaproximacindirectaquenosotrosdesarrolladoresconuntrasfondoenPHP
oRubyquisieramosseguiresdehechoconducenteaerrores:trabajademanera
espectacularalprincipioyparecetenermuchosentido,y de pronto, las cosas se
arruinanenelmomentomenosesperado.
A lo que me refiero con "aproximacin directa" es esto: hacer que los
manipuladores de peticin retornen return() el contenido que ellos quieran
desplegar al usuario, y luego, enviar esta data de respuesta en la funcin
onRequestdevueltaalusuario.
Tanslohagamosesto,yluego,veamosporquestonoestanbuenaidea.
Empecemos con los manipuladores de peticin y hagmoslos retornar, lo que
nosotros queremos desplegar en el browser. Necesitamos modificar
requestHandlers.jsalosiguiente:
functioniniciar(){
console.log("Manipuladordepeticion'iniciar'fuellamado.");
return"HolaIniciar";
}
functionsubir(){
console.log("Manipuladordepeticion'subir'fuellamado.");
return"HolaSubir";
}
exports.iniciar=iniciar;
exports.subir=subir;
functionroute(handle,pathname){
console.log("Apuntoderutearunapeticionpara"+pathname);
if(typeofhandle[pathname]==='function'){
returnhandle[pathname]();
}else{
console.log("Noseencontromanipuladorpara"+pathname);
return"404NoEncontrado";
}
}
exports.route=route;
Comopuedesver,nosotrostambinretornaremosalgntextosilapeticinnoes
http://www.nodebeginner.org/indexes.html
23/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
ruteada.
Porltimo,peronomenosimportante,necesitamosrefactorizarnuestroservidor
para hacerlo responder al browser con el contenido que los manipuladores de
peticinleretornaronviaelrouter,transformandodeestamaneraaserver.jsen:
varhttp=require("http");
varurl=require("url");
functioniniciar(route,handle){
functiononRequest(request,response){
varpathname=url.parse(request.url).pathname;
console.log("Peticionpara"+pathname+"recibida.");
response.writeHead(200,{"ContentType":"text/html"});
varcontent=route(handle,pathname)
response.write(content);
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("ServidorIniciado.");
}
exports.iniciar=iniciar;
Sinosotrosarrancamosnuestraaplicacinreescrita,todovaafuncionaralasmil
maravillas:hacerleunapeticinahttp://localhost:8888/iniciar resulta en "Hola
Iniciar" siendo desplegado en el browser, hacerle una peticin a
http://localhost:8888/subir nos da "Hola Subir", y la peticin a
http://localhost:8888/fooproduce"404NoEncontrado".
OK,entoncesPorquestoesunproblema?Larespuestacortaes:debidoaquesi
uno de los manipuladores de peticin quisiera hacer uso de una operacin no
bloqueante (nonblocking) en el futuro, entonces esta configuracin, como la
tenemos,seraproblemtica.
Tommosnosalgntiempoparalarespuestalarga.
BloqueanteyNoBloqueante
Comosedijo,losproblemasvanasurgircuandonosotrosincluyamosoperaciones
nobloqueantes en los manipuladores de peticin. Pero hablemos acerca de las
operaciones bloqueantes primero, luego, acerca de las operaciones no
bloqueantes.
Y, en vez de intentar explicar que es lo que significa "bloqueante" y "no
http://www.nodebeginner.org/indexes.html
24/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
functioniniciar(){
console.log("Manipuladordepeticion'iniciar'fuellamado.");
functionsleep(milliSeconds){
//obtenlahoraactual
varstartTime=newDate().getTime();
//atascalacpu
while(newDate().getTime()<startTime+milliSeconds);
}
sleep(10000);
return"HolaIniciar";
}
functionsubir(){
console.log("Manipuladordepeticion'subir'fuellamado.");
return"HolaSubir";
}
exports.iniciar=iniciar;
exports.subir=subir;
Dejemos claros que es lo que esto hace: cuando la funcin iniciar() es llamada,
Node.js espera 10 segundos y slo ah retorna "Hola Iniciar". Cuando est
llamandoasubir(),retornainmediatamente,lamismamaneraqueantes.
(Porsupuestolaideaesqueteimaginesque,envezdedormirpor10segundos,
exista una operacin bloqueante verdadera en iniciar(), como algn tipo de
calculodelargoaliento.)
Vemosquesloqueestecambiohace.
Comosiempre,necesitamosreiniciarnuestroservidor.Estavez,tepidosigasun
"protocolo" un poco ms complejo de manera de ver que sucede: Primero, abre
dos ventanas de browser o tablas. En la primera ventana, por favor ingresa
http://localhost:8888/iniciar en la barra de direcciones, pero no abras an esta
url!
http://www.nodebeginner.org/indexes.html
25/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
nuestra
aplicacin
visitaremos
Loquecargaunabellapginaquedespliegaelstring"vacio".Quesloqueest
incorrectoac?
Bueno, como ya habrn adivinado, exec() hace su magia de una manera no
bloqueante. Buena cosa esto, porque de esta manera podemos ejecutar
operacionesdeshellmuycarasenejecucin(como,porejemplo,copiar archivos
enormes o cosas similares) sin tener que forzar a nuestra aplicacin a detenerse
http://www.nodebeginner.org/indexes.html
26/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
comolohizolaoperacinsleep.
(Si quieres probar esto, reemplaza "ls lah" con una operacin ms cara como
"find/").
Pero no estaramos muy felices si nuestra elegante aplicacin no bloqueante no
desplegaraalgnresultado,cierto?.
Bueno,entonces,arreglmosla.Ymientrasestamoseneso,tratemosdeentender
porqulaarquitecturaactualnofunciona.
Elproblemaesqueexec(),parapodertrabajardemaneranobloqueante,haceuso
deunafuncindecallback.
Ennuestroejemplo,esunafuncinannima,lacualespasadacomoelsegundo
parmetrodelallamadaalafuncinexec():
function(error,stdout,stderr){
content=stdout;
}
http://www.nodebeginner.org/indexes.html
27/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
RespondiendoalosManipuladoresdePeticincon
OperacionesNoBloqueantes
Acabo de usar la frase "la manera correcta". Cosa peligrosa. Frecuentemente, no
existeunanica"maneracorrecta".
Pero una posible solucin para esto, frecuente con Node.js es pasar funciones
alrededor.Examinemosesto.
Ahora mismo, nuestra aplicacin es capaz de transportar el contenido desde los
manipuladoresdepeticinalservidorHTTPretornndolohaciaarribaatravsde
lascapasdelaaplicacin(manipuladordepeticin>router>servidor).
Nuestro nuevo enfoque es como sigue: en vez de llevar el contenido al servidor,
llevaremoselservidoralcontenido.Parasermsprecisos,inyectaremoselobjeto
response(respuesta)(desdenuestrafuncindecallbackdeservidoronRequest())
a travs de nuestro router a los manipuladores de peticin. Los manipuladores
serncapacesdeusarlasfuncionesdeesteobjetopararesponderalaspeticiones
ellosmismos.
Suficientes explicaciones, aqu hay una receta paso a paso de como cambiar
nuestraaplicacin.
Empecemosconnuestroservidor,server.js:
varhttp=require("http");
varurl=require("url");
functioniniciar(route,handle){
functiononRequest(request,response){
varpathname=url.parse(request.url).pathname;
console.log("Peticionpara"+pathname+"recibida.");
route(handle,pathname,response);
}
http.createServer(onRequest).listen(8888);
console.log("ServidorIniciado.");
}
exports.iniciar=iniciar;
http://www.nodebeginner.org/indexes.html
28/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
functionroute(handle,pathname,response){
console.log("Apuntoderutearunapeticionpara"+pathname);
if(typeofhandle[pathname]==='function'){
handle[pathname](response);
}else{
console.log("Nohaymanipuladordepeticionpara"+pathname);
response.writeHead(404,{"ContentType":"text/html"});
response.write("404NoEncontrado");
response.end();
}
}
exports.route=route;
varexec=require("child_process").exec;
functioniniciar(response){
console.log("Manipuladordepeticin'iniciar'fuellamado.");
exec("lslah",function(error,stdout,stderr){
response.writeHead(200,{"ContentType":"text/html"});
response.write(stdout);
response.end();
});
}
functionsubir(response){
console.log("Manipuladordepeticin'subir'fuellamado.");
response.writeHead(200,{"ContentType":"text/html"});
response.write("HolaSubir");
response.end();
}
exports.iniciar=iniciar;
exports.subir=subir;
http://www.nodebeginner.org/indexes.html
29/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
varexec=require("child_process").exec;
functioniniciar(response){
console.log("Manipuladordepeticin'iniciar'fuellamado.");
exec("find/",
{timeout:10000,maxBuffer:20000*1024},
function(error,stdout,stderr){
response.writeHead(200,{"ContentType":"text/html"});
response.write(stdout);
response.end();
});
}
functionsubir(response){
console.log("Manipuladordepeticin'subir'fuellamado.");
response.writeHead(200,{"ContentType":"text/html"});
response.write("HolaSubir");
response.end();
}
exports.iniciar=iniciar;
exports.subir=subir;
Sirviendoalgotil
Hasta ahora, lo que hemos hecho es todo simptico y bonito, pero no hemos
creadoanvalorparalosclientesdenuestrositiowebganadordepremios.
Nuestro servidor, router y manipuladores de peticin estn en su lugar, as que
http://www.nodebeginner.org/indexes.html
30/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
ManejandoPeticionesPOST
Mantengamos esto ridculamente simple: presentaremos un rea de texto que
puedaserllenadaporelusuarioyluegoenviadaalservidorenunapeticinPOST.
Unavezrecibidaymanipuladaestapeticin,despliegaremoselcontenidodelrea
detexto.
ElHTMLparaelformulariodeestareadetextonecesitaserservidapornuestro
manipulador de peticin /iniciar, as que agregumoslo de inmediato. En el
archivorequestHandlers.js:
functioniniciar(response){
console.log("Manipuladordepeticiones'iniciar'fuellamado.");
varbody='<html>'+
'<head>'+
'<metahttpequiv="ContentType"content="text/html;
charset=UTF8"/>'+
'</head>'+
'<body>'+
'<formaction="/subir"method="post">'+
'<textareaname="text"rows="20"cols="60"></textarea>'+
'<inputtype="submit"value="Enviartexto"/>'+
'</form>'+
'</body>'+
'</html>';
http://www.nodebeginner.org/indexes.html
31/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
response.writeHead(200,{"ContentType":"text/html"});
response.write(body);
response.end();
}
functionsubir(response){
console.log("Manipuladordepeticiones'subir'fuellamado.");
response.writeHead(200,{"ContentType":"text/html"});
response.write("HolaSubir");
response.end();
}
exports.iniciar=iniciar;
exports.subir=subir;
32/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
Estobsicamenteluceas:
request.addListener("data",function(chunk){
//funcionllamadacuandounnuevotrozo(chunk)
//deinformacion(data)esrecibido.
});
request.addListener("end",function(){
//funcionllamadacuandotodoslostrozos(chunks)
//deinformacion(data)hansidorecibidos.
});
Lapreguntaquesurgeesdndeimplementarstalgica.Nosotrosslo podemos
accederalobjetorequestennuestroservidornoseloestamospasandoalrouter
oalosmanipuladoresdepeticin,comolohicimosconelobjetoresponse.
En mi opinin, es un trabajo del servidor HTTP de darle a la aplicacin toda la
informacin de una peticin que necesite para hacer su trabajo. Luego, sugiero
quemanejemoselprocesamientodelapeticindePOSTenelservidormismoy
pasemoslainformacinfinalalrouteryalosmanipuladoresdepeticin,losque
luegodecidirnquehacerconsta.
Entonces,laideaesponerloscallbacksdatayendenelservidor,recogiendotodo
lostrozosdeinformacinPOSTenelcallbackdata,yllamandoalrouterunavez
recibido el evento end, mientras le entregamos los trozos de informacin
recogidosalrouter,elqueasuvezselospasaalosmanipuladoresdepeticin.
Aquvamos,empezandoconserver.js:
varhttp=require("http");
varurl=require("url");
functioniniciar(route,handle){
functiononRequest(request,response){
vardataPosteada="";
varpathname=url.parse(request.url).pathname;
console.log("Peticionpara"+pathname+"recibida.");
request.setEncoding("utf8");
request.addListener("data",function(trozoPosteado){
dataPosteada+=trozoPosteado;
console.log("RecibidotrozoPOST'"+trozoPosteado+"'.");
});
request.addListener("end",function(){
route(handle,pathname,response,dataPosteada);
});
}
http.createServer(onRequest).listen(8888);
http://www.nodebeginner.org/indexes.html
33/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
console.log("ServidorIniciado");
}
exports.iniciar=iniciar;
Bsicamente hicimos tres cosas aqu: primero, definimos que esperamos que la
codificacin de la informacin recibida sea UTF8, agregamos un listener de
eventos para el evento "data" el cual llena paso a paso nuestra variable
dataPosteada cada vez que un nuevo trozo de informacin POST llega, y
movemoslallamadadesdenuestrorouteralcallbackdeleventoenddemanerade
asegurarnosquesloseallamadocuandotodalainformacinPOSTseareunida.
Adems,pasamoslainformacinPOSTalrouter,yaquelavamosanecesitaren
nuestrosmanipuladoresdeeventos.
Agregarunloggueodeconsolacadavezqueuntrozoesrecibidoesunamalaidea
para cdigo de produccin (megabytes de informacin POST, recuerdan?, pero
tienesentidoparaqueveamosquepasa.
Mejoremos nuestra aplicacin. En la pgina /subir, desplegaremos el contenido
recibido. Para hacer esto posible, necesitamos pasar la dataPosteada a los
manipuladoresdepeticin,enrouter.js.
functionroute(handle,pathname,response,postData){
console.log("Apuntoderutearunapeticionpara"+pathname);
if(typeofhandle[pathname]==='function'){
handle[pathname](response,postData);
}else{
console.log("Nosehaencontradomanipuladorpara"+pathname);
response.writeHead(404,{"ContentType":"text/html"});
response.write("404Noencontrado");
response.end();
}
}
exports.route=route;
functioniniciar(response,postData){
console.log("ManipuladordePeticion'iniciar'fuellamado.");
varbody='<html>'+
'<head>'+
'<metahttpequiv="ContentType"content="text/html;
charset=UTF8"/>'+
'</head>'+
'<body>'+
'<formaction="/subir"method="post">'+
http://www.nodebeginner.org/indexes.html
34/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
'<textareaname="text"rows="20"cols="60"></textarea>'+
'<inputtype="submit"value="Enviartexto"/>'+
'</form>'+
'</body>'+
'</html>';
response.writeHead(200,{"ContentType":"text/html"});
response.write(body);
response.end();
}
functionsubir(response,dataPosteada){
console.log("ManipuladordePeticion'subir'fuellamado.");
response.writeHead(200,{"ContentType":"text/html"});
response.write("Tuenviaste:"+dataPosteada);
response.end();
}
exports.iniciar=iniciar;
exports.subir=subir;
Eso es, ahora somos capaces de recibir informacin POST y usarla en nuestros
manipuladoresdepeticin.
Una ltima cosa para este tema: lo que le estamos pasando al router y los
manipuladores de peticin es el cuerpo (body) de nuestra peticin POST.
Probablementenecesitemosconsumirloscamposindividualesqueconformanla
informacinPOST,enestecaso,elvalordelcampotext.
Nosotros ya hemos ledo acerca del mdulo querystring, el que nos ayuda con
esto:
varquerystring=require("querystring");
functioniniciar(response,postData){
console.log("Manipuladordepeticion'inicio'fuellamado.");
varbody='<html>'+
'<head>'+
'<metahttpequiv="ContentType"content="text/html;
charset=UTF8"/>'+
'</head>'+
'<body>'+
'<formaction="/subir"method="post">'+
'<textareaname="text"rows="20"cols="60"></textarea>'+
'<inputtype="submit"value="Submittext"/>'+
'</form>'+
'</body>'+
'</html>';
response.writeHead(200,{"ContentType":"text/html"});
response.write(body);
response.end();
}
functionsubir(response,dataPosteada){
console.log("Manipuladordepeticion'subir'fuellamado.");
http://www.nodebeginner.org/indexes.html
35/36
31/8/2015
ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
response.writeHead(200,{"ContentType":"text/html"});
response.write("Tuenviasteeltexto::"+
querystring.parse(dataPosteada)["text"]);
response.end();
}
exports.iniciar=iniciar;
exports.subir=subir;
Bueno,parauntutorialdeprincipiantes,estoestodoloquediremosacercadela
informacinPOST.
La prxima vez, hablaremos sobre como usar el excelente mdulo node
formidable para permitirnos lograr nuestro caso de uso final: subir y desplegar
imgenes.
TheNodeBeginnerBookbyManuelKiessling(seeGoogle+profile)islicensedundera
CreativeCommonsAttributionNonCommercialShareAlike3.0UnportedLicense.
Permissionsbeyondthescopeofthislicensemaybeavailableatmanuel@kiessling.net.
http://www.nodebeginner.org/indexes.html
36/36