Académique Documents
Professionnel Documents
Culture Documents
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.
http://www.nodebeginner.org/indexes.html 1/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
Estructuradeestedocumento
AlTrminodeestedocumento,habrscreadounaaplicacinWebcompleta,que
permitaalosusuariosdestaelverpginaswebysubirarchivos.
EmpezaremospormirarcmoeldesarrolloenJavaScriptenNode.jsesdiferente
deldesarrolloenJavaScriptenunbrowser.
Luego,nosmantendremosconlaviejatradicindeescribirunaaplicacin"Hola
Mundo",lacualeslaaplicacinmsbsicadeNode.jsque"hace"algo.
TabladeContenidos
JavaScriptyNode.js
JavaScriptyT
Antesquehablemosdetodalapartetcnica,tommonosunminutoyhablemos
acerca de ti y tu relacin con JavaScript. Este captulo est aqu para permitirte
estimarsitienesentidoelquesigasonoleyendoestedocumento.
http://www.nodebeginner.org/indexes.html 2/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
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.
Lo que realmente quisiste era "la cosa real", queras saber cmo construir sitios
webcomplejosAprendisteunlenguajedeprogramacincomoPHP,Ruby,Java,
yempezasteaescribircdigo"backend".
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
aplicacionesNode.jsesunacosaEntenderelporquellasnecesitanserescritas
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.
Hay,porsupuesto,excelentedocumentacinafuera.Peroladocumentacinpors
solanoessuficiente.Loquesenecesitaesunagua.
http://www.nodebeginner.org/indexes.html 3/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
Miobjetivoesproveerteestagua.
UnaAdvertencia
HayalgunaspersonasrealmenteexcelenteenJavaScript.Nosoyunadeellas.
Yosoyrealmenteeltipodelquetehehabladoenlosprrafosprevios.Sunpar
de cosas acerca de desarrollar aplicaciones backend, pero an soy nuevo al
JavaScript "real" y an ms nuevo a Node.js. He aprendido solo recientemente
algunodelosaspectosavanzadosdeJavaScript.Nosoyexperimentado.
Porloqueestenoesunlibro"desdenoviciohastaexperto".Esteesmsbienun
libro"desdenovicioanovicioavanzado".
Si no fallo, entonces este ser el tipo de documento que deseo hubiese tenido
cuandoempecconNode.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.
Adems,Node.jsvieneconmuchosmdulostiles,demaneraquenotienes que
escribirtododecero,comoporejemplo,algoquepongaunstringalaconsola.
Entonces,Node.jsesenrealidaddoscosas:unentornodeejecucinyunalibrera.
http://www.nodebeginner.org/indexes.html 4/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
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".
console.log("HolaMundo");
Grabaelarchivo,yejectaloatravsdeNode.js:
nodeholamundo.js
EstedeberaretornarHolaMundoentumonitor.
Ok,estoesaburrido,deacuerdo?Asqueescribamosalgunacosareal.
UnaAplicacinWebCompletaconNode.js
LoscasosdeUso
Mantengmoslosimple,perorealista:
ElUsuariodeberasercapazdeocuparnuestraaplicacinconunbrowser.
ElUsuariodeberaverunapginadebienvenidacuandosolicita
http://dominio/inicio,lacualdespliegaunformulariodesubida.
Eligiendounarchivodeimagenparasubiryenviandoelformulario,la
imagendeberasersubidaahttp://dominio/subir,dondeesdesplegadauna
http://www.nodebeginner.org/indexes.html 5/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
vezquelasubidaestefinalizada.
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?
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.
Loque,asuvez,significaqueeltema"Necesitamossercapacesdeservirpginas
webyrecibirpeticionesHTTP"nisiquierasucededentrodePHPmismo.
Bueno,conNode.js,lascosassonunpocodistintas.PorqueconNode.js,nosolo
implementamos nuestra aplicacin, nosotros tambin implementamos todo el
servidorHTTPcompleto.Dehecho,nuestraaplicacinwebysuservidorwebson
bsicamentelomismo.
Esto puede sonar como mucho trabajo, pero veremos en un momento que con
Node.js,noloes.
Empecemosporelprincipioeimplementemoslaprimerapartedenuestrapila,el
servidorHTTP..
ConstruyendolaPiladeAplicaciones
UnServidorHTTPBsico
CuandollegualpuntodondequeraempezarconmiprimeraaplicacinNode.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.
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.
http://www.nodebeginner.org/indexes.html 7/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
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
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.
http://www.nodebeginner.org/indexes.html 8/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
Porfavorignoraporunsegundoaladefinicindefuncinquesiguealallavede
aperturadehttp.createServer.
Nosotrospodramoshaberescritoelcdigoqueiniciaanuestroservidorylohace
escucharalpuerto8888delasiguientemanera:
varhttp=require("http");
varserver=http.createServer();
server.listen(8888);
PasandoFuncionesdeunLadoaOtro
Puedes,porejemplo,haceralgocomoesto:
functiondecir(palabra){
console.log(palabra);
}
functionejecutar(algunaFuncion,valor){
algunaFuncion(valor);
}
ejecutar(decir,"Hola");
Leeestocuidadosamente!Loqueestamoshaciendoaques,nosotrospasamosla
funcin decir() como el primer parmetro de la funcin ejecutar. No el valor de
retornodedecir,sinoquedecir()misma!
http://www.nodebeginner.org/indexes.html 9/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
Por supuesto, dado que decir toma un parmetro, ejecutar puede pasar tal
parmetrocuandollamaaalgunaFuncion.
functionejecutar(algunaFuncion,valor){
algunaFuncion(valor);
}
ejecutar(function(palabra){console.log(palabra)},"Hola");
(N.delT.:functionesunapalabraclavedeJavaScript).
Nosotrosdefinimoslafuncinquequeremospasaraejecutarjustoahenellugar
dondeejecutaresperasuprimerparmetro.
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.
DeQumaneraelpasarfuncioneshaceque
nuestroservidorHTTPfuncione
Conesteconocimiento,VolvamosanuestroservidorHTTPminimalista:
http://www.nodebeginner.org/indexes.html 10/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
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 por qu esto tiene sentido para nosotros, que queremos escribir
aplicacioneswebenNode.js.
http://www.nodebeginner.org/indexes.html 11/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
Cuandonosotrosllamamosalmtodohttp.createServer,porsupuestoquenoslo
queremosqueelservidorsequedeescuchandoenalgnpuerto,sinoquetambin
queremoshaceralgocuandohayunapeticinHTTPaesteservidor.
Elproblemaes,queestosucededemaneraasncrona:Puedesucederencualquier
momento,perosolotenemosunnicoprocesoenelcualnuestroservidorcorre.
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
http://www.nodebeginner.org/indexes.html 12/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
peticinHTTPylafuncincallbackquepasamosnohasidollamada?Probemos:
varhttp=require("http");
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 asncrono y orientado al evento con
callbacksenaccin:)
ComonuestroServidormanipulalaspeticiones
OK, analicemos rpidamente el resto del cdigo de nuestro servidor, esto es, el
cuerpodenuestrafuncindecallbackonRequest().
http://www.nodebeginner.org/indexes.html 13/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
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
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
http://www.nodebeginner.org/indexes.html 14/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
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");
...
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.
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;
http://www.nodebeginner.org/indexes.html 15/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
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
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, creemos un mdulo
http://www.nodebeginner.org/indexes.html 16/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
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
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"]
http://www.nodebeginner.org/indexes.html 17/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
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
sigue los principios que guan a la Identificacin de Recursos (ve por favor el
artculodeWikipediaacercadelaTransferenciadelEstadoRepresentacionalpara
informacindetrasfondo.
OK,eshoradeescribirnuestrorouter.Vamosacrearunnuevoarchivollamado
router.js,conelsiguientecontenido:
functionroute(pathname){
console.log("Apuntoderutearunapeticionpara"+pathname);
}
http://www.nodebeginner.org/indexes.html 18/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
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.");
}
exports.iniciar=iniciar;
Yextendamosnuestroindex.jsadecuadamente,estoes,inyectandolafuncinde
ruteodenuestrorouterenelservidor:
varserver=require("./server");
varrouter=require("./router");
http://www.nodebeginner.org/indexes.html 19/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
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?
De esta manera, podramos haber pasado una cosa, y el servidor hubiese usado
esacosaparahaceralgo.Oye,"CosaRouter",Podrasporfavor rutear esto por
m?
http://www.nodebeginner.org/indexes.html 20/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
YloentendmientraslealaobramaestradeSteveYegge(enIngls)Ejecucinen
elReinodelosSustantivos.Anda,lela,porfavor.Esunodelosmejoresartculos
relacionadosconelsoftwarequehayatenidoelplacerdeencontrar.
Ruteandoalosverdaderosmanipuladoresde
peticiones
Volviendo al tema. Nuestro servidor HTTP y nuestro router de peticiones son
ahoralosmejoresamigosyconversanentreellos,talycomopretendimos.
Porahora,elruteo"termina"enelrouter,yelrouternoesellugardondeseest
"haciendo algo" con las peticiones, ya que esto no escalara bien una vez que
nuestraaplicacinsehagamscompleja.
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 21/40
24/2/2017 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.
Unnmerovariabledeitems,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:
http://www.nodebeginner.org/indexes.html 22/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
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:
Despusdedefinirnuestroobjeto,selopasamosalservidorcomounparmetro
adicional.Modifiquemosnuestroserver.jsparahacerusodeeste:
http://www.nodebeginner.org/indexes.html 23/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.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.writeHead(200,{"ContentType":"text/html"});
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
http://www.nodebeginner.org/indexes.html 24/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
Muybien.Ahora,sitansololosmanipuladoresdepeticinpudieranenviaralgo
devueltaalbrowser,estoseramuchomejor,cierto?
Recuerda,queel"HolaMundo"quetubrowserdespliegaanteunapeticindeuna
pgina,anvienedesdelafuncinonRequestennuestroarchivoserver.js.
Cmonosedebehaceresto?
LaaproximacindirectaquenosotrosdesarrolladoresconuntrasfondoenPHP
oRubyquisiramosseguiresdehechoconducenteaerrores:trabajademanera
espectacularalprincipioyparecetenermuchosentido,y de pronto, las cosas se
arruinanenelmomentomenosesperado.
Tanslohagamosesto,yluego,veamosporquestonoestanbuenaidea.
functioniniciar(){
console.log("Manipuladordepeticion'iniciar'fuellamado.");
return"HolaIniciar";
}
functionsubir(){
console.log("Manipuladordepeticion'subir'fuellamado.");
return"HolaSubir";
}
exports.iniciar=iniciar;
exports.subir=subir;
http://www.nodebeginner.org/indexes.html 25/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
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
ruteada.
Porltimo,peronomenosimportante,necesitamosrefactorizarnuestroservidor
para hacerlo responder al browser con el contenido que los manipuladores de
peticinleretornaronvaelrouter,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://www.nodebeginner.org/indexes.html 26/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
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.
Porfavor,modificarequestHandlers.jscomosigue:
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.");
http://www.nodebeginner.org/indexes.html 27/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
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.)
Veamosquesloqueestecambiohace.
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!
Loqueveremosserlosiguiente:LaURL/iniciotoma10segundosencargar,tal
cual esperamos. Pero la URL /subir tambin toma 10 segundos para cargar,
Aunque no hay definido un sleep() en el manipulador de peticiones
correspondiente!
Por qu? simple, porque inicio() contiene una operacin bloqueante. En otras
palabras"Estbloqueandoeltrabajodecualquierotracosa".
http://www.nodebeginner.org/indexes.html 28/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
Loqueexec()hace,esque,ejecutauncomandodeshelldesdedentrodeNode.js.
Enesteejemplo,vamosausarloparaobtenerunalistadetodoslosarchivosdel
directorioenquenosencontramos("lslah"),permitindonosdesplegarestalista
enelbrowserdeunusuarioqueestepeticionandolaURL/inicio.
Lo que el cdigo hace es claro: crea una nueva variable content() (con el valor
incialde"vaco"),ejecuta"lslah",llenalavariableconelresultado,yloretorna.
Loquecargaunabellapginaquedespliegaelstring"vaco".Quesloqueest
incorrectoac?
(Si quieres probar esto, reemplaza "ls lah" con una operacin ms cara como
"find/").
Bueno,entonces,arreglmosla.Ymientrasestamoseneso,tratemosdeentender
porqulaarquitecturaactualnofunciona.
Elproblemaesqueexec(),parapodertrabajardemaneranobloqueante,haceuso
deunafuncindecallback.
http://www.nodebeginner.org/indexes.html 29/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
Ennuestroejemplo,esunafuncinannima,lacualespasadacomoelsegundo
parmetrodelallamadaalafuncinexec():
function(error,stdout,stderr){
content=stdout;
}
Ahora, "ls lah" es una operacin sencilla y rpida (a menos, claro, que hayan
millones de archivos en el directorio). Por lo que es relativamente necesario
llamaralcallbackperodetodasmanerasestosucededemaneraasncrona.
Estosehacemsobvioaltratarconuncomandomscostoso:"find/"setomaun
minutoenmimaquina,perosireemplazo"lslah"con"find/"enelmanipulador
depeticiones,yoreciboinmediatamenteunarespuestaHTTPcuandoabrolaURL
/inicio est claro que exec() hace algo en el trasfondo, mientras que Node.js
mismocontinaconelflujodelaaplicacin,ypodemosasumirquelafuncinde
callbackqueleentregamosaexec()serllamadaslocuandoelcomando"find/"
hayaterminadodecorrer.
Pero,cmopodemosalcanzarnuestrameta,lademostrarlealusuariounalista
dearchivosdeldirectorioactual?
RespondiendoalosManipuladoresdePeticincon
OperacionesNoBloqueantes
Acabo de usar la frase "la manera correcta". Cosa peligrosa. Frecuentemente, no
existeunanica"maneracorrecta".
http://www.nodebeginner.org/indexes.html 30/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
Pero una posible solucin para esto, frecuente con Node.js es pasar funciones
alrededor.Examinemosesto.
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;
Ahoravienerouter.js:
http://www.nodebeginner.org/indexes.html 31/40
24/2/2017 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 32/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
directamente.
Siarrancamosnuestraaplicacindenuevo(nodeindex.js),estodeberafuncionar
deacuerdoaloesperado.
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.
http://www.nodebeginner.org/indexes.html 33/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
OK,vemoslopasoapaso,peroahora,conlamayoradelastcnicasyprincipios
deJavaScriptexplicadas,acelermoslounpocoalmismotiempo.
Aqu,pasoapasosignificaagrandesrasgosdospasos:vamosaverprimerocomo
manejar peticiones POST entrantes (pero no subidas de archivos), y en un
segundo paso, haremos uso de un modulo externo de Node.js para la
manipulacindesubidadearchivos.Heescogidoestealcancepordosrazones:
ManejandoPeticionesPOST
Mantengamos esto ridculamente simple: presentaremos un rea de texto que
puedaserllenadaporelusuarioyluegoenviadaalservidorenunapeticinPOST.
Unavezrecibidaymanipuladaestapeticin,desplegaremoselcontenidodelrea
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>'+
http://www.nodebeginner.org/indexes.html 34/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
'<body>'+
'<formaction="/subir"method="post">'+
'<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){
console.log("Manipuladordepeticiones'subir'fuellamado.");
response.writeHead(200,{"ContentType":"text/html"});
response.write("HolaSubir");
response.end();
}
exports.iniciar=iniciar;
exports.subir=subir;
Teestoyescuchando:tenercontenidodevistajustoenelmanipuladordepeticin
esfeo.Sinembargo,hedecididonoincluiresenivelextradeabstraccin(estoes,
separarlalgicadevistaycontrolador)enestetutorial,yaquepiensoqueesno
nosenseanadaquevalgalapenasaberenelcontextodeJavaScriptoNode.js.
Mejorusemoselespacioquequedaenpantallaparaunproblemamsinteresante,
estoes,manipularlapeticinPOSTquedarconnuestromanipuladordepeticin
/subircuandoelusuarioenveesteformulario.
Ahoraquenosestamosconvirtiendoen"noviciosexpertos",yanonossorprende
el hecho que manipular informacin de POST este hecho de una manera no
bloqueante,medianteelusodellamadasasncronas.
Loquetienesentido,yaquelaspeticionesPOSTpuedenserpotencialmentemuy
grandesnadadetienealusuariodeintroducirtextoquetengamuchosmegabytes
de tamao. Manipular este gran volumen de informacin de una vez puede
resultarenunaoperacinbloqueante.
http://www.nodebeginner.org/indexes.html 35/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
Parahacerelprocesocompletonobloqueante.Node.jsleentregaanuestrocdigo
la informacin POST en pequeos trozos con callbacks que son llamadas ante
determinados eventos. Estos eventos son data (un nuevo trozo de informacin
POSThallegado)yend(todoslostrozoshansidorecibidos).
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.
Entonces,laideaesponerloscallbacksdatayendenelservidor,recogiendotodo
lostrozosdeinformacinPOSTenelcallbackdata,yllamandoalrouterunavez
recibido el evento end, mientras le entregamos los trozos de informacin
recogidosalrouter,elqueasuvezselospasaalosmanipuladoresdepeticin.
Aquvamos,empezandoconserver.js:
http://www.nodebeginner.org/indexes.html 36/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.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);
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.
Agregarunlogueodeconsolacadavezqueuntrozoesrecibidoesunamalaidea
para cdigo de produccin (megabytes de informacin POST, recuerdan?, pero
tienesentidoparaqueveamosquepasa.
functionroute(handle,pathname,response,postData){
console.log("Apuntoderutearunapeticionpara"+pathname);
http://www.nodebeginner.org/indexes.html 37/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
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">'+
'<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.
http://www.nodebeginner.org/indexes.html 38/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
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.");
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.
http://www.nodebeginner.org/indexes.html 39/40
24/2/2017 ElLibroparaPrincipiantesenNode.jsUntutorialcompletodenode.js
TheNodeBeginnerBookbyManuelKiessling(seeGoogle+profile)islicensedundera
CreativeCommonsAttributionNonCommercialShareAlike3.0UnportedLicense.
Permissionsbeyondthescopeofthislicensemaybeavailableatmanuel@kiessling.net.
Imprint/Impressum/Datenschutz/Haftung
http://www.nodebeginner.org/indexes.html 40/40